1
0
Fork 0

New API security

This commit is contained in:
Julien Salleyron 2019-09-06 15:08:04 +02:00 committed by Traefiker Bot
parent 1959e1fd44
commit d044c0f4cc
90 changed files with 538 additions and 132 deletions

View file

@ -19,30 +19,30 @@ type chainBuilder interface {
}
// NewRouteAppenderAggregator Creates a new RouteAppenderAggregator
func NewRouteAppenderAggregator(ctx context.Context, chainBuilder chainBuilder, conf static.Configuration,
func NewRouteAppenderAggregator(ctx context.Context, conf static.Configuration,
entryPointName string, runtimeConfiguration *runtime.Configuration) *RouteAppenderAggregator {
aggregator := &RouteAppenderAggregator{}
if conf.Ping != nil && conf.Ping.EntryPoint == entryPointName {
aggregator.AddAppender(conf.Ping)
}
if conf.Metrics != nil && conf.Metrics.Prometheus != nil && conf.Metrics.Prometheus.EntryPoint == entryPointName {
aggregator.AddAppender(metrics.PrometheusHandler{})
}
if entryPointName != "traefik" {
return aggregator
}
if conf.Providers != nil && conf.Providers.Rest != nil {
if conf.Providers != nil && conf.Providers.Rest != nil && conf.Providers.Rest.Insecure {
aggregator.AddAppender(conf.Providers.Rest)
}
if conf.API != nil {
if conf.API != nil && conf.API.Insecure {
aggregator.AddAppender(api.New(conf, runtimeConfiguration))
}
if conf.Ping != nil {
aggregator.AddAppender(conf.Ping)
}
if conf.Metrics != nil && conf.Metrics.Prometheus != nil {
aggregator.AddAppender(metrics.PrometheusHandler{})
}
return aggregator
}

View file

@ -6,72 +6,23 @@ import (
"net/http/httptest"
"testing"
"github.com/containous/alice"
"github.com/containous/traefik/v2/pkg/config/static"
"github.com/containous/traefik/v2/pkg/ping"
"github.com/gorilla/mux"
"github.com/stretchr/testify/assert"
)
type ChainBuilderMock struct {
middles map[string]alice.Constructor
}
func (c *ChainBuilderMock) BuildChain(ctx context.Context, middles []string) *alice.Chain {
chain := alice.New()
for _, mName := range middles {
if constructor, ok := c.middles[mName]; ok {
chain = chain.Append(constructor)
}
}
return &chain
}
func TestNewRouteAppenderAggregator(t *testing.T) {
t.Skip("Waiting for new api handler implementation")
testCases := []struct {
desc string
staticConf static.Configuration
middles map[string]alice.Constructor
expected map[string]int
}{
{
desc: "API with auth, ping without auth",
desc: "Secure API",
staticConf: static.Configuration{
Global: &static.Global{},
API: &static.API{
// EntryPoint: "traefik",
// Middlewares: []string{"dumb"},
},
Ping: &ping.Handler{
// EntryPoint: "traefik",
},
EntryPoints: static.EntryPoints{
"traefik": {},
},
},
middles: map[string]alice.Constructor{
"dumb": func(_ http.Handler) (http.Handler, error) {
return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
}), nil
},
},
expected: map[string]int{
"/wrong": http.StatusBadGateway,
"/ping": http.StatusOK,
// "/.well-known/acme-challenge/token": http.StatusNotFound, // FIXME
"/api/rawdata": http.StatusUnauthorized,
},
},
{
desc: "Wrong entrypoint name",
staticConf: static.Configuration{
Global: &static.Global{},
API: &static.API{
// EntryPoint: "no",
API: &static.API{
Insecure: false,
},
EntryPoints: static.EntryPoints{
"traefik": {},
@ -81,6 +32,21 @@ func TestNewRouteAppenderAggregator(t *testing.T) {
"/api/providers": http.StatusBadGateway,
},
},
{
desc: "Insecure API",
staticConf: static.Configuration{
Global: &static.Global{},
API: &static.API{
Insecure: true,
},
EntryPoints: static.EntryPoints{
"traefik": {},
},
},
expected: map[string]int{
"/api/rawdata": http.StatusOK,
},
},
}
for _, test := range testCases {
@ -88,11 +54,9 @@ func TestNewRouteAppenderAggregator(t *testing.T) {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
chainBuilder := &ChainBuilderMock{middles: test.middles}
ctx := context.Background()
router := NewRouteAppenderAggregator(ctx, chainBuilder, test.staticConf, "traefik", nil)
router := NewRouteAppenderAggregator(ctx, test.staticConf, "traefik", nil)
internalMuxRouter := mux.NewRouter()
router.Append(internalMuxRouter)

View file

@ -6,7 +6,6 @@ import (
"github.com/containous/traefik/v2/pkg/config/runtime"
"github.com/containous/traefik/v2/pkg/config/static"
"github.com/containous/traefik/v2/pkg/provider/acme"
"github.com/containous/traefik/v2/pkg/server/middleware"
"github.com/containous/traefik/v2/pkg/types"
)
@ -27,8 +26,8 @@ type RouteAppenderFactory struct {
}
// NewAppender Creates a new RouteAppender
func (r *RouteAppenderFactory) NewAppender(ctx context.Context, middlewaresBuilder *middleware.Builder, runtimeConfiguration *runtime.Configuration) types.RouteAppender {
aggregator := NewRouteAppenderAggregator(ctx, middlewaresBuilder, r.staticConfiguration, r.entryPointName, runtimeConfiguration)
func (r *RouteAppenderFactory) NewAppender(ctx context.Context, runtimeConfiguration *runtime.Configuration) types.RouteAppender {
aggregator := NewRouteAppenderAggregator(ctx, r.staticConfiguration, r.entryPointName, runtimeConfiguration)
for _, p := range r.acmeProvider {
if p != nil && p.HTTPChallenge != nil && p.HTTPChallenge.EntryPoint == r.entryPointName {

View file

@ -306,7 +306,7 @@ func TestRouterManager_Get(t *testing.T) {
Middlewares: test.middlewaresConfig,
},
})
serviceManager := service.NewManager(rtConf.Services, http.DefaultTransport, nil, nil)
serviceManager := service.NewManager(rtConf.Services, http.DefaultTransport, nil, nil, nil, nil)
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager)
responseModifierFactory := responsemodifiers.NewBuilder(rtConf.Middlewares)
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, responseModifierFactory)
@ -407,7 +407,7 @@ func TestAccessLog(t *testing.T) {
Middlewares: test.middlewaresConfig,
},
})
serviceManager := service.NewManager(rtConf.Services, http.DefaultTransport, nil, nil)
serviceManager := service.NewManager(rtConf.Services, http.DefaultTransport, nil, nil, nil, nil)
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager)
responseModifierFactory := responsemodifiers.NewBuilder(rtConf.Middlewares)
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, responseModifierFactory)
@ -693,7 +693,7 @@ func TestRuntimeConfiguration(t *testing.T) {
Middlewares: test.middlewareConfig,
},
})
serviceManager := service.NewManager(rtConf.Services, http.DefaultTransport, nil, nil)
serviceManager := service.NewManager(rtConf.Services, http.DefaultTransport, nil, nil, nil, nil)
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager)
responseModifierFactory := responsemodifiers.NewBuilder(map[string]*runtime.MiddlewareInfo{})
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, responseModifierFactory)
@ -767,7 +767,7 @@ func BenchmarkRouterServe(b *testing.B) {
Middlewares: map[string]*dynamic.Middleware{},
},
})
serviceManager := service.NewManager(rtConf.Services, &staticTransport{res}, nil, nil)
serviceManager := service.NewManager(rtConf.Services, &staticTransport{res}, nil, nil, nil, nil)
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager)
responseModifierFactory := responsemodifiers.NewBuilder(rtConf.Middlewares)
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, responseModifierFactory)
@ -808,7 +808,7 @@ func BenchmarkService(b *testing.B) {
Services: serviceConfig,
},
})
serviceManager := service.NewManager(rtConf.Services, &staticTransport{res}, nil, nil)
serviceManager := service.NewManager(rtConf.Services, &staticTransport{res}, nil, nil, nil, nil)
w := httptest.NewRecorder()
req := testhelpers.MustNewRequest(http.MethodGet, "http://foo.bar/", nil)

