Extreme Makeover: server refactoring
This commit is contained in:
parent
bddb4cc33c
commit
eac20d61df
19 changed files with 2356 additions and 1965 deletions
316
server/server_middlewares.go
Normal file
316
server/server_middlewares.go
Normal file
|
@ -0,0 +1,316 @@
|
|||
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 {
|
||||
log.Debugf("Configured IP Whitelists: %v", frontend.WhiteList.SourceRange)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)))
|
||||
}
|
||||
|
||||
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, "Træfik")
|
||||
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, "Træfik")
|
||||
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
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue