343 lines
12 KiB
Go
343 lines
12 KiB
Go
package server
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/containous/traefik/configuration"
|
|
"github.com/containous/traefik/log"
|
|
"github.com/containous/traefik/middlewares"
|
|
"github.com/containous/traefik/middlewares/accesslog"
|
|
mauth "github.com/containous/traefik/middlewares/auth"
|
|
"github.com/containous/traefik/middlewares/errorpages"
|
|
"github.com/containous/traefik/middlewares/redirect"
|
|
"github.com/containous/traefik/types"
|
|
thoas_stats "github.com/thoas/stats"
|
|
"github.com/unrolled/secure"
|
|
"github.com/urfave/negroni"
|
|
)
|
|
|
|
type handlerPostConfig func(backendsHandlers map[string]http.Handler) error
|
|
|
|
type modifyResponse func(*http.Response) error
|
|
|
|
func (s *Server) buildMiddlewares(frontendName string, frontend *types.Frontend,
|
|
backends map[string]*types.Backend,
|
|
entryPointName string, entryPoint *configuration.EntryPoint,
|
|
providerName string) ([]negroni.Handler, modifyResponse, handlerPostConfig, error) {
|
|
|
|
var middle []negroni.Handler
|
|
var postConfig handlerPostConfig
|
|
|
|
// Error pages
|
|
if len(frontend.Errors) > 0 {
|
|
handlers, err := buildErrorPagesMiddleware(frontendName, frontend, backends, entryPointName, providerName)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
|
|
postConfig = errorPagesPostConfig(handlers)
|
|
|
|
for _, handler := range handlers {
|
|
middle = append(middle, handler)
|
|
}
|
|
}
|
|
|
|
// Metrics
|
|
if s.metricsRegistry.IsEnabled() {
|
|
handler := middlewares.NewBackendMetricsMiddleware(s.metricsRegistry, frontend.Backend)
|
|
middle = append(middle, handler)
|
|
}
|
|
|
|
// Whitelist
|
|
ipWhitelistMiddleware, err := buildIPWhiteLister(frontend.WhiteList, frontend.WhitelistSourceRange)
|
|
if err != nil {
|
|
return nil, nil, nil, fmt.Errorf("error creating IP Whitelister: %s", err)
|
|
}
|
|
if ipWhitelistMiddleware != nil {
|
|
if frontend.WhiteList != nil {
|
|
log.Debugf("Configured IP Whitelists: %v", frontend.WhiteList.SourceRange)
|
|
} else {
|
|
log.Debugf("Configured IP Whitelists: %v", frontend.WhitelistSourceRange)
|
|
}
|
|
|
|
handler := s.tracingMiddleware.NewNegroniHandlerWrapper(
|
|
"IP whitelist",
|
|
s.wrapNegroniHandlerWithAccessLog(ipWhitelistMiddleware, fmt.Sprintf("ipwhitelister for %s", frontendName)),
|
|
false)
|
|
middle = append(middle, handler)
|
|
}
|
|
|
|
// Redirect
|
|
if frontend.Redirect != nil && entryPointName != frontend.Redirect.EntryPoint {
|
|
rewrite, err := s.buildRedirectHandler(entryPointName, frontend.Redirect)
|
|
if err != nil {
|
|
return nil, nil, nil, fmt.Errorf("error creating Frontend Redirect: %v", err)
|
|
}
|
|
|
|
handler := s.wrapNegroniHandlerWithAccessLog(rewrite, fmt.Sprintf("frontend redirect for %s", frontendName))
|
|
middle = append(middle, handler)
|
|
|
|
log.Debugf("Frontend %s redirect created", frontendName)
|
|
}
|
|
|
|
// Header
|
|
headerMiddleware := middlewares.NewHeaderFromStruct(frontend.Headers)
|
|
if headerMiddleware != nil {
|
|
log.Debugf("Adding header middleware for frontend %s", frontendName)
|
|
|
|
handler := s.tracingMiddleware.NewNegroniHandlerWrapper("Header", headerMiddleware, false)
|
|
middle = append(middle, handler)
|
|
}
|
|
|
|
// Secure
|
|
secureMiddleware := middlewares.NewSecure(frontend.Headers)
|
|
if secureMiddleware != nil {
|
|
log.Debugf("Adding secure middleware for frontend %s", frontendName)
|
|
|
|
handler := negroni.HandlerFunc(secureMiddleware.HandlerFuncWithNextForRequestOnly)
|
|
middle = append(middle, handler)
|
|
}
|
|
|
|
// Basic auth
|
|
if len(frontend.BasicAuth) > 0 {
|
|
log.Debugf("Adding basic authentication for frontend %s", frontendName)
|
|
|
|
authMiddleware, err := s.buildBasicAuthMiddleware(frontend.BasicAuth)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
|
|
handler := s.wrapNegroniHandlerWithAccessLog(authMiddleware, fmt.Sprintf("Basic Auth for %s", frontendName))
|
|
middle = append(middle, handler)
|
|
}
|
|
|
|
// Authentication
|
|
if frontend.Auth != nil {
|
|
authMiddleware, err := mauth.NewAuthenticator(frontend.Auth, s.tracingMiddleware)
|
|
if err != nil {
|
|
return nil, nil, nil, err
|
|
}
|
|
|
|
handler := s.wrapNegroniHandlerWithAccessLog(authMiddleware, fmt.Sprintf("Auth for %s", frontendName))
|
|
middle = append(middle, handler)
|
|
}
|
|
|
|
// TLSClientHeaders
|
|
tlsClientHeadersMiddleware := middlewares.NewTLSClientHeaders(frontend)
|
|
if tlsClientHeadersMiddleware != nil {
|
|
log.Debugf("Adding TLSClientHeaders middleware for frontend %s", frontendName)
|
|
|
|
handler := s.tracingMiddleware.NewNegroniHandlerWrapper("TLSClientHeaders", tlsClientHeadersMiddleware, false)
|
|
middle = append(middle, handler)
|
|
}
|
|
|
|
return middle, buildModifyResponse(secureMiddleware, headerMiddleware), postConfig, nil
|
|
}
|
|
|
|
func (s *Server) buildServerEntryPointMiddlewares(serverEntryPointName string, serverEntryPoint *serverEntryPoint) ([]negroni.Handler, error) {
|
|
serverMiddlewares := []negroni.Handler{middlewares.NegroniRecoverHandler()}
|
|
|
|
if s.tracingMiddleware.IsEnabled() {
|
|
serverMiddlewares = append(serverMiddlewares, s.tracingMiddleware.NewEntryPoint(serverEntryPointName))
|
|
}
|
|
|
|
if s.accessLoggerMiddleware != nil {
|
|
serverMiddlewares = append(serverMiddlewares, s.accessLoggerMiddleware)
|
|
}
|
|
|
|
if s.metricsRegistry.IsEnabled() {
|
|
serverMiddlewares = append(serverMiddlewares, middlewares.NewEntryPointMetricsMiddleware(s.metricsRegistry, serverEntryPointName))
|
|
}
|
|
|
|
if s.globalConfiguration.API != nil {
|
|
if s.globalConfiguration.API.Stats == nil {
|
|
s.globalConfiguration.API.Stats = thoas_stats.New()
|
|
}
|
|
serverMiddlewares = append(serverMiddlewares, s.globalConfiguration.API.Stats)
|
|
if s.globalConfiguration.API.Statistics != nil {
|
|
if s.globalConfiguration.API.StatsRecorder == nil {
|
|
s.globalConfiguration.API.StatsRecorder = middlewares.NewStatsRecorder(s.globalConfiguration.API.Statistics.RecentErrors)
|
|
}
|
|
serverMiddlewares = append(serverMiddlewares, s.globalConfiguration.API.StatsRecorder)
|
|
}
|
|
}
|
|
|
|
if s.entryPoints[serverEntryPointName].Configuration.Auth != nil {
|
|
authMiddleware, err := mauth.NewAuthenticator(s.entryPoints[serverEntryPointName].Configuration.Auth, s.tracingMiddleware)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create authentication middleware: %v", err)
|
|
}
|
|
serverMiddlewares = append(serverMiddlewares, s.wrapNegroniHandlerWithAccessLog(authMiddleware, fmt.Sprintf("Auth for entrypoint %s", serverEntryPointName)))
|
|
}
|
|
|
|
if s.entryPoints[serverEntryPointName].Configuration.Compress {
|
|
serverMiddlewares = append(serverMiddlewares, &middlewares.Compress{})
|
|
}
|
|
|
|
ipWhitelistMiddleware, err := buildIPWhiteLister(
|
|
s.entryPoints[serverEntryPointName].Configuration.WhiteList,
|
|
s.entryPoints[serverEntryPointName].Configuration.WhitelistSourceRange)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create ip whitelist middleware: %v", err)
|
|
}
|
|
if ipWhitelistMiddleware != nil {
|
|
serverMiddlewares = append(serverMiddlewares, s.wrapNegroniHandlerWithAccessLog(ipWhitelistMiddleware, fmt.Sprintf("ipwhitelister for entrypoint %s", serverEntryPointName)))
|
|
}
|
|
|
|
// RequestHost Cannonizer
|
|
serverMiddlewares = append(serverMiddlewares, &middlewares.RequestHost{})
|
|
|
|
return serverMiddlewares, nil
|
|
}
|
|
|
|
func errorPagesPostConfig(epHandlers []*errorpages.Handler) handlerPostConfig {
|
|
return func(backendsHandlers map[string]http.Handler) error {
|
|
for _, errorPageHandler := range epHandlers {
|
|
if handler, ok := backendsHandlers[errorPageHandler.BackendName]; ok {
|
|
err := errorPageHandler.PostLoad(handler)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to configure error pages for backend %s: %v", errorPageHandler.BackendName, err)
|
|
}
|
|
} else {
|
|
err := errorPageHandler.PostLoad(nil)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to configure error pages for %s: %v", errorPageHandler.FallbackURL, err)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func buildErrorPagesMiddleware(frontendName string, frontend *types.Frontend, backends map[string]*types.Backend, entryPointName string, providerName string) ([]*errorpages.Handler, error) {
|
|
var errorPageHandlers []*errorpages.Handler
|
|
|
|
for errorPageName, errorPage := range frontend.Errors {
|
|
if frontend.Backend == errorPage.Backend {
|
|
log.Errorf("Error when creating error page %q for frontend %q: error pages backend %q is the same as backend for the frontend (infinite call risk).",
|
|
errorPageName, frontendName, errorPage.Backend)
|
|
} else if backends[errorPage.Backend] == nil {
|
|
log.Errorf("Error when creating error page %q for frontend %q: the backend %q doesn't exist.",
|
|
errorPageName, frontendName, errorPage.Backend)
|
|
} else {
|
|
errorPagesHandler, err := errorpages.NewHandler(errorPage, entryPointName+providerName+errorPage.Backend)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error creating error pages: %v", err)
|
|
}
|
|
|
|
if errorPageServer, ok := backends[errorPage.Backend].Servers["error"]; ok {
|
|
errorPagesHandler.FallbackURL = errorPageServer.URL
|
|
}
|
|
|
|
errorPageHandlers = append(errorPageHandlers, errorPagesHandler)
|
|
}
|
|
}
|
|
|
|
return errorPageHandlers, nil
|
|
}
|
|
|
|
func (s *Server) buildBasicAuthMiddleware(authData []string) (*mauth.Authenticator, error) {
|
|
users := types.Users{}
|
|
for _, user := range authData {
|
|
users = append(users, user)
|
|
}
|
|
|
|
auth := &types.Auth{}
|
|
auth.Basic = &types.Basic{
|
|
Users: users,
|
|
}
|
|
|
|
authMiddleware, err := mauth.NewAuthenticator(auth, s.tracingMiddleware)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error creating Basic Auth: %v", err)
|
|
}
|
|
|
|
return authMiddleware, nil
|
|
}
|
|
|
|
func (s *Server) buildEntryPointRedirect() (map[string]negroni.Handler, error) {
|
|
redirectHandlers := map[string]negroni.Handler{}
|
|
|
|
for entryPointName, ep := range s.entryPoints {
|
|
entryPoint := ep.Configuration
|
|
|
|
if entryPoint.Redirect != nil && entryPointName != entryPoint.Redirect.EntryPoint {
|
|
handler, err := s.buildRedirectHandler(entryPointName, entryPoint.Redirect)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error loading configuration for entrypoint %s: %v", entryPointName, err)
|
|
}
|
|
|
|
handlerToUse := s.wrapNegroniHandlerWithAccessLog(handler, fmt.Sprintf("entrypoint redirect for %s", entryPointName))
|
|
redirectHandlers[entryPointName] = handlerToUse
|
|
}
|
|
}
|
|
|
|
return redirectHandlers, nil
|
|
}
|
|
|
|
func (s *Server) buildRedirectHandler(srcEntryPointName string, opt *types.Redirect) (negroni.Handler, error) {
|
|
// entry point redirect
|
|
if len(opt.EntryPoint) > 0 {
|
|
entryPoint := s.entryPoints[opt.EntryPoint].Configuration
|
|
if entryPoint == nil {
|
|
return nil, fmt.Errorf("unknown target entrypoint %q", srcEntryPointName)
|
|
}
|
|
log.Debugf("Creating entry point redirect %s -> %s", srcEntryPointName, opt.EntryPoint)
|
|
return redirect.NewEntryPointHandler(entryPoint, opt.Permanent)
|
|
}
|
|
|
|
// regex redirect
|
|
redirection, err := redirect.NewRegexHandler(opt.Regex, opt.Replacement, opt.Permanent)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
log.Debugf("Creating regex redirect %s -> %s -> %s", srcEntryPointName, opt.Regex, opt.Replacement)
|
|
|
|
return redirection, nil
|
|
}
|
|
|
|
func buildIPWhiteLister(whiteList *types.WhiteList, wlRange []string) (*middlewares.IPWhiteLister, error) {
|
|
if whiteList != nil &&
|
|
len(whiteList.SourceRange) > 0 {
|
|
return middlewares.NewIPWhiteLister(whiteList.SourceRange, whiteList.UseXForwardedFor)
|
|
} else if len(wlRange) > 0 {
|
|
return middlewares.NewIPWhiteLister(wlRange, false)
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func (s *Server) wrapNegroniHandlerWithAccessLog(handler negroni.Handler, frontendName string) negroni.Handler {
|
|
if s.accessLoggerMiddleware != nil {
|
|
saveBackend := accesslog.NewSaveNegroniBackend(handler, "Traefik")
|
|
saveFrontend := accesslog.NewSaveNegroniFrontend(saveBackend, frontendName)
|
|
return saveFrontend
|
|
}
|
|
return handler
|
|
}
|
|
|
|
func (s *Server) wrapHTTPHandlerWithAccessLog(handler http.Handler, frontendName string) http.Handler {
|
|
if s.accessLoggerMiddleware != nil {
|
|
saveBackend := accesslog.NewSaveBackend(handler, "Traefik")
|
|
saveFrontend := accesslog.NewSaveFrontend(saveBackend, frontendName)
|
|
return saveFrontend
|
|
}
|
|
return handler
|
|
}
|
|
|
|
func buildModifyResponse(secure *secure.Secure, header *middlewares.HeaderStruct) func(res *http.Response) error {
|
|
return func(res *http.Response) error {
|
|
if secure != nil {
|
|
if err := secure.ModifyResponseHeaders(res); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if header != nil {
|
|
if err := header.ModifyResponseHeaders(res); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
}
|