2017-04-17 20:47:53 +00:00
package server
2016-01-13 21:45:49 +00:00
import (
2016-08-16 15:26:10 +00:00
"context"
2016-01-13 21:45:49 +00:00
"crypto/tls"
2016-06-15 20:38:40 +00:00
"crypto/x509"
2016-01-29 19:34:17 +00:00
"encoding/json"
2016-01-13 21:45:49 +00:00
"errors"
2017-10-10 12:50:03 +00:00
"fmt"
2016-06-15 20:38:40 +00:00
"io/ioutil"
2017-11-18 00:10:03 +00:00
stdlog "log"
2017-08-18 13:34:04 +00:00
"net"
2016-02-26 14:29:53 +00:00
"net/http"
"net/url"
"os"
"os/signal"
"reflect"
"regexp"
"sort"
2017-10-30 11:54:03 +00:00
"strings"
2017-04-04 09:36:23 +00:00
"sync"
2016-02-26 14:29:53 +00:00
"time"
2016-02-19 22:55:23 +00:00
2017-11-18 00:10:03 +00:00
"github.com/Sirupsen/logrus"
2017-08-25 19:32:03 +00:00
"github.com/armon/go-proxyproto"
2016-06-03 15:58:33 +00:00
"github.com/containous/mux"
2016-08-18 11:03:10 +00:00
"github.com/containous/traefik/cluster"
2017-08-25 14:10:03 +00:00
"github.com/containous/traefik/configuration"
2016-11-26 18:48:49 +00:00
"github.com/containous/traefik/healthcheck"
2016-08-18 12:20:11 +00:00
"github.com/containous/traefik/log"
2017-08-23 18:46:03 +00:00
"github.com/containous/traefik/metrics"
2016-02-24 15:43:39 +00:00
"github.com/containous/traefik/middlewares"
2017-05-09 12:02:44 +00:00
"github.com/containous/traefik/middlewares/accesslog"
2017-09-18 15:48:07 +00:00
mauth "github.com/containous/traefik/middlewares/auth"
2016-02-24 15:43:39 +00:00
"github.com/containous/traefik/provider"
2016-03-31 16:57:08 +00:00
"github.com/containous/traefik/safe"
2017-10-10 09:10:02 +00:00
"github.com/containous/traefik/server/cookie"
2017-11-09 11:16:03 +00:00
traefikTls "github.com/containous/traefik/tls"
2016-02-24 15:43:39 +00:00
"github.com/containous/traefik/types"
2017-10-10 12:50:03 +00:00
"github.com/containous/traefik/whitelist"
2017-11-17 09:26:03 +00:00
"github.com/eapache/channels"
2017-08-25 14:10:03 +00:00
thoas_stats "github.com/thoas/stats"
2017-07-19 10:02:51 +00:00
"github.com/urfave/negroni"
2016-06-15 17:07:33 +00:00
"github.com/vulcand/oxy/connlimit"
"github.com/vulcand/oxy/forward"
2017-09-09 11:36:03 +00:00
"github.com/vulcand/oxy/ratelimit"
2016-06-15 17:07:33 +00:00
"github.com/vulcand/oxy/roundrobin"
"github.com/vulcand/oxy/utils"
2017-08-18 13:34:04 +00:00
"golang.org/x/net/http2"
2016-01-13 21:45:49 +00:00
)
2017-08-25 14:10:03 +00:00
var (
2017-11-18 00:10:03 +00:00
httpServerLogger = stdlog . New ( log . WriterLevel ( logrus . DebugLevel ) , "" , 0 )
2017-08-25 14:10:03 +00:00
)
2016-01-13 21:46:44 +00:00
2016-01-13 21:45:49 +00:00
// Server is the reverse-proxy/load-balancer engine
type Server struct {
2017-08-18 13:34:04 +00:00
serverEntryPoints serverEntryPoints
configurationChan chan types . ConfigMessage
configurationValidatedChan chan types . ConfigMessage
signals chan os . Signal
stopChan chan bool
providers [ ] provider . Provider
currentConfigurations safe . Safe
2017-08-25 14:10:03 +00:00
globalConfiguration configuration . GlobalConfiguration
2017-08-18 13:34:04 +00:00
accessLoggerMiddleware * accesslog . LogHandler
routinesPool * safe . Pool
leadership * cluster . Leadership
defaultForwardingRoundTripper http . RoundTripper
2017-08-23 18:46:03 +00:00
metricsRegistry metrics . Registry
2016-01-13 21:45:49 +00:00
}
2016-02-25 17:30:13 +00:00
type serverEntryPoints map [ string ] * serverEntryPoint
2016-01-29 19:34:17 +00:00
type serverEntryPoint struct {
2017-03-09 22:27:09 +00:00
httpServer * http . Server
2017-08-25 19:32:03 +00:00
listener net . Listener
2016-03-04 10:32:23 +00:00
httpRouter * middlewares . HandlerSwitcher
2017-11-09 11:16:03 +00:00
certs safe . Safe
2016-01-29 19:34:17 +00:00
}
2016-03-27 00:05:17 +00:00
type serverRoute struct {
2017-03-24 11:07:59 +00:00
route * mux . Route
stripPrefixes [ ] string
stripPrefixesRegex [ ] string
addPrefix string
replacePath string
2017-10-30 11:54:03 +00:00
replacePathRegex string
2016-03-27 00:05:17 +00:00
}
2016-01-13 21:45:49 +00:00
// NewServer returns an initialized Server.
2017-08-25 14:10:03 +00:00
func NewServer ( globalConfiguration configuration . GlobalConfiguration ) * Server {
2016-01-13 21:45:49 +00:00
server := new ( Server )
2016-02-25 17:30:13 +00:00
server . serverEntryPoints = make ( map [ string ] * serverEntryPoint )
2016-05-19 18:09:01 +00:00
server . configurationChan = make ( chan types . ConfigMessage , 100 )
server . configurationValidatedChan = make ( chan types . ConfigMessage , 100 )
2016-01-29 19:34:17 +00:00
server . signals = make ( chan os . Signal , 1 )
2016-04-13 18:36:23 +00:00
server . stopChan = make ( chan bool , 1 )
2016-01-13 21:45:49 +00:00
server . providers = [ ] provider . Provider { }
2017-08-11 10:04:58 +00:00
server . configureSignals ( )
2017-08-25 14:10:03 +00:00
currentConfigurations := make ( types . Configurations )
2016-04-13 18:36:23 +00:00
server . currentConfigurations . Set ( currentConfigurations )
2016-01-13 21:45:49 +00:00
server . globalConfiguration = globalConfiguration
2017-11-09 15:12:04 +00:00
if server . globalConfiguration . API != nil {
server . globalConfiguration . API . CurrentConfigurations = & server . currentConfigurations
}
2016-08-18 11:03:10 +00:00
server . routinesPool = safe . NewPool ( context . Background ( ) )
2017-08-18 13:34:04 +00:00
server . defaultForwardingRoundTripper = createHTTPTransport ( globalConfiguration )
2017-08-23 18:46:03 +00:00
server . metricsRegistry = metrics . NewVoidRegistry ( )
2017-11-09 15:12:04 +00:00
if globalConfiguration . Metrics != nil {
server . registerMetricClients ( globalConfiguration . Metrics )
2017-08-23 18:46:03 +00:00
}
2016-08-18 11:03:10 +00:00
if globalConfiguration . Cluster != nil {
// leadership creation if cluster mode
server . leadership = cluster . NewLeadership ( server . routinesPool . Ctx ( ) , globalConfiguration . Cluster )
}
2016-01-13 21:45:49 +00:00
2017-05-25 11:25:53 +00:00
if globalConfiguration . AccessLogsFile != "" {
globalConfiguration . AccessLog = & types . AccessLog { FilePath : globalConfiguration . AccessLogsFile , Format : accesslog . CommonFormat }
}
if globalConfiguration . AccessLog != nil {
var err error
server . accessLoggerMiddleware , err = accesslog . NewLogHandler ( globalConfiguration . AccessLog )
if err != nil {
log . Warnf ( "Unable to create log handler: %s" , err )
}
2017-05-22 19:39:29 +00:00
}
2016-01-13 21:45:49 +00:00
return server
}
2017-08-18 13:34:04 +00:00
// createHTTPTransport creates an http.Transport configured with the GlobalConfiguration settings.
// For the settings that can't be configured in Traefik it uses the default http.Transport settings.
// An exception to this is the MaxIdleConns setting as we only provide the option MaxIdleConnsPerHost
// in Traefik at this point in time. Setting this value to the default of 100 could lead to confusing
// behaviour and backwards compatibility issues.
2017-08-25 14:10:03 +00:00
func createHTTPTransport ( globalConfiguration configuration . GlobalConfiguration ) * http . Transport {
2017-08-18 13:34:04 +00:00
dialer := & net . Dialer {
2017-09-20 16:14:03 +00:00
Timeout : configuration . DefaultDialTimeout ,
2017-08-18 13:34:04 +00:00
KeepAlive : 30 * time . Second ,
DualStack : true ,
}
if globalConfiguration . ForwardingTimeouts != nil {
dialer . Timeout = time . Duration ( globalConfiguration . ForwardingTimeouts . DialTimeout )
}
transport := & http . Transport {
Proxy : http . ProxyFromEnvironment ,
DialContext : dialer . DialContext ,
MaxIdleConnsPerHost : globalConfiguration . MaxIdleConnsPerHost ,
IdleConnTimeout : 90 * time . Second ,
TLSHandshakeTimeout : 10 * time . Second ,
ExpectContinueTimeout : 1 * time . Second ,
}
if globalConfiguration . ForwardingTimeouts != nil {
transport . ResponseHeaderTimeout = time . Duration ( globalConfiguration . ForwardingTimeouts . ResponseHeaderTimeout )
}
if globalConfiguration . InsecureSkipVerify {
transport . TLSClientConfig = & tls . Config { InsecureSkipVerify : true }
}
if len ( globalConfiguration . RootCAs ) > 0 {
transport . TLSClientConfig = & tls . Config {
RootCAs : createRootCACertPool ( globalConfiguration . RootCAs ) ,
}
}
2017-10-10 10:14:03 +00:00
http2 . ConfigureTransport ( transport )
2017-08-18 13:34:04 +00:00
return transport
}
2017-11-09 11:16:03 +00:00
func createRootCACertPool ( rootCAs traefikTls . RootCAs ) * x509 . CertPool {
2017-08-18 13:34:04 +00:00
roots := x509 . NewCertPool ( )
for _ , cert := range rootCAs {
certContent , err := cert . Read ( )
if err != nil {
log . Error ( "Error while read RootCAs" , err )
continue
}
roots . AppendCertsFromPEM ( certContent )
}
return roots
}
2016-10-25 15:59:39 +00:00
// Start starts the server.
2016-01-13 21:45:49 +00:00
func ( server * Server ) Start ( ) {
2016-02-25 17:30:13 +00:00
server . startHTTPServers ( )
2016-08-18 12:20:11 +00:00
server . startLeadership ( )
2016-04-13 18:36:23 +00:00
server . routinesPool . Go ( func ( stop chan bool ) {
server . listenProviders ( stop )
2016-03-31 16:57:08 +00:00
} )
2016-04-13 18:36:23 +00:00
server . routinesPool . Go ( func ( stop chan bool ) {
server . listenConfigurations ( stop )
2016-03-31 16:57:08 +00:00
} )
2016-01-13 21:45:49 +00:00
server . configureProviders ( )
server . startProviders ( )
go server . listenSignals ( )
2016-10-25 15:59:39 +00:00
}
// Wait blocks until server is shutted down.
func ( server * Server ) Wait ( ) {
2016-01-13 21:45:49 +00:00
<- server . stopChan
}
// Stop stops the server
func ( server * Server ) Stop ( ) {
2017-03-09 22:27:09 +00:00
defer log . Info ( "Server stopped" )
var wg sync . WaitGroup
for sepn , sep := range server . serverEntryPoints {
wg . Add ( 1 )
go func ( serverEntryPointName string , serverEntryPoint * serverEntryPoint ) {
defer wg . Done ( )
2017-09-26 08:22:03 +00:00
graceTimeOut := time . Duration ( server . globalConfiguration . LifeCycle . GraceTimeOut )
2017-03-27 09:51:53 +00:00
ctx , cancel := context . WithTimeout ( context . Background ( ) , graceTimeOut )
log . Debugf ( "Waiting %s seconds before killing connections on entrypoint %s..." , graceTimeOut , serverEntryPointName )
2017-03-09 22:27:09 +00:00
if err := serverEntryPoint . httpServer . Shutdown ( ctx ) ; err != nil {
log . Debugf ( "Wait is over due to: %s" , err )
serverEntryPoint . httpServer . Close ( )
}
2016-07-13 15:50:57 +00:00
cancel ( )
2017-03-09 22:27:09 +00:00
log . Debugf ( "Entrypoint %s closed" , serverEntryPointName )
} ( sepn , sep )
2016-01-29 19:34:17 +00:00
}
2017-03-09 22:27:09 +00:00
wg . Wait ( )
2016-01-13 21:45:49 +00:00
server . stopChan <- true
}
// Close destroys the server
func ( server * Server ) Close ( ) {
2017-09-26 08:22:03 +00:00
ctx , cancel := context . WithTimeout ( context . Background ( ) , time . Duration ( server . globalConfiguration . LifeCycle . GraceTimeOut ) )
2016-07-13 15:50:57 +00:00
go func ( ctx context . Context ) {
<- ctx . Done ( )
if ctx . Err ( ) == context . Canceled {
return
} else if ctx . Err ( ) == context . DeadlineExceeded {
2017-05-26 15:03:14 +00:00
log . Warn ( "Timeout while stopping traefik, killing instance ✝" )
2016-07-13 15:50:57 +00:00
os . Exit ( 1 )
}
} ( ctx )
2017-08-23 18:46:03 +00:00
stopMetricsClients ( )
2016-08-18 11:03:10 +00:00
server . stopLeadership ( )
2016-11-15 19:14:11 +00:00
server . routinesPool . Cleanup ( )
2016-01-13 21:46:44 +00:00
close ( server . configurationChan )
2016-01-29 19:34:17 +00:00
close ( server . configurationValidatedChan )
2016-07-14 08:22:18 +00:00
signal . Stop ( server . signals )
2016-01-29 19:34:17 +00:00
close ( server . signals )
2016-01-13 21:46:44 +00:00
close ( server . stopChan )
2017-05-22 19:39:29 +00:00
if server . accessLoggerMiddleware != nil {
if err := server . accessLoggerMiddleware . Close ( ) ; err != nil {
log . Errorf ( "Error closing access log file: %s" , err )
}
}
2016-07-13 15:50:57 +00:00
cancel ( )
2016-01-13 21:45:49 +00:00
}
2016-08-18 11:03:10 +00:00
func ( server * Server ) startLeadership ( ) {
if server . leadership != nil {
server . leadership . Participate ( server . routinesPool )
}
}
func ( server * Server ) stopLeadership ( ) {
if server . leadership != nil {
2016-08-18 12:20:11 +00:00
server . leadership . Stop ( )
2016-08-18 11:03:10 +00:00
}
}
2016-02-25 17:30:13 +00:00
func ( server * Server ) startHTTPServers ( ) {
server . serverEntryPoints = server . buildEntryPoints ( server . globalConfiguration )
2017-01-12 13:34:54 +00:00
2016-02-25 17:30:13 +00:00
for newServerEntryPointName , newServerEntryPoint := range server . serverEntryPoints {
2017-07-08 10:21:14 +00:00
serverEntryPoint := server . setupServerEntryPoint ( newServerEntryPointName , newServerEntryPoint )
2017-08-25 19:32:03 +00:00
go server . startServer ( serverEntryPoint , server . globalConfiguration )
2017-07-08 10:21:14 +00:00
}
}
func ( server * Server ) setupServerEntryPoint ( newServerEntryPointName string , newServerEntryPoint * serverEntryPoint ) * serverEntryPoint {
2017-08-25 14:10:03 +00:00
serverMiddlewares := [ ] negroni . Handler { middlewares . NegroniRecoverHandler ( ) }
2017-11-09 15:12:04 +00:00
serverInternalMiddlewares := [ ] negroni . Handler { middlewares . NegroniRecoverHandler ( ) }
2017-07-08 10:21:14 +00:00
if server . accessLoggerMiddleware != nil {
serverMiddlewares = append ( serverMiddlewares , server . accessLoggerMiddleware )
}
2017-08-23 18:46:03 +00:00
if server . metricsRegistry . IsEnabled ( ) {
serverMiddlewares = append ( serverMiddlewares , middlewares . NewMetricsWrapper ( server . metricsRegistry , newServerEntryPointName ) )
2017-07-08 10:21:14 +00:00
}
2017-11-09 15:12:04 +00:00
if server . globalConfiguration . API != nil {
server . globalConfiguration . API . Stats = thoas_stats . New ( )
serverMiddlewares = append ( serverMiddlewares , server . globalConfiguration . API . Stats )
if server . globalConfiguration . API . Statistics != nil {
server . globalConfiguration . API . StatsRecorder = middlewares . NewStatsRecorder ( server . globalConfiguration . API . Statistics . RecentErrors )
serverMiddlewares = append ( serverMiddlewares , server . globalConfiguration . API . StatsRecorder )
2017-08-25 14:10:03 +00:00
}
2017-11-09 15:12:04 +00:00
2017-07-08 10:21:14 +00:00
}
if server . globalConfiguration . EntryPoints [ newServerEntryPointName ] . Auth != nil {
2017-09-18 15:48:07 +00:00
authMiddleware , err := mauth . NewAuthenticator ( server . globalConfiguration . EntryPoints [ newServerEntryPointName ] . Auth )
2017-07-08 10:21:14 +00:00
if err != nil {
log . Fatal ( "Error starting server: " , err )
2016-09-28 21:07:06 +00:00
}
2017-07-08 10:21:14 +00:00
serverMiddlewares = append ( serverMiddlewares , authMiddleware )
2017-11-09 15:12:04 +00:00
serverInternalMiddlewares = append ( serverInternalMiddlewares , authMiddleware )
2017-07-08 10:21:14 +00:00
}
if server . globalConfiguration . EntryPoints [ newServerEntryPointName ] . Compress {
serverMiddlewares = append ( serverMiddlewares , & middlewares . Compress { } )
}
if len ( server . globalConfiguration . EntryPoints [ newServerEntryPointName ] . WhitelistSourceRange ) > 0 {
ipWhitelistMiddleware , err := middlewares . NewIPWhitelister ( server . globalConfiguration . EntryPoints [ newServerEntryPointName ] . WhitelistSourceRange )
2016-02-25 17:30:13 +00:00
if err != nil {
2017-07-08 10:21:14 +00:00
log . Fatal ( "Error starting server: " , err )
2016-02-25 17:30:13 +00:00
}
2017-07-08 10:21:14 +00:00
serverMiddlewares = append ( serverMiddlewares , ipWhitelistMiddleware )
2017-11-09 15:12:04 +00:00
serverInternalMiddlewares = append ( serverInternalMiddlewares , ipWhitelistMiddleware )
2016-02-25 17:30:13 +00:00
}
2017-11-09 15:12:04 +00:00
newSrv , listener , err := server . prepareServer ( newServerEntryPointName , server . globalConfiguration . EntryPoints [ newServerEntryPointName ] , newServerEntryPoint . httpRouter , serverMiddlewares , serverInternalMiddlewares )
2017-07-08 10:21:14 +00:00
if err != nil {
log . Fatal ( "Error preparing server: " , err )
}
serverEntryPoint := server . serverEntryPoints [ newServerEntryPointName ]
2017-08-25 14:10:03 +00:00
serverEntryPoint . httpServer = newSrv
2017-08-25 19:32:03 +00:00
serverEntryPoint . listener = listener
2017-07-08 10:21:14 +00:00
return serverEntryPoint
2016-02-25 17:30:13 +00:00
}
2016-04-13 18:36:23 +00:00
func ( server * Server ) listenProviders ( stop chan bool ) {
2016-01-13 21:45:49 +00:00
for {
2016-04-13 18:36:23 +00:00
select {
case <- stop :
return
case configMsg , ok := <- server . configurationChan :
2017-11-09 11:16:03 +00:00
if ! ok || configMsg . Configuration == nil {
2016-04-13 18:36:23 +00:00
return
}
2017-11-09 11:16:03 +00:00
server . preLoadConfiguration ( configMsg )
}
}
}
func ( server * Server ) preLoadConfiguration ( configMsg types . ConfigMessage ) {
2017-11-17 09:26:03 +00:00
providerConfigUpdateMap := map [ string ] chan types . ConfigMessage { }
providersThrottleDuration := time . Duration ( server . globalConfiguration . ProvidersThrottleDuration )
2017-11-09 11:16:03 +00:00
server . defaultConfigurationValues ( configMsg . Configuration )
currentConfigurations := server . currentConfigurations . Get ( ) . ( types . Configurations )
jsonConf , _ := json . Marshal ( configMsg . Configuration )
log . Debugf ( "Configuration received from provider %s: %s" , configMsg . ProviderName , string ( jsonConf ) )
if configMsg . Configuration == nil || configMsg . Configuration . Backends == nil && configMsg . Configuration . Frontends == nil && configMsg . Configuration . TLSConfiguration == nil {
log . Infof ( "Skipping empty Configuration for provider %s" , configMsg . ProviderName )
} else if reflect . DeepEqual ( currentConfigurations [ configMsg . ProviderName ] , configMsg . Configuration ) {
log . Infof ( "Skipping same configuration for provider %s" , configMsg . ProviderName )
} else {
2017-11-17 09:26:03 +00:00
if _ , ok := providerConfigUpdateMap [ configMsg . ProviderName ] ; ! ok {
providerConfigUpdate := make ( chan types . ConfigMessage )
providerConfigUpdateMap [ configMsg . ProviderName ] = providerConfigUpdate
server . routinesPool . Go ( func ( stop chan bool ) {
throttleProviderConfigReload ( providersThrottleDuration , server . configurationValidatedChan , providerConfigUpdate , stop )
2017-11-09 11:16:03 +00:00
} )
2016-06-22 16:31:14 +00:00
}
2017-11-17 09:26:03 +00:00
providerConfigUpdateMap [ configMsg . ProviderName ] <- 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.
func throttleProviderConfigReload ( throttle time . Duration , publish chan <- types . ConfigMessage , in <- chan types . ConfigMessage , stop chan bool ) {
ring := channels . NewRingChannel ( 1 )
safe . Go ( func ( ) {
for {
select {
case <- stop :
return
case nextConfig := <- ring . Out ( ) :
publish <- nextConfig . ( types . ConfigMessage )
time . Sleep ( throttle )
}
}
} )
for {
select {
case <- stop :
return
case nextConfig := <- in :
ring . In ( ) <- nextConfig
}
2016-06-22 16:31:14 +00:00
}
}
func ( server * Server ) defaultConfigurationValues ( configuration * types . Configuration ) {
if configuration == nil || configuration . Frontends == nil {
return
}
2017-05-10 22:34:47 +00:00
server . configureFrontends ( configuration . Frontends )
2017-05-15 21:53:35 +00:00
server . configureBackends ( configuration . Backends )
2016-01-13 21:45:49 +00:00
}
2016-04-13 18:36:23 +00:00
func ( server * Server ) listenConfigurations ( stop chan bool ) {
2016-01-13 21:45:49 +00:00
for {
2016-04-13 18:36:23 +00:00
select {
case <- stop :
return
case configMsg , ok := <- server . configurationValidatedChan :
2017-11-09 11:16:03 +00:00
if ! ok || configMsg . Configuration == nil {
2016-04-13 18:36:23 +00:00
return
2016-01-13 21:45:49 +00:00
}
2017-11-09 11:16:03 +00:00
server . loadConfiguration ( configMsg )
}
}
}
// loadConfiguration manages dynamically frontends, backends and TLS configurations
func ( server * Server ) loadConfiguration ( configMsg types . ConfigMessage ) {
currentConfigurations := server . currentConfigurations . Get ( ) . ( types . Configurations )
// Copy configurations to new map so we don't change current if LoadConfig fails
newConfigurations := make ( types . Configurations )
for k , v := range currentConfigurations {
newConfigurations [ k ] = v
}
newConfigurations [ configMsg . ProviderName ] = configMsg . Configuration
newServerEntryPoints , err := server . loadConfig ( newConfigurations , server . globalConfiguration )
if err == nil {
for newServerEntryPointName , newServerEntryPoint := range newServerEntryPoints {
server . serverEntryPoints [ newServerEntryPointName ] . httpRouter . UpdateHandler ( newServerEntryPoint . httpRouter . GetHandler ( ) )
if & newServerEntryPoint . certs != nil {
server . serverEntryPoints [ newServerEntryPointName ] . certs . Set ( newServerEntryPoint . certs . Get ( ) )
}
log . Infof ( "Server configuration reloaded on %s" , server . serverEntryPoints [ newServerEntryPointName ] . httpServer . Addr )
}
server . currentConfigurations . Set ( newConfigurations )
server . postLoadConfiguration ( )
} else {
log . Error ( "Error loading new configuration, aborted " , err )
}
}
2016-04-13 18:36:23 +00:00
2017-11-09 11:16:03 +00:00
// loadHTTPSConfiguration add/delete HTTPS certificate managed dynamically
func ( server * Server ) loadHTTPSConfiguration ( configurations types . Configurations ) ( map [ string ] * traefikTls . DomainsCertificates , error ) {
newEPCertificates := make ( map [ string ] * traefikTls . DomainsCertificates )
// Get all certificates
for _ , configuration := range configurations {
if configuration . TLSConfiguration != nil && len ( configuration . TLSConfiguration ) > 0 {
if err := traefikTls . SortTLSConfigurationPerEntryPoints ( configuration . TLSConfiguration , newEPCertificates ) ; err != nil {
return nil , err
2016-06-22 16:31:14 +00:00
}
2017-11-09 11:16:03 +00:00
}
}
return newEPCertificates , nil
}
2016-06-22 16:31:14 +00:00
2017-11-09 11:16:03 +00:00
// getCertificate allows to customize tlsConfig.Getcertificate behaviour to get the certificates inserted dynamically
func ( s * serverEntryPoint ) getCertificate ( clientHello * tls . ClientHelloInfo ) ( * tls . Certificate , error ) {
if s . certs . Get ( ) != nil {
domainToCheck := types . CanonicalDomain ( clientHello . ServerName )
for domains , cert := range * s . certs . Get ( ) . ( * traefikTls . DomainsCertificates ) {
for _ , domain := range strings . Split ( domains , "," ) {
selector := "^" + strings . Replace ( domain , "*." , "[^\\.]*\\.?" , - 1 ) + "$"
domainCheck , _ := regexp . MatchString ( selector , domainToCheck )
if domainCheck {
return cert , nil
2016-04-13 18:36:23 +00:00
}
2016-01-13 21:45:49 +00:00
}
}
}
2017-11-09 11:16:03 +00:00
return nil , nil
2016-01-13 21:45:49 +00:00
}
2017-11-09 11:16:03 +00:00
func ( server * Server ) postLoadConfiguration ( ) {
2016-08-18 12:20:11 +00:00
if server . globalConfiguration . ACME == nil {
return
}
if server . leadership != nil && ! server . leadership . IsLeader ( ) {
return
}
if server . globalConfiguration . ACME . OnHostRule {
2017-08-25 14:10:03 +00:00
currentConfigurations := server . currentConfigurations . Get ( ) . ( types . Configurations )
for _ , config := range currentConfigurations {
for _ , frontend := range config . Frontends {
2017-01-05 11:32:56 +00:00
// check if one of the frontend entrypoints is configured with TLS
2017-04-07 13:48:58 +00:00
// and is configured with ACME
ACMEEnabled := false
2017-11-09 11:16:03 +00:00
for _ , entryPoint := range frontend . EntryPoints {
if server . globalConfiguration . ACME . EntryPoint == entryPoint && server . globalConfiguration . EntryPoints [ entryPoint ] . TLS != nil {
2017-04-07 13:48:58 +00:00
ACMEEnabled = true
2017-01-05 11:32:56 +00:00
break
2016-08-05 18:42:45 +00:00
}
}
2017-04-07 13:48:58 +00:00
if ACMEEnabled {
2017-01-05 11:32:56 +00:00
for _ , route := range frontend . Routes {
rules := Rules { }
domains , err := rules . ParseDomains ( route . Rule )
if err != nil {
log . Errorf ( "Error parsing domains: %v" , err )
} else {
server . globalConfiguration . ACME . LoadCertificateForDomains ( domains )
}
}
}
2016-08-05 18:42:45 +00:00
}
}
}
}
2016-01-13 21:45:49 +00:00
func ( server * Server ) configureProviders ( ) {
// configure providers
if server . globalConfiguration . Docker != nil {
server . providers = append ( server . providers , server . globalConfiguration . Docker )
}
if server . globalConfiguration . Marathon != nil {
server . providers = append ( server . providers , server . globalConfiguration . Marathon )
}
if server . globalConfiguration . File != nil {
server . providers = append ( server . providers , server . globalConfiguration . File )
}
2017-11-09 15:12:04 +00:00
if server . globalConfiguration . Rest != nil {
server . providers = append ( server . providers , server . globalConfiguration . Rest )
server . globalConfiguration . Rest . CurrentConfigurations = & server . currentConfigurations
2016-01-13 21:45:49 +00:00
}
if server . globalConfiguration . Consul != nil {
server . providers = append ( server . providers , server . globalConfiguration . Consul )
}
2016-02-02 17:03:40 +00:00
if server . globalConfiguration . ConsulCatalog != nil {
server . providers = append ( server . providers , server . globalConfiguration . ConsulCatalog )
}
2016-01-13 21:45:49 +00:00
if server . globalConfiguration . Etcd != nil {
server . providers = append ( server . providers , server . globalConfiguration . Etcd )
}
if server . globalConfiguration . Zookeeper != nil {
server . providers = append ( server . providers , server . globalConfiguration . Zookeeper )
}
if server . globalConfiguration . Boltdb != nil {
server . providers = append ( server . providers , server . globalConfiguration . Boltdb )
}
2016-02-08 20:57:32 +00:00
if server . globalConfiguration . Kubernetes != nil {
server . providers = append ( server . providers , server . globalConfiguration . Kubernetes )
}
2016-07-20 09:56:14 +00:00
if server . globalConfiguration . Mesos != nil {
server . providers = append ( server . providers , server . globalConfiguration . Mesos )
}
2016-08-31 20:43:05 +00:00
if server . globalConfiguration . Eureka != nil {
server . providers = append ( server . providers , server . globalConfiguration . Eureka )
}
2017-01-05 14:24:17 +00:00
if server . globalConfiguration . ECS != nil {
server . providers = append ( server . providers , server . globalConfiguration . ECS )
}
2017-01-28 23:01:56 +00:00
if server . globalConfiguration . Rancher != nil {
server . providers = append ( server . providers , server . globalConfiguration . Rancher )
2017-03-09 01:53:34 +00:00
}
if server . globalConfiguration . DynamoDB != nil {
server . providers = append ( server . providers , server . globalConfiguration . DynamoDB )
2017-01-28 23:01:56 +00:00
}
2016-01-13 21:45:49 +00:00
}
func ( server * Server ) startProviders ( ) {
// start providers
2017-08-25 14:10:03 +00:00
for _ , p := range server . providers {
providerType := reflect . TypeOf ( p )
jsonConf , _ := json . Marshal ( p )
2017-03-07 12:09:11 +00:00
log . Infof ( "Starting provider %v %s" , providerType , jsonConf )
2017-08-25 14:10:03 +00:00
currentProvider := p
2016-03-31 16:57:08 +00:00
safe . Go ( func ( ) {
2016-08-18 11:03:10 +00:00
err := currentProvider . Provide ( server . configurationChan , server . routinesPool , server . globalConfiguration . Constraints )
2016-01-13 21:45:49 +00:00
if err != nil {
2017-03-07 12:09:11 +00:00
log . Errorf ( "Error starting provider %v: %s" , providerType , err )
2016-01-13 21:45:49 +00:00
}
2016-03-31 16:57:08 +00:00
} )
2016-01-13 21:45:49 +00:00
}
}
2017-11-09 11:16:03 +00:00
func createClientTLSConfig ( entryPointName string , tlsOption * traefikTls . TLS ) ( * tls . Config , error ) {
2017-04-06 22:10:02 +00:00
if tlsOption == nil {
return nil , errors . New ( "no TLS provided" )
}
2017-11-09 11:16:03 +00:00
config , _ , err := tlsOption . Certificates . CreateTLSConfig ( entryPointName )
2017-04-06 22:10:02 +00:00
if err != nil {
return nil , err
}
if len ( tlsOption . ClientCAFiles ) > 0 {
2017-11-10 09:30:04 +00:00
log . Warnf ( "Deprecated configuration found during client TLS configuration creation: %s. Please use %s (which allows to make the CA Files optional)." , "tls.ClientCAFiles" , "tls.ClientCA.files" )
tlsOption . ClientCA . Files = tlsOption . ClientCAFiles
tlsOption . ClientCA . Optional = false
}
if len ( tlsOption . ClientCA . Files ) > 0 {
2017-04-06 22:10:02 +00:00
pool := x509 . NewCertPool ( )
2017-11-10 09:30:04 +00:00
for _ , caFile := range tlsOption . ClientCA . Files {
2017-04-06 22:10:02 +00:00
data , err := ioutil . ReadFile ( caFile )
if err != nil {
return nil , err
}
if ! pool . AppendCertsFromPEM ( data ) {
return nil , errors . New ( "invalid certificate(s) in " + caFile )
}
}
config . RootCAs = pool
}
config . BuildNameToCertificate ( )
return config , nil
}
2016-01-13 21:45:49 +00:00
// creates a TLS config that allows terminating HTTPS for multiple domains using SNI
2017-11-09 11:16:03 +00:00
func ( server * Server ) createTLSConfig ( entryPointName string , tlsOption * traefikTls . TLS , router * middlewares . HandlerSwitcher ) ( * tls . Config , error ) {
2016-01-29 19:34:17 +00:00
if tlsOption == nil {
return nil , nil
}
2016-01-13 21:45:49 +00:00
2017-11-09 11:16:03 +00:00
config , epDomainsCertificates , err := tlsOption . Certificates . CreateTLSConfig ( entryPointName )
2016-06-27 10:19:14 +00:00
if err != nil {
return nil , err
2016-03-21 10:10:18 +00:00
}
2017-11-09 11:16:03 +00:00
epDomainsCertificatesTmp := new ( traefikTls . DomainsCertificates )
if epDomainsCertificates [ entryPointName ] != nil {
epDomainsCertificatesTmp = epDomainsCertificates [ entryPointName ]
} else {
* epDomainsCertificatesTmp = make ( map [ string ] * tls . Certificate )
}
server . serverEntryPoints [ entryPointName ] . certs . Set ( epDomainsCertificatesTmp )
2016-11-09 16:56:41 +00:00
// ensure http2 enabled
config . NextProtos = [ ] string { "h2" , "http/1.1" }
2016-06-15 20:38:40 +00:00
if len ( tlsOption . ClientCAFiles ) > 0 {
2017-11-10 09:30:04 +00:00
log . Warnf ( "Deprecated configuration found during TLS configuration creation: %s. Please use %s (which allows to make the CA Files optional)." , "tls.ClientCAFiles" , "tls.ClientCA.files" )
tlsOption . ClientCA . Files = tlsOption . ClientCAFiles
tlsOption . ClientCA . Optional = false
}
if len ( tlsOption . ClientCA . Files ) > 0 {
2016-06-15 20:38:40 +00:00
pool := x509 . NewCertPool ( )
2017-11-10 09:30:04 +00:00
for _ , caFile := range tlsOption . ClientCA . Files {
2016-06-15 20:38:40 +00:00
data , err := ioutil . ReadFile ( caFile )
if err != nil {
return nil , err
}
ok := pool . AppendCertsFromPEM ( data )
if ! ok {
return nil , errors . New ( "invalid certificate(s) in " + caFile )
}
}
config . ClientCAs = pool
2017-11-10 09:30:04 +00:00
if tlsOption . ClientCA . Optional {
config . ClientAuth = tls . VerifyClientCertIfGiven
} else {
config . ClientAuth = tls . RequireAndVerifyClientCert
}
2016-06-15 20:38:40 +00:00
}
2016-03-21 10:10:18 +00:00
if server . globalConfiguration . ACME != nil {
if _ , ok := server . serverEntryPoints [ server . globalConfiguration . ACME . EntryPoint ] ; ok {
if entryPointName == server . globalConfiguration . ACME . EntryPoint {
checkOnDemandDomain := func ( domain string ) bool {
2016-07-11 14:43:23 +00:00
routeMatch := & mux . RouteMatch { }
router := router . GetHandler ( )
match := router . Match ( & http . Request { URL : & url . URL { } , Host : domain } , routeMatch )
if match && routeMatch . Route != nil {
2016-03-21 10:10:18 +00:00
return true
}
return false
}
2016-08-18 12:20:11 +00:00
if server . leadership == nil {
2017-11-09 11:16:03 +00:00
err := server . globalConfiguration . ACME . CreateLocalConfig ( config , & server . serverEntryPoints [ entryPointName ] . certs , checkOnDemandDomain )
2016-08-18 12:20:11 +00:00
if err != nil {
return nil , err
}
} else {
2017-11-09 11:16:03 +00:00
err := server . globalConfiguration . ACME . CreateClusterConfig ( server . leadership , config , & server . serverEntryPoints [ entryPointName ] . certs , checkOnDemandDomain )
2016-08-18 12:20:11 +00:00
if err != nil {
return nil , err
}
2016-03-21 10:10:18 +00:00
}
}
} else {
return nil , errors . New ( "Unknown entrypoint " + server . globalConfiguration . ACME . EntryPoint + " for ACME configuration" )
}
2017-11-09 11:16:03 +00:00
} else {
config . GetCertificate = server . serverEntryPoints [ entryPointName ] . getCertificate
2016-03-21 10:10:18 +00:00
}
if len ( config . Certificates ) == 0 {
return nil , errors . New ( "No certificates found for TLS entrypoint " + entryPointName )
2016-01-13 21:45:49 +00:00
}
// BuildNameToCertificate parses the CommonName and SubjectAlternateName fields
// in each certificate and populates the config.NameToCertificate map.
config . BuildNameToCertificate ( )
2016-09-20 06:06:06 +00:00
//Set the minimum TLS version if set in the config TOML
2017-11-09 11:16:03 +00:00
if minConst , exists := traefikTls . MinVersion [ server . globalConfiguration . EntryPoints [ entryPointName ] . TLS . MinVersion ] ; exists {
2016-09-20 06:06:06 +00:00
config . PreferServerCipherSuites = true
config . MinVersion = minConst
}
//Set the list of CipherSuites if set in the config TOML
if server . globalConfiguration . EntryPoints [ entryPointName ] . TLS . CipherSuites != nil {
//if our list of CipherSuites is defined in the entrypoint config, we can re-initilize the suites list as empty
config . CipherSuites = make ( [ ] uint16 , 0 )
for _ , cipher := range server . globalConfiguration . EntryPoints [ entryPointName ] . TLS . CipherSuites {
2017-11-09 11:16:03 +00:00
if cipherConst , exists := traefikTls . CipherSuites [ cipher ] ; exists {
2016-09-20 06:06:06 +00:00
config . CipherSuites = append ( config . CipherSuites , cipherConst )
} else {
//CipherSuite listed in the toml does not exist in our listed
return nil , errors . New ( "Invalid CipherSuite: " + cipher )
}
}
}
2016-01-13 21:45:49 +00:00
return config , nil
}
2017-08-25 19:32:03 +00:00
func ( server * Server ) startServer ( serverEntryPoint * serverEntryPoint , globalConfiguration configuration . GlobalConfiguration ) {
log . Infof ( "Starting server on %s" , serverEntryPoint . httpServer . Addr )
2017-03-09 22:27:09 +00:00
var err error
2017-08-25 19:32:03 +00:00
if serverEntryPoint . httpServer . TLSConfig != nil {
err = serverEntryPoint . httpServer . ServeTLS ( serverEntryPoint . listener , "" , "" )
2016-01-13 21:45:49 +00:00
} else {
2017-08-25 19:32:03 +00:00
err = serverEntryPoint . httpServer . Serve ( serverEntryPoint . listener )
2017-03-09 22:27:09 +00:00
}
2017-10-11 08:38:03 +00:00
if err != http . ErrServerClosed {
2017-03-09 22:27:09 +00:00
log . Error ( "Error creating server: " , err )
2016-01-13 21:45:49 +00:00
}
}
2017-11-09 15:12:04 +00:00
func ( server * Server ) addInternalRoutes ( entryPointName string , router * mux . Router ) {
if server . globalConfiguration . Metrics != nil && server . globalConfiguration . Metrics . Prometheus != nil && server . globalConfiguration . Metrics . Prometheus . EntryPoint == entryPointName {
metrics . PrometheusHandler { } . AddRoutes ( router )
}
if server . globalConfiguration . Rest != nil && server . globalConfiguration . Rest . EntryPoint == entryPointName {
server . globalConfiguration . Rest . AddRoutes ( router )
}
if server . globalConfiguration . API != nil && server . globalConfiguration . API . EntryPoint == entryPointName {
server . globalConfiguration . API . AddRoutes ( router )
}
}
func ( server * Server ) addInternalPublicRoutes ( entryPointName string , router * mux . Router ) {
if server . globalConfiguration . Ping != nil && server . globalConfiguration . Ping . EntryPoint != "" && server . globalConfiguration . Ping . EntryPoint == entryPointName {
server . globalConfiguration . Ping . AddRoutes ( router )
}
}
func ( server * Server ) prepareServer ( entryPointName string , entryPoint * configuration . EntryPoint , router * middlewares . HandlerSwitcher , middlewares [ ] negroni . Handler , internalMiddlewares [ ] negroni . Handler ) ( * http . Server , net . Listener , error ) {
2017-08-18 13:34:04 +00:00
readTimeout , writeTimeout , idleTimeout := buildServerTimeouts ( server . globalConfiguration )
log . Infof ( "Preparing server %s %+v with readTimeout=%s writeTimeout=%s idleTimeout=%s" , entryPointName , entryPoint , readTimeout , writeTimeout , idleTimeout )
2016-01-13 21:45:49 +00:00
// middlewares
2017-08-18 13:34:04 +00:00
n := negroni . New ( )
2016-01-13 21:45:49 +00:00
for _ , middleware := range middlewares {
2017-08-18 13:34:04 +00:00
n . Use ( middleware )
2016-01-13 21:45:49 +00:00
}
2017-08-18 13:34:04 +00:00
n . UseHandler ( router )
2017-11-09 15:12:04 +00:00
path := "/"
if server . globalConfiguration . Web != nil && server . globalConfiguration . Web . Path != "" {
path = server . globalConfiguration . Web . Path
}
internalMuxRouter := server . buildInternalRouter ( entryPointName , path , internalMiddlewares )
internalMuxRouter . NotFoundHandler = n
2016-03-21 10:10:18 +00:00
tlsConfig , err := server . createTLSConfig ( entryPointName , entryPoint . TLS , router )
2016-01-13 21:45:49 +00:00
if err != nil {
2017-01-12 10:04:11 +00:00
log . Errorf ( "Error creating TLS config: %s" , err )
2017-08-25 19:32:03 +00:00
return nil , nil , err
}
listener , err := net . Listen ( "tcp" , entryPoint . Address )
if err != nil {
log . Error ( "Error opening listener " , err )
2017-09-07 18:14:03 +00:00
return nil , nil , err
2017-08-25 19:32:03 +00:00
}
2017-10-10 12:50:03 +00:00
if entryPoint . ProxyProtocol != nil {
2017-10-16 10:46:03 +00:00
IPs , err := whitelist . NewIP ( entryPoint . ProxyProtocol . TrustedIPs , entryPoint . ProxyProtocol . Insecure )
2017-10-10 12:50:03 +00:00
if err != nil {
2017-11-09 15:12:04 +00:00
return nil , nil , fmt . Errorf ( "error creating whitelist: %s" , err )
2017-10-10 12:50:03 +00:00
}
log . Infof ( "Enabling ProxyProtocol for trusted IPs %v" , entryPoint . ProxyProtocol . TrustedIPs )
listener = & proxyproto . Listener {
Listener : listener ,
SourceCheck : func ( addr net . Addr ) ( bool , error ) {
ip , ok := addr . ( * net . TCPAddr )
if ! ok {
2017-11-09 15:12:04 +00:00
return false , fmt . Errorf ( "type error %v" , addr )
2017-10-10 12:50:03 +00:00
}
return IPs . ContainsIP ( ip . IP )
} ,
}
2016-01-13 21:45:49 +00:00
}
2017-03-09 22:27:09 +00:00
return & http . Server {
2017-08-25 19:32:03 +00:00
Addr : entryPoint . Address ,
2017-11-09 15:12:04 +00:00
Handler : internalMuxRouter ,
2017-08-25 19:32:03 +00:00
TLSConfig : tlsConfig ,
ReadTimeout : readTimeout ,
WriteTimeout : writeTimeout ,
IdleTimeout : idleTimeout ,
2017-11-18 00:10:03 +00:00
ErrorLog : httpServerLogger ,
2017-08-25 19:32:03 +00:00
} ,
listener ,
nil
2016-01-13 21:45:49 +00:00
}
2017-11-09 15:12:04 +00:00
func ( server * Server ) buildInternalRouter ( entryPointName , path string , internalMiddlewares [ ] negroni . Handler ) * mux . Router {
internalMuxRouter := mux . NewRouter ( )
internalMuxRouter . StrictSlash ( true )
internalMuxRouter . SkipClean ( true )
internalMuxSubrouter := internalMuxRouter . PathPrefix ( path ) . Subrouter ( )
internalMuxSubrouter . StrictSlash ( true )
internalMuxSubrouter . SkipClean ( true )
server . addInternalRoutes ( entryPointName , internalMuxSubrouter )
internalMuxRouter . Walk ( wrapRoute ( internalMiddlewares ) )
server . addInternalPublicRoutes ( entryPointName , internalMuxSubrouter )
return internalMuxRouter
}
// wrapRoute with middlewares
func wrapRoute ( middlewares [ ] negroni . Handler ) func ( * mux . Route , * mux . Router , [ ] * mux . Route ) error {
return func ( route * mux . Route , router * mux . Router , ancestors [ ] * mux . Route ) error {
middles := append ( middlewares , negroni . Wrap ( route . GetHandler ( ) ) )
route . Handler ( negroni . New ( middles ... ) )
return nil
}
}
2017-08-25 14:10:03 +00:00
func buildServerTimeouts ( globalConfig configuration . GlobalConfiguration ) ( readTimeout , writeTimeout , idleTimeout time . Duration ) {
2017-08-18 13:34:04 +00:00
readTimeout = time . Duration ( 0 )
writeTimeout = time . Duration ( 0 )
if globalConfig . RespondingTimeouts != nil {
readTimeout = time . Duration ( globalConfig . RespondingTimeouts . ReadTimeout )
writeTimeout = time . Duration ( globalConfig . RespondingTimeouts . WriteTimeout )
}
2017-09-20 16:14:03 +00:00
// Prefer legacy idle timeout parameter for backwards compatibility reasons
if globalConfig . IdleTimeout > 0 {
2017-08-18 13:34:04 +00:00
idleTimeout = time . Duration ( globalConfig . IdleTimeout )
2017-09-20 16:14:03 +00:00
log . Warn ( "top-level idle timeout configuration has been deprecated -- please use responding timeouts" )
} else if globalConfig . RespondingTimeouts != nil {
idleTimeout = time . Duration ( globalConfig . RespondingTimeouts . IdleTimeout )
2017-08-18 13:34:04 +00:00
} else {
2017-08-25 14:10:03 +00:00
idleTimeout = time . Duration ( configuration . DefaultIdleTimeout )
2017-08-18 13:34:04 +00:00
}
return readTimeout , writeTimeout , idleTimeout
}
2017-08-25 14:10:03 +00:00
func ( server * Server ) buildEntryPoints ( globalConfiguration configuration . GlobalConfiguration ) map [ string ] * serverEntryPoint {
2016-02-25 17:30:13 +00:00
serverEntryPoints := make ( map [ string ] * serverEntryPoint )
2016-01-29 19:34:17 +00:00
for entryPointName := range globalConfiguration . EntryPoints {
router := server . buildDefaultHTTPRouter ( )
2016-02-25 17:30:13 +00:00
serverEntryPoints [ entryPointName ] = & serverEntryPoint {
2016-03-04 10:32:23 +00:00
httpRouter : middlewares . NewHandlerSwitcher ( router ) ,
2016-01-29 19:34:17 +00:00
}
}
return serverEntryPoints
}
2017-08-18 13:34:04 +00:00
// getRoundTripper will either use server.defaultForwardingRoundTripper or create a new one
// given a custom TLS configuration is passed and the passTLSCert option is set to true.
2017-11-09 11:16:03 +00:00
func ( server * Server ) getRoundTripper ( entryPointName string , globalConfiguration configuration . GlobalConfiguration , passTLSCert bool , tls * traefikTls . TLS ) ( http . RoundTripper , error ) {
2017-08-18 13:34:04 +00:00
if passTLSCert {
2017-11-09 11:16:03 +00:00
tlsConfig , err := createClientTLSConfig ( entryPointName , tls )
2017-08-18 13:34:04 +00:00
if err != nil {
log . Errorf ( "Failed to create TLSClientConfig: %s" , err )
return nil , err
}
transport := createHTTPTransport ( globalConfiguration )
transport . TLSClientConfig = tlsConfig
return transport , nil
2017-04-06 22:10:02 +00:00
}
2017-08-18 13:34:04 +00:00
return server . defaultForwardingRoundTripper , nil
2017-04-06 22:10:02 +00:00
}
2016-01-13 21:45:49 +00:00
// LoadConfig returns a new gorilla.mux Route from the specified global configuration and the dynamic
// provider configurations.
2017-08-25 14:10:03 +00:00
func ( server * Server ) loadConfig ( configurations types . Configurations , globalConfiguration configuration . GlobalConfiguration ) ( map [ string ] * serverEntryPoint , error ) {
2016-01-29 19:34:17 +00:00
serverEntryPoints := server . buildEntryPoints ( globalConfiguration )
2017-03-10 18:43:20 +00:00
redirectHandlers := make ( map [ string ] negroni . Handler )
2016-01-13 21:45:49 +00:00
backends := map [ string ] http . Handler { }
2017-08-25 14:10:03 +00:00
backendsHealthCheck := map [ string ] * healthcheck . BackendHealthCheck { }
2017-05-03 08:20:33 +00:00
errorHandler := NewRecordingErrorHandler ( middlewares . DefaultNetErrorRecorder { } )
2017-04-13 14:37:07 +00:00
2017-08-25 14:10:03 +00:00
for _ , config := range configurations {
frontendNames := sortedFrontendNamesForConfig ( config )
2016-06-20 13:19:52 +00:00
frontend :
2016-02-19 22:55:23 +00:00
for _ , frontendName := range frontendNames {
2017-08-25 14:10:03 +00:00
frontend := config . Frontends [ frontendName ]
2016-02-19 22:55:23 +00:00
2016-01-29 19:34:17 +00:00
log . Debugf ( "Creating frontend %s" , frontendName )
2016-07-04 17:30:32 +00:00
2016-03-22 00:32:02 +00:00
if len ( frontend . EntryPoints ) == 0 {
2016-06-20 13:19:52 +00:00
log . Errorf ( "No entrypoint defined for frontend %s, defaultEntryPoints:%s" , frontendName , globalConfiguration . DefaultEntryPoints )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-03-22 00:32:02 +00:00
}
2017-04-19 09:14:05 +00:00
2016-01-29 19:34:17 +00:00
for _ , entryPointName := range frontend . EntryPoints {
log . Debugf ( "Wiring frontend %s to entryPoint %s" , frontendName , entryPointName )
if _ , ok := serverEntryPoints [ entryPointName ] ; ! ok {
2016-06-20 13:19:52 +00:00
log . Errorf ( "Undefined entrypoint '%s' for frontend %s" , entryPointName , frontendName )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-01-13 21:45:49 +00:00
}
2017-04-19 09:14:05 +00:00
2016-03-27 00:05:17 +00:00
newServerRoute := & serverRoute { route : serverEntryPoints [ entryPointName ] . httpRouter . GetHandler ( ) . NewRoute ( ) . Name ( frontendName ) }
2016-01-29 19:34:17 +00:00
for routeName , route := range frontend . Routes {
2016-03-30 17:05:43 +00:00
err := getRoute ( newServerRoute , & route )
2016-01-29 19:34:17 +00:00
if err != nil {
2016-06-20 13:19:52 +00:00
log . Errorf ( "Error creating route for frontend %s: %v" , frontendName , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-01-29 19:34:17 +00:00
}
2016-03-30 17:05:43 +00:00
log . Debugf ( "Creating route %s %s" , routeName , route . Rule )
2016-01-13 21:45:49 +00:00
}
2017-04-19 09:14:05 +00:00
2016-01-29 19:34:17 +00:00
entryPoint := globalConfiguration . EntryPoints [ entryPointName ]
2017-08-25 14:10:03 +00:00
n := negroni . New ( )
2016-01-29 19:34:17 +00:00
if entryPoint . Redirect != nil {
if redirectHandlers [ entryPointName ] != nil {
2017-08-25 14:10:03 +00:00
n . Use ( redirectHandlers [ entryPointName ] )
2016-01-29 19:34:17 +00:00
} else if handler , err := server . loadEntryPointConfig ( entryPointName , entryPoint ) ; err != nil {
2016-06-20 13:19:52 +00:00
log . Errorf ( "Error loading entrypoint configuration for frontend %s: %v" , frontendName , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-01-29 19:34:17 +00:00
} else {
2017-05-22 19:39:29 +00:00
if server . accessLoggerMiddleware != nil {
saveFrontend := accesslog . NewSaveNegroniFrontend ( handler , frontendName )
2017-08-25 14:10:03 +00:00
n . Use ( saveFrontend )
2017-05-22 19:39:29 +00:00
redirectHandlers [ entryPointName ] = saveFrontend
} else {
2017-08-25 14:10:03 +00:00
n . Use ( handler )
2017-05-22 19:39:29 +00:00
redirectHandlers [ entryPointName ] = handler
}
2016-01-13 21:45:49 +00:00
}
2017-03-10 18:43:20 +00:00
}
2017-04-13 14:37:07 +00:00
if backends [ entryPointName + frontend . Backend ] == nil {
2017-03-10 18:43:20 +00:00
log . Debugf ( "Creating backend %s" , frontend . Backend )
2017-04-06 22:10:02 +00:00
2017-11-09 11:16:03 +00:00
roundTripper , err := server . getRoundTripper ( entryPointName , globalConfiguration , frontend . PassTLSCert , entryPoint . TLS )
2017-08-18 13:34:04 +00:00
if err != nil {
log . Errorf ( "Failed to create RoundTripper for frontend %s: %v" , frontendName , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2017-04-06 22:10:02 +00:00
}
2017-10-16 10:46:03 +00:00
rewriter , err := NewHeaderRewriter ( entryPoint . ForwardedHeaders . TrustedIPs , entryPoint . ForwardedHeaders . Insecure )
if err != nil {
log . Errorf ( "Error creating rewriter for frontend %s: %v" , frontendName , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
}
2017-05-03 08:20:33 +00:00
fwd , err := forward . New (
2017-11-22 17:20:03 +00:00
forward . Stream ( true ) ,
2017-05-03 08:20:33 +00:00
forward . PassHostHeader ( frontend . PassHostHeader ) ,
2017-08-18 13:34:04 +00:00
forward . RoundTripper ( roundTripper ) ,
2017-05-03 08:20:33 +00:00
forward . ErrorHandler ( errorHandler ) ,
2017-10-16 10:46:03 +00:00
forward . Rewriter ( rewriter ) ,
2017-05-03 08:20:33 +00:00
)
2017-08-18 13:34:04 +00:00
2017-04-06 22:10:02 +00:00
if err != nil {
log . Errorf ( "Error creating forwarder for frontend %s: %v" , frontendName , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
}
2017-05-22 19:39:29 +00:00
var rr * roundrobin . RoundRobin
var saveFrontend http . Handler
if server . accessLoggerMiddleware != nil {
saveBackend := accesslog . NewSaveBackend ( fwd , frontend . Backend )
saveFrontend = accesslog . NewSaveFrontend ( saveBackend , frontendName )
rr , _ = roundrobin . New ( saveFrontend )
} else {
rr , _ = roundrobin . New ( fwd )
}
2017-04-06 22:10:02 +00:00
2017-08-25 14:10:03 +00:00
if config . Backends [ frontend . Backend ] == nil {
2017-03-10 18:43:20 +00:00
log . Errorf ( "Undefined backend '%s' for frontend %s" , frontend . Backend , frontendName )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
}
2016-05-13 14:22:11 +00:00
2017-08-25 14:10:03 +00:00
lbMethod , err := types . NewLoadBalancerMethod ( config . Backends [ frontend . Backend ] . LoadBalancer )
2017-03-10 18:43:20 +00:00
if err != nil {
2017-08-25 14:10:03 +00:00
log . Errorf ( "Error loading load balancer method '%+v' for frontend %s: %v" , config . Backends [ frontend . Backend ] . LoadBalancer , frontendName , err )
2017-03-10 18:43:20 +00:00
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
}
2016-05-13 14:22:11 +00:00
2017-03-10 18:43:20 +00:00
var sticky * roundrobin . StickySession
2017-10-10 09:10:02 +00:00
var cookieName string
if stickiness := config . Backends [ frontend . Backend ] . LoadBalancer . Stickiness ; stickiness != nil {
cookieName = cookie . GetName ( stickiness . CookieName , frontend . Backend )
2017-08-25 14:10:03 +00:00
sticky = roundrobin . NewStickySession ( cookieName )
2017-03-10 18:43:20 +00:00
}
2016-05-13 14:22:11 +00:00
2017-08-18 13:34:04 +00:00
var lb http . Handler
2017-04-13 14:37:07 +00:00
switch lbMethod {
case types . Drr :
log . Debugf ( "Creating load-balancer drr" )
2017-11-22 17:20:03 +00:00
rebalancer , _ := roundrobin . NewRebalancer ( rr )
2017-10-10 09:10:02 +00:00
if sticky != nil {
2017-08-25 14:10:03 +00:00
log . Debugf ( "Sticky session with cookie %v" , cookieName )
2017-11-22 17:20:03 +00:00
rebalancer , _ = roundrobin . NewRebalancer ( rr , roundrobin . RebalancerStickySession ( sticky ) )
2017-04-13 14:37:07 +00:00
}
lb = rebalancer
2017-08-25 14:10:03 +00:00
if err := configureLBServers ( rebalancer , config , frontend ) ; err != nil {
2017-07-08 08:33:17 +00:00
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
}
2017-08-25 14:10:03 +00:00
hcOpts := parseHealthCheckOptions ( rebalancer , frontend . Backend , config . Backends [ frontend . Backend ] . HealthCheck , globalConfiguration . HealthCheck )
2017-07-08 08:33:17 +00:00
if hcOpts != nil {
log . Debugf ( "Setting up backend health check %s" , * hcOpts )
2017-11-21 10:06:03 +00:00
hcOpts . Transport = server . defaultForwardingRoundTripper
2017-08-25 14:10:03 +00:00
backendsHealthCheck [ entryPointName + frontend . Backend ] = healthcheck . NewBackendHealthCheck ( * hcOpts )
2016-01-29 19:34:17 +00:00
}
2017-07-10 10:11:44 +00:00
lb = middlewares . NewEmptyBackendHandler ( rebalancer , lb )
2017-04-13 14:37:07 +00:00
case types . Wrr :
log . Debugf ( "Creating load-balancer wrr" )
2017-10-10 09:10:02 +00:00
if sticky != nil {
2017-08-25 14:10:03 +00:00
log . Debugf ( "Sticky session with cookie %v" , cookieName )
2017-05-22 19:39:29 +00:00
if server . accessLoggerMiddleware != nil {
rr , _ = roundrobin . New ( saveFrontend , roundrobin . EnableStickySession ( sticky ) )
} else {
rr , _ = roundrobin . New ( fwd , roundrobin . EnableStickySession ( sticky ) )
}
2017-04-13 14:37:07 +00:00
}
lb = rr
2017-08-25 14:10:03 +00:00
if err := configureLBServers ( rr , config , frontend ) ; err != nil {
2017-07-08 08:33:17 +00:00
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-04-13 08:11:36 +00:00
}
2017-08-25 14:10:03 +00:00
hcOpts := parseHealthCheckOptions ( rr , frontend . Backend , config . Backends [ frontend . Backend ] . HealthCheck , globalConfiguration . HealthCheck )
2017-04-13 14:37:07 +00:00
if hcOpts != nil {
log . Debugf ( "Setting up backend health check %s" , * hcOpts )
2017-11-21 10:06:03 +00:00
hcOpts . Transport = server . defaultForwardingRoundTripper
2017-08-25 14:10:03 +00:00
backendsHealthCheck [ entryPointName + frontend . Backend ] = healthcheck . NewBackendHealthCheck ( * hcOpts )
2016-03-29 20:25:32 +00:00
}
2017-07-10 10:11:44 +00:00
lb = middlewares . NewEmptyBackendHandler ( rr , lb )
2017-04-13 14:37:07 +00:00
}
2017-06-30 23:04:18 +00:00
if len ( frontend . Errors ) > 0 {
for _ , errorPage := range frontend . Errors {
2017-08-25 14:10:03 +00:00
if config . Backends [ errorPage . Backend ] != nil && config . Backends [ errorPage . Backend ] . Servers [ "error" ] . URL != "" {
errorPageHandler , err := middlewares . NewErrorPagesHandler ( errorPage , config . Backends [ errorPage . Backend ] . Servers [ "error" ] . URL )
2017-06-30 23:04:18 +00:00
if err != nil {
log . Errorf ( "Error creating custom error page middleware, %v" , err )
} else {
2017-08-25 14:10:03 +00:00
n . Use ( errorPageHandler )
2017-06-30 23:04:18 +00:00
}
} else {
log . Errorf ( "Error Page is configured for Frontend %s, but either Backend %s is not set or Backend URL is missing" , frontendName , errorPage . Backend )
}
}
}
2017-09-09 11:36:03 +00:00
if frontend . RateLimit != nil && len ( frontend . RateLimit . RateSet ) > 0 {
lb , err = server . buildRateLimiter ( lb , frontend . RateLimit )
if err != nil {
log . Errorf ( "Error creating rate limiter: %v" , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
}
}
2017-08-25 14:10:03 +00:00
maxConns := config . Backends [ frontend . Backend ] . MaxConn
2017-04-13 14:37:07 +00:00
if maxConns != nil && maxConns . Amount != 0 {
extractFunc , err := utils . NewExtractor ( maxConns . ExtractorFunc )
if err != nil {
log . Errorf ( "Error creating connlimit: %v" , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
}
log . Debugf ( "Creating load-balancer connlimit" )
2017-11-22 17:20:03 +00:00
lb , err = connlimit . New ( lb , extractFunc , maxConns . Amount )
2017-04-13 14:37:07 +00:00
if err != nil {
log . Errorf ( "Error creating connlimit: %v" , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
}
}
2017-04-18 06:22:06 +00:00
2017-04-13 14:37:07 +00:00
if globalConfiguration . Retry != nil {
2017-08-28 10:50:02 +00:00
countServers := len ( config . Backends [ frontend . Backend ] . Servers )
lb = server . buildRetryMiddleware ( lb , globalConfiguration , countServers , frontend . Backend )
2017-04-13 14:37:07 +00:00
}
2017-08-28 10:50:02 +00:00
2017-08-23 18:46:03 +00:00
if server . metricsRegistry . IsEnabled ( ) {
2017-08-25 14:10:03 +00:00
n . Use ( middlewares . NewMetricsWrapper ( server . metricsRegistry , frontend . Backend ) )
2017-04-13 14:37:07 +00:00
}
2017-05-26 15:03:14 +00:00
2017-04-30 09:22:07 +00:00
ipWhitelistMiddleware , err := configureIPWhitelistMiddleware ( frontend . WhitelistSourceRange )
if err != nil {
log . Fatalf ( "Error creating IP Whitelister: %s" , err )
} else if ipWhitelistMiddleware != nil {
2017-08-25 14:10:03 +00:00
n . Use ( ipWhitelistMiddleware )
2017-04-30 09:22:07 +00:00
log . Infof ( "Configured IP Whitelists: %s" , frontend . WhitelistSourceRange )
}
2017-11-18 12:50:03 +00:00
if len ( frontend . Redirect ) > 0 {
proto := "http"
if server . globalConfiguration . EntryPoints [ frontend . Redirect ] . TLS != nil {
proto = "https"
}
regex , replacement , err := server . buildRedirect ( proto , entryPoint )
rewrite , err := middlewares . NewRewrite ( regex , replacement , true )
if err != nil {
log . Fatalf ( "Error creating Frontend Redirect: %v" , err )
}
n . Use ( rewrite )
log . Debugf ( "Creating frontend %s redirect to %s" , frontendName , proto )
}
2017-04-13 14:37:07 +00:00
if len ( frontend . BasicAuth ) > 0 {
users := types . Users { }
for _ , user := range frontend . BasicAuth {
users = append ( users , user )
2017-01-12 13:34:54 +00:00
}
2017-04-19 09:14:05 +00:00
2017-04-13 14:37:07 +00:00
auth := & types . Auth { }
auth . Basic = & types . Basic {
Users : users ,
2016-01-13 21:45:49 +00:00
}
2017-09-18 15:48:07 +00:00
authMiddleware , err := mauth . NewAuthenticator ( auth )
2017-04-13 14:37:07 +00:00
if err != nil {
2017-05-15 07:02:32 +00:00
log . Errorf ( "Error creating Auth: %s" , err )
} else {
2017-08-25 14:10:03 +00:00
n . Use ( authMiddleware )
2017-04-13 14:37:07 +00:00
}
2016-01-13 21:45:49 +00:00
}
2017-05-26 15:03:14 +00:00
2017-06-13 00:48:21 +00:00
if frontend . Headers . HasCustomHeadersDefined ( ) {
headerMiddleware := middlewares . NewHeaderFromStruct ( frontend . Headers )
log . Debugf ( "Adding header middleware for frontend %s" , frontendName )
2017-08-25 14:10:03 +00:00
n . Use ( headerMiddleware )
2017-06-13 00:48:21 +00:00
}
if frontend . Headers . HasSecureHeadersDefined ( ) {
secureMiddleware := middlewares . NewSecure ( frontend . Headers )
log . Debugf ( "Adding secure middleware for frontend %s" , frontendName )
2017-08-25 14:10:03 +00:00
n . UseFunc ( secureMiddleware . HandlerFuncWithNext )
2017-06-13 00:48:21 +00:00
}
2017-08-25 14:10:03 +00:00
if config . Backends [ frontend . Backend ] . CircuitBreaker != nil {
log . Debugf ( "Creating circuit breaker %s" , config . Backends [ frontend . Backend ] . CircuitBreaker . Expression )
2017-11-22 17:20:03 +00:00
circuitBreaker , err := middlewares . NewCircuitBreaker ( lb , config . Backends [ frontend . Backend ] . CircuitBreaker . Expression )
2017-04-13 14:37:07 +00:00
if err != nil {
log . Errorf ( "Error creating circuit breaker: %v" , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
}
2017-08-25 14:10:03 +00:00
n . Use ( circuitBreaker )
2017-04-13 14:37:07 +00:00
} else {
2017-08-25 14:10:03 +00:00
n . UseHandler ( lb )
2016-06-03 15:58:33 +00:00
}
2017-08-25 14:10:03 +00:00
backends [ entryPointName + frontend . Backend ] = n
2017-04-13 14:37:07 +00:00
} else {
log . Debugf ( "Reusing backend %s" , frontend . Backend )
}
if frontend . Priority > 0 {
newServerRoute . route . Priority ( frontend . Priority )
}
server . wireFrontendBackend ( newServerRoute , backends [ entryPointName + frontend . Backend ] )
2017-03-10 18:43:20 +00:00
2016-03-27 00:05:17 +00:00
err := newServerRoute . route . GetError ( )
2016-01-29 19:34:17 +00:00
if err != nil {
log . Errorf ( "Error building route: %s" , err )
2016-01-13 21:45:49 +00:00
}
}
2016-01-29 19:34:17 +00:00
}
}
2017-08-25 14:10:03 +00:00
healthcheck . GetHealthCheck ( ) . SetBackendsConfiguration ( server . routinesPool . Ctx ( ) , backendsHealthCheck )
2017-11-09 11:16:03 +00:00
// Get new certificates list sorted per entrypoints
// Update certificates
entryPointsCertificates , err := server . loadHTTPSConfiguration ( configurations )
//sort routes and update certificates
for serverEntryPointName , serverEntryPoint := range serverEntryPoints {
2016-06-03 15:58:33 +00:00
serverEntryPoint . httpRouter . GetHandler ( ) . SortRoutes ( )
2017-11-09 11:16:03 +00:00
_ , exists := entryPointsCertificates [ serverEntryPointName ]
if exists {
serverEntryPoint . certs . Set ( entryPointsCertificates [ serverEntryPointName ] )
}
2016-06-03 15:58:33 +00:00
}
2017-11-09 11:16:03 +00:00
return serverEntryPoints , err
2016-01-29 19:34:17 +00:00
}
2016-01-13 21:45:49 +00:00
2017-07-08 08:33:17 +00:00
func configureLBServers ( lb healthcheck . LoadBalancer , config * types . Configuration , frontend * types . Frontend ) error {
for serverName , server := range config . Backends [ frontend . Backend ] . Servers {
u , err := url . Parse ( server . URL )
if err != nil {
log . Errorf ( "Error parsing server URL %s: %v" , server . URL , err )
return err
}
log . Debugf ( "Creating server %s at %s with weight %d" , serverName , u , server . Weight )
if err := lb . UpsertServer ( u , roundrobin . Weight ( server . Weight ) ) ; err != nil {
log . Errorf ( "Error adding server %s to load balancer: %v" , server . URL , err )
return err
}
}
return nil
}
2017-04-30 09:22:07 +00:00
func configureIPWhitelistMiddleware ( whitelistSourceRanges [ ] string ) ( negroni . Handler , error ) {
if len ( whitelistSourceRanges ) > 0 {
ipSourceRanges := whitelistSourceRanges
ipWhitelistMiddleware , err := middlewares . NewIPWhitelister ( ipSourceRanges )
if err != nil {
return nil , err
}
return ipWhitelistMiddleware , nil
}
return nil , nil
}
2016-03-27 00:05:17 +00:00
func ( server * Server ) wireFrontendBackend ( serverRoute * serverRoute , handler http . Handler ) {
2017-05-09 20:31:16 +00:00
// path replace - This needs to always be the very last on the handler chain (first in the order in this function)
// -- Replacing Path should happen at the very end of the Modifier chain, after all the Matcher+Modifiers ran
if len ( serverRoute . replacePath ) > 0 {
handler = & middlewares . ReplacePath {
Path : serverRoute . replacePath ,
Handler : handler ,
}
}
2017-10-30 11:54:03 +00:00
if len ( serverRoute . replacePathRegex ) > 0 {
sp := strings . Split ( serverRoute . replacePathRegex , " " )
if len ( sp ) == 2 {
handler = middlewares . NewReplacePathRegexHandler ( sp [ 0 ] , sp [ 1 ] , handler )
} else {
log . Warnf ( "Invalid syntax for ReplacePathRegex: %s. Separate the regular expression and the replacement by a space." , serverRoute . replacePathRegex )
}
}
2017-05-09 20:31:16 +00:00
// add prefix - This needs to always be right before ReplacePath on the chain (second in order in this function)
// -- Adding Path Prefix should happen after all *Strip Matcher+Modifiers ran, but before Replace (in case it's configured)
2016-12-02 12:40:18 +00:00
if len ( serverRoute . addPrefix ) > 0 {
handler = & middlewares . AddPrefix {
Prefix : serverRoute . addPrefix ,
Handler : handler ,
}
}
2016-02-26 14:29:53 +00:00
// strip prefix
2016-04-06 11:06:31 +00:00
if len ( serverRoute . stripPrefixes ) > 0 {
2016-12-02 12:40:18 +00:00
handler = & middlewares . StripPrefix {
2016-04-06 11:06:31 +00:00
Prefixes : serverRoute . stripPrefixes ,
Handler : handler ,
2016-12-02 12:40:18 +00:00
}
2017-04-25 18:13:39 +00:00
}
2017-03-24 11:07:59 +00:00
// strip prefix with regex
if len ( serverRoute . stripPrefixesRegex ) > 0 {
handler = middlewares . NewStripPrefixRegex ( handler , serverRoute . stripPrefixesRegex )
}
2016-12-02 12:40:18 +00:00
serverRoute . route . Handler ( handler )
2016-02-26 14:29:53 +00:00
}
2017-08-25 14:10:03 +00:00
func ( server * Server ) loadEntryPointConfig ( entryPointName string , entryPoint * configuration . EntryPoint ) ( negroni . Handler , error ) {
2016-01-29 19:34:17 +00:00
regex := entryPoint . Redirect . Regex
replacement := entryPoint . Redirect . Replacement
2017-11-18 12:50:03 +00:00
var err error
2016-01-29 19:34:17 +00:00
if len ( entryPoint . Redirect . EntryPoint ) > 0 {
2017-11-18 12:50:03 +00:00
var protocol = "http"
2016-01-29 19:34:17 +00:00
if server . globalConfiguration . EntryPoints [ entryPoint . Redirect . EntryPoint ] . TLS != nil {
protocol = "https"
2016-01-13 21:45:49 +00:00
}
2017-11-18 12:50:03 +00:00
regex , replacement , err = server . buildRedirect ( protocol , entryPoint )
2016-01-13 21:45:49 +00:00
}
2016-01-29 19:34:17 +00:00
rewrite , err := middlewares . NewRewrite ( regex , replacement , true )
if err != nil {
return nil , err
}
log . Debugf ( "Creating entryPoint redirect %s -> %s : %s -> %s" , entryPointName , entryPoint . Redirect . EntryPoint , regex , replacement )
2017-03-10 18:43:20 +00:00
return rewrite , nil
2016-01-29 19:34:17 +00:00
}
2017-11-18 12:50:03 +00:00
func ( server * Server ) buildRedirect ( protocol string , entryPoint * configuration . EntryPoint ) ( string , string , error ) {
regex := ` ^(?:https?:\/\/)?([\w\._-]+)(?::\d+)?(.*)$ `
if server . globalConfiguration . EntryPoints [ entryPoint . Redirect . EntryPoint ] == nil {
return "" , "" , fmt . Errorf ( "unknown target entrypoint %q" , entryPoint . Redirect . EntryPoint )
}
r , _ := regexp . Compile ( ` (:\d+) ` )
match := r . FindStringSubmatch ( server . globalConfiguration . EntryPoints [ entryPoint . Redirect . EntryPoint ] . Address )
if len ( match ) == 0 {
return "" , "" , fmt . Errorf ( "bad Address format %q" , server . globalConfiguration . EntryPoints [ entryPoint . Redirect . EntryPoint ] . Address )
}
replacement := protocol + "://$1" + match [ 0 ] + "$2"
return regex , replacement , nil
}
2016-01-29 19:34:17 +00:00
func ( server * Server ) buildDefaultHTTPRouter ( ) * mux . Router {
router := mux . NewRouter ( )
router . NotFoundHandler = http . HandlerFunc ( notFoundHandler )
2016-02-26 14:29:53 +00:00
router . StrictSlash ( true )
2016-09-20 14:43:09 +00:00
router . SkipClean ( true )
2016-01-29 19:34:17 +00:00
return router
2016-01-13 21:45:49 +00:00
}
2016-02-19 22:55:23 +00:00
2017-08-25 14:10:03 +00:00
func parseHealthCheckOptions ( lb healthcheck . LoadBalancer , backend string , hc * types . HealthCheck , hcConfig * configuration . HealthCheckConfig ) * healthcheck . Options {
2017-05-15 07:02:32 +00:00
if hc == nil || hc . Path == "" || hcConfig == nil {
2017-03-15 18:16:06 +00:00
return nil
}
2017-03-24 08:36:33 +00:00
interval := time . Duration ( hcConfig . Interval )
2017-03-15 18:16:06 +00:00
if hc . Interval != "" {
intervalOverride , err := time . ParseDuration ( hc . Interval )
switch {
case err != nil :
log . Errorf ( "Illegal healthcheck interval for backend '%s': %s" , backend , err )
case intervalOverride <= 0 :
log . Errorf ( "Healthcheck interval smaller than zero for backend '%s', backend" , backend )
default :
interval = intervalOverride
}
}
return & healthcheck . Options {
Path : hc . Path ,
2017-09-18 13:50:03 +00:00
Port : hc . Port ,
2017-03-15 18:16:06 +00:00
Interval : interval ,
LB : lb ,
}
}
2016-03-30 17:05:43 +00:00
func getRoute ( serverRoute * serverRoute , route * types . Route ) error {
2016-03-27 00:05:17 +00:00
rules := Rules { route : serverRoute }
newRoute , err := rules . Parse ( route . Rule )
if err != nil {
return err
2016-02-26 14:29:53 +00:00
}
2016-06-03 15:58:33 +00:00
newRoute . Priority ( serverRoute . route . GetPriority ( ) + len ( route . Rule ) )
2016-03-27 00:05:17 +00:00
serverRoute . route = newRoute
return nil
2016-02-26 14:29:53 +00:00
}
2016-02-19 22:55:23 +00:00
func sortedFrontendNamesForConfig ( configuration * types . Configuration ) [ ] string {
keys := [ ] string { }
for key := range configuration . Frontends {
keys = append ( keys , key )
}
sort . Strings ( keys )
return keys
}
2017-05-10 22:34:47 +00:00
func ( server * Server ) configureFrontends ( frontends map [ string ] * types . Frontend ) {
for _ , frontend := range frontends {
// default endpoints if not defined in frontends
if len ( frontend . EntryPoints ) == 0 {
frontend . EntryPoints = server . globalConfiguration . DefaultEntryPoints
}
}
}
2017-05-15 21:53:35 +00:00
func ( * Server ) configureBackends ( backends map [ string ] * types . Backend ) {
2017-10-16 15:38:03 +00:00
for backendName := range backends {
backend := backends [ backendName ]
2017-10-10 09:10:02 +00:00
if backend . LoadBalancer != nil && backend . LoadBalancer . Sticky {
2017-10-12 15:50:03 +00:00
log . Warnf ( "Deprecated configuration found: %s. Please use %s." , "backend.LoadBalancer.Sticky" , "backend.LoadBalancer.Stickiness" )
2017-10-10 09:10:02 +00:00
}
2017-05-10 22:34:47 +00:00
_ , err := types . NewLoadBalancerMethod ( backend . LoadBalancer )
2017-10-10 09:10:02 +00:00
if err == nil {
if backend . LoadBalancer != nil && backend . LoadBalancer . Stickiness == nil && backend . LoadBalancer . Sticky {
2017-10-12 15:50:03 +00:00
backend . LoadBalancer . Stickiness = & types . Stickiness {
CookieName : "_TRAEFIK_BACKEND" ,
}
2017-10-10 09:10:02 +00:00
}
} else {
2017-05-10 22:34:47 +00:00
log . Debugf ( "Validation of load balancer method for backend %s failed: %s. Using default method wrr." , backendName , err )
2017-10-10 09:10:02 +00:00
var stickiness * types . Stickiness
2017-05-10 22:34:47 +00:00
if backend . LoadBalancer != nil {
2017-10-12 15:50:03 +00:00
if backend . LoadBalancer . Stickiness == nil {
if backend . LoadBalancer . Sticky {
stickiness = & types . Stickiness {
CookieName : "_TRAEFIK_BACKEND" ,
}
2017-10-10 09:10:02 +00:00
}
2017-10-12 15:50:03 +00:00
} else {
stickiness = backend . LoadBalancer . Stickiness
2017-10-10 09:10:02 +00:00
}
2017-05-10 22:34:47 +00:00
}
backend . LoadBalancer = & types . LoadBalancer {
2017-10-10 09:10:02 +00:00
Method : "wrr" ,
Stickiness : stickiness ,
2017-05-10 22:34:47 +00:00
}
}
}
}
2017-04-18 06:22:06 +00:00
2017-08-23 18:46:03 +00:00
func ( server * Server ) registerMetricClients ( metricsConfig * types . Metrics ) {
registries := [ ] metrics . Registry { }
2017-07-20 22:26:43 +00:00
2017-08-23 18:46:03 +00:00
if metricsConfig . Prometheus != nil {
registries = append ( registries , metrics . RegisterPrometheus ( metricsConfig . Prometheus ) )
log . Debug ( "Configured Prometheus metrics" )
}
if metricsConfig . Datadog != nil {
registries = append ( registries , metrics . RegisterDatadog ( metricsConfig . Datadog ) )
log . Debugf ( "Configured DataDog metrics pushing to %s once every %s" , metricsConfig . Datadog . Address , metricsConfig . Datadog . PushInterval )
}
if metricsConfig . StatsD != nil {
registries = append ( registries , metrics . RegisterStatsd ( metricsConfig . StatsD ) )
log . Debugf ( "Configured StatsD metrics pushing to %s once every %s" , metricsConfig . StatsD . Address , metricsConfig . StatsD . PushInterval )
2017-04-18 06:22:06 +00:00
}
2017-11-08 14:14:03 +00:00
if metricsConfig . InfluxDB != nil {
registries = append ( registries , metrics . RegisterInfluxDB ( metricsConfig . InfluxDB ) )
log . Debugf ( "Configured InfluxDB metrics pushing to %s once every %s" , metricsConfig . InfluxDB . Address , metricsConfig . InfluxDB . PushInterval )
}
2017-04-18 06:22:06 +00:00
2017-08-23 18:46:03 +00:00
if len ( registries ) > 0 {
server . metricsRegistry = metrics . NewMultiRegistry ( registries )
2017-07-20 22:26:43 +00:00
}
}
2017-08-23 18:46:03 +00:00
func stopMetricsClients ( ) {
metrics . StopDatadog ( )
metrics . StopStatsd ( )
2017-11-08 14:14:03 +00:00
metrics . StopInfluxDB ( )
2017-07-20 22:26:43 +00:00
}
2017-09-09 11:36:03 +00:00
func ( server * Server ) buildRateLimiter ( handler http . Handler , rlConfig * types . RateLimit ) ( http . Handler , error ) {
extractFunc , err := utils . NewExtractor ( rlConfig . ExtractorFunc )
if err != nil {
return nil , err
}
log . Debugf ( "Creating load-balancer rate limiter" )
rateSet := ratelimit . NewRateSet ( )
for _ , rate := range rlConfig . RateSet {
if err := rateSet . Add ( time . Duration ( rate . Period ) , rate . Average , rate . Burst ) ; err != nil {
return nil , err
}
}
2017-11-22 17:20:03 +00:00
return ratelimit . New ( handler , extractFunc , rateSet )
2017-09-09 11:36:03 +00:00
}
2017-08-28 10:50:02 +00:00
func ( server * Server ) buildRetryMiddleware ( handler http . Handler , globalConfig configuration . GlobalConfiguration , countServers int , backendName string ) http . Handler {
retryListeners := middlewares . RetryListeners { }
if server . metricsRegistry . IsEnabled ( ) {
retryListeners = append ( retryListeners , middlewares . NewMetricsRetryListener ( server . metricsRegistry , backendName ) )
}
if server . accessLoggerMiddleware != nil {
retryListeners = append ( retryListeners , & accesslog . SaveRetries { } )
}
retryAttempts := countServers
2017-04-18 06:22:06 +00:00
if globalConfig . Retry . Attempts > 0 {
2017-08-28 10:50:02 +00:00
retryAttempts = globalConfig . Retry . Attempts
2017-04-18 06:22:06 +00:00
}
2017-08-28 10:50:02 +00:00
log . Debugf ( "Creating retries max attempts %d" , retryAttempts )
2017-04-18 06:22:06 +00:00
2017-08-28 10:50:02 +00:00
return middlewares . NewRetry ( retryAttempts , handler , retryListeners )
2017-04-18 06:22:06 +00:00
}