1
0
Fork 0

Add TCP Middlewares support

This commit is contained in:
Romain 2021-06-11 15:30:05 +02:00 committed by GitHub
parent 679def0151
commit fc9f41b955
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
134 changed files with 5865 additions and 1852 deletions

View file

@ -18,8 +18,9 @@ func mergeConfiguration(configurations dynamic.Configurations, defaultEntryPoint
ServersTransports: make(map[string]*dynamic.ServersTransport),
},
TCP: &dynamic.TCPConfiguration{
Routers: make(map[string]*dynamic.TCPRouter),
Services: make(map[string]*dynamic.TCPService),
Routers: make(map[string]*dynamic.TCPRouter),
Services: make(map[string]*dynamic.TCPService),
Middlewares: make(map[string]*dynamic.TCPMiddleware),
},
UDP: &dynamic.UDPConfiguration{
Routers: make(map[string]*dynamic.UDPRouter),
@ -63,6 +64,9 @@ func mergeConfiguration(configurations dynamic.Configurations, defaultEntryPoint
for routerName, router := range configuration.TCP.Routers {
conf.TCP.Routers[provider.MakeQualifiedName(pvd, routerName)] = router
}
for middlewareName, middleware := range configuration.TCP.Middlewares {
conf.TCP.Middlewares[provider.MakeQualifiedName(pvd, middlewareName)] = middleware
}
for serviceName, service := range configuration.TCP.Services {
conf.TCP.Services[provider.MakeQualifiedName(pvd, serviceName)] = service
}

View file

@ -253,7 +253,7 @@ func isEmptyConfiguration(conf *dynamic.Configuration) bool {
httpEmpty := conf.HTTP.Routers == nil && conf.HTTP.Services == nil && conf.HTTP.Middlewares == nil
tlsEmpty := conf.TLS == nil || conf.TLS.Certificates == nil && conf.TLS.Stores == nil && conf.TLS.Options == nil
tcpEmpty := conf.TCP.Routers == nil && conf.TCP.Services == nil
tcpEmpty := conf.TCP.Routers == nil && conf.TCP.Services == nil && conf.TCP.Middlewares == nil
udpEmpty := conf.UDP.Routers == nil && conf.UDP.Services == nil
return httpEmpty && tlsEmpty && tcpEmpty && udpEmpty

View file

@ -70,8 +70,9 @@ func TestNewConfigurationWatcher(t *testing.T) {
th.WithLoadBalancerServices(),
),
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Services: map[string]*dynamic.TCPService{},
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{
@ -225,8 +226,9 @@ func TestListenProvidersDoesNotSkipFlappingConfiguration(t *testing.T) {
th.WithMiddlewares(),
),
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Services: map[string]*dynamic.TCPService{},
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
@ -284,8 +286,9 @@ func TestListenProvidersPublishesConfigForEachProvider(t *testing.T) {
th.WithMiddlewares(),
),
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Services: map[string]*dynamic.TCPService{},
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{

View file

@ -0,0 +1,110 @@
package tcpmiddleware
import (
"context"
"fmt"
"strings"
"github.com/traefik/traefik/v2/pkg/config/runtime"
ipwhitelist "github.com/traefik/traefik/v2/pkg/middlewares/tcp/ipwhitelist"
"github.com/traefik/traefik/v2/pkg/server/provider"
"github.com/traefik/traefik/v2/pkg/tcp"
)
type middlewareStackType int
const (
middlewareStackKey middlewareStackType = iota
)
// Builder the middleware builder.
type Builder struct {
configs map[string]*runtime.TCPMiddlewareInfo
}
// NewBuilder creates a new Builder.
func NewBuilder(configs map[string]*runtime.TCPMiddlewareInfo) *Builder {
return &Builder{configs: configs}
}
// BuildChain creates a middleware chain.
func (b *Builder) BuildChain(ctx context.Context, middlewares []string) *tcp.Chain {
chain := tcp.NewChain()
for _, name := range middlewares {
middlewareName := provider.GetQualifiedName(ctx, name)
chain = chain.Append(func(next tcp.Handler) (tcp.Handler, error) {
constructorContext := provider.AddInContext(ctx, middlewareName)
if midInf, ok := b.configs[middlewareName]; !ok || midInf.TCPMiddleware == nil {
return nil, fmt.Errorf("middleware %q does not exist", middlewareName)
}
var err error
if constructorContext, err = checkRecursion(constructorContext, middlewareName); err != nil {
b.configs[middlewareName].AddError(err, true)
return nil, err
}
constructor, err := b.buildConstructor(constructorContext, middlewareName)
if err != nil {
b.configs[middlewareName].AddError(err, true)
return nil, err
}
handler, err := constructor(next)
if err != nil {
b.configs[middlewareName].AddError(err, true)
return nil, err
}
return handler, nil
})
}
return &chain
}
func checkRecursion(ctx context.Context, middlewareName string) (context.Context, error) {
currentStack, ok := ctx.Value(middlewareStackKey).([]string)
if !ok {
currentStack = []string{}
}
if inSlice(middlewareName, currentStack) {
return ctx, fmt.Errorf("could not instantiate middleware %s: recursion detected in %s", middlewareName, strings.Join(append(currentStack, middlewareName), "->"))
}
return context.WithValue(ctx, middlewareStackKey, append(currentStack, middlewareName)), nil
}
func (b *Builder) buildConstructor(ctx context.Context, middlewareName string) (tcp.Constructor, error) {
config := b.configs[middlewareName]
if config == nil || config.TCPMiddleware == nil {
return nil, fmt.Errorf("invalid middleware %q configuration", middlewareName)
}
var middleware tcp.Constructor
// IPWhiteList
if config.IPWhiteList != nil {
middleware = func(next tcp.Handler) (tcp.Handler, error) {
return ipwhitelist.New(ctx, next, *config.IPWhiteList, middlewareName)
}
}
if middleware == nil {
return nil, fmt.Errorf("invalid middleware %q configuration: invalid middleware type or middleware does not exist", middlewareName)
}
return middleware, nil
}
func inSlice(element string, stack []string) bool {
for _, value := range stack {
if value == element {
return true
}
}
return false
}

View file

@ -23,29 +23,36 @@ const (
defaultTLSStoreName = "default"
)
type middlewareBuilder interface {
BuildChain(ctx context.Context, names []string) *tcp.Chain
}
// NewManager Creates a new Manager.
func NewManager(conf *runtime.Configuration,
serviceManager *tcpservice.Manager,
middlewaresBuilder middlewareBuilder,
httpHandlers map[string]http.Handler,
httpsHandlers map[string]http.Handler,
tlsManager *traefiktls.Manager,
) *Manager {
return &Manager{
serviceManager: serviceManager,
httpHandlers: httpHandlers,
httpsHandlers: httpsHandlers,
tlsManager: tlsManager,
conf: conf,
serviceManager: serviceManager,
middlewaresBuilder: middlewaresBuilder,
httpHandlers: httpHandlers,
httpsHandlers: httpsHandlers,
tlsManager: tlsManager,
conf: conf,
}
}
// Manager is a route/router manager.
type Manager struct {
serviceManager *tcpservice.Manager
httpHandlers map[string]http.Handler
httpsHandlers map[string]http.Handler
tlsManager *traefiktls.Manager
conf *runtime.Configuration
serviceManager *tcpservice.Manager
middlewaresBuilder middlewareBuilder
httpHandlers map[string]http.Handler
httpsHandlers map[string]http.Handler
tlsManager *traefiktls.Manager
conf *runtime.Configuration
}
func (m *Manager) getTCPRouters(ctx context.Context, entryPoints []string) map[string]map[string]*runtime.TCPRouterInfo {
@ -239,7 +246,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
continue
}
handler, err := m.serviceManager.BuildTCP(ctxRouter, routerConfig.Service)
handler, err := m.buildTCPHandler(ctxRouter, routerConfig)
if err != nil {
routerConfig.AddError(err, true)
logger.Error(err)
@ -299,6 +306,27 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
return router, nil
}
func (m *Manager) buildTCPHandler(ctx context.Context, router *runtime.TCPRouterInfo) (tcp.Handler, error) {
var qualifiedNames []string
for _, name := range router.Middlewares {
qualifiedNames = append(qualifiedNames, provider.GetQualifiedName(ctx, name))
}
router.Middlewares = qualifiedNames
if router.Service == "" {
return nil, errors.New("the service is missing on the router")
}
sHandler, err := m.serviceManager.BuildTCP(ctx, router.Service)
if err != nil {
return nil, err
}
mHandler := m.middlewaresBuilder.BuildChain(ctx, router.Middlewares)
return tcp.NewChain().Extend(*mHandler).Then(sHandler)
}
func findTLSOptionName(tlsOptionsForHost map[string]string, host string) string {
tlsOptions, ok := tlsOptionsForHost[host]
if ok {

View file

@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/traefik/traefik/v2/pkg/config/dynamic"
"github.com/traefik/traefik/v2/pkg/config/runtime"
tcpmiddleware "github.com/traefik/traefik/v2/pkg/server/middleware/tcp"
"github.com/traefik/traefik/v2/pkg/server/service/tcp"
traefiktls "github.com/traefik/traefik/v2/pkg/tls"
)
@ -302,7 +303,9 @@ func TestRuntimeConfiguration(t *testing.T) {
},
[]*traefiktls.CertAndStores{})
routerManager := NewManager(conf, serviceManager,
middlewaresBuilder := tcpmiddleware.NewBuilder(conf.TCPMiddlewares)
routerManager := NewManager(conf, serviceManager, middlewaresBuilder,
nil, nil, tlsManager)
_ = routerManager.BuildHandlers(context.Background(), entryPoints)
@ -532,7 +535,9 @@ func TestDomainFronting(t *testing.T) {
"web": http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {}),
}
routerManager := NewManager(conf, serviceManager, nil, httpsHandler, tlsManager)
middlewaresBuilder := tcpmiddleware.NewBuilder(conf.TCPMiddlewares)
routerManager := NewManager(conf, serviceManager, middlewaresBuilder, nil, httpsHandler, tlsManager)
routers := routerManager.BuildHandlers(context.Background(), entryPoints)

View file

@ -8,6 +8,7 @@ import (
"github.com/traefik/traefik/v2/pkg/log"
"github.com/traefik/traefik/v2/pkg/metrics"
"github.com/traefik/traefik/v2/pkg/server/middleware"
middlewaretcp "github.com/traefik/traefik/v2/pkg/server/middleware/tcp"
"github.com/traefik/traefik/v2/pkg/server/router"
routertcp "github.com/traefik/traefik/v2/pkg/server/router/tcp"
routerudp "github.com/traefik/traefik/v2/pkg/server/router/udp"
@ -81,7 +82,9 @@ func (f *RouterFactory) CreateRouters(rtConf *runtime.Configuration) (map[string
// TCP
svcTCPManager := tcp.NewManager(rtConf)
rtTCPManager := routertcp.NewManager(rtConf, svcTCPManager, handlersNonTLS, handlersTLS, f.tlsManager)
middlewaresTCPBuilder := middlewaretcp.NewBuilder(rtConf.TCPMiddlewares)
rtTCPManager := routertcp.NewManager(rtConf, svcTCPManager, middlewaresTCPBuilder, handlersNonTLS, handlersTLS, f.tlsManager)
routersTCP := rtTCPManager.BuildHandlers(ctx, f.entryPointsTCP)
// UDP