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"
2016-06-15 20:38:40 +00:00
"io/ioutil"
2016-02-26 14:29:53 +00:00
"net/http"
"net/url"
"os"
"os/signal"
"reflect"
"regexp"
"sort"
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
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"
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"
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"
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"
2016-02-24 15:43:39 +00:00
"github.com/containous/traefik/types"
2016-04-13 18:36:23 +00:00
"github.com/streamrail/concurrent-map"
2017-07-19 10:02:51 +00:00
"github.com/urfave/negroni"
2016-06-15 17:07:33 +00:00
"github.com/vulcand/oxy/cbreaker"
"github.com/vulcand/oxy/connlimit"
"github.com/vulcand/oxy/forward"
"github.com/vulcand/oxy/roundrobin"
"github.com/vulcand/oxy/utils"
2016-01-13 21:45:49 +00:00
)
2016-04-20 18:54:57 +00:00
var oxyLogger = & OxyLogger { }
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 {
2016-02-25 17:30:13 +00:00
serverEntryPoints serverEntryPoints
2016-01-13 21:45:49 +00:00
configurationChan chan types . ConfigMessage
2016-01-29 19:34:17 +00:00
configurationValidatedChan chan types . ConfigMessage
signals chan os . Signal
2016-01-13 21:45:49 +00:00
stopChan chan bool
providers [ ] provider . Provider
2016-04-13 18:36:23 +00:00
currentConfigurations safe . Safe
2016-01-13 21:45:49 +00:00
globalConfiguration GlobalConfiguration
2017-05-09 12:02:44 +00:00
accessLoggerMiddleware * accesslog . LogHandler
2016-08-18 11:03:10 +00:00
routinesPool * safe . Pool
leadership * cluster . Leadership
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
2016-03-04 10:32:23 +00:00
httpRouter * middlewares . HandlerSwitcher
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
2016-03-27 00:05:17 +00:00
}
2016-01-13 21:45:49 +00:00
// NewServer returns an initialized Server.
func NewServer ( globalConfiguration GlobalConfiguration ) * Server {
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 ( )
2016-04-13 18:36:23 +00:00
currentConfigurations := make ( configs )
server . currentConfigurations . Set ( currentConfigurations )
2016-01-13 21:45:49 +00:00
server . globalConfiguration = globalConfiguration
2016-08-18 11:03:10 +00:00
server . routinesPool = safe . NewPool ( context . Background ( ) )
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
}
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-03-27 09:51:53 +00:00
graceTimeOut := time . Duration ( server . globalConfiguration . GraceTimeOut )
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-03-27 09:51:53 +00:00
ctx , cancel := context . WithTimeout ( context . Background ( ) , time . Duration ( server . globalConfiguration . 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-07-20 22:26:43 +00:00
stopMetricsClients ( server . globalConfiguration )
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 )
go server . startServer ( serverEntryPoint . httpServer , server . globalConfiguration )
}
}
func ( server * Server ) setupServerEntryPoint ( newServerEntryPointName string , newServerEntryPoint * serverEntryPoint ) * serverEntryPoint {
serverMiddlewares := [ ] negroni . Handler { middlewares . NegroniRecoverHandler ( ) , metrics }
if server . accessLoggerMiddleware != nil {
serverMiddlewares = append ( serverMiddlewares , server . accessLoggerMiddleware )
}
2017-07-20 22:26:43 +00:00
initializeMetricsClients ( server . globalConfiguration )
2017-07-08 10:21:14 +00:00
metrics := newMetrics ( server . globalConfiguration , newServerEntryPointName )
if metrics != nil {
serverMiddlewares = append ( serverMiddlewares , middlewares . NewMetricsWrapper ( metrics ) )
}
if server . globalConfiguration . Web != nil && server . globalConfiguration . Web . Statistics != nil {
statsRecorder = middlewares . NewStatsRecorder ( server . globalConfiguration . Web . Statistics . RecentErrors )
serverMiddlewares = append ( serverMiddlewares , statsRecorder )
}
if server . globalConfiguration . EntryPoints [ newServerEntryPointName ] . Auth != nil {
authMiddleware , err := middlewares . NewAuthenticator ( server . globalConfiguration . EntryPoints [ newServerEntryPointName ] . Auth )
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 )
}
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 )
2016-02-25 17:30:13 +00:00
}
2017-07-08 10:21:14 +00:00
newsrv , err := server . prepareServer ( newServerEntryPointName , newServerEntryPoint . httpRouter , server . globalConfiguration . EntryPoints [ newServerEntryPointName ] , serverMiddlewares ... )
if err != nil {
log . Fatal ( "Error preparing server: " , err )
}
serverEntryPoint := server . serverEntryPoints [ newServerEntryPointName ]
serverEntryPoint . httpServer = newsrv
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 ) {
lastReceivedConfiguration := safe . New ( time . Unix ( 0 , 0 ) )
lastConfigs := cmap . New ( )
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 :
if ! ok {
return
}
2016-06-22 16:31:14 +00:00
server . defaultConfigurationValues ( configMsg . Configuration )
currentConfigurations := server . currentConfigurations . Get ( ) . ( configs )
2016-04-13 18:36:23 +00:00
jsonConf , _ := json . Marshal ( configMsg . Configuration )
log . Debugf ( "Configuration received from provider %s: %s" , configMsg . ProviderName , string ( jsonConf ) )
2016-06-22 16:31:14 +00:00
if configMsg . Configuration == nil || configMsg . Configuration . Backends == nil && configMsg . Configuration . Frontends == 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 )
2016-04-13 18:36:23 +00:00
} else {
2016-06-22 16:31:14 +00:00
lastConfigs . Set ( configMsg . ProviderName , & configMsg )
lastReceivedConfigurationValue := lastReceivedConfiguration . Get ( ) . ( time . Time )
2017-03-27 09:51:53 +00:00
providersThrottleDuration := time . Duration ( server . globalConfiguration . ProvidersThrottleDuration )
if time . Now ( ) . After ( lastReceivedConfigurationValue . Add ( providersThrottleDuration ) ) {
2016-10-21 14:02:18 +00:00
log . Debugf ( "Last %s config received more than %s, OK" , configMsg . ProviderName , server . globalConfiguration . ProvidersThrottleDuration . String ( ) )
2016-06-22 16:31:14 +00:00
// last config received more than n s ago
server . configurationValidatedChan <- configMsg
} else {
2016-10-21 14:02:18 +00:00
log . Debugf ( "Last %s config received less than %s, waiting..." , configMsg . ProviderName , server . globalConfiguration . ProvidersThrottleDuration . String ( ) )
2016-06-22 16:31:14 +00:00
safe . Go ( func ( ) {
2017-03-27 09:51:53 +00:00
<- time . After ( providersThrottleDuration )
2016-06-22 16:31:14 +00:00
lastReceivedConfigurationValue := lastReceivedConfiguration . Get ( ) . ( time . Time )
2017-03-27 09:51:53 +00:00
if time . Now ( ) . After ( lastReceivedConfigurationValue . Add ( time . Duration ( providersThrottleDuration ) ) ) {
2016-06-22 16:31:14 +00:00
log . Debugf ( "Waited for %s config, OK" , configMsg . ProviderName )
if lastConfig , ok := lastConfigs . Get ( configMsg . ProviderName ) ; ok {
server . configurationValidatedChan <- * lastConfig . ( * types . ConfigMessage )
}
2016-04-13 18:36:23 +00:00
}
2016-06-22 16:31:14 +00:00
} )
}
lastReceivedConfiguration . Set ( time . Now ( ) )
2016-04-13 18:36:23 +00:00
}
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 :
if ! ok {
return
2016-01-13 21:45:49 +00:00
}
2016-04-13 18:36:23 +00:00
currentConfigurations := server . currentConfigurations . Get ( ) . ( configs )
2016-06-22 16:31:14 +00:00
// Copy configurations to new map so we don't change current if LoadConfig fails
newConfigurations := make ( configs )
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 ( ) )
log . Infof ( "Server configuration reloaded on %s" , server . serverEntryPoints [ newServerEntryPointName ] . httpServer . Addr )
2016-04-13 18:36:23 +00:00
}
2016-06-22 16:31:14 +00:00
server . currentConfigurations . Set ( newConfigurations )
2016-08-05 18:42:45 +00:00
server . postLoadConfig ( )
2016-06-22 16:31:14 +00:00
} else {
log . Error ( "Error loading new configuration, aborted " , err )
2016-01-13 21:45:49 +00:00
}
}
}
}
2016-08-05 18:42:45 +00:00
func ( server * Server ) postLoadConfig ( ) {
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 {
2016-08-05 18:42:45 +00:00
currentConfigurations := server . currentConfigurations . Get ( ) . ( configs )
for _ , configuration := range currentConfigurations {
for _ , frontend := range configuration . 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-01-05 11:32:56 +00:00
for _ , entrypoint := range frontend . EntryPoints {
2017-04-07 13:48:58 +00:00
if server . globalConfiguration . ACME . EntryPoint == entrypoint && server . globalConfiguration . EntryPoints [ entrypoint ] . TLS != nil {
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 )
}
if server . globalConfiguration . Web != nil {
server . globalConfiguration . Web . server = server
server . providers = append ( server . providers , server . globalConfiguration . Web )
}
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
for _ , provider := range server . providers {
2017-03-07 12:09:11 +00:00
providerType := reflect . TypeOf ( provider )
2016-01-29 19:34:17 +00:00
jsonConf , _ := json . Marshal ( provider )
2017-03-07 12:09:11 +00:00
log . Infof ( "Starting provider %v %s" , providerType , jsonConf )
2016-01-13 21:45:49 +00:00
currentProvider := provider
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-04-06 22:10:02 +00:00
func createClientTLSConfig ( tlsOption * TLS ) ( * tls . Config , error ) {
if tlsOption == nil {
return nil , errors . New ( "no TLS provided" )
}
config , err := tlsOption . Certificates . CreateTLSConfig ( )
if err != nil {
return nil , err
}
if len ( tlsOption . ClientCAFiles ) > 0 {
pool := x509 . NewCertPool ( )
for _ , caFile := range tlsOption . ClientCAFiles {
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
2016-03-21 10:10:18 +00:00
func ( server * Server ) createTLSConfig ( entryPointName string , tlsOption * 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
2016-06-27 10:19:14 +00:00
config , err := tlsOption . Certificates . CreateTLSConfig ( )
if err != nil {
return nil , err
2016-03-21 10:10:18 +00:00
}
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 {
pool := x509 . NewCertPool ( )
for _ , caFile := range tlsOption . ClientCAFiles {
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
config . ClientAuth = tls . RequireAndVerifyClientCert
}
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 {
err := server . globalConfiguration . ACME . CreateLocalConfig ( config , checkOnDemandDomain )
if err != nil {
return nil , err
}
} else {
err := server . globalConfiguration . ACME . CreateClusterConfig ( server . leadership , config , checkOnDemandDomain )
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" )
}
}
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
if minConst , exists := minVersion [ server . globalConfiguration . EntryPoints [ entryPointName ] . TLS . MinVersion ] ; exists {
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 {
if cipherConst , exists := cipherSuites [ cipher ] ; exists {
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 )
}
}
}
2017-04-06 22:10:02 +00:00
2016-01-13 21:45:49 +00:00
return config , nil
}
2017-03-09 22:27:09 +00:00
func ( server * Server ) startServer ( srv * http . Server , globalConfiguration GlobalConfiguration ) {
2016-02-25 17:30:13 +00:00
log . Infof ( "Starting server on %s" , srv . Addr )
2017-03-09 22:27:09 +00:00
var err error
2016-01-13 21:45:49 +00:00
if srv . TLSConfig != nil {
2017-03-09 22:27:09 +00:00
err = srv . ListenAndServeTLS ( "" , "" )
2016-01-13 21:45:49 +00:00
} else {
2017-03-09 22:27:09 +00:00
err = srv . ListenAndServe ( )
}
if err != nil {
log . Error ( "Error creating server: " , err )
2016-01-13 21:45:49 +00:00
}
}
2017-03-09 22:27:09 +00:00
func ( server * Server ) prepareServer ( entryPointName string , router * middlewares . HandlerSwitcher , entryPoint * EntryPoint , middlewares ... negroni . Handler ) ( * http . Server , error ) {
2016-03-21 10:10:18 +00:00
log . Infof ( "Preparing server %s %+v" , entryPointName , entryPoint )
2016-01-13 21:45:49 +00:00
// middlewares
var negroni = negroni . New ( )
for _ , middleware := range middlewares {
negroni . Use ( middleware )
}
negroni . UseHandler ( router )
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 )
2016-01-13 21:45:49 +00:00
return nil , err
}
2017-03-09 22:27:09 +00:00
return & http . Server {
2017-04-04 09:36:23 +00:00
Addr : entryPoint . Address ,
Handler : negroni ,
TLSConfig : tlsConfig ,
IdleTimeout : time . Duration ( server . globalConfiguration . IdleTimeout ) ,
2017-03-09 22:27:09 +00:00
} , nil
2016-01-13 21:45:49 +00:00
}
2016-02-25 17:30:13 +00:00
func ( server * Server ) buildEntryPoints ( globalConfiguration GlobalConfiguration ) map [ string ] * serverEntryPoint {
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-04-06 22:10:02 +00:00
// clientTLSRoundTripper is used for forwarding client authentication to
// backend server
func clientTLSRoundTripper ( config * tls . Config ) http . RoundTripper {
if config == nil {
return http . DefaultTransport
}
return & http . Transport {
TLSClientConfig : config ,
}
}
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.
2016-02-25 17:30:13 +00:00
func ( server * Server ) loadConfig ( configurations configs , globalConfiguration 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 { }
2016-11-30 21:49:57 +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
2016-01-13 21:45:49 +00:00
for _ , configuration := range configurations {
2016-02-19 22:55:23 +00:00
frontendNames := sortedFrontendNamesForConfig ( configuration )
2016-06-20 13:19:52 +00:00
frontend :
2016-02-19 22:55:23 +00:00
for _ , frontendName := range frontendNames {
frontend := configuration . Frontends [ frontendName ]
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-03-10 18:43:20 +00:00
negroni := negroni . New ( )
2016-01-29 19:34:17 +00:00
if entryPoint . Redirect != nil {
if redirectHandlers [ entryPointName ] != nil {
2017-03-10 18:43:20 +00:00
negroni . 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 )
negroni . Use ( saveFrontend )
redirectHandlers [ entryPointName ] = saveFrontend
} else {
negroni . Use ( handler )
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
var (
tlsConfig * tls . Config
err error
lb http . Handler
)
if frontend . PassTLSCert {
tlsConfig , err = createClientTLSConfig ( entryPoint . TLS )
if err != nil {
log . Errorf ( "Failed to create TLS config for frontend %s: %v" , frontendName , err )
continue frontend
}
}
// passing nil will use the roundtripper http.DefaultTransport
rt := clientTLSRoundTripper ( tlsConfig )
2017-05-03 08:20:33 +00:00
fwd , err := forward . New (
forward . Logger ( oxyLogger ) ,
forward . PassHostHeader ( frontend . PassHostHeader ) ,
forward . RoundTripper ( rt ) ,
forward . ErrorHandler ( errorHandler ) ,
)
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-03-10 18:43:20 +00:00
if configuration . Backends [ frontend . Backend ] == nil {
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-03-10 18:43:20 +00:00
lbMethod , err := types . NewLoadBalancerMethod ( configuration . Backends [ frontend . Backend ] . LoadBalancer )
if err != nil {
log . Errorf ( "Error loading load balancer method '%+v' for frontend %s: %v" , configuration . Backends [ frontend . Backend ] . LoadBalancer , frontendName , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
}
2016-05-13 14:22:11 +00:00
2017-03-10 18:43:20 +00:00
stickysession := configuration . Backends [ frontend . Backend ] . LoadBalancer . Sticky
2017-06-07 15:04:06 +00:00
cookiename := "_TRAEFIK_BACKEND_" + frontend . Backend
2017-03-10 18:43:20 +00:00
var sticky * roundrobin . StickySession
2016-05-26 17:38:55 +00:00
2017-03-10 18:43:20 +00:00
if stickysession {
sticky = roundrobin . NewStickySession ( cookiename )
}
2016-05-13 14:22:11 +00:00
2017-04-13 14:37:07 +00:00
switch lbMethod {
case types . Drr :
log . Debugf ( "Creating load-balancer drr" )
rebalancer , _ := roundrobin . NewRebalancer ( rr , roundrobin . RebalancerLogger ( oxyLogger ) )
if stickysession {
log . Debugf ( "Sticky session with cookie %v" , cookiename )
rebalancer , _ = roundrobin . NewRebalancer ( rr , roundrobin . RebalancerLogger ( oxyLogger ) , roundrobin . RebalancerStickySession ( sticky ) )
}
lb = rebalancer
2017-07-08 08:33:17 +00:00
if err := configureLBServers ( rebalancer , configuration , frontend ) ; err != nil {
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
}
hcOpts := parseHealthCheckOptions ( rebalancer , frontend . Backend , configuration . Backends [ frontend . Backend ] . HealthCheck , globalConfiguration . HealthCheck )
if hcOpts != nil {
log . Debugf ( "Setting up backend health check %s" , * hcOpts )
2017-08-10 16:00:31 +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" )
if stickysession {
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-07-08 08:33:17 +00:00
if err := configureLBServers ( rr , configuration , frontend ) ; err != nil {
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
2016-04-13 08:11:36 +00:00
}
2017-05-15 07:02:32 +00:00
hcOpts := parseHealthCheckOptions ( rr , frontend . Backend , configuration . 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-08-10 16:00:31 +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 {
if configuration . Backends [ errorPage . Backend ] != nil && configuration . Backends [ errorPage . Backend ] . Servers [ "error" ] . URL != "" {
errorPageHandler , err := middlewares . NewErrorPagesHandler ( errorPage , configuration . Backends [ errorPage . Backend ] . Servers [ "error" ] . URL )
if err != nil {
log . Errorf ( "Error creating custom error page middleware, %v" , err )
} else {
negroni . Use ( errorPageHandler )
}
} 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-04-13 14:37:07 +00:00
maxConns := configuration . Backends [ frontend . Backend ] . MaxConn
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" )
lb , err = connlimit . New ( lb , extractFunc , maxConns . Amount , connlimit . Logger ( oxyLogger ) )
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
metrics := newMetrics ( server . globalConfiguration , frontend . Backend )
2017-04-13 14:37:07 +00:00
if globalConfiguration . Retry != nil {
2017-04-18 06:22:06 +00:00
retryListener := middlewares . NewMetricsRetryListener ( metrics )
lb = registerRetryMiddleware ( lb , globalConfiguration , configuration , frontend . Backend , retryListener )
2017-04-13 14:37:07 +00:00
}
2017-04-18 06:22:06 +00:00
if metrics != nil {
negroni . Use ( middlewares . NewMetricsWrapper ( metrics ) )
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 {
negroni . Use ( ipWhitelistMiddleware )
log . Infof ( "Configured IP Whitelists: %s" , frontend . WhitelistSourceRange )
}
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-04-13 14:37:07 +00:00
authMiddleware , err := middlewares . NewAuthenticator ( auth )
if err != nil {
2017-05-15 07:02:32 +00:00
log . Errorf ( "Error creating Auth: %s" , err )
} else {
negroni . 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 )
negroni . Use ( headerMiddleware )
}
if frontend . Headers . HasSecureHeadersDefined ( ) {
secureMiddleware := middlewares . NewSecure ( frontend . Headers )
log . Debugf ( "Adding secure middleware for frontend %s" , frontendName )
negroni . UseFunc ( secureMiddleware . HandlerFuncWithNext )
}
2017-04-13 14:37:07 +00:00
if configuration . Backends [ frontend . Backend ] . CircuitBreaker != nil {
log . Debugf ( "Creating circuit breaker %s" , configuration . Backends [ frontend . Backend ] . CircuitBreaker . Expression )
cbreaker , err := middlewares . NewCircuitBreaker ( lb , configuration . Backends [ frontend . Backend ] . CircuitBreaker . Expression , cbreaker . Logger ( oxyLogger ) )
if err != nil {
log . Errorf ( "Error creating circuit breaker: %v" , err )
log . Errorf ( "Skipping frontend %s..." , frontendName )
continue frontend
}
negroni . Use ( cbreaker )
} else {
negroni . UseHandler ( lb )
2016-06-03 15:58:33 +00:00
}
2017-04-13 14:37:07 +00:00
backends [ entryPointName + frontend . Backend ] = negroni
} 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-02-06 08:31:20 +00:00
healthcheck . GetHealthCheck ( ) . SetBackendsConfiguration ( server . routinesPool . Ctx ( ) , backendsHealthcheck )
2016-06-03 15:58:33 +00:00
//sort routes
for _ , serverEntryPoint := range serverEntryPoints {
serverEntryPoint . httpRouter . GetHandler ( ) . SortRoutes ( )
}
2016-01-29 19:34:17 +00:00
return serverEntryPoints , nil
}
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 ,
}
}
// 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-03-10 18:43:20 +00:00
func ( server * Server ) loadEntryPointConfig ( entryPointName string , entryPoint * EntryPoint ) ( negroni . Handler , error ) {
2016-01-29 19:34:17 +00:00
regex := entryPoint . Redirect . Regex
replacement := entryPoint . Redirect . Replacement
if len ( entryPoint . Redirect . EntryPoint ) > 0 {
2017-08-18 00:18:02 +00:00
regex = ` ^(?:https?:\/\/)?([\w\._-]+)(?::\d+)?(.*)$ `
2016-01-29 19:34:17 +00:00
if server . globalConfiguration . EntryPoints [ entryPoint . Redirect . EntryPoint ] == nil {
2016-03-03 20:29:52 +00:00
return nil , errors . New ( "Unknown entrypoint " + entryPoint . Redirect . EntryPoint )
2016-01-29 19:34:17 +00:00
}
protocol := "http"
if server . globalConfiguration . EntryPoints [ entryPoint . Redirect . EntryPoint ] . TLS != nil {
protocol = "https"
2016-01-13 21:45:49 +00:00
}
2017-08-18 00:18:02 +00:00
r , _ := regexp . Compile ( ` (:\d+) ` )
2016-01-29 19:34:17 +00:00
match := r . FindStringSubmatch ( server . globalConfiguration . EntryPoints [ entryPoint . Redirect . EntryPoint ] . Address )
if len ( match ) == 0 {
return nil , errors . New ( "Bad Address format: " + server . globalConfiguration . EntryPoints [ entryPoint . Redirect . EntryPoint ] . Address )
}
replacement = protocol + "://$1" + match [ 0 ] + "$2"
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
}
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-05-15 07:02:32 +00:00
func parseHealthCheckOptions ( lb healthcheck . LoadBalancer , backend string , hc * types . HealthCheck , hcConfig * HealthCheckConfig ) * healthcheck . Options {
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 ,
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-05-10 22:34:47 +00:00
for backendName , backend := range backends {
_ , err := types . NewLoadBalancerMethod ( backend . LoadBalancer )
if err != nil {
log . Debugf ( "Validation of load balancer method for backend %s failed: %s. Using default method wrr." , backendName , err )
var sticky bool
if backend . LoadBalancer != nil {
sticky = backend . LoadBalancer . Sticky
}
backend . LoadBalancer = & types . LoadBalancer {
Method : "wrr" ,
Sticky : sticky ,
}
}
}
}
2017-04-18 06:22:06 +00:00
// newMetrics instantiates the proper Metrics implementation, depending on the global configuration.
// Note that given there is no metrics instrumentation configured, it will return nil.
func newMetrics ( globalConfig GlobalConfiguration , name string ) middlewares . Metrics {
metricsEnabled := globalConfig . Web != nil && globalConfig . Web . Metrics != nil
2017-07-20 22:26:43 +00:00
if metricsEnabled {
// Create MultiMetric
metrics := [ ] middlewares . Metrics { }
if globalConfig . Web . Metrics . Prometheus != nil {
metric , _ , err := middlewares . NewPrometheus ( name , globalConfig . Web . Metrics . Prometheus )
if err != nil {
log . Errorf ( "Error creating Prometheus metrics implementation: %s" , err )
}
log . Debug ( "Configured Prometheus metrics" )
metrics = append ( metrics , metric )
2017-04-18 06:22:06 +00:00
}
2017-07-20 22:26:43 +00:00
if globalConfig . Web . Metrics . Datadog != nil {
metric := middlewares . NewDataDog ( name )
log . Debugf ( "Configured DataDog metrics pushing to %s once every %s" , globalConfig . Web . Metrics . Datadog . Address , globalConfig . Web . Metrics . Datadog . PushInterval )
metrics = append ( metrics , metric )
}
if globalConfig . Web . Metrics . StatsD != nil {
metric := middlewares . NewStatsD ( name )
log . Debugf ( "Configured StatsD metrics pushing to %s once every %s" , globalConfig . Web . Metrics . StatsD . Address , globalConfig . Web . Metrics . StatsD . PushInterval )
metrics = append ( metrics , metric )
}
return middlewares . NewMultiMetrics ( metrics )
2017-04-18 06:22:06 +00:00
}
return nil
}
2017-07-20 22:26:43 +00:00
func initializeMetricsClients ( globalConfig GlobalConfiguration ) {
metricsEnabled := globalConfig . Web != nil && globalConfig . Web . Metrics != nil
if metricsEnabled {
if globalConfig . Web . Metrics . Datadog != nil {
middlewares . InitDatadogClient ( globalConfig . Web . Metrics . Datadog )
}
if globalConfig . Web . Metrics . StatsD != nil {
middlewares . InitStatsdClient ( globalConfig . Web . Metrics . StatsD )
}
}
}
func stopMetricsClients ( globalConfig GlobalConfiguration ) {
middlewares . StopDatadogClient ( )
middlewares . StopStatsdClient ( )
}
2017-04-18 06:22:06 +00:00
func registerRetryMiddleware (
httpHandler http . Handler ,
globalConfig GlobalConfiguration ,
config * types . Configuration ,
backend string ,
listener middlewares . RetryListener ,
) http . Handler {
retries := len ( config . Backends [ backend ] . Servers )
if globalConfig . Retry . Attempts > 0 {
retries = globalConfig . Retry . Attempts
}
httpHandler = middlewares . NewRetry ( retries , httpHandler , listener )
log . Debugf ( "Creating retries max attempts %d" , retries )
return httpHandler
}