traefik/server/server_configuration.go

338 lines
11 KiB
Go
Raw Normal View History

2018-06-11 09:36:03 +00:00
package server
import (
2018-11-14 09:18:03 +00:00
"context"
2018-06-11 09:36:03 +00:00
"crypto/tls"
"encoding/json"
"fmt"
"net/http"
"reflect"
"time"
2018-11-14 09:18:03 +00:00
"github.com/containous/alice"
2018-06-11 09:36:03 +00:00
"github.com/containous/mux"
2018-11-14 09:18:03 +00:00
"github.com/containous/traefik/config"
2018-06-11 09:36:03 +00:00
"github.com/containous/traefik/log"
2018-11-14 09:18:03 +00:00
"github.com/containous/traefik/middlewares/accesslog"
"github.com/containous/traefik/middlewares/requestdecorator"
"github.com/containous/traefik/middlewares/tracing"
"github.com/containous/traefik/responsemodifiers"
"github.com/containous/traefik/server/middleware"
"github.com/containous/traefik/server/router"
"github.com/containous/traefik/server/service"
2018-06-11 09:36:03 +00:00
traefiktls "github.com/containous/traefik/tls"
"github.com/eapache/channels"
2018-07-03 10:44:04 +00:00
"github.com/sirupsen/logrus"
2018-06-11 09:36:03 +00:00
)
// loadConfiguration manages dynamically frontends, backends and TLS configurations
2018-11-14 09:18:03 +00:00
func (s *Server) loadConfiguration(configMsg config.Message) {
logger := log.FromContext(log.With(context.Background(), log.Str(log.ProviderName, configMsg.ProviderName)))
currentConfigurations := s.currentConfigurations.Get().(config.Configurations)
2018-06-11 09:36:03 +00:00
// Copy configurations to new map so we don't change current if LoadConfig fails
2018-11-14 09:18:03 +00:00
newConfigurations := make(config.Configurations)
2018-06-11 09:36:03 +00:00
for k, v := range currentConfigurations {
newConfigurations[k] = v
}
newConfigurations[configMsg.ProviderName] = configMsg.Configuration
s.metricsRegistry.ConfigReloadsCounter().Add(1)
handlers, certificates := s.loadConfig(newConfigurations)
2018-06-11 09:36:03 +00:00
s.metricsRegistry.LastConfigReloadSuccessGauge().Set(float64(time.Now().Unix()))
2018-11-14 09:18:03 +00:00
for entryPointName, handler := range handlers {
s.entryPoints[entryPointName].httpRouter.UpdateHandler(handler)
2018-11-14 09:18:03 +00:00
}
2018-06-11 09:36:03 +00:00
for entryPointName, entryPoint := range s.entryPoints {
2018-11-14 09:18:03 +00:00
eLogger := logger.WithField(log.EntryPointName, entryPointName)
if entryPoint.Certs == nil {
2018-11-14 09:18:03 +00:00
if len(certificates[entryPointName]) > 0 {
eLogger.Debugf("Cannot configure certificates for the non-TLS %s entryPoint.", entryPointName)
2018-06-11 09:36:03 +00:00
}
} else {
entryPoint.Certs.DynamicCerts.Set(certificates[entryPointName])
entryPoint.Certs.ResetCache()
2018-06-11 09:36:03 +00:00
}
eLogger.Infof("Server configuration reloaded on %s", s.entryPoints[entryPointName].httpServer.Addr)
2018-06-11 09:36:03 +00:00
}
s.currentConfigurations.Set(newConfigurations)
for _, listener := range s.configurationListeners {
listener(*configMsg.Configuration)
}
s.postLoadConfiguration()
}
// loadConfig returns a new gorilla.mux Route from the specified global configuration and the dynamic
// provider configurations.
func (s *Server) loadConfig(configurations config.Configurations) (map[string]http.Handler, map[string]map[string]*tls.Certificate) {
2018-06-11 09:36:03 +00:00
2018-11-14 09:18:03 +00:00
ctx := context.TODO()
2018-06-11 09:36:03 +00:00
2018-11-14 09:18:03 +00:00
// FIXME manage duplicates
conf := config.Configuration{
Routers: make(map[string]*config.Router),
Middlewares: make(map[string]*config.Middleware),
Services: make(map[string]*config.Service),
}
for _, config := range configurations {
for key, value := range config.Middlewares {
conf.Middlewares[key] = value
}
2018-06-11 09:36:03 +00:00
2018-11-14 09:18:03 +00:00
for key, value := range config.Services {
conf.Services[key] = value
2018-06-11 09:36:03 +00:00
}
2018-11-14 09:18:03 +00:00
for key, value := range config.Routers {
conf.Routers[key] = value
2018-06-11 09:36:03 +00:00
}
2018-11-14 09:18:03 +00:00
conf.TLS = append(conf.TLS, config.TLS...)
2018-06-11 09:36:03 +00:00
}
2018-11-14 09:18:03 +00:00
handlers := s.applyConfiguration(ctx, conf)
2018-06-11 09:36:03 +00:00
2018-11-14 09:18:03 +00:00
// Get new certificates list sorted per entry points
2018-06-11 09:36:03 +00:00
// Update certificates
entryPointsCertificates := s.loadHTTPSConfiguration(configurations)
2018-06-11 09:36:03 +00:00
2018-11-14 09:18:03 +00:00
return handlers, entryPointsCertificates
2018-06-11 09:36:03 +00:00
}
2018-11-14 09:18:03 +00:00
func (s *Server) applyConfiguration(ctx context.Context, configuration config.Configuration) map[string]http.Handler {
var entryPoints []string
for entryPointName := range s.entryPoints {
entryPoints = append(entryPoints, entryPointName)
2018-06-11 09:36:03 +00:00
}
2018-11-14 09:18:03 +00:00
serviceManager := service.NewManager(configuration.Services, s.defaultRoundTripper)
middlewaresBuilder := middleware.NewBuilder(configuration.Middlewares, serviceManager)
responseModifierFactory := responsemodifiers.NewBuilder(configuration.Middlewares)
2018-06-11 09:36:03 +00:00
2018-11-14 09:18:03 +00:00
routerManager := router.NewManager(configuration.Routers, serviceManager, middlewaresBuilder, responseModifierFactory)
2018-06-11 09:36:03 +00:00
handlers := routerManager.BuildHandlers(ctx, entryPoints)
2018-06-11 09:36:03 +00:00
2018-11-14 09:18:03 +00:00
routerHandlers := make(map[string]http.Handler)
2018-06-11 09:36:03 +00:00
2018-11-14 09:18:03 +00:00
for _, entryPointName := range entryPoints {
internalMuxRouter := mux.NewRouter().
SkipClean(true)
2018-09-17 18:40:04 +00:00
2018-11-14 09:18:03 +00:00
ctx = log.With(ctx, log.Str(log.EntryPointName, entryPointName))
2018-06-11 09:36:03 +00:00
2018-11-14 09:18:03 +00:00
factory := s.entryPoints[entryPointName].RouteAppenderFactory
if factory != nil {
// FIXME remove currentConfigurations
appender := factory.NewAppender(ctx, middlewaresBuilder, &s.currentConfigurations)
appender.Append(internalMuxRouter)
}
2018-06-11 09:36:03 +00:00
2018-11-14 09:18:03 +00:00
if h, ok := handlers[entryPointName]; ok {
internalMuxRouter.NotFoundHandler = h
2018-06-11 09:36:03 +00:00
} else {
internalMuxRouter.NotFoundHandler = buildDefaultHTTPRouter()
2018-06-11 09:36:03 +00:00
}
2018-11-14 09:18:03 +00:00
routerHandlers[entryPointName] = internalMuxRouter
2018-06-11 09:36:03 +00:00
2018-11-14 09:18:03 +00:00
chain := alice.New()
2018-06-11 09:36:03 +00:00
2018-11-14 09:18:03 +00:00
if s.accessLoggerMiddleware != nil {
chain = chain.Append(accesslog.WrapHandler(s.accessLoggerMiddleware))
2018-06-11 09:36:03 +00:00
}
2018-11-14 09:18:03 +00:00
if s.tracer != nil {
chain = chain.Append(tracing.WrapEntryPointHandler(ctx, s.tracer, entryPointName))
2018-10-29 17:42:03 +00:00
}
2018-06-11 09:36:03 +00:00
2018-11-14 09:18:03 +00:00
chain = chain.Append(requestdecorator.WrapHandler(s.requestDecorator))
2018-06-11 09:36:03 +00:00
2018-11-14 09:18:03 +00:00
handler, err := chain.Then(internalMuxRouter.NotFoundHandler)
2018-06-11 09:36:03 +00:00
if err != nil {
2018-11-14 09:18:03 +00:00
log.FromContext(ctx).Error(err)
continue
2018-06-11 09:36:03 +00:00
}
2018-11-14 09:18:03 +00:00
internalMuxRouter.NotFoundHandler = handler
2018-06-11 09:36:03 +00:00
}
2018-11-14 09:18:03 +00:00
return routerHandlers
2018-06-11 09:36:03 +00:00
}
2018-11-14 09:18:03 +00:00
func (s *Server) preLoadConfiguration(configMsg config.Message) {
2018-06-11 09:36:03 +00:00
s.defaultConfigurationValues(configMsg.Configuration)
2018-11-14 09:18:03 +00:00
currentConfigurations := s.currentConfigurations.Get().(config.Configurations)
2018-06-11 09:36:03 +00:00
2018-11-14 09:18:03 +00:00
logger := log.WithoutContext().WithField(log.ProviderName, configMsg.ProviderName)
2018-07-03 10:44:04 +00:00
if log.GetLevel() == logrus.DebugLevel {
jsonConf, _ := json.Marshal(configMsg.Configuration)
2018-11-14 09:18:03 +00:00
logger.Debugf("Configuration received from provider %s: %s", configMsg.ProviderName, string(jsonConf))
2018-07-03 10:44:04 +00:00
}
2018-06-11 09:36:03 +00:00
2018-11-14 09:18:03 +00:00
if configMsg.Configuration == nil || configMsg.Configuration.Routers == nil && configMsg.Configuration.Services == nil && configMsg.Configuration.Middlewares == nil && configMsg.Configuration.TLS == nil {
logger.Infof("Skipping empty Configuration for provider %s", configMsg.ProviderName)
2018-06-11 09:36:03 +00:00
return
}
if reflect.DeepEqual(currentConfigurations[configMsg.ProviderName], configMsg.Configuration) {
2018-11-14 09:18:03 +00:00
logger.Infof("Skipping same configuration for provider %s", configMsg.ProviderName)
2018-06-11 09:36:03 +00:00
return
}
providerConfigUpdateCh, ok := s.providerConfigUpdateMap[configMsg.ProviderName]
if !ok {
2018-11-14 09:18:03 +00:00
providerConfigUpdateCh = make(chan config.Message)
2018-06-11 09:36:03 +00:00
s.providerConfigUpdateMap[configMsg.ProviderName] = providerConfigUpdateCh
s.routinesPool.Go(func(stop chan bool) {
s.throttleProviderConfigReload(s.providersThrottleDuration, s.configurationValidatedChan, providerConfigUpdateCh, stop)
2018-06-11 09:36:03 +00:00
})
}
providerConfigUpdateCh <- configMsg
}
2018-11-14 09:18:03 +00:00
func (s *Server) defaultConfigurationValues(configuration *config.Configuration) {
// FIXME create a config hook
2018-06-11 09:36:03 +00:00
}
func (s *Server) listenConfigurations(stop chan bool) {
for {
select {
case <-stop:
return
case configMsg, ok := <-s.configurationValidatedChan:
if !ok || configMsg.Configuration == nil {
return
}
s.loadConfiguration(configMsg)
}
}
}
// throttleProviderConfigReload throttles the configuration reload speed for a single provider.
// It will immediately publish a new configuration and then only publish the next configuration after the throttle duration.
// Note that in the case it receives N new configs in the timeframe of the throttle duration after publishing,
// it will publish the last of the newly received configurations.
2018-11-14 09:18:03 +00:00
func (s *Server) throttleProviderConfigReload(throttle time.Duration, publish chan<- config.Message, in <-chan config.Message, stop chan bool) {
2018-06-11 09:36:03 +00:00
ring := channels.NewRingChannel(1)
defer ring.Close()
s.routinesPool.Go(func(stop chan bool) {
for {
select {
case <-stop:
return
case nextConfig := <-ring.Out():
2018-11-14 09:18:03 +00:00
if config, ok := nextConfig.(config.Message); ok {
2018-09-06 12:24:03 +00:00
publish <- config
time.Sleep(throttle)
}
2018-06-11 09:36:03 +00:00
}
}
})
for {
select {
case <-stop:
return
case nextConfig := <-in:
ring.In() <- nextConfig
}
}
}
func (s *Server) postLoadConfiguration() {
2018-11-14 09:18:03 +00:00
// FIXME metrics
// if s.metricsRegistry.IsEnabled() {
// activeConfig := s.currentConfigurations.Get().(config.Configurations)
// metrics.OnConfigurationUpdate(activeConfig)
// }
2018-06-11 09:36:03 +00:00
2018-11-14 09:18:03 +00:00
// FIXME acme
// if s.staticConfiguration.ACME == nil || s.leadership == nil || !s.leadership.IsLeader() {
// return
// }
//
// if s.staticConfiguration.ACME.OnHostRule {
2018-11-14 09:18:03 +00:00
// currentConfigurations := s.currentConfigurations.Get().(config.Configurations)
// for _, config := range currentConfigurations {
// for _, frontend := range config.Frontends {
//
// // check if one of the frontend entrypoints is configured with TLS
// // and is configured with ACME
// acmeEnabled := false
// for _, entryPoint := range frontend.EntryPoints {
// if s.staticConfiguration.ACME.EntryPoint == entryPoint && s.entryPoints[entryPoint].Configuration.TLS != nil {
2018-11-14 09:18:03 +00:00
// acmeEnabled = true
// break
// }
// }
//
// if acmeEnabled {
// for _, route := range frontend.Routes {
// rls := rules.Rules{}
// domains, err := rls.ParseDomains(route.Rule)
// if err != nil {
// log.Errorf("Error parsing domains: %v", err)
// } else if len(domains) == 0 {
// log.Debugf("No domain parsed in rule %q", route.Rule)
// } else {
// s.staticConfiguration.ACME.LoadCertificateForDomains(domains)
2018-11-14 09:18:03 +00:00
// }
// }
// }
// }
// }
// }
2018-06-11 09:36:03 +00:00
}
// loadHTTPSConfiguration add/delete HTTPS certificate managed dynamically
func (s *Server) loadHTTPSConfiguration(configurations config.Configurations) map[string]map[string]*tls.Certificate {
var entryPoints []string
for entryPointName := range s.entryPoints {
entryPoints = append(entryPoints, entryPointName)
}
2018-06-11 09:36:03 +00:00
newEPCertificates := make(map[string]map[string]*tls.Certificate)
// Get all certificates
for _, config := range configurations {
if config.TLS != nil && len(config.TLS) > 0 {
traefiktls.SortTLSPerEntryPoints(config.TLS, newEPCertificates, entryPoints)
2018-06-11 09:36:03 +00:00
}
}
return newEPCertificates
2018-06-11 09:36:03 +00:00
}
func buildDefaultHTTPRouter() *mux.Router {
2018-11-14 09:18:03 +00:00
rt := mux.NewRouter()
rt.NotFoundHandler = http.HandlerFunc(http.NotFound)
rt.SkipClean(true)
return rt
}
func buildDefaultCertificate(defaultCertificate *traefiktls.Certificate) (*tls.Certificate, error) {
certFile, err := defaultCertificate.CertFile.Read()
if err != nil {
return nil, fmt.Errorf("failed to get cert file content: %v", err)
}
keyFile, err := defaultCertificate.KeyFile.Read()
if err != nil {
return nil, fmt.Errorf("failed to get key file content: %v", err)
}
cert, err := tls.X509KeyPair(certFile, keyFile)
if err != nil {
return nil, fmt.Errorf("failed to load X509 key pair: %v", err)
}
return &cert, nil
}