View file

@ -9,6 +9,7 @@ import (
"sync"
"time"
"github.com/containous/traefik/v2/pkg/api"
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/config/runtime"
"github.com/containous/traefik/v2/pkg/config/static"
@ -18,7 +19,6 @@ import (
"github.com/containous/traefik/v2/pkg/middlewares/requestdecorator"
"github.com/containous/traefik/v2/pkg/provider"
"github.com/containous/traefik/v2/pkg/safe"
"github.com/containous/traefik/v2/pkg/server/middleware"
"github.com/containous/traefik/v2/pkg/tls"
"github.com/containous/traefik/v2/pkg/tracing"
"github.com/containous/traefik/v2/pkg/tracing/jaeger"
@ -44,11 +44,13 @@ type Server struct {
requestDecorator *requestdecorator.RequestDecorator
providersThrottleDuration time.Duration
tlsManager *tls.Manager
api func(configuration *runtime.Configuration) http.Handler
restHandler http.Handler
}
// RouteAppenderFactory the route appender factory interface
type RouteAppenderFactory interface {
NewAppender(ctx context.Context, middlewaresBuilder *middleware.Builder, runtimeConfiguration *runtime.Configuration) types.RouteAppender
NewAppender(ctx context.Context, runtimeConfiguration *runtime.Configuration) types.RouteAppender
}
func setupTracing(conf *static.Tracing) tracing.Backend {
@ -103,6 +105,14 @@ func setupTracing(conf *static.Tracing) tracing.Backend {
func NewServer(staticConfiguration static.Configuration, provider provider.Provider, entryPoints TCPEntryPoints, tlsManager *tls.Manager) *Server {
server := &Server{}
if staticConfiguration.API != nil {
server.api = api.NewBuilder(staticConfiguration)
}
if staticConfiguration.Providers != nil && staticConfiguration.Providers.Rest != nil {
server.restHandler = staticConfiguration.Providers.Rest.Handler()
}
server.provider = provider
server.entryPointsTCP = entryPoints
server.configurationChan = make(chan dynamic.Message, 100)

View file

@ -97,7 +97,12 @@ func (s *Server) createTCPRouters(ctx context.Context, configuration *runtime.Co
// createHTTPHandlers returns, for the given configuration and entryPoints, the HTTP handlers for non-TLS connections, and for the TLS ones. the given configuration must not be nil. its fields will get mutated.
func (s *Server) createHTTPHandlers(ctx context.Context, configuration *runtime.Configuration, entryPoints []string) (map[string]http.Handler, map[string]http.Handler) {
serviceManager := service.NewManager(configuration.Services, s.defaultRoundTripper, s.metricsRegistry, s.routinesPool)
var apiHandler http.Handler
if s.api != nil {
apiHandler = s.api(configuration)
}
serviceManager := service.NewManager(configuration.Services, s.defaultRoundTripper, s.metricsRegistry, s.routinesPool, apiHandler, s.restHandler)
middlewaresBuilder := middleware.NewBuilder(configuration.Middlewares, serviceManager)
responseModifierFactory := responsemodifiers.NewBuilder(configuration.Middlewares)
routerManager := router.NewManager(configuration, serviceManager, middlewaresBuilder, responseModifierFactory)
@ -114,7 +119,7 @@ func (s *Server) createHTTPHandlers(ctx context.Context, configuration *runtime.
factory := s.entryPointsTCP[entryPointName].RouteAppenderFactory
if factory != nil {
// FIXME remove currentConfigurations
appender := factory.NewAppender(ctx, middlewaresBuilder, configuration)
appender := factory.NewAppender(ctx, configuration)
appender.Append(internalMuxRouter)
}

View file

@ -34,7 +34,7 @@ const (
)
// NewManager creates a new Manager
func NewManager(configs map[string]*runtime.ServiceInfo, defaultRoundTripper http.RoundTripper, metricsRegistry metrics.Registry, routinePool *safe.Pool) *Manager {
func NewManager(configs map[string]*runtime.ServiceInfo, defaultRoundTripper http.RoundTripper, metricsRegistry metrics.Registry, routinePool *safe.Pool, api http.Handler, rest http.Handler) *Manager {
return &Manager{
routinePool: routinePool,
metricsRegistry: metricsRegistry,
@ -42,6 +42,8 @@ func NewManager(configs map[string]*runtime.ServiceInfo, defaultRoundTripper htt
defaultRoundTripper: defaultRoundTripper,
balancers: make(map[string][]healthcheck.BalancerHandler),
configs: configs,
api: api,
rest: rest,
}
}
@ -53,10 +55,26 @@ type Manager struct {
defaultRoundTripper http.RoundTripper
balancers map[string][]healthcheck.BalancerHandler
configs map[string]*runtime.ServiceInfo
api http.Handler
rest http.Handler
}
// BuildHTTP Creates a http.Handler for a service configuration.
func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string, responseModifier func(*http.Response) error) (http.Handler, error) {
if serviceName == "api@internal" {
if m.api == nil {
return nil, errors.New("api is not enabled")
}
return m.api, nil
}
if serviceName == "rest@internal" {
if m.rest == nil {
return nil, errors.New("rest is not enabled")
}
return m.rest, nil
}
ctx := log.With(rootCtx, log.Str(log.ServiceName, serviceName))
serviceName = internal.GetQualifiedName(ctx, serviceName)

View file

@ -80,7 +80,7 @@ func TestGetLoadBalancer(t *testing.T) {
}
func TestGetLoadBalancerServiceHandler(t *testing.T) {
sm := NewManager(nil, http.DefaultTransport, nil, nil)
sm := NewManager(nil, http.DefaultTransport, nil, nil, nil, nil)
server1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-From", "first")
@ -332,7 +332,7 @@ func TestManager_Build(t *testing.T) {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
manager := NewManager(test.configs, http.DefaultTransport, nil, nil)
manager := NewManager(test.configs, http.DefaultTransport, nil, nil, nil, nil)
ctx := context.Background()
if len(test.providerName) > 0 {
@ -353,7 +353,7 @@ func TestMultipleTypeOnBuildHTTP(t *testing.T) {
Weighted: &dynamic.WeightedRoundRobin{},
},
},
}, http.DefaultTransport, nil, nil)
}, http.DefaultTransport, nil, nil, nil, nil)
_, err := manager.BuildHTTP(context.Background(), "test@file", nil)
assert.Error(t, err, "cannot create service: multi-types service not supported, consider declaring two different pieces of service instead")