diff --git a/anonymize/anonymize_config_test.go b/anonymize/anonymize_config_test.go index b90e03cb7..f0bc25fc6 100644 --- a/anonymize/anonymize_config_test.go +++ b/anonymize/anonymize_config_test.go @@ -80,8 +80,12 @@ func TestDo_globalConfiguration(t *testing.T) { TrustForwardHeader: true, }, }, - WhitelistSourceRange: []string{"foo WhitelistSourceRange 1", "foo WhitelistSourceRange 2", "foo WhitelistSourceRange 3"}, - Compress: &configuration.Compress{}, + WhiteList: &types.WhiteList{ + SourceRange: []string{ + "127.0.0.1/32", + }, + }, + Compress: &configuration.Compress{}, ProxyProtocol: &configuration.ProxyProtocol{ TrustedIPs: []string{"127.0.0.1/32", "192.168.0.1"}, }, @@ -125,8 +129,12 @@ func TestDo_globalConfiguration(t *testing.T) { TrustForwardHeader: true, }, }, - WhitelistSourceRange: []string{"fii WhitelistSourceRange 1", "fii WhitelistSourceRange 2", "fii WhitelistSourceRange 3"}, - Compress: &configuration.Compress{}, + WhiteList: &types.WhiteList{ + SourceRange: []string{ + "127.0.0.1/32", + }, + }, + Compress: &configuration.Compress{}, ProxyProtocol: &configuration.ProxyProtocol{ TrustedIPs: []string{"127.0.0.1/32", "192.168.0.1"}, }, diff --git a/autogen/gentemplates/gen.go b/autogen/gentemplates/gen.go index 9558658f6..8c4a79c69 100644 --- a/autogen/gentemplates/gen.go +++ b/autogen/gentemplates/gen.go @@ -179,7 +179,13 @@ var _templatesConsul_catalogTmpl = []byte(`[backends] sourceRange = [{{range $whitelist.SourceRange }} "{{.}}", {{end}}] - useXForwardedFor = {{ $whitelist.UseXForwardedFor }} + {{if $whitelist.IPStrategy }} + [frontends."frontend-{{ $service.ServiceName }}".whiteList.IPStrategy] + depth = {{ $whitelist.IPStrategy.Depth }} + excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }} + "{{.}}", + {{end}}] + {{end}} {{end}} {{ $redirect := getRedirect $service.TraefikLabels }} @@ -418,7 +424,13 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}} sourceRange = [{{range $whitelist.SourceRange }} "{{.}}", {{end}}] - useXForwardedFor = {{ $whitelist.UseXForwardedFor }} + {{if $whitelist.IPStrategy }} + [frontends."frontend-{{ $frontendName }}".whiteList.IPStrategy] + depth = {{ $whitelist.IPStrategy.Depth }} + excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }} + "{{.}}", + {{end}}] + {{end}} {{end}} {{ $redirect := getRedirect $container.SegmentLabels }} @@ -657,7 +669,13 @@ var _templatesEcsTmpl = []byte(`[backends] sourceRange = [{{range $whitelist.SourceRange }} "{{.}}", {{end}}] - useXForwardedFor = {{ $whitelist.UseXForwardedFor }} + {{if $whitelist.IPStrategy }} + [frontends."frontend-{{ $serviceName }}".whiteList.IPStrategy] + depth = {{ $whitelist.IPStrategy.Depth }} + excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }} + "{{.}}", + {{end}}] + {{end}} {{end}} {{ $redirect := getRedirect $instance.TraefikLabels }} @@ -904,10 +922,16 @@ var _templatesKubernetesTmpl = []byte(`[backends] {{if $frontend.WhiteList }} [frontends."{{ $frontendName }}".whiteList] - sourceRange = [{{range $frontend.WhiteList.SourceRange }} + sourceRange = [{{range $frontend.Whitelist.SourceRange }} "{{.}}", {{end}}] - useXForwardedFor = {{ $frontend.WhiteList.UseXForwardedFor }} + {{if $frontend.Whitelist.IPStrategy }} + [frontends."{{ $frontendName }}".whiteList.IPStrategy] + depth = {{ $frontend.Whitelist.IPStrategy.Depth }} + excludedIPs = [{{range $frontend.Whitelist.IPStrategy.ExcludedIPs }} + "{{.}}", + {{end}}] + {{end}} {{end}} {{if $frontend.Redirect }} @@ -1148,7 +1172,13 @@ var _templatesKvTmpl = []byte(`[backends] sourceRange = [{{range $whitelist.SourceRange }} "{{.}}", {{end}}] - useXForwardedFor = {{ $whitelist.UseXForwardedFor }} + {{if $whitelist.IPStrategy }} + [frontends."{{ $frontendName }}".whiteList.IPStrategy] + depth = {{ $whitelist.IPStrategy.Depth }} + excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }} + "{{.}}", + {{end}}] + {{end}} {{end}} {{ $redirect := getRedirect $frontend }} @@ -1404,7 +1434,13 @@ var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }} sourceRange = [{{range $whitelist.SourceRange }} "{{.}}", {{end}}] - useXForwardedFor = {{ $whitelist.UseXForwardedFor }} + {{if $whitelist.IPStrategy }} + [frontends."{{ $frontendName }}".whiteList.IPStrategy] + depth = {{ $whitelist.IPStrategy.Depth }} + excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }} + "{{.}}", + {{end}}] + {{end}} {{end}} {{ $redirect := getRedirect $app.SegmentLabels }} @@ -1645,7 +1681,13 @@ var _templatesMesosTmpl = []byte(`[backends] sourceRange = [{{range $whitelist.SourceRange }} "{{.}}", {{end}}] - useXForwardedFor = {{ $whitelist.UseXForwardedFor }} + {{if $whitelist.IPStrategy }} + [frontends."frontend-{{ $frontendName }}".whiteList.IPStrategy] + depth = {{ $whitelist.IPStrategy.Depth }} + excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }} + "{{.}}", + {{end}}] + {{end}} {{end}} {{ $redirect := getRedirect $app.TraefikLabels }} @@ -1908,7 +1950,13 @@ var _templatesRancherTmpl = []byte(`{{ $backendServers := .Backends }} sourceRange = [{{range $whitelist.SourceRange }} "{{.}}", {{end}}] - useXForwardedFor = {{ $whitelist.UseXForwardedFor }} + {{if $whitelist.IPStrategy }} + [frontends."frontend-{{ $frontendName }}".whiteList.IPStrategy] + depth = {{ $whitelist.IPStrategy.Depth }} + excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }} + "{{.}}", + {{end}}] + {{end}} {{end}} {{ $redirect := getRedirect $service.SegmentLabels }} diff --git a/configuration/configuration.go b/configuration/configuration.go index 48aaecab6..85453aae9 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -108,7 +108,7 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) { if len(gc.EntryPoints) == 0 { gc.EntryPoints = map[string]*EntryPoint{"http": { Address: ":80", - ForwardedHeaders: &ForwardedHeaders{Insecure: true}, + ForwardedHeaders: &ForwardedHeaders{}, }} gc.DefaultEntryPoints = []string{"http"} } @@ -126,18 +126,7 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) { entryPoint := gc.EntryPoints[entryPointName] // ForwardedHeaders must be remove in the next breaking version if entryPoint.ForwardedHeaders == nil { - entryPoint.ForwardedHeaders = &ForwardedHeaders{Insecure: true} - } - - if len(entryPoint.WhitelistSourceRange) > 0 { - log.Warnf("Deprecated configuration found: %s. Please use %s.", "whiteListSourceRange", "whiteList.sourceRange") - - if entryPoint.WhiteList == nil { - entryPoint.WhiteList = &types.WhiteList{ - SourceRange: entryPoint.WhitelistSourceRange, - } - entryPoint.WhitelistSourceRange = nil - } + entryPoint.ForwardedHeaders = &ForwardedHeaders{} } } diff --git a/configuration/entrypoints.go b/configuration/entrypoints.go index cf5081f67..01f4558a5 100644 --- a/configuration/entrypoints.go +++ b/configuration/entrypoints.go @@ -2,6 +2,7 @@ package configuration import ( "fmt" + "strconv" "strings" "github.com/containous/traefik/log" @@ -11,20 +12,19 @@ import ( // EntryPoint holds an entry point configuration of the reverse proxy (ip, port, TLS...) type EntryPoint struct { - Address string - TLS *tls.TLS `export:"true"` - Redirect *types.Redirect `export:"true"` - Auth *types.Auth `export:"true"` - WhitelistSourceRange []string // Deprecated - WhiteList *types.WhiteList `export:"true"` - Compress *Compress `export:"true"` - ProxyProtocol *ProxyProtocol `export:"true"` - ForwardedHeaders *ForwardedHeaders `export:"true"` + Address string + TLS *tls.TLS `export:"true"` + Redirect *types.Redirect `export:"true"` + Auth *types.Auth `export:"true"` + WhiteList *types.WhiteList `export:"true"` + Compress *Compress `export:"true"` + ProxyProtocol *ProxyProtocol `export:"true"` + ForwardedHeaders *ForwardedHeaders `export:"true"` + ClientIPStrategy *types.IPStrategy `export:"true"` } // Compress contains compress configuration -type Compress struct { -} +type Compress struct{} // ProxyProtocol contains Proxy-Protocol configuration type ProxyProtocol struct { @@ -68,11 +68,6 @@ func (ep *EntryPoints) Type() string { func (ep *EntryPoints) Set(value string) error { result := parseEntryPointsConfiguration(value) - var whiteListSourceRange []string - if len(result["whitelistsourcerange"]) > 0 { - whiteListSourceRange = strings.Split(result["whitelistsourcerange"], ",") - } - var compress *Compress if len(result["compress"]) > 0 { compress = &Compress{} @@ -84,29 +79,42 @@ func (ep *EntryPoints) Set(value string) error { } (*ep)[result["name"]] = &EntryPoint{ - Address: result["address"], - TLS: configTLS, - Auth: makeEntryPointAuth(result), - Redirect: makeEntryPointRedirect(result), - Compress: compress, - WhitelistSourceRange: whiteListSourceRange, - WhiteList: makeWhiteList(result), - ProxyProtocol: makeEntryPointProxyProtocol(result), - ForwardedHeaders: makeEntryPointForwardedHeaders(result), + Address: result["address"], + TLS: configTLS, + Auth: makeEntryPointAuth(result), + Redirect: makeEntryPointRedirect(result), + Compress: compress, + WhiteList: makeWhiteList(result), + ProxyProtocol: makeEntryPointProxyProtocol(result), + ForwardedHeaders: makeEntryPointForwardedHeaders(result), + ClientIPStrategy: makeIPStrategy("clientipstrategy", result), } return nil } func makeWhiteList(result map[string]string) *types.WhiteList { - var wl *types.WhiteList if rawRange, ok := result["whitelist_sourcerange"]; ok { - wl = &types.WhiteList{ - SourceRange: strings.Split(rawRange, ","), - UseXForwardedFor: toBool(result, "whitelist_usexforwardedfor"), + return &types.WhiteList{ + SourceRange: strings.Split(rawRange, ","), + IPStrategy: makeIPStrategy("whitelist_ipstrategy", result), } } - return wl + return nil +} + +func makeIPStrategy(prefix string, result map[string]string) *types.IPStrategy { + depth := toInt(result, prefix+"_depth") + excludedIPs := result[prefix+"_excludedips"] + + if depth == 0 && len(excludedIPs) == 0 { + return nil + } + + return &types.IPStrategy{ + Depth: depth, + ExcludedIPs: strings.Split(excludedIPs, ","), + } } func makeEntryPointAuth(result map[string]string) *types.Auth { @@ -184,15 +192,14 @@ func makeEntryPointProxyProtocol(result map[string]string) *ProxyProtocol { } if proxyProtocol != nil && proxyProtocol.Insecure { - log.Warn("ProxyProtocol.Insecure:true is dangerous. Please use 'ProxyProtocol.TrustedIPs:IPs' and remove 'ProxyProtocol.Insecure:true'") + log.Warn("ProxyProtocol.insecure:true is dangerous. Please use 'ProxyProtocol.TrustedIPs:IPs' and remove 'ProxyProtocol.insecure:true'") } return proxyProtocol } func makeEntryPointForwardedHeaders(result map[string]string) *ForwardedHeaders { - // TODO must be changed to false by default in the next breaking version. - forwardedHeaders := &ForwardedHeaders{Insecure: true} + forwardedHeaders := &ForwardedHeaders{} if _, ok := result["forwardedheaders_insecure"]; ok { forwardedHeaders.Insecure = toBool(result, "forwardedheaders_insecure") } @@ -300,3 +307,14 @@ func toBool(conf map[string]string, key string) bool { } return false } + +func toInt(conf map[string]string, key string) int { + if val, ok := conf[key]; ok { + intVal, err := strconv.Atoi(val) + if err != nil { + return 0 + } + return intVal + } + return 0 +} diff --git a/configuration/entrypoints_test.go b/configuration/entrypoints_test.go index f595c9999..3557dc87c 100644 --- a/configuration/entrypoints_test.go +++ b/configuration/entrypoints_test.go @@ -45,9 +45,11 @@ func Test_parseEntryPointsConfiguration(t *testing.T) { "Auth.Forward.TLS.Cert:path/to/foo.cert " + "Auth.Forward.TLS.Key:path/to/foo.key " + "Auth.Forward.TLS.InsecureSkipVerify:true " + - "WhiteListSourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " + - "whiteList.sourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " + - "whiteList.useXForwardedFor:true ", + "WhiteList.SourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " + + "WhiteList.IPStrategy.depth:3 " + + "WhiteList.IPStrategy.ExcludedIPs:10.0.0.3/24,20.0.0.3/24 " + + "ClientIPStrategy.depth:3 " + + "ClientIPStrategy.ExcludedIPs:10.0.0.3/24,20.0.0.3/24 ", expectedResult: map[string]string{ "address": ":8000", "auth_basic_users": "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", @@ -73,13 +75,15 @@ func Test_parseEntryPointsConfiguration(t *testing.T) { "redirect_permanent": "true", "redirect_regex": "http://localhost/(.*)", "redirect_replacement": "http://mydomain/$1", - "tls": "goo,gii", - "tls_acme": "TLS", - "tls_ciphersuites": "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", - "tls_minversion": "VersionTLS11", - "whitelistsourcerange": "10.42.0.0/16,152.89.1.33/32,afed:be44::/16", - "whitelist_sourcerange": "10.42.0.0/16,152.89.1.33/32,afed:be44::/16", - "whitelist_usexforwardedfor": "true", + "tls": "goo,gii", + "tls_acme": "TLS", + "tls_ciphersuites": "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", + "tls_minversion": "VersionTLS11", + "whitelist_sourcerange": "10.42.0.0/16,152.89.1.33/32,afed:be44::/16", + "whitelist_ipstrategy_depth": "3", + "whitelist_ipstrategy_excludedips": "10.0.0.3/24,20.0.0.3/24", + "clientipstrategy_depth": "3", + "clientipstrategy_excludedips": "10.0.0.3/24,20.0.0.3/24", }, }, { @@ -206,9 +210,11 @@ func TestEntryPoints_Set(t *testing.T) { "Auth.Forward.TLS.Cert:path/to/foo.cert " + "Auth.Forward.TLS.Key:path/to/foo.key " + "Auth.Forward.TLS.InsecureSkipVerify:true " + - "WhiteListSourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " + - "whiteList.sourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " + - "whiteList.useXForwardedFor:true ", + "WhiteList.SourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " + + "WhiteList.IPStrategy.depth:3 " + + "WhiteList.IPStrategy.ExcludedIPs:10.0.0.3/24,20.0.0.3/24 " + + "ClientIPStrategy.depth:3 " + + "ClientIPStrategy.ExcludedIPs:10.0.0.3/24,20.0.0.3/24 ", expectedEntryPointName: "foo", expectedEntryPoint: &EntryPoint{ Address: ":8000", @@ -265,18 +271,19 @@ func TestEntryPoints_Set(t *testing.T) { }, HeaderField: "X-WebAuth-User", }, - WhitelistSourceRange: []string{ - "10.42.0.0/16", - "152.89.1.33/32", - "afed:be44::/16", - }, WhiteList: &types.WhiteList{ SourceRange: []string{ "10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16", }, - UseXForwardedFor: true, + IPStrategy: &types.IPStrategy{ + Depth: 3, + ExcludedIPs: []string{ + "10.0.0.3/24", + "20.0.0.3/24", + }, + }, }, Compress: &Compress{}, ProxyProtocol: &ProxyProtocol{ @@ -290,6 +297,13 @@ func TestEntryPoints_Set(t *testing.T) { "20.0.0.3/24", }, }, + ClientIPStrategy: &types.IPStrategy{ + Depth: 3, + ExcludedIPs: []string{ + "10.0.0.3/24", + "20.0.0.3/24", + }, + }, }, }, { @@ -307,7 +321,7 @@ func TestEntryPoints_Set(t *testing.T) { "redirect.replacement:http://mydomain/$1 " + "redirect.permanent:true " + "compress:true " + - "whiteListSourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " + + "whiteList.sourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " + "proxyProtocol.TrustedIPs:192.168.0.1 " + "forwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24 " + "auth.basic.users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0 " + @@ -375,10 +389,12 @@ func TestEntryPoints_Set(t *testing.T) { }, HeaderField: "X-WebAuth-User", }, - WhitelistSourceRange: []string{ - "10.42.0.0/16", - "152.89.1.33/32", - "afed:be44::/16", + WhiteList: &types.WhiteList{ + SourceRange: []string{ + "10.42.0.0/16", + "152.89.1.33/32", + "afed:be44::/16", + }, }, Compress: &Compress{}, ProxyProtocol: &ProxyProtocol{ @@ -399,12 +415,12 @@ func TestEntryPoints_Set(t *testing.T) { expression: "Name:foo", expectedEntryPointName: "foo", expectedEntryPoint: &EntryPoint{ - ForwardedHeaders: &ForwardedHeaders{Insecure: true}, + ForwardedHeaders: &ForwardedHeaders{Insecure: false}, }, }, { name: "ForwardedHeaders insecure true", - expression: "Name:foo ForwardedHeaders.Insecure:true", + expression: "Name:foo ForwardedHeaders.insecure:true", expectedEntryPointName: "foo", expectedEntryPoint: &EntryPoint{ ForwardedHeaders: &ForwardedHeaders{Insecure: true}, @@ -412,7 +428,7 @@ func TestEntryPoints_Set(t *testing.T) { }, { name: "ForwardedHeaders insecure false", - expression: "Name:foo ForwardedHeaders.Insecure:false", + expression: "Name:foo ForwardedHeaders.insecure:false", expectedEntryPointName: "foo", expectedEntryPoint: &EntryPoint{ ForwardedHeaders: &ForwardedHeaders{Insecure: false}, @@ -430,19 +446,19 @@ func TestEntryPoints_Set(t *testing.T) { }, { name: "ProxyProtocol insecure true", - expression: "Name:foo ProxyProtocol.Insecure:true", + expression: "Name:foo ProxyProtocol.insecure:true", expectedEntryPointName: "foo", expectedEntryPoint: &EntryPoint{ - ForwardedHeaders: &ForwardedHeaders{Insecure: true}, + ForwardedHeaders: &ForwardedHeaders{}, ProxyProtocol: &ProxyProtocol{Insecure: true}, }, }, { name: "ProxyProtocol insecure false", - expression: "Name:foo ProxyProtocol.Insecure:false", + expression: "Name:foo ProxyProtocol.insecure:false", expectedEntryPointName: "foo", expectedEntryPoint: &EntryPoint{ - ForwardedHeaders: &ForwardedHeaders{Insecure: true}, + ForwardedHeaders: &ForwardedHeaders{}, ProxyProtocol: &ProxyProtocol{}, }, }, @@ -451,7 +467,7 @@ func TestEntryPoints_Set(t *testing.T) { expression: "Name:foo ProxyProtocol.TrustedIPs:10.0.0.3/24,20.0.0.3/24", expectedEntryPointName: "foo", expectedEntryPoint: &EntryPoint{ - ForwardedHeaders: &ForwardedHeaders{Insecure: true}, + ForwardedHeaders: &ForwardedHeaders{}, ProxyProtocol: &ProxyProtocol{ TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"}, }, @@ -463,7 +479,7 @@ func TestEntryPoints_Set(t *testing.T) { expectedEntryPointName: "foo", expectedEntryPoint: &EntryPoint{ Compress: &Compress{}, - ForwardedHeaders: &ForwardedHeaders{Insecure: true}, + ForwardedHeaders: &ForwardedHeaders{}, }, }, { @@ -472,7 +488,7 @@ func TestEntryPoints_Set(t *testing.T) { expectedEntryPointName: "foo", expectedEntryPoint: &EntryPoint{ Compress: &Compress{}, - ForwardedHeaders: &ForwardedHeaders{Insecure: true}, + ForwardedHeaders: &ForwardedHeaders{}, }, }, } diff --git a/configuration/router/internal_router.go b/configuration/router/internal_router.go index 1233d533f..8c6777fd5 100644 --- a/configuration/router/internal_router.go +++ b/configuration/router/internal_router.go @@ -16,9 +16,17 @@ func NewInternalRouterAggregator(globalConfiguration configuration.GlobalConfigu var serverMiddlewares []negroni.Handler if globalConfiguration.EntryPoints[entryPointName].WhiteList != nil { - ipWhitelistMiddleware, err := middlewares.NewIPWhiteLister( - globalConfiguration.EntryPoints[entryPointName].WhiteList.SourceRange, - globalConfiguration.EntryPoints[entryPointName].WhiteList.UseXForwardedFor) + ipStrategy := globalConfiguration.EntryPoints[entryPointName].ClientIPStrategy + if globalConfiguration.EntryPoints[entryPointName].WhiteList.IPStrategy != nil { + ipStrategy = globalConfiguration.EntryPoints[entryPointName].WhiteList.IPStrategy + } + + strategy, err := ipStrategy.Get() + if err != nil { + log.Fatalf("Error creating whitelist middleware: %s", err) + } + + ipWhitelistMiddleware, err := middlewares.NewIPWhiteLister(globalConfiguration.EntryPoints[entryPointName].WhiteList.SourceRange, strategy) if err != nil { log.Fatalf("Error creating whitelist middleware: %s", err) } diff --git a/docs/configuration/backends/consulcatalog.md b/docs/configuration/backends/consulcatalog.md index 098cec1eb..dabab3387 100644 --- a/docs/configuration/backends/consulcatalog.md +++ b/docs/configuration/backends/consulcatalog.md @@ -94,61 +94,63 @@ Additional settings can be defined using Consul Catalog tags. !!! note The default prefix is `traefik`. -| Label | Description | -|-------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `.enable=false` | Disables this container in Træfik. | -| `.protocol=https` | Overrides the default `http` protocol. | -| `.weight=10` | Assigns this weight to the container. | -| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. | -| `.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend. ex: `NetworkErrorRatio() > 0.` | -| `.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. | -| `.backend.healthcheck.interval=1s` | Defines the health check interval. | -| `.backend.healthcheck.port=8080` | Sets a different port for the health check. | -| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. | -| `.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. | -| `.backend.healthcheck.headers=EXPR` | Defines the health check request headers
Format: HEADER:value||HEADER2:value2 | -| `.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm. | -| `.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions. | -| `.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions. | -| `.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | -| `.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | -| `.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). | -| `.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | -| `.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. | -| `.frontend.auth.basic.usersfile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | -| `.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | -| `.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | -| `.frontend.auth.digest.usersfile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | -| `.frontend.auth.forward.address=https://example.com`| Sets the URL of the authentication server. | -| `.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | -| `.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | -| `.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | -| `.frontend.auth.forward.tls.insecureSkipVerify=true`| If set to true invalid SSL certificates are accepted. | -| `.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | -| `.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | -| `.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. | -| `.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | -| `.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `.frontend.errors..status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. | -| `.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. | -| `.frontend.priority=10` | Overrides default frontend priority. | -| `.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `.frontend.rateLimit.rateSet..period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `.frontend.rateLimit.rateSet..average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `.frontend.rateLimit.rateSet..burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS). | -| `.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.replacement`. | -| `.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.regex`. | -| `.frontend.redirect.permanent=true` | Returns 301 instead of 302. | -| `.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{{.ServiceName}}.{{.Domain}}`. | -| `.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | -| `.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. | +| Label | Description | +|--------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `.enable=false` | Disables this container in Træfik. | +| `.protocol=https` | Overrides the default `http` protocol. | +| `.weight=10` | Assigns this weight to the container. | +| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. | +| `.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend. ex: `NetworkErrorRatio() > 0.` | +| `.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. | +| `.backend.healthcheck.interval=1s` | Defines the health check interval. | +| `.backend.healthcheck.port=8080` | Sets a different port for the health check. | +| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. | +| `.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. | +| `.backend.healthcheck.headers=EXPR` | Defines the health check request headers
Format: HEADER:value||HEADER2:value2 | +| `.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm. | +| `.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions. | +| `.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions. | +| `.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | +| `.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | +| `.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). | +| `.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | +| `.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. | +| `.frontend.auth.basic.usersfile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | +| `.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | +| `.frontend.auth.digest.usersfile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. | +| `.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | +| `.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | +| `.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | +| `.frontend.auth.forward.tls.insecureSkipVerify=true` | If set to true invalid SSL certificates are accepted. | +| `.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | +| `.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | +| `.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. | +| `.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | +| `.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `.frontend.errors..status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. | +| `.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. | +| `.frontend.priority=10` | Overrides default frontend priority. | +| `.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `.frontend.rateLimit.rateSet..period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `.frontend.rateLimit.rateSet..average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `.frontend.rateLimit.rateSet..burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS). | +| `.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.replacement`. | +| `.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.regex`. | +| `.frontend.redirect.permanent=true` | Returns 301 instead of 302. | +| `.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{{.ServiceName}}.{{.Domain}}`. | +| `.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | +| `.frontend.whiteList.ipStrategy=true` | Uses the default IPStrategy.
Can be used when there is an existing `clientIPStrategy` but you want the remote address for whitelisting. | +| `.frontend.whiteList.ipStrategy.depth=5` | See [whitelist](/configuration/entrypoints/#white-listing) | +| `.frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | See [whitelist](/configuration/entrypoints/#white-listing) | ### Custom Headers diff --git a/docs/configuration/backends/docker.md b/docs/configuration/backends/docker.md index 9443cdd2b..69abc0209 100644 --- a/docs/configuration/backends/docker.md +++ b/docs/configuration/backends/docker.md @@ -207,66 +207,68 @@ services: Labels can be used on containers to override default behavior. -| Label | Description | -|------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `traefik.docker.network` | Overrides the default docker network to use for connections to the container. [1] | -| `traefik.domain` | Sets the default domain for the frontend rules. | -| `traefik.enable=false` | Disables this container in Træfik. | -| `traefik.port=80` | Registers this port. Useful when the container exposes multiples ports. | -| `traefik.protocol=https` | Overrides the default `http` protocol | -| `traefik.weight=10` | Assigns this weight to the container | -| `traefik.backend=foo` | Gives the name `foo` to the generated backend for this container. | -| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend | -| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. | -| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. | -| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. | -| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. | -| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. | -| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers
Format: HEADER:value||HEADER2:value2 | -| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm | -| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions | -| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions | -| `traefik.backend.loadbalancer.swarm=true` | Uses Swarm's inbuilt load balancer (only relevant under Swarm Mode). | -| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | -| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | -| `traefik.frontend.auth.basic=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` [2] (DEPRECATED). | -| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | -| `traefik.frontend.auth.basic.users=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` [2]. | -| `traefik.frontend.auth.basic.usersfile=/path/.htpasswd` | Sets the basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | -| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | -| `traefik.frontend.auth.digest.users=EXPR` | Sets the digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | -| `traefik.frontend.auth.digest.usersfile=/path/.htdigest` | Sets the digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | -| `traefik.frontend.auth.forward.address=https://example.com`| Sets the URL of the authentication server. | -| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | -| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true`| If set to true invalid SSL certificates are accepted. | -| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | -| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header user to pass the authenticated user to the application. | -| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | -| `traefik.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.errors..status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. | -| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. | -| `traefik.frontend.priority=10` | Overrides default frontend priority | -| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) | -| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.replacement`. | -| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.regex`. | -| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. | -| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{containerName}.{domain}` or `Host:{service}.{project_name}.{domain}` if you are using `docker-compose`. | -| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access.
If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | -| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. | +| Label | Description | +|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `traefik.docker.network` | Overrides the default docker network to use for connections to the container. [1] | +| `traefik.domain` | Sets the default domain for the frontend rules. | +| `traefik.enable=false` | Disables this container in Træfik. | +| `traefik.port=80` | Registers this port. Useful when the container exposes multiples ports. | +| `traefik.protocol=https` | Overrides the default `http` protocol | +| `traefik.weight=10` | Assigns this weight to the container | +| `traefik.backend=foo` | Gives the name `foo` to the generated backend for this container. | +| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend | +| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. | +| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. | +| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. | +| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. | +| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. | +| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers
Format: HEADER:value||HEADER2:value2 | +| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm | +| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions | +| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions | +| `traefik.backend.loadbalancer.swarm=true` | Uses Swarm's inbuilt load balancer (only relevant under Swarm Mode). | +| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | +| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | +| `traefik.frontend.auth.basic=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` [2] (DEPRECATED). | +| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | +| `traefik.frontend.auth.basic.users=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` [2]. | +| `traefik.frontend.auth.basic.usersfile=/path/.htpasswd` | Sets the basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | +| `traefik.frontend.auth.digest.users=EXPR` | Sets the digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | +| `traefik.frontend.auth.digest.usersfile=/path/.htdigest` | Sets the digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. | +| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | +| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true` | If set to true invalid SSL certificates are accepted. | +| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | +| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header user to pass the authenticated user to the application. | +| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | +| `traefik.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.errors..status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. | +| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. | +| `traefik.frontend.priority=10` | Overrides default frontend priority | +| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) | +| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.replacement`. | +| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.regex`. | +| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. | +| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{containerName}.{domain}` or `Host:{service}.{project_name}.{domain}` if you are using `docker-compose`. | +| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access.
If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | +| `traefik.frontend.whiteList.ipStrategy=true` | Uses the default IPStrategy.
Can be used when there is an existing `clientIPStrategy` but you want the remote address for whitelisting. | +| `traefik.frontend.whiteList.ipStrategy.depth=5` | See [whitelist](/configuration/entrypoints/#white-listing) | +| `traefik.frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | See [whitelist](/configuration/entrypoints/#white-listing) | [1] `traefik.docker.network`: If a container is linked to several networks, be sure to set the proper network name (you can check with `docker inspect `) otherwise it will randomly pick one (depending on how docker is returning them). @@ -278,7 +280,6 @@ To create `user:password` pair, it's possible to use this command: `echo $(htpasswd -nb user password) | sed -e s/\\$/\\$\\$/g`. The result will be `user:$$apr1$$9Cv/OMGj$$ZomWQzuQbL.3TRCS81A1g/`, note additional symbol `$` makes escaping. - #### Custom Headers | Label | Description | @@ -319,46 +320,48 @@ You can define as many segments as ports exposed in a container. Segment labels override the default behavior. -| Label | Description | -|---------------------------------------------------------------------------|---------------------------------------------------------------| -| `traefik..backend=BACKEND` | Same as `traefik.backend` | -| `traefik..domain=DOMAIN` | Same as `traefik.domain` | -| `traefik..port=PORT` | Same as `traefik.port` | -| `traefik..protocol=http` | Same as `traefik.protocol` | -| `traefik..weight=10` | Same as `traefik.weight` | -| `traefik..frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` | -| `traefik..frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` | -| `traefik..frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` | -| `traefik..frontend.auth.basic.usersfile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersfile` | -| `traefik..frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` | -| `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | -| `traefik..frontend.auth.digest.usersfile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersfile` | -| `traefik..frontend.auth.forward.address=https://example.com`| Same as `traefik.frontend.auth.forward.address` | -| `traefik..frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` | -| `traefik..frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` | -| `traefik..frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` | -| `traefik..frontend.auth.forward.tls.insecureSkipVerify=true`| Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify`| -| `traefik..frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` | -| `traefik..frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` | -| `traefik..frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` | -| `traefik..frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` | -| `traefik..frontend.errors..backend=NAME` | Same as `traefik.frontend.errors..backend` | -| `traefik..frontend.errors..query=PATH` | Same as `traefik.frontend.errors..query` | -| `traefik..frontend.errors..status=RANGE` | Same as `traefik.frontend.errors..status` | -| `traefik..frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` | -| `traefik..frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` | -| `traefik..frontend.priority=10` | Same as `traefik.frontend.priority` | -| `traefik..frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` | -| `traefik..frontend.rateLimit.rateSet..period=6` | Same as `traefik.frontend.rateLimit.rateSet..period` | -| `traefik..frontend.rateLimit.rateSet..average=6` | Same as `traefik.frontend.rateLimit.rateSet..average` | -| `traefik..frontend.rateLimit.rateSet..burst=6` | Same as `traefik.frontend.rateLimit.rateSet..burst` | -| `traefik..frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` | -| `traefik..frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` | -| `traefik..frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` | -| `traefik..frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` | -| `traefik..frontend.rule=EXP` | Same as `traefik.frontend.rule` | -| `traefik..frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` | -| `traefik..frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` | +| Label | Description | +|------------------------------------------------------------------------------|----------------------------------------------------------------| +| `traefik..backend=BACKEND` | Same as `traefik.backend` | +| `traefik..domain=DOMAIN` | Same as `traefik.domain` | +| `traefik..port=PORT` | Same as `traefik.port` | +| `traefik..protocol=http` | Same as `traefik.protocol` | +| `traefik..weight=10` | Same as `traefik.weight` | +| `traefik..frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` | +| `traefik..frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` | +| `traefik..frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` | +| `traefik..frontend.auth.basic.usersfile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersfile` | +| `traefik..frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` | +| `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | +| `traefik..frontend.auth.digest.usersfile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersfile` | +| `traefik..frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` | +| `traefik..frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` | +| `traefik..frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` | +| `traefik..frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` | +| `traefik..frontend.auth.forward.tls.insecureSkipVerify=true` | Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` | +| `traefik..frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` | +| `traefik..frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` | +| `traefik..frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` | +| `traefik..frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` | +| `traefik..frontend.errors..backend=NAME` | Same as `traefik.frontend.errors..backend` | +| `traefik..frontend.errors..query=PATH` | Same as `traefik.frontend.errors..query` | +| `traefik..frontend.errors..status=RANGE` | Same as `traefik.frontend.errors..status` | +| `traefik..frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` | +| `traefik..frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` | +| `traefik..frontend.priority=10` | Same as `traefik.frontend.priority` | +| `traefik..frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` | +| `traefik..frontend.rateLimit.rateSet..period=6` | Same as `traefik.frontend.rateLimit.rateSet..period` | +| `traefik..frontend.rateLimit.rateSet..average=6` | Same as `traefik.frontend.rateLimit.rateSet..average` | +| `traefik..frontend.rateLimit.rateSet..burst=6` | Same as `traefik.frontend.rateLimit.rateSet..burst` | +| `traefik..frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` | +| `traefik..frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` | +| `traefik..frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` | +| `traefik..frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` | +| `traefik..frontend.rule=EXP` | Same as `traefik.frontend.rule` | +| `traefik..frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` | +| `traefik..frontend.whiteList.ipStrategy=true` | Same as `traefik.frontend.whiteList.ipStrategy` | +| `traefik..frontend.whiteList.ipStrategy.depth=5` | Same as `traefik.frontend.whiteList.ipStrategy.depth` | +| `traefik..frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | Same as `traefik.frontend.whiteList.ipStrategy.excludedIPs` | #### Custom Headers diff --git a/docs/configuration/backends/ecs.md b/docs/configuration/backends/ecs.md index a7194b41f..03e715440 100644 --- a/docs/configuration/backends/ecs.md +++ b/docs/configuration/backends/ecs.md @@ -130,65 +130,67 @@ Træfik needs the following policy to read ECS information: Labels can be used on task containers to override default behavior: -| Label | Description | -|------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `traefik.domain` | Sets the default domain for frontend rules. | -| `traefik.enable=false` | Disables this container in Træfik. | -| `traefik.port=80` | Overrides the default `port` value. Overrides `NetworkBindings` from Docker Container | -| `traefik.protocol=https` | Overrides the default `http` protocol | -| `traefik.weight=10` | Assigns this weight to the container | -| `traefik.backend=foo` | Gives the name `foo` to the generated backend for this container. | -| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend | -| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. | -| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. (Default: 30s) | -| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. | -| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. | -| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. | -| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers
Format: HEADER:value||HEADER2:value2 | -| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm | -| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions | -| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie manually name for sticky sessions | -| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | -| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | -| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). | -| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | -| `traefik.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. | -| `traefik.frontend.auth.basic.usersfile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | -| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | -| `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | -| `traefik.frontend.auth.digest.usersfile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | -| `traefik.frontend.auth.forward.address=https://example.com`| Sets the URL of the authentication server. | -| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | -| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true`| If set to true invalid SSL certificates are accepted. | -| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | -| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. | -| `traefik.frontend.auth.removeHeader=true` | If set to true, removes the Authorization header. | -| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | -| `traefik.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.errors..status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. | -| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. | -| `traefik.frontend.priority=10` | Overrides default frontend priority | -| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) | -| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.replacement`. | -| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.regex`. | -| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. | -| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{instance_name}.{domain}`. | -| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | -| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. | +| Label | Description | +|-------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `traefik.domain` | Sets the default domain for frontend rules. | +| `traefik.enable=false` | Disables this container in Træfik. | +| `traefik.port=80` | Overrides the default `port` value. Overrides `NetworkBindings` from Docker Container | +| `traefik.protocol=https` | Overrides the default `http` protocol | +| `traefik.weight=10` | Assigns this weight to the container | +| `traefik.backend=foo` | Gives the name `foo` to the generated backend for this container. | +| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend | +| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. | +| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. (Default: 30s) | +| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. | +| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. | +| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. | +| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers
Format: HEADER:value||HEADER2:value2 | +| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm | +| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions | +| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie manually name for sticky sessions | +| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | +| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | +| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). | +| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | +| `traefik.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. | +| `traefik.frontend.auth.basic.usersfile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | +| `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | +| `traefik.frontend.auth.digest.usersfile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. | +| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | +| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true` | If set to true invalid SSL certificates are accepted. | +| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | +| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. | +| `traefik.frontend.auth.removeHeader=true` | If set to true, removes the Authorization header. | +| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | +| `traefik.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.errors..status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. | +| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. | +| `traefik.frontend.priority=10` | Overrides default frontend priority | +| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) | +| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.replacement`. | +| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.regex`. | +| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. | +| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{instance_name}.{domain}`. | +| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | +| `traefik.frontend.whiteList.ipStrategy=true` | Uses the default IPStrategy.
Can be used when there is an existing `clientIPStrategy` but you want the remote address for whitelisting. | +| `traefik.frontend.whiteList.ipStrategy.depth=5` | See [whitelist](/configuration/entrypoints/#white-listing) | +| `traefik.frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1 ` | See [whitelist](/configuration/entrypoints/#white-listing) | ### Custom Headers diff --git a/docs/configuration/backends/file.md b/docs/configuration/backends/file.md index 03885635e..b4f0c284a 100644 --- a/docs/configuration/backends/file.md +++ b/docs/configuration/backends/file.md @@ -85,7 +85,9 @@ Træfik can be configured with a file. [frontends.frontend1.whiteList] sourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"] - useXForwardedFor = true + [frontends.frontend1.whiteList.IPStrategy] + depth = 6 + excludedIPs = ["152.89.1.33/32", "afed:be44::/16"] [frontends.frontend1.routes] [frontends.frontend1.routes.route0] diff --git a/docs/configuration/backends/kubernetes.md b/docs/configuration/backends/kubernetes.md index ad6c15218..0dda22870 100644 --- a/docs/configuration/backends/kubernetes.md +++ b/docs/configuration/backends/kubernetes.md @@ -140,27 +140,29 @@ If the service port defined in the ingress spec is 443, then the backend communi The following general annotations are applicable on the Ingress object: -| Annotation | Description | -|---------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------| -| `traefik.ingress.kubernetes.io/buffering: ` | (3) See [buffering](/configuration/commons/#buffering) section. | -| `traefik.ingress.kubernetes.io/error-pages: ` | (1) See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.ingress.kubernetes.io/frontend-entry-points: http,https` | Override the default frontend endpoints. | -| `traefik.ingress.kubernetes.io/pass-tls-cert: "true"` | Override the default frontend PassTLSCert value. Default: `false`. | -| `traefik.ingress.kubernetes.io/preserve-host: "true"` | Forward client `Host` header to the backend. | -| `traefik.ingress.kubernetes.io/priority: "3"` | Override the default frontend rule priority. | -| `traefik.ingress.kubernetes.io/rate-limit: ` | (2) See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.ingress.kubernetes.io/redirect-entry-point: https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS). | -| `traefik.ingress.kubernetes.io/redirect-permanent: "true"` | Return 301 instead of 302. | -| `traefik.ingress.kubernetes.io/redirect-regex: ^http://localhost/(.*)` | Redirect to another URL for that frontend. Must be set with `traefik.ingress.kubernetes.io/redirect-replacement`. | -| `traefik.ingress.kubernetes.io/redirect-replacement: http://mydomain/$1` | Redirect to another URL for that frontend. Must be set with `traefik.ingress.kubernetes.io/redirect-regex`. | -| `traefik.ingress.kubernetes.io/rewrite-target: /users` | Replaces each matched Ingress path with the specified one, and adds the old path to the `X-Replaced-Path` header. | -| `traefik.ingress.kubernetes.io/rule-type: PathPrefixStrip` | Override the default frontend rule type. Only path related matchers can be used [(`Path`, `PathPrefix`, `PathStrip`, `PathPrefixStrip`)](/basics/#path-matcher-usage-guidelines). Note: ReplacePath is deprecated in this annotation, use the `traefik.ingress.kubernetes.io/request-modifier` annotation instead. Default: `PathPrefix`. | -| `traefik.ingress.kubernetes.io/request-modifier: AddPrefix: /users` | Add a [request modifier](/basics/#modifiers) to the backend request. | -| `traefik.ingress.kubernetes.io/whitelist-source-range: "1.2.3.0/24, fe80::/16"` | A comma-separated list of IP ranges permitted for access (6). | -| `ingress.kubernetes.io/whitelist-x-forwarded-for: "true"` | Use `X-Forwarded-For` header as valid source of IP for the white list. | -| `traefik.ingress.kubernetes.io/app-root: "/index.html"` | Redirects all requests for `/` to the defined path. (4) | -| `traefik.ingress.kubernetes.io/service-weights: ` | Set ingress backend weights specified as percentage or decimal numbers in YAML. (5) -| `ingress.kubernetes.io/protocol: ` | Set the protocol Traefik will use to communicate with pods. +| Annotation | Description | +|---------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `traefik.ingress.kubernetes.io/buffering: ` | (3) See [buffering](/configuration/commons/#buffering) section. | +| `traefik.ingress.kubernetes.io/error-pages: ` | (1) See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.ingress.kubernetes.io/frontend-entry-points: http,https` | Override the default frontend endpoints. | +| `traefik.ingress.kubernetes.io/pass-tls-cert: "true"` | Override the default frontend PassTLSCert value. Default: `false`. | +| `traefik.ingress.kubernetes.io/preserve-host: "true"` | Forward client `Host` header to the backend. | +| `traefik.ingress.kubernetes.io/priority: "3"` | Override the default frontend rule priority. | +| `traefik.ingress.kubernetes.io/rate-limit: ` | (2) See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.ingress.kubernetes.io/redirect-entry-point: https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS). | +| `traefik.ingress.kubernetes.io/redirect-permanent: "true"` | Return 301 instead of 302. | +| `traefik.ingress.kubernetes.io/redirect-regex: ^http://localhost/(.*)` | Redirect to another URL for that frontend. Must be set with `traefik.ingress.kubernetes.io/redirect-replacement`. | +| `traefik.ingress.kubernetes.io/redirect-replacement: http://mydomain/$1` | Redirect to another URL for that frontend. Must be set with `traefik.ingress.kubernetes.io/redirect-regex`. | +| `traefik.ingress.kubernetes.io/rewrite-target: /users` | Replaces each matched Ingress path with the specified one, and adds the old path to the `X-Replaced-Path` header. | +| `traefik.ingress.kubernetes.io/rule-type: PathPrefixStrip` | Override the default frontend rule type. Only path related matchers can be used [(`Path`, `PathPrefix`, `PathStrip`, `PathPrefixStrip`)](/basics/#path-matcher-usage-guidelines). Note: ReplacePath is deprecated in this annotation, use the `traefik.ingress.kubernetes.io/request-modifier` annotation instead. Default: `PathPrefix`. | +| `traefik.ingress.kubernetes.io/request-modifier: AddPrefix: /users` | Add a [request modifier](/basics/#modifiers) to the backend request. | +| `traefik.ingress.kubernetes.io/whitelist-source-range: "1.2.3.0/24, fe80::/16"` | A comma-separated list of IP ranges permitted for access (6). | +| `traefik.ingress.kubernetes.io/whiteList-ipstrategy=true` | Uses the default IPStrategy.
Can be used when there is an existing `clientIPStrategy` but you want the remote address for whitelisting. | +| `traefik.ingress.kubernetes.io/whiteList-ipstrategy-depth=5` | See [whitelist](/configuration/entrypoints/#white-listing) | +| `traefik.ingress.kubernetes.io/whiteList-ipstrategy-excludedIPs=127.0.0. 1` | See [whitelist](/configuration/entrypoints/#white-listing) | +| `traefik.ingress.kubernetes.io/app-root: "/index.html"` | Redirects all requests for `/` to the defined path. (4) | +| `traefik.ingress.kubernetes.io/service-weights: ` | Set ingress backend weights specified as percentage or decimal numbers in YAML. (5) | +| `ingress.kubernetes.io/protocol: ` | Set the protocol Traefik will use to communicate with pods. | <1> `traefik.ingress.kubernetes.io/error-pages` example: diff --git a/docs/configuration/backends/marathon.md b/docs/configuration/backends/marathon.md index 886323aee..8dcf7c4ba 100644 --- a/docs/configuration/backends/marathon.md +++ b/docs/configuration/backends/marathon.md @@ -193,66 +193,68 @@ They may be specified on one of two levels: Application or service. The following labels can be defined on Marathon applications. They adjust the behavior for the entire application. -| Label | Description | -|------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `traefik.domain` | Sets the default domain used for the frontend rules. | -| `traefik.enable=false` | Disables this container in Træfik. | -| `traefik.port=80` | Registers this port. Useful when the container exposes multiples ports. | -| `traefik.portIndex=1` | Registers port by index in the application's ports array. Useful when the application exposes multiple ports. | -| `traefik.protocol=https` | Overrides the default `http` protocol. | -| `traefik.weight=10` | Assigns this weight to the container. | -| `traefik.backend=foo` | Gives the name `foo` to the generated backend for this container. | -| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend | -| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. | -| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. (Default: 30s) | -| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. | -| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. | -| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. | -| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers
Format: HEADER:value||HEADER2:value2 | -| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm | -| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions | -| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions | -| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | -| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | -| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). | -| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | -| `traefik.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. | -| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | -| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | -| `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | -| `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | -| `traefik.frontend.auth.forward.address=https://example.com`| Sets the URL of the authentication server. | -| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | -| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true`| If set to true invalid SSL certificates are accepted. | -| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | -| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. | -| `traefik.frontend.auth.removeHeader=true` | If set to true, removes the Authorization header. | -| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | -| `traefik.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.errors..status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. | -| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. | -| `traefik.frontend.priority=10` | Overrides default frontend priority | -| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) | -| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.replacement`. | -| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.regex`. | -| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. | -| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{sub_domain}.{domain}`. | -| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | -| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. | +| Label | Description | +|-----------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `traefik.domain` | Sets the default domain used for the frontend rules. | +| `traefik.enable=false` | Disables this container in Træfik. | +| `traefik.port=80` | Registers this port. Useful when the container exposes multiples ports. | +| `traefik.portIndex=1` | Registers port by index in the application's ports array. Useful when the application exposes multiple ports. | +| `traefik.protocol=https` | Overrides the default `http` protocol. | +| `traefik.weight=10` | Assigns this weight to the container. | +| `traefik.backend=foo` | Gives the name `foo` to the generated backend for this container. | +| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend | +| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. | +| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. (Default: 30s) | +| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. | +| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. | +| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. | +| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers
Format: HEADER:value||HEADER2:value2 | +| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm | +| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions | +| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions | +| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | +| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | +| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). | +| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | +| `traefik.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. | +| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | +| `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | +| `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. | +| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | +| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true` | If set to true invalid SSL certificates are accepted. | +| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | +| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. | +| `traefik.frontend.auth.removeHeader=true` | If set to true, removes the Authorization header. | +| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | +| `traefik.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.errors..status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. | +| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. | +| `traefik.frontend.priority=10` | Overrides default frontend priority | +| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) | +| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.replacement`. | +| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.regex`. | +| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. | +| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{sub_domain}.{domain}`. | +| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | +| `traefik.frontend.whiteList.ipStrategy=true` | Uses the default IPStrategy.
Can be used when there is an existing `clientIPStrategy` but you want the remote address for whitelisting. | +| `traefik.frontend.whiteList.ipStrategy.depth=5` | See [whitelist](/configuration/entrypoints/#white-listing) | +| `traefik.frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | See [whitelist](/configuration/entrypoints/#white-listing) | #### Custom Headers @@ -295,48 +297,50 @@ You can define as many segments as ports exposed in an application. Segment labels override the default behavior. -| Label | Description | -|---------------------------------------------------------------------------|----------------------------------------------------------------| -| `traefik..backend=BACKEND` | Same as `traefik.backend` | -| `traefik..domain=DOMAIN` | Same as `traefik.domain` | -| `traefik..portIndex=1` | Same as `traefik.portIndex` | -| `traefik..port=PORT` | Same as `traefik.port` | -| `traefik..protocol=http` | Same as `traefik.protocol` | -| `traefik..weight=10` | Same as `traefik.weight` | -| `traefik..frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` | -| `traefik..frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` | -| `traefik..frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` | -| `traefik..frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` | -| `traefik..frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` | -| `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | -| `traefik..frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` | -| `traefik..frontend.auth.forward.address=https://example.com`| Same as `traefik.frontend.auth.forward.address` | -| `traefik..frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` | -| `traefik..frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` | -| `traefik..frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` | -| `traefik..frontend.auth.forward.tls.insecureSkipVerify=true`| Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` | -| `traefik..frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` | -| `traefik..frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` | -| `traefik..frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` | -| `traefik..frontend.auth.removeHeader=true` | Same as `traefik.frontend.auth.removeHeader` | -| `traefik..frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` | -| `traefik..frontend.errors..backend=NAME` | Same as `traefik.frontend.errors..backend` | -| `traefik..frontend.errors..query=PATH` | Same as `traefik.frontend.errors..query` | -| `traefik..frontend.errors..status=RANGE` | Same as `traefik.frontend.errors..status` | -| `traefik..frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` | -| `traefik..frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` | -| `traefik..frontend.priority=10` | Same as `traefik.frontend.priority` | -| `traefik..frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` | -| `traefik..frontend.rateLimit.rateSet..period=6` | Same as `traefik.frontend.rateLimit.rateSet..period` | -| `traefik..frontend.rateLimit.rateSet..average=6` | Same as `traefik.frontend.rateLimit.rateSet..average` | -| `traefik..frontend.rateLimit.rateSet..burst=6` | Same as `traefik.frontend.rateLimit.rateSet..burst` | -| `traefik..frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` | -| `traefik..frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` | -| `traefik..frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` | -| `traefik..frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` | -| `traefik..frontend.rule=EXP` | Same as `traefik.frontend.rule` | -| `traefik..frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` | -| `traefik..frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` | +| Label | Description | +|------------------------------------------------------------------------------ |----------------------------------------------------------------| +| `traefik..backend=BACKEND` | Same as `traefik.backend` | +| `traefik..domain=DOMAIN` | Same as `traefik.domain` | +| `traefik..portIndex=1` | Same as `traefik.portIndex` | +| `traefik..port=PORT` | Same as `traefik.port` | +| `traefik..protocol=http` | Same as `traefik.protocol` | +| `traefik..weight=10` | Same as `traefik.weight` | +| `traefik..frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` | +| `traefik..frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` | +| `traefik..frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` | +| `traefik..frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` | +| `traefik..frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` | +| `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | +| `traefik..frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` | +| `traefik..frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` | +| `traefik..frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` | +| `traefik..frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` | +| `traefik..frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` | +| `traefik..frontend.auth.forward.tls.insecureSkipVerify=true` | Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` | +| `traefik..frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` | +| `traefik..frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` | +| `traefik..frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` | +| `traefik..frontend.auth.removeHeader=true` | Same as `traefik.frontend.auth.removeHeader` | +| `traefik..frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` | +| `traefik..frontend.errors..backend=NAME` | Same as `traefik.frontend.errors..backend` | +| `traefik..frontend.errors..query=PATH` | Same as `traefik.frontend.errors..query` | +| `traefik..frontend.errors..status=RANGE` | Same as `traefik.frontend.errors..status` | +| `traefik..frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` | +| `traefik..frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` | +| `traefik..frontend.priority=10` | Same as `traefik.frontend.priority` | +| `traefik..frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` | +| `traefik..frontend.rateLimit.rateSet..period=6` | Same as `traefik.frontend.rateLimit.rateSet..period` | +| `traefik..frontend.rateLimit.rateSet..average=6` | Same as `traefik.frontend.rateLimit.rateSet..average` | +| `traefik..frontend.rateLimit.rateSet..burst=6` | Same as `traefik.frontend.rateLimit.rateSet..burst` | +| `traefik..frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` | +| `traefik..frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` | +| `traefik..frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` | +| `traefik..frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` | +| `traefik..frontend.rule=EXP` | Same as `traefik.frontend.rule` | +| `traefik..frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` | +| `traefik..frontend.whiteList.ipStrategy=true` | Same as `traefik.frontend.whiteList.ipStrategy` | +| `traefik..frontend.whiteList.ipStrategy.depth=5` | Same as `traefik.frontend.whiteList.ipStrategy.depth` | +| `traefik..frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | Same as `traefik.frontend.whiteList.ipStrategy.excludedIPs` | #### Custom Headers diff --git a/docs/configuration/backends/mesos.md b/docs/configuration/backends/mesos.md index e1b83e85a..3f4febf15 100644 --- a/docs/configuration/backends/mesos.md +++ b/docs/configuration/backends/mesos.md @@ -106,67 +106,69 @@ domain = "mesos.localhost" The following labels can be defined on Mesos tasks. They adjust the behavior for the entire application. -| Label | Description | -|------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `traefik.domain` | Sets the default domain for the frontend rules. | -| `traefik.enable=false` | Disables this container in Træfik. | -| `traefik.port=80` | Registers this port. Useful when the application exposes multiple ports. | -| `traefik.portName=web` | Registers port by name in the application's ports array. Useful when the application exposes multiple ports. | -| `traefik.portIndex=1` | Registers port by index in the application's ports array. Useful when the application exposes multiple ports. | -| `traefik.protocol=https` | Overrides the default `http` protocol | -| `traefik.weight=10` | Assigns this weight to the container | -| `traefik.backend=foo` | Gives the name `foo` to the generated backend for this container. | -| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend | -| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. | -| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. (Default: 30s) | -| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. | -| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. | -| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. | -| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers
Format: HEADER:value||HEADER2:value2 | -| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm | -| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions | -| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie manually name for sticky sessions | -| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | -| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | -| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). | -| `traefik.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. | -| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | -| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | -| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | -| `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | -| `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | -| `traefik.frontend.auth.forward.address=https://example.com`| Sets the URL of the authentication server. | -| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | -| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true`| If set to true invalid SSL certificates are accepted. | -| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | -| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. | -| `traefik.frontend.auth.removeHeader=true` | If set to true, removes the Authorization header. | -| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | -| `traefik.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.errors..status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. | -| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. | -| `traefik.frontend.priority=10` | Overrides default frontend priority | -| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) | -| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.replacement`. | -| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.regex`. | -| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. | -| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{discovery_name}.{domain}`. | -| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | -| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. | +| Label | Description | +|-----------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `traefik.domain` | Sets the default domain for the frontend rules. | +| `traefik.enable=false` | Disables this container in Træfik. | +| `traefik.port=80` | Registers this port. Useful when the application exposes multiple ports. | +| `traefik.portName=web` | Registers port by name in the application's ports array. Useful when the application exposes multiple ports. | +| `traefik.portIndex=1` | Registers port by index in the application's ports array. Useful when the application exposes multiple ports. | +| `traefik.protocol=https` | Overrides the default `http` protocol | +| `traefik.weight=10` | Assigns this weight to the container | +| `traefik.backend=foo` | Gives the name `foo` to the generated backend for this container. | +| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend | +| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. | +| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. (Default: 30s) | +| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. | +| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. | +| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. | +| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers
Format: HEADER:value||HEADER2:value2 | +| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm | +| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions | +| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie manually name for sticky sessions | +| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | +| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | +| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). | +| `traefik.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. | +| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | +| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | +| `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | +| `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. | +| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | +| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true` | If set to true invalid SSL certificates are accepted. | +| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | +| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. | +| `traefik.frontend.auth.removeHeader=true` | If set to true, removes the Authorization header. | +| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | +| `traefik.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.errors..status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. | +| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. | +| `traefik.frontend.priority=10` | Overrides default frontend priority | +| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) | +| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.replacement`. | +| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.regex`. | +| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. | +| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{discovery_name}.{domain}`. | +| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | +| `traefik.frontend.whiteList.ipStrategy=true` | Uses the default IPStrategy.
Can be used when there is an existing `clientIPStrategy` but you want the remote address for whitelisting. | +| `traefik.frontend.whiteList.ipStrategy.depth=5` | See [whitelist](/configuration/entrypoints/#white-listing) | +| `traefik.frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | See [whitelist](/configuration/entrypoints/#white-listing) | ### Custom Headers @@ -210,49 +212,51 @@ Additionally, if a segment name matches a named port, that port will be used unl Segment labels override the default behavior. -| Label | Description | -|---------------------------------------------------------------------------|----------------------------------------------------------------| -| `traefik..backend=BACKEND` | Same as `traefik.backend` | -| `traefik..domain=DOMAIN` | Same as `traefik.domain` | -| `traefik..portIndex=1` | Same as `traefik.portIndex` | -| `traefik..portName=web` | Same as `traefik.portName` | -| `traefik..port=PORT` | Same as `traefik.port` | -| `traefik..protocol=http` | Same as `traefik.protocol` | -| `traefik..weight=10` | Same as `traefik.weight` | -| `traefik..frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` | -| `traefik..frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` | -| `traefik..frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` | -| `traefik..frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` | -| `traefik..frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` | -| `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | -| `traefik..frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` | -| `traefik..frontend.auth.forward.address=https://example.com`| Same as `traefik.frontend.auth.forward.address` | -| `traefik..frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` | -| `traefik..frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` | -| `traefik..frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` | -| `traefik..frontend.auth.forward.tls.insecureSkipVerify=true`| Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` | -| `traefik..frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` | -| `traefik..frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` | -| `traefik..frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` | -| `traefik..frontend.auth.removeHeader=true` | Same as `traefik.frontend.auth.removeHeader` | -| `traefik..frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` | -| `traefik..frontend.errors..backend=NAME` | Same as `traefik.frontend.errors..backend` | -| `traefik..frontend.errors..query=PATH` | Same as `traefik.frontend.errors..query` | -| `traefik..frontend.errors..status=RANGE` | Same as `traefik.frontend.errors..status` | -| `traefik..frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` | -| `traefik..frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` | -| `traefik..frontend.priority=10` | Same as `traefik.frontend.priority` | -| `traefik..frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` | -| `traefik..frontend.rateLimit.rateSet..period=6` | Same as `traefik.frontend.rateLimit.rateSet..period` | -| `traefik..frontend.rateLimit.rateSet..average=6` | Same as `traefik.frontend.rateLimit.rateSet..average` | -| `traefik..frontend.rateLimit.rateSet..burst=6` | Same as `traefik.frontend.rateLimit.rateSet..burst` | -| `traefik..frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` | -| `traefik..frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` | -| `traefik..frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` | -| `traefik..frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` | -| `traefik..frontend.rule=EXP` | Same as `traefik.frontend.rule` | -| `traefik..frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` | -| `traefik..frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` | +| Label | Description | +|------------------------------------------------------------------------------|----------------------------------------------------------------| +| `traefik..backend=BACKEND` | Same as `traefik.backend` | +| `traefik..domain=DOMAIN` | Same as `traefik.domain` | +| `traefik..portIndex=1` | Same as `traefik.portIndex` | +| `traefik..portName=web` | Same as `traefik.portName` | +| `traefik..port=PORT` | Same as `traefik.port` | +| `traefik..protocol=http` | Same as `traefik.protocol` | +| `traefik..weight=10` | Same as `traefik.weight` | +| `traefik..frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` | +| `traefik..frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` | +| `traefik..frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` | +| `traefik..frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` | +| `traefik..frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` | +| `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | +| `traefik..frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` | +| `traefik..frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` | +| `traefik..frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` | +| `traefik..frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` | +| `traefik..frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` | +| `traefik..frontend.auth.forward.tls.insecureSkipVerify=true` | Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` | +| `traefik..frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` | +| `traefik..frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` | +| `traefik..frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` | +| `traefik..frontend.auth.removeHeader=true` | Same as `traefik.frontend.auth.removeHeader` | +| `traefik..frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` | +| `traefik..frontend.errors..backend=NAME` | Same as `traefik.frontend.errors..backend` | +| `traefik..frontend.errors..query=PATH` | Same as `traefik.frontend.errors..query` | +| `traefik..frontend.errors..status=RANGE` | Same as `traefik.frontend.errors..status` | +| `traefik..frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` | +| `traefik..frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` | +| `traefik..frontend.priority=10` | Same as `traefik.frontend.priority` | +| `traefik..frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` | +| `traefik..frontend.rateLimit.rateSet..period=6` | Same as `traefik.frontend.rateLimit.rateSet..period` | +| `traefik..frontend.rateLimit.rateSet..average=6` | Same as `traefik.frontend.rateLimit.rateSet..average` | +| `traefik..frontend.rateLimit.rateSet..burst=6` | Same as `traefik.frontend.rateLimit.rateSet..burst` | +| `traefik..frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` | +| `traefik..frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` | +| `traefik..frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` | +| `traefik..frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` | +| `traefik..frontend.rule=EXP` | Same as `traefik.frontend.rule` | +| `traefik..frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` | +| `traefik..frontend.whiteList.ipStrategy=true` | Same as `traefik.frontend.whiteList.ipStrategy` | +| `traefik..frontend.whiteList.ipStrategy.depth=5` | Same as `traefik.frontend.whiteList.ipStrategy.depth` | +| `traefik..frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | Same as `traefik.frontend.whiteList.ipStrategy.excludedIPs` | #### Custom Headers diff --git a/docs/configuration/backends/rancher.md b/docs/configuration/backends/rancher.md index 2a2e2f84f..792fe3164 100644 --- a/docs/configuration/backends/rancher.md +++ b/docs/configuration/backends/rancher.md @@ -138,64 +138,66 @@ secretKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" Labels can be used on task containers to override default behavior: -| Label | Description | -|------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `traefik.domain` | Sets the default domain for the frontend rules. | -| `traefik.enable=false` | Disables this container in Træfik. | -| `traefik.port=80` | Registers this port. Useful when the container exposes multiple ports. | -| `traefik.protocol=https` | Overrides the default `http` protocol. | -| `traefik.weight=10` | Assigns this weight to the container. | -| `traefik.backend=foo` | Gives the name `foo` to the generated backend for this container. | -| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend | -| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. | -| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. | -| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. | -| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. | -| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. | -| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers
Format: HEADER:value||HEADER2:value2 | -| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm | -| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions | -| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions | -| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | -| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | -| `traefik.frontend.auth.basic=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). | -| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | -| `traefik.frontend.auth.basic.users=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` . | -| `traefik.frontend.auth.basic.usersfile=/path/.htpasswd` | Sets the basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | -| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | -| `traefik.frontend.auth.digest.users=EXPR` | Sets the digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | -| `traefik.frontend.auth.digest.usersfile=/path/.htdigest` | Sets the digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | -| `traefik.frontend.auth.forward.address=https://example.com`| Sets the URL of the authentication server. | -| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | -| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true`| If set to true invalid SSL certificates are accepted. | -| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | -| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. | -| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | -| `traefik.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.errors..status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. | -| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. | -| `traefik.frontend.priority=10` | Overrides default frontend priority | -| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) | -| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.replacement`. | -| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.regex`. | -| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. | -| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{containerName}.{domain}` or `Host:{service}.{project_name}.{domain}` if you are using `docker-compose`. | -| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access.
If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | -| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. | +| Label | Description | +|-----------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `traefik.domain` | Sets the default domain for the frontend rules. | +| `traefik.enable=false` | Disables this container in Træfik. | +| `traefik.port=80` | Registers this port. Useful when the container exposes multiple ports. | +| `traefik.protocol=https` | Overrides the default `http` protocol. | +| `traefik.weight=10` | Assigns this weight to the container. | +| `traefik.backend=foo` | Gives the name `foo` to the generated backend for this container. | +| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend | +| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. | +| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. | +| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. | +| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. | +| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. | +| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers
Format: HEADER:value||HEADER2:value2 | +| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm | +| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions | +| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions | +| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | +| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | +| `traefik.frontend.auth.basic=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). | +| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | +| `traefik.frontend.auth.basic.users=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` . | +| `traefik.frontend.auth.basic.usersfile=/path/.htpasswd` | Sets the basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | +| `traefik.frontend.auth.digest.users=EXPR` | Sets the digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | +| `traefik.frontend.auth.digest.usersfile=/path/.htdigest` | Sets the digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. | +| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | +| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true` | If set to true invalid SSL certificates are accepted. | +| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | +| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. | +| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | +| `traefik.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.errors..status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. | +| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. | +| `traefik.frontend.priority=10` | Overrides default frontend priority | +| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) | +| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.replacement`. | +| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.regex`. | +| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. | +| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{containerName}.{domain}` or `Host:{service}.{project_name}.{domain}` if you are using `docker-compose`. | +| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access.
If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | +| `traefik.frontend.whiteList.ipStrategy=true` | Uses the default IPStrategy.
Can be used when there is an existing `clientIPStrategy` but you want the remote address for whitelisting. | +| `traefik.frontend.whiteList.ipStrategy.depth=5` | See [whitelist](/configuration/entrypoints/#white-listing) | +| `traefik.frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | See [whitelist](/configuration/entrypoints/#white-listing) | #### Custom Headers @@ -237,46 +239,48 @@ You can define as many segments as ports exposed in a container. Segment labels override the default behavior. -| Label | Description | -|---------------------------------------------------------------------------|---------------------------------------------------------------| -| `traefik..backend=BACKEND` | Same as `traefik.backend` | -| `traefik..domain=DOMAIN` | Same as `traefik.domain` | -| `traefik..port=PORT` | Same as `traefik.port` | -| `traefik..protocol=http` | Same as `traefik.protocol` | -| `traefik..weight=10` | Same as `traefik.weight` | -| `traefik..frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` | -| `traefik..frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` | -| `traefik..frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` | -| `traefik..frontend.auth.basic.usersfile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersfile` | -| `traefik..frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` | -| `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | -| `traefik..frontend.auth.digest.usersfile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersfile` | -| `traefik..frontend.auth.forward.address=https://example.com`| Same as `traefik.frontend.auth.forward.address` | -| `traefik..frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` | -| `traefik..frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` | -| `traefik..frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` | -| `traefik..frontend.auth.forward.tls.insecureSkipVerify=true`| Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify`| -| `traefik..frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` | -| `traefik..frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` | -| `traefik..frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` | -| `traefik..frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` | -| `traefik..frontend.errors..backend=NAME` | Same as `traefik.frontend.errors..backend` | -| `traefik..frontend.errors..query=PATH` | Same as `traefik.frontend.errors..query` | -| `traefik..frontend.errors..status=RANGE` | Same as `traefik.frontend.errors..status` | -| `traefik..frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` | -| `traefik..frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` | -| `traefik..frontend.priority=10` | Same as `traefik.frontend.priority` | -| `traefik..frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` | -| `traefik..frontend.rateLimit.rateSet..period=6` | Same as `traefik.frontend.rateLimit.rateSet..period` | -| `traefik..frontend.rateLimit.rateSet..average=6` | Same as `traefik.frontend.rateLimit.rateSet..average` | -| `traefik..frontend.rateLimit.rateSet..burst=6` | Same as `traefik.frontend.rateLimit.rateSet..burst` | -| `traefik..frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` | -| `traefik..frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` | -| `traefik..frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` | -| `traefik..frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` | -| `traefik..frontend.rule=EXP` | Same as `traefik.frontend.rule` | -| `traefik..frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` | -| `traefik..frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` | +| Label | Description | +|------------------------------------------------------------------------------|----------------------------------------------------------------| +| `traefik..backend=BACKEND` | Same as `traefik.backend` | +| `traefik..domain=DOMAIN` | Same as `traefik.domain` | +| `traefik..port=PORT` | Same as `traefik.port` | +| `traefik..protocol=http` | Same as `traefik.protocol` | +| `traefik..weight=10` | Same as `traefik.weight` | +| `traefik..frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` | +| `traefik..frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` | +| `traefik..frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` | +| `traefik..frontend.auth.basic.usersfile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersfile` | +| `traefik..frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` | +| `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | +| `traefik..frontend.auth.digest.usersfile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersfile` | +| `traefik..frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` | +| `traefik..frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` | +| `traefik..frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` | +| `traefik..frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` | +| `traefik..frontend.auth.forward.tls.insecureSkipVerify=true` | Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` | +| `traefik..frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` | +| `traefik..frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` | +| `traefik..frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` | +| `traefik..frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` | +| `traefik..frontend.errors..backend=NAME` | Same as `traefik.frontend.errors..backend` | +| `traefik..frontend.errors..query=PATH` | Same as `traefik.frontend.errors..query` | +| `traefik..frontend.errors..status=RANGE` | Same as `traefik.frontend.errors..status` | +| `traefik..frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` | +| `traefik..frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` | +| `traefik..frontend.priority=10` | Same as `traefik.frontend.priority` | +| `traefik..frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` | +| `traefik..frontend.rateLimit.rateSet..period=6` | Same as `traefik.frontend.rateLimit.rateSet..period` | +| `traefik..frontend.rateLimit.rateSet..average=6` | Same as `traefik.frontend.rateLimit.rateSet..average` | +| `traefik..frontend.rateLimit.rateSet..burst=6` | Same as `traefik.frontend.rateLimit.rateSet..burst` | +| `traefik..frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` | +| `traefik..frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` | +| `traefik..frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` | +| `traefik..frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` | +| `traefik..frontend.rule=EXP` | Same as `traefik.frontend.rule` | +| `traefik..frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` | +| `traefik..frontend.whiteList.ipStrategy=true` | Same as `traefik.frontend.whiteList.ipStrategy` | +| `traefik..frontend.whiteList.ipStrategy.depth=5` | Same as `traefik.frontend.whiteList.ipStrategy.depth` | +| `traefik..frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | Same as `traefik.frontend.whiteList.ipStrategy.excludedIPs` | #### Custom Headers diff --git a/docs/configuration/backends/servicefabric.md b/docs/configuration/backends/servicefabric.md index e69af6f69..f08b9522b 100644 --- a/docs/configuration/backends/servicefabric.md +++ b/docs/configuration/backends/servicefabric.md @@ -94,36 +94,38 @@ curl -X PUT \ Labels, set through extensions or the property manager, can be used on services to override default behavior. -| Label | Description | -|------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `traefik.enable=false` | Disable this container in Træfik | -| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend | +| Label | Description | +|-----------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `traefik.enable=false` | Disable this container in Træfik | +| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend | | `traefik.servicefabric.groupname` | Group all services with the same name into a single backend in Træfik | | `traefik.servicefabric.groupweight` | Set the weighting of the current services nodes in the backend group | -| `traefik.servicefabric.enablelabeloverrides` | Toggle whether labels can be overridden using the Service Fabric Property Manager API | -| `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. | -| `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. | -| `traefik.backend.healthcheck.interval=1s` | Define the health check interval. | -| `traefik.backend.healthcheck.hostname=foobar.com` | Define the health check hostname. | -| `traefik.backend.healthcheck.headers=EXPR` | Define the health check request headers
Format: HEADER:value||HEADER2:value2 | -| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm | -| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions | -| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions | -| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | -| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | -| `traefik.backend.weight=10` | Assign this weight to the container | -| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` | -| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | -| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. | -| `traefik.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. | -| `traefik.frontend.priority=10` | Override default frontend priority | -| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) | -| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.
Must be set with `traefik.frontend.redirect.replacement`. | -| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.
Must be set with `traefik.frontend.redirect.regex`. | -| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. | -| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Defaults to SF address. | -| `traefik.frontend.whiteList.sourceRange=RANGE` | List of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access.
If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | -| `traefik.frontend.whiteList.useXForwardedFor=true` | Use `X-Forwarded-For` header as valid source of IP for the white list. | +| `traefik.servicefabric.enablelabeloverrides` | Toggle whether labels can be overridden using the Service Fabric Property Manager API | +| `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. | +| `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. | +| `traefik.backend.healthcheck.interval=1s` | Define the health check interval. | +| `traefik.backend.healthcheck.hostname=foobar.com` | Define the health check hostname. | +| `traefik.backend.healthcheck.headers=EXPR` | Define the health check request headers
Format: HEADER:value||HEADER2:value2 | +| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm | +| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions | +| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions | +| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | +| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | +| `traefik.backend.weight=10` | Assign this weight to the container | +| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` | +| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | +| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. | +| `traefik.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. | +| `traefik.frontend.priority=10` | Override default frontend priority | +| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) | +| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.
Must be set with `traefik.frontend.redirect.replacement`. | +| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.
Must be set with `traefik.frontend.redirect.regex`. | +| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. | +| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Defaults to SF address. | +| `traefik.frontend.whiteList.sourceRange=RANGE` | List of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access.
If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | +| `traefik.frontend.whiteList.ipStrategy=true` | Uses the default IPStrategy.
Can be used when there is an existing `clientIPStrategy` but you want the remote address for whitelisting. | +| `traefik.frontend.whiteList.ipStrategy.depth=5` | See [whitelist](/configuration/entrypoints/#white-listing) | +| `traefik.frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | See [whitelist](/configuration/entrypoints/#white-listing) | ### Custom Headers diff --git a/docs/configuration/entrypoints.md b/docs/configuration/entrypoints.md index 2580c248b..d07149209 100644 --- a/docs/configuration/entrypoints.md +++ b/docs/configuration/entrypoints.md @@ -14,11 +14,17 @@ defaultEntryPoints = ["http", "https"] [entryPoints.http] address = ":80" [entryPoints.http.compress] + + [entryPoints.http.clientIPStrategy] + depth = 5 + excludedIPs = ["127.0.0.1/32", "192.168.1.7"] [entryPoints.http.whitelist] sourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"] - useXForwardedFor = true - + [entryPoints.http.whitelist.IPStrategy] + depth = 5 + excludedIPs = ["127.0.0.1/32", "192.168.1.7"] + [entryPoints.http.tls] minVersion = "VersionTLS12" cipherSuites = [ @@ -75,6 +81,7 @@ defaultEntryPoints = ["http", "https"] [entryPoints.http.forwardedHeaders] trustedIPs = ["10.10.10.1", "10.10.10.2"] + insecure = false [entryPoints.https] # ... @@ -129,7 +136,8 @@ Redirect.Replacement:http://mydomain/$1 Redirect.Permanent:true Compress:true WhiteList.SourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 -WhiteList.UseXForwardedFor:true +WhiteList.IPStrategy.depth:3 +WhiteList.IPStrategy.ExcludedIPs:10.0.0.3/24,20.0.0.3/24 ProxyProtocol.TrustedIPs:192.168.0.1 ProxyProtocol.Insecure:true ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24 @@ -464,7 +472,9 @@ Responses are compressed when: ## White Listing -To enable IP white listing at the entry point level. +Træfik supports whitelisting to accept or refuse requests based on the client IP. + +The following example enables IP white listing and accepts requests from client IPs defined in `sourceRange`. ```toml [entryPoints] @@ -473,9 +483,97 @@ To enable IP white listing at the entry point level. [entryPoints.http.whiteList] sourceRange = ["127.0.0.1/32", "192.168.1.7"] - # useXForwardedFor = true + # [entryPoints.http.whiteList.IPStrategy] + # Override the clientIPStrategy ``` +By default, Træfik uses the client IP (see [ClientIPStrategy](/configuration/entrypoints/#clientipstrategy)) for the whitelisting. + +If you want to use another IP than the one determined by `ClientIPStrategy` for the whitelisting, you can define the `IPStrategy` option: + +```toml +[entryPoints] + [entryPoints.http.clientIPStrategy] + depth = 4 + [entryPoints.http] + address = ":80" + + [entryPoints.http.whiteList] + sourceRange = ["127.0.0.1/32", "192.168.1.7"] + [entryPoints.http.whiteList.IPStrategy] + depth = 2 +``` + +In the above example, if the value of the `X-Forwarded-For` header was `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` then the client IP would be `"10.0.0.1"` (`clientIPStrategy.depth=4`) but the IP used for the whitelisting would be `"12.0.0.1"` (`whitelist.IPStrategy.depth=2`). + +## ClientIPStrategy + +The `clientIPStrategy` defines how you want Træfik to determine the client IP (used for whitelisting for example). + +There are several option available: + +### Depth + +This option uses the `X-Forwarded-For` header and takes the IP located at the `depth` position (starting from the right). +```toml +[entryPoints] + [entryPoints.http] + address = ":80" + + [entryPoints.http.clientIPStrategy] +``` + +```toml +[entryPoints] + [entryPoints.http] + address = ":80" + + [entryPoints.http.clientIPStrategy] + depth = 5 +``` + +!!! note + - If `depth` is greater than the total number of IPs in `X-Forwarded-For`, then clientIP will be empty. + - If `depth` is lesser than or equal to 0, then the option is ignored. + +Examples: + +| `X-Forwarded-For` | `depth` | clientIP | +|-----------------------------------------|---------|--------------| +| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `1` | `"13.0.0.1"` | +| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `3` | `"11.0.0.1"` | +| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `5` | `""` | + +### Excluded IPs + +Træfik will scan the `X-Forwarded-For` header (from the right) and pick the first IP not in the `excludedIPs` list. + +```toml +[entryPoints] + [entryPoints.http] + address = ":80" + + [entryPoints.http.clientIPStrategy] + excludedIPs = ["127.0.0.1/32", "192.168.1.7"] +``` + +!!! note + If `depth` is specified, `excludedIPs` is ignored. + +Examples: + +| `X-Forwarded-For` | `excludedIPs` | clientIP | +|-----------------------------------------|-----------------------|--------------| +| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `"12.0.0.1,13.0.0.1"` | `"11.0.0.1"` | +| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `"15.0.0.1,13.0.0.1"` | `"12.0.0.1"` | +| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `"10.0.0.1,13.0.0.1"` | `"12.0.0.1"` | +| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `"15.0.0.1,16.0.0.1"` | `"13.0.0.1"` | +| `"10.0.0.1,11.0.0.1"` | `"10.0.0.1,11.0.0.1"` | `""` | + +### Default + +If there are no `depth` or `excludedIPs`, then the client IP is the IP of the computer that initiated the connection with the Træfik server (the remote address). + ## ProxyProtocol To enable [ProxyProtocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) support. @@ -524,4 +622,12 @@ Only IPs in `trustedIPs` will be authorized to trust the client forwarded header # Default: [] # trustedIPs = ["127.0.0.1/32", "192.168.1.7"] + + # Insecure mode + # + # Optional + # Default: false + # + # insecure = true + ``` diff --git a/integration/basic_test.go b/integration/basic_test.go index 11c11299c..f1bc295db 100644 --- a/integration/basic_test.go +++ b/integration/basic_test.go @@ -3,6 +3,7 @@ package integration import ( "fmt" "net/http" + "net/http/httptest" "os" "strings" "syscall" @@ -253,7 +254,6 @@ func (s *SimpleSuite) TestNoAuthOnPing(c *check.C) { } func (s *SimpleSuite) TestDefaultEntrypointHTTP(c *check.C) { - s.createComposeProject(c, "base") s.composeProject.Start(c) @@ -272,7 +272,6 @@ func (s *SimpleSuite) TestDefaultEntrypointHTTP(c *check.C) { } func (s *SimpleSuite) TestWithUnexistingEntrypoint(c *check.C) { - s.createComposeProject(c, "base") s.composeProject.Start(c) @@ -291,7 +290,6 @@ func (s *SimpleSuite) TestWithUnexistingEntrypoint(c *check.C) { } func (s *SimpleSuite) TestMetricsPrometheusDefaultEntrypoint(c *check.C) { - s.createComposeProject(c, "base") s.composeProject.Start(c) @@ -313,15 +311,16 @@ func (s *SimpleSuite) TestMetricsPrometheusDefaultEntrypoint(c *check.C) { } func (s *SimpleSuite) TestMultipleProviderSameBackendName(c *check.C) { - s.createComposeProject(c, "base") s.composeProject.Start(c) + ipWhoami01 := s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress ipWhoami02 := s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress file := s.adaptFile(c, "fixtures/multiple_provider.toml", struct{ IP string }{ IP: ipWhoami02, }) defer os.Remove(file) + cmd, output := s.traefikCmd(withConfigFile(file)) defer output(c) @@ -339,3 +338,80 @@ func (s *SimpleSuite) TestMultipleProviderSameBackendName(c *check.C) { c.Assert(err, checker.IsNil) } + +func (s *SimpleSuite) TestIPStrategyWhitelist(c *check.C) { + s.createComposeProject(c, "whitelist") + s.composeProject.Start(c) + + cmd, output := s.traefikCmd(withConfigFile("fixtures/simple_whitelist.toml")) + defer output(c) + + err := cmd.Start() + c.Assert(err, checker.IsNil) + defer cmd.Process.Kill() + + err = try.GetRequest("http://127.0.0.1:8080/api/providers", 1*time.Second, try.BodyContains("override")) + c.Assert(err, checker.IsNil) + + testCases := []struct { + desc string + xForwardedFor string + host string + expectedStatusCode int + }{ + { + desc: "default client ip strategy accept", + xForwardedFor: "8.8.8.8,127.0.0.1", + host: "no.override.whitelist.docker.local", + expectedStatusCode: 200, + }, + { + desc: "default client ip strategy reject", + xForwardedFor: "8.8.8.10,127.0.0.1", + host: "no.override.whitelist.docker.local", + expectedStatusCode: 403, + }, + { + desc: "override remote addr reject", + xForwardedFor: "8.8.8.8,8.8.8.8", + host: "override.remoteaddr.whitelist.docker.local", + expectedStatusCode: 403, + }, + { + desc: "override depth accept", + xForwardedFor: "8.8.8.8,10.0.0.1,127.0.0.1", + host: "override.depth.whitelist.docker.local", + expectedStatusCode: 200, + }, + { + desc: "override depth reject", + xForwardedFor: "10.0.0.1,8.8.8.8,127.0.0.1", + host: "override.depth.whitelist.docker.local", + expectedStatusCode: 403, + }, + { + desc: "override excludedIPs reject", + xForwardedFor: "10.0.0.3,10.0.0.1,10.0.0.2", + host: "override.excludedips.whitelist.docker.local", + expectedStatusCode: 403, + }, + { + desc: "override excludedIPs accept", + xForwardedFor: "8.8.8.8,10.0.0.1,10.0.0.2", + host: "override.excludedips.whitelist.docker.local", + expectedStatusCode: 200, + }, + } + + for _, test := range testCases { + req := httptest.NewRequest(http.MethodGet, "http://127.0.0.1:8000", nil) + req.Header.Set("X-Forwarded-For", test.xForwardedFor) + req.Host = test.host + req.RequestURI = "" + + err = try.Request(req, 1*time.Second, try.StatusCodeIs(test.expectedStatusCode)) + if err != nil { + c.Fatalf("Error while %s: %v", test.desc, err) + } + } +} diff --git a/integration/fixtures/access_log_config.toml b/integration/fixtures/access_log_config.toml index 64a4b3db9..d9aebb735 100644 --- a/integration/fixtures/access_log_config.toml +++ b/integration/fixtures/access_log_config.toml @@ -17,7 +17,8 @@ checkNewVersion = false entryPoint = "http" [entryPoints.httpWhitelistReject] address = ":8002" - whiteListSourceRange = ["8.8.8.8/32"] + [entryPoints.httpWhitelistReject.whiteList] + sourceRange = ["8.8.8.8/32"] [entryPoints.httpAuth] address = ":8004" [entryPoints.httpAuth.auth.basic] diff --git a/integration/fixtures/simple_whitelist.toml b/integration/fixtures/simple_whitelist.toml new file mode 100644 index 000000000..dc3010e30 --- /dev/null +++ b/integration/fixtures/simple_whitelist.toml @@ -0,0 +1,13 @@ +logLevel = "DEBUG" +defaultEntryPoints = ["http"] + +[entryPoints] + [entryPoints.http] + address = ":8000" + [entryPoints.http.ForwardedHeaders] + insecure=true + [entryPoints.http.ClientIPStrategy] + depth=2 + +[api] +[docker] diff --git a/integration/resources/compose/access_log.yml b/integration/resources/compose/access_log.yml index fad10bbab..1a1d542f8 100644 --- a/integration/resources/compose/access_log.yml +++ b/integration/resources/compose/access_log.yml @@ -102,6 +102,6 @@ frontendWhitelist: - traefik.enable=true - traefik.port=80 - traefik.backend=backend3 - - traefik.frontend.whitelistSourceRange=8.8.8.8/32 + - traefik.frontend.whiteList.sourceRange=8.8.8.8/32 - traefik.frontend.entryPoints=http - traefik.frontend.rule=Host:frontend.whitelist.docker.local diff --git a/integration/resources/compose/whitelist.yml b/integration/resources/compose/whitelist.yml new file mode 100644 index 000000000..856ba2cf9 --- /dev/null +++ b/integration/resources/compose/whitelist.yml @@ -0,0 +1,34 @@ +noOverrideWhitelist: + image: containous/whoami + labels: + - traefik.enable=true + - traefik.port=80 + - traefik.frontend.rule=Host:no.override.whitelist.docker.local + - traefik.frontend.whiteList.sourceRange=8.8.8.8 + +overrideIPStrategyRemoteAddrWhitelist: + image: containous/whoami + labels: + - traefik.enable=true + - traefik.port=80 + - traefik.frontend.rule=Host:override.remoteaddr.whitelist.docker.local + - traefik.frontend.whiteList.sourceRange=8.8.8.8 + - traefik.frontend.whiteList.ipStrategy=true + +overrideIPStrategyDepthWhitelist: + image: containous/whoami + labels: + - traefik.enable=true + - traefik.port=80 + - traefik.frontend.rule=Host:override.depth.whitelist.docker.local + - traefik.frontend.whiteList.sourceRange=8.8.8.8 + - traefik.frontend.whiteList.ipStrategy.depth=3 + +overrideIPStrategyExcludedIPsWhitelist: + image: containous/whoami + labels: + - traefik.enable=true + - traefik.port=80 + - traefik.frontend.rule=Host:override.excludedips.whitelist.docker.local + - traefik.frontend.whiteList.sourceRange=8.8.8.8 + - traefik.frontend.whiteList.ipStrategy.excludedIPs=10.0.0.1,10.0.0.2 diff --git a/ip/checker.go b/ip/checker.go new file mode 100644 index 000000000..3cc3dccf0 --- /dev/null +++ b/ip/checker.go @@ -0,0 +1,99 @@ +package ip + +import ( + "errors" + "fmt" + "net" + "strings" +) + +// Checker allows to check that addresses are in a trusted IPs +type Checker struct { + authorizedIPs []*net.IP + authorizedIPsNet []*net.IPNet +} + +// NewChecker builds a new Checker given a list of CIDR-Strings to trusted IPs +func NewChecker(trustedIPs []string) (*Checker, error) { + if len(trustedIPs) == 0 { + return nil, errors.New("no trusted IPs provided") + } + + checker := &Checker{} + + for _, ipMask := range trustedIPs { + if ipAddr := net.ParseIP(ipMask); ipAddr != nil { + checker.authorizedIPs = append(checker.authorizedIPs, &ipAddr) + } else { + _, ipAddr, err := net.ParseCIDR(ipMask) + if err != nil { + return nil, fmt.Errorf("parsing CIDR trusted IPs %s: %v", ipAddr, err) + } + checker.authorizedIPsNet = append(checker.authorizedIPsNet, ipAddr) + } + } + + return checker, nil +} + +// IsAuthorized checks if provided request is authorized by the trusted IPs +func (ip *Checker) IsAuthorized(addr string) error { + var invalidMatches []string + + host, _, err := net.SplitHostPort(addr) + if err != nil { + host = addr + } + + ok, err := ip.Contains(host) + if err != nil { + return err + } + + if !ok { + invalidMatches = append(invalidMatches, addr) + return fmt.Errorf("%q matched none of the trusted IPs", strings.Join(invalidMatches, ", ")) + } + + return nil +} + +// Contains checks if provided address is in the trusted IPs +func (ip *Checker) Contains(addr string) (bool, error) { + if len(addr) <= 0 { + return false, errors.New("empty IP address") + } + + ipAddr, err := parseIP(addr) + if err != nil { + return false, fmt.Errorf("unable to parse address: %s: %s", addr, err) + } + + return ip.ContainsIP(ipAddr), nil +} + +// ContainsIP checks if provided address is in the trusted IPs +func (ip *Checker) ContainsIP(addr net.IP) bool { + for _, authorizedIP := range ip.authorizedIPs { + if authorizedIP.Equal(addr) { + return true + } + } + + for _, authorizedNet := range ip.authorizedIPsNet { + if authorizedNet.Contains(addr) { + return true + } + } + + return false +} + +func parseIP(addr string) (net.IP, error) { + userIP := net.ParseIP(addr) + if userIP == nil { + return nil, fmt.Errorf("can't parse IP from address %s", addr) + } + + return userIP, nil +} diff --git a/ip/checker_test.go b/ip/checker_test.go new file mode 100644 index 000000000..0e7a5c6fd --- /dev/null +++ b/ip/checker_test.go @@ -0,0 +1,326 @@ +package ip + +import ( + "net" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestIsAuthorized(t *testing.T) { + testCases := []struct { + desc string + whiteList []string + remoteAddr string + authorized bool + }{ + { + desc: "remoteAddr not in range", + whiteList: []string{"1.2.3.4/24"}, + remoteAddr: "10.2.3.1:123", + authorized: false, + }, + { + desc: "remoteAddr in range", + whiteList: []string{"1.2.3.4/24"}, + remoteAddr: "1.2.3.1:123", + authorized: true, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + ipChecker, err := NewChecker(test.whiteList) + require.NoError(t, err) + + err = ipChecker.IsAuthorized(test.remoteAddr) + if test.authorized { + require.NoError(t, err) + } else { + require.Error(t, err) + } + }) + } +} + +func TestNew(t *testing.T) { + testCases := []struct { + desc string + trustedIPs []string + expectedAuthorizedIPs []*net.IPNet + errMessage string + }{ + { + desc: "nil trusted IPs", + trustedIPs: nil, + expectedAuthorizedIPs: nil, + errMessage: "no trusted IPs provided", + }, { + desc: "empty trusted IPs", + trustedIPs: []string{}, + expectedAuthorizedIPs: nil, + errMessage: "no trusted IPs provided", + }, { + desc: "trusted IPs containing empty string", + trustedIPs: []string{ + "1.2.3.4/24", + "", + "fe80::/16", + }, + expectedAuthorizedIPs: nil, + errMessage: "parsing CIDR trusted IPs : invalid CIDR address: ", + }, { + desc: "trusted IPs containing only an empty string", + trustedIPs: []string{ + "", + }, + expectedAuthorizedIPs: nil, + errMessage: "parsing CIDR trusted IPs : invalid CIDR address: ", + }, { + desc: "trusted IPs containing an invalid string", + trustedIPs: []string{ + "foo", + }, + expectedAuthorizedIPs: nil, + errMessage: "parsing CIDR trusted IPs : invalid CIDR address: foo", + }, { + desc: "IPv4 & IPv6 trusted IPs", + trustedIPs: []string{ + "1.2.3.4/24", + "fe80::/16", + }, + expectedAuthorizedIPs: []*net.IPNet{ + {IP: net.IPv4(1, 2, 3, 0).To4(), Mask: net.IPv4Mask(255, 255, 255, 0)}, + {IP: net.ParseIP("fe80::"), Mask: net.IPMask(net.ParseIP("ffff::"))}, + }, + errMessage: "", + }, { + desc: "IPv4 only", + trustedIPs: []string{ + "127.0.0.1/8", + }, + expectedAuthorizedIPs: []*net.IPNet{ + {IP: net.IPv4(127, 0, 0, 0).To4(), Mask: net.IPv4Mask(255, 0, 0, 0)}, + }, + errMessage: "", + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + ipChecker, err := NewChecker(test.trustedIPs) + if test.errMessage != "" { + require.EqualError(t, err, test.errMessage) + } else { + require.NoError(t, err) + for index, actual := range ipChecker.authorizedIPsNet { + expected := test.expectedAuthorizedIPs[index] + assert.Equal(t, expected.IP, actual.IP) + assert.Equal(t, expected.Mask.String(), actual.Mask.String()) + } + } + }) + } +} + +func TestContainsIsAllowed(t *testing.T) { + testCases := []struct { + desc string + trustedIPs []string + passIPs []string + rejectIPs []string + }{ + { + desc: "IPv4", + trustedIPs: []string{"1.2.3.4/24"}, + passIPs: []string{ + "1.2.3.1", + "1.2.3.32", + "1.2.3.156", + "1.2.3.255", + }, + rejectIPs: []string{ + "1.2.16.1", + "1.2.32.1", + "127.0.0.1", + "8.8.8.8", + }, + }, + { + desc: "IPv4 single IP", + trustedIPs: []string{"8.8.8.8"}, + passIPs: []string{"8.8.8.8"}, + rejectIPs: []string{ + "8.8.8.7", + "8.8.8.9", + "8.8.8.0", + "8.8.8.255", + "4.4.4.4", + "127.0.0.1", + }, + }, + { + desc: "IPv4 Net single IP", + trustedIPs: []string{"8.8.8.8/32"}, + passIPs: []string{"8.8.8.8"}, + rejectIPs: []string{ + "8.8.8.7", + "8.8.8.9", + "8.8.8.0", + "8.8.8.255", + "4.4.4.4", + "127.0.0.1", + }, + }, + { + desc: "multiple IPv4", + trustedIPs: []string{"1.2.3.4/24", "8.8.8.8/8"}, + passIPs: []string{ + "1.2.3.1", + "1.2.3.32", + "1.2.3.156", + "1.2.3.255", + "8.8.4.4", + "8.0.0.1", + "8.32.42.128", + "8.255.255.255", + }, + rejectIPs: []string{ + "1.2.16.1", + "1.2.32.1", + "127.0.0.1", + "4.4.4.4", + "4.8.8.8", + }, + }, + { + desc: "IPv6", + trustedIPs: []string{"2a03:4000:6:d080::/64"}, + passIPs: []string{ + "2a03:4000:6:d080::", + "2a03:4000:6:d080::1", + "2a03:4000:6:d080:dead:beef:ffff:ffff", + "2a03:4000:6:d080::42", + }, + rejectIPs: []string{ + "2a03:4000:7:d080::", + "2a03:4000:7:d080::1", + "fe80::", + "4242::1", + }, + }, + { + desc: "IPv6 single IP", + trustedIPs: []string{"2a03:4000:6:d080::42/128"}, + passIPs: []string{"2a03:4000:6:d080::42"}, + rejectIPs: []string{ + "2a03:4000:6:d080::1", + "2a03:4000:6:d080:dead:beef:ffff:ffff", + "2a03:4000:6:d080::43", + }, + }, + { + desc: "multiple IPv6", + trustedIPs: []string{"2a03:4000:6:d080::/64", "fe80::/16"}, + passIPs: []string{ + "2a03:4000:6:d080::", + "2a03:4000:6:d080::1", + "2a03:4000:6:d080:dead:beef:ffff:ffff", + "2a03:4000:6:d080::42", + "fe80::1", + "fe80:aa00:00bb:4232:ff00:eeee:00ff:1111", + "fe80::fe80", + }, + rejectIPs: []string{ + "2a03:4000:7:d080::", + "2a03:4000:7:d080::1", + "4242::1", + }, + }, + { + desc: "multiple IPv6 & IPv4", + trustedIPs: []string{"2a03:4000:6:d080::/64", "fe80::/16", "1.2.3.4/24", "8.8.8.8/8"}, + passIPs: []string{ + "2a03:4000:6:d080::", + "2a03:4000:6:d080::1", + "2a03:4000:6:d080:dead:beef:ffff:ffff", + "2a03:4000:6:d080::42", + "fe80::1", + "fe80:aa00:00bb:4232:ff00:eeee:00ff:1111", + "fe80::fe80", + "1.2.3.1", + "1.2.3.32", + "1.2.3.156", + "1.2.3.255", + "8.8.4.4", + "8.0.0.1", + "8.32.42.128", + "8.255.255.255", + }, + rejectIPs: []string{ + "2a03:4000:7:d080::", + "2a03:4000:7:d080::1", + "4242::1", + "1.2.16.1", + "1.2.32.1", + "127.0.0.1", + "4.4.4.4", + "4.8.8.8", + }, + }, + { + desc: "broken IP-addresses", + trustedIPs: []string{"127.0.0.1/32"}, + passIPs: nil, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + ipChecker, err := NewChecker(test.trustedIPs) + + require.NoError(t, err) + require.NotNil(t, ipChecker) + + for _, testIP := range test.passIPs { + allowed, err := ipChecker.Contains(testIP) + require.NoError(t, err) + assert.Truef(t, allowed, "%s should have passed.", testIP) + } + + for _, testIP := range test.rejectIPs { + allowed, err := ipChecker.Contains(testIP) + require.NoError(t, err) + assert.Falsef(t, allowed, "%s should not have passed.", testIP) + } + }) + } +} + +func TestContainsBrokenIPs(t *testing.T) { + brokenIPs := []string{ + "foo", + "10.0.0.350", + "fe:::80", + "", + "\\&$§&/(", + } + + ipChecker, err := NewChecker([]string{"1.2.3.4/24"}) + require.NoError(t, err) + + for _, testIP := range brokenIPs { + _, err := ipChecker.Contains(testIP) + assert.Error(t, err) + } +} diff --git a/ip/strategy.go b/ip/strategy.go new file mode 100644 index 000000000..f946bceb9 --- /dev/null +++ b/ip/strategy.go @@ -0,0 +1,62 @@ +package ip + +import ( + "net/http" + "strings" +) + +const ( + xForwardedFor = "X-Forwarded-For" +) + +// Strategy a strategy for IP selection +type Strategy interface { + GetIP(req *http.Request) string +} + +// RemoteAddrStrategy a strategy that always return the remote address +type RemoteAddrStrategy struct{} + +// GetIP return the selected IP +func (s *RemoteAddrStrategy) GetIP(req *http.Request) string { + return req.RemoteAddr +} + +// DepthStrategy a strategy based on the depth inside the X-Forwarded-For from right to left +type DepthStrategy struct { + Depth int +} + +// GetIP return the selected IP +func (s *DepthStrategy) GetIP(req *http.Request) string { + xff := req.Header.Get(xForwardedFor) + xffs := strings.Split(xff, ",") + + if len(xffs) < s.Depth { + return "" + } + return xffs[len(xffs)-s.Depth] +} + +// CheckerStrategy a strategy based on an IP Checker +// allows to check that addresses are in a trusted IPs +type CheckerStrategy struct { + Checker *Checker +} + +// GetIP return the selected IP +func (s *CheckerStrategy) GetIP(req *http.Request) string { + if s.Checker == nil { + return "" + } + + xff := req.Header.Get(xForwardedFor) + xffs := strings.Split(xff, ",") + + for i := len(xffs) - 1; i >= 0; i-- { + if contain, _ := s.Checker.Contains(xffs[i]); !contain { + return xffs[i] + } + } + return "" +} diff --git a/ip/strategy_test.go b/ip/strategy_test.go new file mode 100644 index 000000000..54fe64837 --- /dev/null +++ b/ip/strategy_test.go @@ -0,0 +1,125 @@ +package ip + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRemoteAddrStrategy_GetIP(t *testing.T) { + testCases := []struct { + desc string + expected string + }{ + { + desc: "Use RemoteAddr", + expected: "192.0.2.1:1234", + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + strategy := RemoteAddrStrategy{} + req := httptest.NewRequest(http.MethodGet, "http://127.0.0.1", nil) + actual := strategy.GetIP(req) + assert.Equal(t, test.expected, actual) + }) + } +} + +func TestDepthStrategy_GetIP(t *testing.T) { + testCases := []struct { + desc string + depth int + xForwardedFor string + expected string + }{ + { + desc: "Use depth", + depth: 3, + xForwardedFor: "10.0.0.4,10.0.0.3,10.0.0.2,10.0.0.1", + expected: "10.0.0.3", + }, + { + desc: "Use non existing depth in XForwardedFor", + depth: 2, + xForwardedFor: "", + expected: "", + }, + { + desc: "Use depth that match the first IP in XForwardedFor", + depth: 2, + xForwardedFor: "10.0.0.2,10.0.0.1", + expected: "10.0.0.2", + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + strategy := DepthStrategy{Depth: test.depth} + req := httptest.NewRequest(http.MethodGet, "http://127.0.0.1", nil) + req.Header.Set(xForwardedFor, test.xForwardedFor) + actual := strategy.GetIP(req) + assert.Equal(t, test.expected, actual) + }) + } +} + +func TestExcludedIPsStrategy_GetIP(t *testing.T) { + testCases := []struct { + desc string + excludedIPs []string + xForwardedFor string + expected string + }{ + { + desc: "Use excluded all IPs", + excludedIPs: []string{"10.0.0.4", "10.0.0.3", "10.0.0.2", "10.0.0.1"}, + xForwardedFor: "10.0.0.4,10.0.0.3,10.0.0.2,10.0.0.1", + expected: "", + }, + { + desc: "Use excluded IPs", + excludedIPs: []string{"10.0.0.2", "10.0.0.1"}, + xForwardedFor: "10.0.0.4,10.0.0.3,10.0.0.2,10.0.0.1", + expected: "10.0.0.3", + }, + { + desc: "Use excluded IPs CIDR", + excludedIPs: []string{"10.0.0.1/24"}, + xForwardedFor: "127.0.0.1,10.0.0.4,10.0.0.3,10.0.0.2,10.0.0.1", + expected: "127.0.0.1", + }, + { + desc: "Use excluded all IPs CIDR", + excludedIPs: []string{"10.0.0.1/24"}, + xForwardedFor: "10.0.0.4,10.0.0.3,10.0.0.2,10.0.0.1", + expected: "", + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + checker, err := NewChecker(test.excludedIPs) + require.NoError(t, err) + + strategy := CheckerStrategy{Checker: checker} + req := httptest.NewRequest(http.MethodGet, "http://127.0.0.1", nil) + req.Header.Set(xForwardedFor, test.xForwardedFor) + actual := strategy.GetIP(req) + assert.Equal(t, test.expected, actual) + }) + } +} diff --git a/middlewares/forwardedheaders/forwarded_header.go b/middlewares/forwardedheaders/forwarded_header.go new file mode 100644 index 000000000..1d00db543 --- /dev/null +++ b/middlewares/forwardedheaders/forwarded_header.go @@ -0,0 +1,52 @@ +package forwardedheaders + +import ( + "net/http" + + "github.com/containous/traefik/ip" + "github.com/vulcand/oxy/forward" + "github.com/vulcand/oxy/utils" +) + +// XForwarded filter for XForwarded headers +type XForwarded struct { + insecure bool + trustedIps []string + ipChecker *ip.Checker +} + +// NewXforwarded creates a new XForwarded +func NewXforwarded(insecure bool, trustedIps []string) (*XForwarded, error) { + var ipChecker *ip.Checker + if len(trustedIps) > 0 { + var err error + ipChecker, err = ip.NewChecker(trustedIps) + if err != nil { + return nil, err + } + } + + return &XForwarded{ + insecure: insecure, + trustedIps: trustedIps, + ipChecker: ipChecker, + }, nil +} + +func (x *XForwarded) isTrustedIP(ip string) bool { + if x.ipChecker == nil { + return false + } + return x.ipChecker.IsAuthorized(ip) == nil +} + +func (x *XForwarded) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + if !x.insecure && !x.isTrustedIP(r.RemoteAddr) { + utils.RemoveHeaders(r.Header, forward.XHeaders...) + } + + // If there is a next, call it. + if next != nil { + next(w, r) + } +} diff --git a/middlewares/forwardedheaders/forwarded_header_test.go b/middlewares/forwardedheaders/forwarded_header_test.go new file mode 100644 index 000000000..f59107798 --- /dev/null +++ b/middlewares/forwardedheaders/forwarded_header_test.go @@ -0,0 +1,128 @@ +package forwardedheaders + +import ( + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestServeHTTP(t *testing.T) { + testCases := []struct { + desc string + insecure bool + trustedIps []string + incomingHeaders map[string]string + remoteAddr string + expectedHeaders map[string]string + }{ + { + desc: "all Empty", + insecure: true, + trustedIps: nil, + remoteAddr: "", + incomingHeaders: map[string]string{}, + expectedHeaders: map[string]string{ + "X-Forwarded-for": "", + }, + }, + { + desc: "insecure true with incoming X-Forwarded-For", + insecure: true, + trustedIps: nil, + remoteAddr: "", + incomingHeaders: map[string]string{ + "X-Forwarded-for": "10.0.1.0, 10.0.1.12", + }, + expectedHeaders: map[string]string{ + "X-Forwarded-for": "10.0.1.0, 10.0.1.12", + }, + }, + { + desc: "insecure false with incoming X-Forwarded-For", + insecure: false, + trustedIps: nil, + remoteAddr: "", + incomingHeaders: map[string]string{ + "X-Forwarded-for": "10.0.1.0, 10.0.1.12", + }, + expectedHeaders: map[string]string{ + "X-Forwarded-for": "", + }, + }, + { + desc: "insecure false with incoming X-Forwarded-For and valid Trusted Ips", + insecure: false, + trustedIps: []string{"10.0.1.100"}, + remoteAddr: "10.0.1.100:80", + incomingHeaders: map[string]string{ + "X-Forwarded-for": "10.0.1.0, 10.0.1.12", + }, + expectedHeaders: map[string]string{ + "X-Forwarded-for": "10.0.1.0, 10.0.1.12", + }, + }, + { + desc: "insecure false with incoming X-Forwarded-For and invalid Trusted Ips", + insecure: false, + trustedIps: []string{"10.0.1.100"}, + remoteAddr: "10.0.1.101:80", + incomingHeaders: map[string]string{ + "X-Forwarded-for": "10.0.1.0, 10.0.1.12", + }, + expectedHeaders: map[string]string{ + "X-Forwarded-for": "", + }, + }, + { + desc: "insecure false with incoming X-Forwarded-For and valid Trusted Ips CIDR", + insecure: false, + trustedIps: []string{"1.2.3.4/24"}, + remoteAddr: "1.2.3.156:80", + incomingHeaders: map[string]string{ + "X-Forwarded-for": "10.0.1.0, 10.0.1.12", + }, + expectedHeaders: map[string]string{ + "X-Forwarded-for": "10.0.1.0, 10.0.1.12", + }, + }, + { + desc: "insecure false with incoming X-Forwarded-For and invalid Trusted Ips CIDR", + insecure: false, + trustedIps: []string{"1.2.3.4/24"}, + remoteAddr: "10.0.1.101:80", + incomingHeaders: map[string]string{ + "X-Forwarded-for": "10.0.1.0, 10.0.1.12", + }, + expectedHeaders: map[string]string{ + "X-Forwarded-for": "", + }, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + req, err := http.NewRequest(http.MethodGet, "", nil) + require.NoError(t, err) + + req.RemoteAddr = test.remoteAddr + + for k, v := range test.incomingHeaders { + req.Header.Set(k, v) + } + + m, err := NewXforwarded(test.insecure, test.trustedIps) + require.NoError(t, err) + + m.ServeHTTP(nil, req, nil) + + for k, v := range test.expectedHeaders { + assert.Equal(t, v, req.Header.Get(k)) + } + }) + } +} diff --git a/middlewares/ip_whitelister.go b/middlewares/ip_whitelister.go index a352aad12..cc66f4e92 100644 --- a/middlewares/ip_whitelister.go +++ b/middlewares/ip_whitelister.go @@ -4,9 +4,9 @@ import ( "fmt" "net/http" + "github.com/containous/traefik/ip" "github.com/containous/traefik/log" "github.com/containous/traefik/middlewares/tracing" - "github.com/containous/traefik/whitelist" "github.com/pkg/errors" "github.com/urfave/negroni" ) @@ -14,22 +14,25 @@ import ( // IPWhiteLister is a middleware that provides Checks of the Requesting IP against a set of Whitelists type IPWhiteLister struct { handler negroni.Handler - whiteLister *whitelist.IP + whiteLister *ip.Checker + strategy ip.Strategy } // NewIPWhiteLister builds a new IPWhiteLister given a list of CIDR-Strings to whitelist -func NewIPWhiteLister(whiteList []string, useXForwardedFor bool) (*IPWhiteLister, error) { +func NewIPWhiteLister(whiteList []string, strategy ip.Strategy) (*IPWhiteLister, error) { if len(whiteList) == 0 { return nil, errors.New("no white list provided") } - whiteLister := IPWhiteLister{} - - ip, err := whitelist.NewIP(whiteList, false, useXForwardedFor) + checker, err := ip.NewChecker(whiteList) if err != nil { return nil, fmt.Errorf("parsing CIDR whitelist %s: %v", whiteList, err) } - whiteLister.whiteLister = ip + + whiteLister := IPWhiteLister{ + strategy: strategy, + whiteLister: checker, + } whiteLister.handler = negroni.HandlerFunc(whiteLister.handle) log.Debugf("configured IP white list: %s", whiteList) @@ -38,13 +41,13 @@ func NewIPWhiteLister(whiteList []string, useXForwardedFor bool) (*IPWhiteLister } func (wl *IPWhiteLister) handle(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { - err := wl.whiteLister.IsAuthorized(r) + err := wl.whiteLister.IsAuthorized(wl.strategy.GetIP(r)) if err != nil { tracing.SetErrorAndDebugLog(r, "request %+v - rejecting: %v", r, err) reject(w) return } - + log.Debugf("Accept %s: %+v", wl.strategy.GetIP(r), r) tracing.SetErrorAndDebugLog(r, "request %+v matched white list %s - passing", r, wl.whiteLister) next.ServeHTTP(w, r) } diff --git a/middlewares/ip_whitelister_test.go b/middlewares/ip_whitelister_test.go index 07ee800ba..e8750cdf2 100644 --- a/middlewares/ip_whitelister_test.go +++ b/middlewares/ip_whitelister_test.go @@ -5,29 +5,26 @@ import ( "net/http/httptest" "testing" - "github.com/containous/traefik/whitelist" + "github.com/containous/traefik/ip" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestNewIPWhiteLister(t *testing.T) { testCases := []struct { - desc string - whiteList []string - useXForwardedFor bool - expectedError string + desc string + whiteList []string + expectedError string }{ { - desc: "invalid IP", - whiteList: []string{"foo"}, - useXForwardedFor: false, - expectedError: "parsing CIDR whitelist [foo]: parsing CIDR white list : invalid CIDR address: foo", + desc: "invalid IP", + whiteList: []string{"foo"}, + expectedError: "parsing CIDR whitelist [foo]: parsing CIDR trusted IPs : invalid CIDR address: foo", }, { - desc: "valid IP", - whiteList: []string{"10.10.10.10"}, - useXForwardedFor: false, - expectedError: "", + desc: "valid IP", + whiteList: []string{"10.10.10.10"}, + expectedError: "", }, } @@ -36,7 +33,7 @@ func TestNewIPWhiteLister(t *testing.T) { t.Run(test.desc, func(t *testing.T) { t.Parallel() - whiteLister, err := NewIPWhiteLister(test.whiteList, test.useXForwardedFor) + whiteLister, err := NewIPWhiteLister(test.whiteList, &ip.RemoteAddrStrategy{}) if len(test.expectedError) > 0 { assert.EqualError(t, err, test.expectedError) @@ -50,57 +47,22 @@ func TestNewIPWhiteLister(t *testing.T) { func TestIPWhiteLister_ServeHTTP(t *testing.T) { testCases := []struct { - desc string - whiteList []string - useXForwardedFor bool - remoteAddr string - xForwardedFor []string - expected int + desc string + whiteList []string + remoteAddr string + expected int }{ { - desc: "authorized with remote address", - whiteList: []string{"20.20.20.20"}, - useXForwardedFor: false, - remoteAddr: "20.20.20.20:1234", - xForwardedFor: nil, - expected: 200, + desc: "authorized with remote address", + whiteList: []string{"20.20.20.20"}, + remoteAddr: "20.20.20.20:1234", + expected: 200, }, { - desc: "non authorized with remote address", - whiteList: []string{"20.20.20.20"}, - useXForwardedFor: false, - remoteAddr: "20.20.20.21:1234", - xForwardedFor: nil, - expected: 403, - }, - { - desc: "non authorized with remote address (X-Forwarded-For possible)", - whiteList: []string{"20.20.20.20"}, - useXForwardedFor: false, - remoteAddr: "20.20.20.21:1234", - xForwardedFor: []string{"20.20.20.20", "40.40.40.40"}, - expected: 403, - }, - { - desc: "authorized with X-Forwarded-For", - whiteList: []string{"30.30.30.30"}, - useXForwardedFor: true, - xForwardedFor: []string{"30.30.30.30", "40.40.40.40"}, - expected: 200, - }, - { - desc: "authorized with only one X-Forwarded-For", - whiteList: []string{"30.30.30.30"}, - useXForwardedFor: true, - xForwardedFor: []string{"30.30.30.30"}, - expected: 200, - }, - { - desc: "non authorized with X-Forwarded-For", - whiteList: []string{"30.30.30.30"}, - useXForwardedFor: true, - xForwardedFor: []string{"30.30.30.31", "40.40.40.40"}, - expected: 403, + desc: "non authorized with remote address", + whiteList: []string{"20.20.20.20"}, + remoteAddr: "20.20.20.21:1234", + expected: 403, }, } @@ -109,7 +71,7 @@ func TestIPWhiteLister_ServeHTTP(t *testing.T) { t.Run(test.desc, func(t *testing.T) { t.Parallel() - whiteLister, err := NewIPWhiteLister(test.whiteList, test.useXForwardedFor) + whiteLister, err := NewIPWhiteLister(test.whiteList, &ip.RemoteAddrStrategy{}) require.NoError(t, err) recorder := httptest.NewRecorder() @@ -120,12 +82,6 @@ func TestIPWhiteLister_ServeHTTP(t *testing.T) { req.RemoteAddr = test.remoteAddr } - if len(test.xForwardedFor) > 0 { - for _, xff := range test.xForwardedFor { - req.Header.Add(whitelist.XForwardedFor, xff) - } - } - next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) whiteLister.ServeHTTP(recorder, req, next) diff --git a/provider/consulcatalog/config_test.go b/provider/consulcatalog/config_test.go index 6b2ce8218..57b4294b9 100644 --- a/provider/consulcatalog/config_test.go +++ b/provider/consulcatalog/config_test.go @@ -375,7 +375,8 @@ func TestProviderBuildConfiguration(t *testing.T) { label.TraefikFrontendRedirectPermanent + "=true", label.TraefikFrontendRule + "=Host:traefik.io", label.TraefikFrontendWhiteListSourceRange + "=10.10.10.10", - label.TraefikFrontendWhiteListUseXForwardedFor + "=true", + label.TraefikFrontendWhiteListIPStrategyExcludedIPS + "=10.10.10.10,10.10.10.11", + label.TraefikFrontendWhiteListIPStrategyDepth + "=5", label.TraefikFrontendRequestHeaders + "=Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", label.TraefikFrontendResponseHeaders + "=Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", @@ -478,7 +479,10 @@ func TestProviderBuildConfiguration(t *testing.T) { SourceRange: []string{ "10.10.10.10", }, - UseXForwardedFor: true, + IPStrategy: &types.IPStrategy{ + Depth: 5, + ExcludedIPs: []string{"10.10.10.10", "10.10.10.11"}, + }, }, Headers: &types.Headers{ CustomRequestHeaders: map[string]string{ diff --git a/provider/docker/config_container_docker_test.go b/provider/docker/config_container_docker_test.go index fa527ae72..47eead526 100644 --- a/provider/docker/config_container_docker_test.go +++ b/provider/docker/config_container_docker_test.go @@ -403,17 +403,18 @@ func TestDockerBuildConfiguration(t *testing.T) { label.TraefikFrontendAuthForwardTLSInsecureSkipVerify: "true", label.TraefikFrontendAuthHeaderField: "X-WebAuth-User", - label.TraefikFrontendEntryPoints: "http,https", - label.TraefikFrontendPassHostHeader: "true", - label.TraefikFrontendPassTLSCert: "true", - label.TraefikFrontendPriority: "666", - label.TraefikFrontendRedirectEntryPoint: "https", - label.TraefikFrontendRedirectRegex: "nope", - label.TraefikFrontendRedirectReplacement: "nope", - label.TraefikFrontendRedirectPermanent: "true", - label.TraefikFrontendRule: "Host:traefik.io", - label.TraefikFrontendWhiteListSourceRange: "10.10.10.10", - label.TraefikFrontendWhiteListUseXForwardedFor: "true", + label.TraefikFrontendEntryPoints: "http,https", + label.TraefikFrontendPassHostHeader: "true", + label.TraefikFrontendPassTLSCert: "true", + label.TraefikFrontendPriority: "666", + label.TraefikFrontendRedirectEntryPoint: "https", + label.TraefikFrontendRedirectRegex: "nope", + label.TraefikFrontendRedirectReplacement: "nope", + label.TraefikFrontendRedirectPermanent: "true", + label.TraefikFrontendRule: "Host:traefik.io", + label.TraefikFrontendWhiteListSourceRange: "10.10.10.10", + label.TraefikFrontendWhiteListIPStrategyExcludedIPS: "10.10.10.10,10.10.10.11", + label.TraefikFrontendWhiteListIPStrategyDepth: "5", label.TraefikFrontendRequestHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", label.TraefikFrontendResponseHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", @@ -484,8 +485,11 @@ func TestDockerBuildConfiguration(t *testing.T) { }, }, WhiteList: &types.WhiteList{ - SourceRange: []string{"10.10.10.10"}, - UseXForwardedFor: true, + SourceRange: []string{"10.10.10.10"}, + IPStrategy: &types.IPStrategy{ + Depth: 5, + ExcludedIPs: []string{"10.10.10.10", "10.10.10.11"}, + }, }, Headers: &types.Headers{ CustomRequestHeaders: map[string]string{ diff --git a/provider/docker/config_container_swarm_test.go b/provider/docker/config_container_swarm_test.go index ad7f95784..985ebe3f0 100644 --- a/provider/docker/config_container_swarm_test.go +++ b/provider/docker/config_container_swarm_test.go @@ -347,17 +347,18 @@ func TestSwarmBuildConfiguration(t *testing.T) { label.TraefikFrontendAuthForwardTLSInsecureSkipVerify: "true", label.TraefikFrontendAuthHeaderField: "X-WebAuth-User", - label.TraefikFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", - label.TraefikFrontendEntryPoints: "http,https", - label.TraefikFrontendPassHostHeader: "true", - label.TraefikFrontendPassTLSCert: "true", - label.TraefikFrontendPriority: "666", - label.TraefikFrontendRedirectEntryPoint: "https", - label.TraefikFrontendRedirectRegex: "nope", - label.TraefikFrontendRedirectReplacement: "nope", - label.TraefikFrontendRule: "Host:traefik.io", - label.TraefikFrontendWhiteListSourceRange: "10.10.10.10", - label.TraefikFrontendWhiteListUseXForwardedFor: "true", + label.TraefikFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", + label.TraefikFrontendEntryPoints: "http,https", + label.TraefikFrontendPassHostHeader: "true", + label.TraefikFrontendPassTLSCert: "true", + label.TraefikFrontendPriority: "666", + label.TraefikFrontendRedirectEntryPoint: "https", + label.TraefikFrontendRedirectRegex: "nope", + label.TraefikFrontendRedirectReplacement: "nope", + label.TraefikFrontendRule: "Host:traefik.io", + label.TraefikFrontendWhiteListSourceRange: "10.10.10.10", + label.TraefikFrontendWhiteListIPStrategyExcludedIPS: "10.10.10.10,10.10.10.11", + label.TraefikFrontendWhiteListIPStrategyDepth: "5", label.TraefikFrontendRequestHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", label.TraefikFrontendResponseHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", @@ -426,8 +427,11 @@ func TestSwarmBuildConfiguration(t *testing.T) { }, }, WhiteList: &types.WhiteList{ - SourceRange: []string{"10.10.10.10"}, - UseXForwardedFor: true, + SourceRange: []string{"10.10.10.10"}, + IPStrategy: &types.IPStrategy{ + Depth: 5, + ExcludedIPs: []string{"10.10.10.10", "10.10.10.11"}, + }, }, Headers: &types.Headers{ CustomRequestHeaders: map[string]string{ diff --git a/provider/docker/config_segment_test.go b/provider/docker/config_segment_test.go index a3438c62d..53d6cf91a 100644 --- a/provider/docker/config_segment_test.go +++ b/provider/docker/config_segment_test.go @@ -301,17 +301,18 @@ func TestSegmentBuildConfiguration(t *testing.T) { label.Prefix + "sauternes." + label.SuffixFrontendAuthForwardTLSInsecureSkipVerify: "true", label.Prefix + "sauternes." + label.SuffixFrontendAuthHeaderField: "X-WebAuth-User", - label.Prefix + "sauternes." + label.SuffixFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", - label.Prefix + "sauternes." + label.SuffixFrontendEntryPoints: "http,https", - label.Prefix + "sauternes." + label.SuffixFrontendPassHostHeader: "true", - label.Prefix + "sauternes." + label.SuffixFrontendPassTLSCert: "true", - label.Prefix + "sauternes." + label.SuffixFrontendPriority: "666", - label.Prefix + "sauternes." + label.SuffixFrontendRedirectEntryPoint: "https", - label.Prefix + "sauternes." + label.SuffixFrontendRedirectRegex: "nope", - label.Prefix + "sauternes." + label.SuffixFrontendRedirectReplacement: "nope", - label.Prefix + "sauternes." + label.SuffixFrontendRedirectPermanent: "true", - label.Prefix + "sauternes." + label.SuffixFrontendWhiteListSourceRange: "10.10.10.10", - label.Prefix + "sauternes." + label.SuffixFrontendWhiteListUseXForwardedFor: "true", + label.Prefix + "sauternes." + label.SuffixFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", + label.Prefix + "sauternes." + label.SuffixFrontendEntryPoints: "http,https", + label.Prefix + "sauternes." + label.SuffixFrontendPassHostHeader: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSCert: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPriority: "666", + label.Prefix + "sauternes." + label.SuffixFrontendRedirectEntryPoint: "https", + label.Prefix + "sauternes." + label.SuffixFrontendRedirectRegex: "nope", + label.Prefix + "sauternes." + label.SuffixFrontendRedirectReplacement: "nope", + label.Prefix + "sauternes." + label.SuffixFrontendRedirectPermanent: "true", + label.Prefix + "sauternes." + label.SuffixFrontendWhiteListSourceRange: "10.10.10.10", + label.Prefix + "sauternes." + label.SuffixFrontendWhiteListIPStrategyExcludedIPS: "10.10.10.10,10.10.10.11", + label.Prefix + "sauternes." + label.SuffixFrontendWhiteListIPStrategyDepth: "5", label.Prefix + "sauternes." + label.SuffixFrontendRequestHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", label.Prefix + "sauternes." + label.SuffixFrontendResponseHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", @@ -377,8 +378,11 @@ func TestSegmentBuildConfiguration(t *testing.T) { }, }, WhiteList: &types.WhiteList{ - SourceRange: []string{"10.10.10.10"}, - UseXForwardedFor: true, + SourceRange: []string{"10.10.10.10"}, + IPStrategy: &types.IPStrategy{ + Depth: 5, + ExcludedIPs: []string{"10.10.10.10", "10.10.10.11"}, + }, }, Headers: &types.Headers{ CustomRequestHeaders: map[string]string{ diff --git a/provider/ecs/config_test.go b/provider/ecs/config_test.go index 92754e862..edb017378 100644 --- a/provider/ecs/config_test.go +++ b/provider/ecs/config_test.go @@ -368,17 +368,18 @@ func TestBuildConfiguration(t *testing.T) { label.TraefikFrontendAuthForwardTLSInsecureSkipVerify: aws.String("true"), label.TraefikFrontendAuthHeaderField: aws.String("X-WebAuth-User"), - label.TraefikFrontendEntryPoints: aws.String("http,https"), - label.TraefikFrontendPassHostHeader: aws.String("true"), - label.TraefikFrontendPassTLSCert: aws.String("true"), - label.TraefikFrontendPriority: aws.String("666"), - label.TraefikFrontendRedirectEntryPoint: aws.String("https"), - label.TraefikFrontendRedirectRegex: aws.String("nope"), - label.TraefikFrontendRedirectReplacement: aws.String("nope"), - label.TraefikFrontendRedirectPermanent: aws.String("true"), - label.TraefikFrontendRule: aws.String("Host:traefik.io"), - label.TraefikFrontendWhiteListSourceRange: aws.String("10.10.10.10"), - label.TraefikFrontendWhiteListUseXForwardedFor: aws.String("true"), + label.TraefikFrontendEntryPoints: aws.String("http,https"), + label.TraefikFrontendPassHostHeader: aws.String("true"), + label.TraefikFrontendPassTLSCert: aws.String("true"), + label.TraefikFrontendPriority: aws.String("666"), + label.TraefikFrontendRedirectEntryPoint: aws.String("https"), + label.TraefikFrontendRedirectRegex: aws.String("nope"), + label.TraefikFrontendRedirectReplacement: aws.String("nope"), + label.TraefikFrontendRedirectPermanent: aws.String("true"), + label.TraefikFrontendRule: aws.String("Host:traefik.io"), + label.TraefikFrontendWhiteListSourceRange: aws.String("10.10.10.10"), + label.TraefikFrontendWhiteListIPStrategyExcludedIPS: aws.String("10.10.10.10,10.10.10.11"), + label.TraefikFrontendWhiteListIPStrategyDepth: aws.String("5"), label.TraefikFrontendRequestHeaders: aws.String("Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8"), label.TraefikFrontendResponseHeaders: aws.String("Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8"), @@ -492,8 +493,11 @@ func TestBuildConfiguration(t *testing.T) { }, }, WhiteList: &types.WhiteList{ - SourceRange: []string{"10.10.10.10"}, - UseXForwardedFor: true, + SourceRange: []string{"10.10.10.10"}, + IPStrategy: &types.IPStrategy{ + Depth: 5, + ExcludedIPs: []string{"10.10.10.10", "10.10.10.11"}, + }, }, Headers: &types.Headers{ CustomRequestHeaders: map[string]string{ @@ -610,18 +614,17 @@ func TestBuildConfiguration(t *testing.T) { label.TraefikBackendBufferingMemRequestBodyBytes: aws.String("2097152"), label.TraefikBackendBufferingRetryExpression: aws.String("IsNetworkError() && Attempts() <= 2"), - label.TraefikFrontendAuthBasicUsers: aws.String("test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), - label.TraefikFrontendEntryPoints: aws.String("http,https"), - label.TraefikFrontendPassHostHeader: aws.String("true"), - label.TraefikFrontendPassTLSCert: aws.String("true"), - label.TraefikFrontendPriority: aws.String("666"), - label.TraefikFrontendRedirectEntryPoint: aws.String("https"), - label.TraefikFrontendRedirectRegex: aws.String("nope"), - label.TraefikFrontendRedirectReplacement: aws.String("nope"), - label.TraefikFrontendRedirectPermanent: aws.String("true"), - label.TraefikFrontendRule: aws.String("Host:traefik.io"), - label.TraefikFrontendWhiteListSourceRange: aws.String("10.10.10.10"), - label.TraefikFrontendWhiteListUseXForwardedFor: aws.String("true"), + label.TraefikFrontendAuthBasicUsers: aws.String("test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), + label.TraefikFrontendEntryPoints: aws.String("http,https"), + label.TraefikFrontendPassHostHeader: aws.String("true"), + label.TraefikFrontendPassTLSCert: aws.String("true"), + label.TraefikFrontendPriority: aws.String("666"), + label.TraefikFrontendRedirectEntryPoint: aws.String("https"), + label.TraefikFrontendRedirectRegex: aws.String("nope"), + label.TraefikFrontendRedirectReplacement: aws.String("nope"), + label.TraefikFrontendRedirectPermanent: aws.String("true"), + label.TraefikFrontendRule: aws.String("Host:traefik.io"), + label.TraefikFrontendWhiteListSourceRange: aws.String("10.10.10.10"), label.TraefikFrontendRequestHeaders: aws.String("Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8"), label.TraefikFrontendResponseHeaders: aws.String("Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8"), @@ -696,18 +699,17 @@ func TestBuildConfiguration(t *testing.T) { label.TraefikBackendBufferingMemRequestBodyBytes: aws.String("2097152"), label.TraefikBackendBufferingRetryExpression: aws.String("IsNetworkError() && Attempts() <= 2"), - label.TraefikFrontendAuthBasic: aws.String("test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), - label.TraefikFrontendEntryPoints: aws.String("http,https"), - label.TraefikFrontendPassHostHeader: aws.String("true"), - label.TraefikFrontendPassTLSCert: aws.String("true"), - label.TraefikFrontendPriority: aws.String("666"), - label.TraefikFrontendRedirectEntryPoint: aws.String("https"), - label.TraefikFrontendRedirectRegex: aws.String("nope"), - label.TraefikFrontendRedirectReplacement: aws.String("nope"), - label.TraefikFrontendRedirectPermanent: aws.String("true"), - label.TraefikFrontendRule: aws.String("Host:traefik.io"), - label.TraefikFrontendWhiteListSourceRange: aws.String("10.10.10.10"), - label.TraefikFrontendWhiteListUseXForwardedFor: aws.String("true"), + label.TraefikFrontendAuthBasic: aws.String("test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), + label.TraefikFrontendEntryPoints: aws.String("http,https"), + label.TraefikFrontendPassHostHeader: aws.String("true"), + label.TraefikFrontendPassTLSCert: aws.String("true"), + label.TraefikFrontendPriority: aws.String("666"), + label.TraefikFrontendRedirectEntryPoint: aws.String("https"), + label.TraefikFrontendRedirectRegex: aws.String("nope"), + label.TraefikFrontendRedirectReplacement: aws.String("nope"), + label.TraefikFrontendRedirectPermanent: aws.String("true"), + label.TraefikFrontendRule: aws.String("Host:traefik.io"), + label.TraefikFrontendWhiteListSourceRange: aws.String("10.10.10.10"), label.TraefikFrontendRequestHeaders: aws.String("Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8"), label.TraefikFrontendResponseHeaders: aws.String("Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8"), @@ -822,8 +824,7 @@ func TestBuildConfiguration(t *testing.T) { }, }, WhiteList: &types.WhiteList{ - SourceRange: []string{"10.10.10.10"}, - UseXForwardedFor: true, + SourceRange: []string{"10.10.10.10"}, }, Headers: &types.Headers{ CustomRequestHeaders: map[string]string{ diff --git a/provider/kubernetes/annotations.go b/provider/kubernetes/annotations.go index ee78735b9..e9111f062 100644 --- a/provider/kubernetes/annotations.go +++ b/provider/kubernetes/annotations.go @@ -5,41 +5,43 @@ import ( ) const ( - annotationKubernetesIngressClass = "kubernetes.io/ingress.class" - annotationKubernetesAuthRealm = "ingress.kubernetes.io/auth-realm" - annotationKubernetesAuthType = "ingress.kubernetes.io/auth-type" - annotationKubernetesAuthSecret = "ingress.kubernetes.io/auth-secret" - annotationKubernetesAuthHeaderField = "ingress.kubernetes.io/auth-header-field" - annotationKubernetesAuthForwardResponseHeaders = "ingress.kubernetes.io/auth-response-headers" - annotationKubernetesAuthRemoveHeader = "ingress.kubernetes.io/auth-remove-header" - annotationKubernetesAuthForwardURL = "ingress.kubernetes.io/auth-url" - annotationKubernetesAuthForwardTrustHeaders = "ingress.kubernetes.io/auth-trust-headers" - annotationKubernetesAuthForwardTLSSecret = "ingress.kubernetes.io/auth-tls-secret" - annotationKubernetesAuthForwardTLSInsecure = "ingress.kubernetes.io/auth-tls-insecure" - annotationKubernetesRewriteTarget = "ingress.kubernetes.io/rewrite-target" - annotationKubernetesWhiteListSourceRange = "ingress.kubernetes.io/whitelist-source-range" - annotationKubernetesWhiteListUseXForwardedFor = "ingress.kubernetes.io/whitelist-x-forwarded-for" - annotationKubernetesPreserveHost = "ingress.kubernetes.io/preserve-host" - annotationKubernetesPassTLSCert = "ingress.kubernetes.io/pass-tls-cert" - annotationKubernetesFrontendEntryPoints = "ingress.kubernetes.io/frontend-entry-points" - annotationKubernetesPriority = "ingress.kubernetes.io/priority" - annotationKubernetesCircuitBreakerExpression = "ingress.kubernetes.io/circuit-breaker-expression" - annotationKubernetesLoadBalancerMethod = "ingress.kubernetes.io/load-balancer-method" - annotationKubernetesAffinity = "ingress.kubernetes.io/affinity" - annotationKubernetesSessionCookieName = "ingress.kubernetes.io/session-cookie-name" - annotationKubernetesRuleType = "ingress.kubernetes.io/rule-type" - annotationKubernetesRedirectEntryPoint = "ingress.kubernetes.io/redirect-entry-point" - annotationKubernetesRedirectPermanent = "ingress.kubernetes.io/redirect-permanent" - annotationKubernetesRedirectRegex = "ingress.kubernetes.io/redirect-regex" - annotationKubernetesRedirectReplacement = "ingress.kubernetes.io/redirect-replacement" - annotationKubernetesMaxConnAmount = "ingress.kubernetes.io/max-conn-amount" - annotationKubernetesMaxConnExtractorFunc = "ingress.kubernetes.io/max-conn-extractor-func" - annotationKubernetesRateLimit = "ingress.kubernetes.io/rate-limit" - annotationKubernetesErrorPages = "ingress.kubernetes.io/error-pages" - annotationKubernetesBuffering = "ingress.kubernetes.io/buffering" - annotationKubernetesAppRoot = "ingress.kubernetes.io/app-root" - annotationKubernetesServiceWeights = "ingress.kubernetes.io/service-weights" - annotationKubernetesRequestModifier = "ingress.kubernetes.io/request-modifier" + annotationKubernetesIngressClass = "kubernetes.io/ingress.class" + annotationKubernetesAuthRealm = "ingress.kubernetes.io/auth-realm" + annotationKubernetesAuthType = "ingress.kubernetes.io/auth-type" + annotationKubernetesAuthSecret = "ingress.kubernetes.io/auth-secret" + annotationKubernetesAuthHeaderField = "ingress.kubernetes.io/auth-header-field" + annotationKubernetesAuthForwardResponseHeaders = "ingress.kubernetes.io/auth-response-headers" + annotationKubernetesAuthRemoveHeader = "ingress.kubernetes.io/auth-remove-header" + annotationKubernetesAuthForwardURL = "ingress.kubernetes.io/auth-url" + annotationKubernetesAuthForwardTrustHeaders = "ingress.kubernetes.io/auth-trust-headers" + annotationKubernetesAuthForwardTLSSecret = "ingress.kubernetes.io/auth-tls-secret" + annotationKubernetesAuthForwardTLSInsecure = "ingress.kubernetes.io/auth-tls-insecure" + annotationKubernetesRewriteTarget = "ingress.kubernetes.io/rewrite-target" + annotationKubernetesWhiteListSourceRange = "ingress.kubernetes.io/whitelist-source-range" + annotationKubernetesWhiteListIPStrategy = "ingress.kubernetes.io/whitelist-ipstrategy" + annotationKubernetesWhiteListIPStrategyDepth = "ingress.kubernetes.io/whitelist-ipstrategy-depth" + annotationKubernetesWhiteListIPStrategyExcludedIPs = "ingress.kubernetes.io/whitelist-ipstrategy-excluded-ips" + annotationKubernetesPreserveHost = "ingress.kubernetes.io/preserve-host" + annotationKubernetesPassTLSCert = "ingress.kubernetes.io/pass-tls-cert" + annotationKubernetesFrontendEntryPoints = "ingress.kubernetes.io/frontend-entry-points" + annotationKubernetesPriority = "ingress.kubernetes.io/priority" + annotationKubernetesCircuitBreakerExpression = "ingress.kubernetes.io/circuit-breaker-expression" + annotationKubernetesLoadBalancerMethod = "ingress.kubernetes.io/load-balancer-method" + annotationKubernetesAffinity = "ingress.kubernetes.io/affinity" + annotationKubernetesSessionCookieName = "ingress.kubernetes.io/session-cookie-name" + annotationKubernetesRuleType = "ingress.kubernetes.io/rule-type" + annotationKubernetesRedirectEntryPoint = "ingress.kubernetes.io/redirect-entry-point" + annotationKubernetesRedirectPermanent = "ingress.kubernetes.io/redirect-permanent" + annotationKubernetesRedirectRegex = "ingress.kubernetes.io/redirect-regex" + annotationKubernetesRedirectReplacement = "ingress.kubernetes.io/redirect-replacement" + annotationKubernetesMaxConnAmount = "ingress.kubernetes.io/max-conn-amount" + annotationKubernetesMaxConnExtractorFunc = "ingress.kubernetes.io/max-conn-extractor-func" + annotationKubernetesRateLimit = "ingress.kubernetes.io/rate-limit" + annotationKubernetesErrorPages = "ingress.kubernetes.io/error-pages" + annotationKubernetesBuffering = "ingress.kubernetes.io/buffering" + annotationKubernetesAppRoot = "ingress.kubernetes.io/app-root" + annotationKubernetesServiceWeights = "ingress.kubernetes.io/service-weights" + annotationKubernetesRequestModifier = "ingress.kubernetes.io/request-modifier" annotationKubernetesSSLForceHost = "ingress.kubernetes.io/ssl-force-host" annotationKubernetesSSLRedirect = "ingress.kubernetes.io/ssl-redirect" diff --git a/provider/kubernetes/builder_configuration_test.go b/provider/kubernetes/builder_configuration_test.go index 4711113e4..b66aa4aa0 100644 --- a/provider/kubernetes/builder_configuration_test.go +++ b/provider/kubernetes/builder_configuration_test.go @@ -257,13 +257,29 @@ func fwdAuthTLS(cert, key string, insecure bool) func(*types.Forward) { } } -func whiteList(useXFF bool, ranges ...string) func(*types.Frontend) { +func whiteListRange(ranges ...string) func(*types.WhiteList) { + return func(wl *types.WhiteList) { + wl.SourceRange = ranges + } +} + +func whiteListIPStrategy(depth int, excludedIPs ...string) func(*types.WhiteList) { + return func(wl *types.WhiteList) { + wl.IPStrategy = &types.IPStrategy{ + Depth: depth, + ExcludedIPs: excludedIPs, + } + } +} + +func whiteList(opts ...func(*types.WhiteList)) func(*types.Frontend) { return func(f *types.Frontend) { if f.WhiteList == nil { f.WhiteList = &types.WhiteList{} } - f.WhiteList.UseXForwardedFor = useXFF - f.WhiteList.SourceRange = ranges + for _, opt := range opts { + opt(f.WhiteList) + } } } diff --git a/provider/kubernetes/kubernetes.go b/provider/kubernetes/kubernetes.go index 926424b9f..954a3da43 100644 --- a/provider/kubernetes/kubernetes.go +++ b/provider/kubernetes/kubernetes.go @@ -902,8 +902,23 @@ func getWhiteList(i *extensionsv1beta1.Ingress) *types.WhiteList { } return &types.WhiteList{ - SourceRange: ranges, - UseXForwardedFor: getBoolValue(i.Annotations, annotationKubernetesWhiteListUseXForwardedFor, false), + SourceRange: ranges, + IPStrategy: getIPStrategy(i.Annotations), + } +} + +func getIPStrategy(annotations map[string]string) *types.IPStrategy { + ipStrategy := getBoolValue(annotations, annotationKubernetesWhiteListIPStrategy, false) + depth := getIntValue(annotations, annotationKubernetesWhiteListIPStrategyDepth, 0) + excludedIPs := getSliceStringValue(annotations, annotationKubernetesWhiteListIPStrategyExcludedIPs) + + if depth == 0 && len(excludedIPs) == 0 && !ipStrategy { + return nil + } + + return &types.IPStrategy{ + Depth: depth, + ExcludedIPs: excludedIPs, } } diff --git a/provider/kubernetes/kubernetes_test.go b/provider/kubernetes/kubernetes_test.go index e205473f8..acc089235 100644 --- a/provider/kubernetes/kubernetes_test.go +++ b/provider/kubernetes/kubernetes_test.go @@ -1080,13 +1080,24 @@ func TestIngressAnnotations(t *testing.T) { buildIngress( iNamespace("testing"), iAnnotation(annotationKubernetesWhiteListSourceRange, "1.1.1.1/24, 1234:abcd::42/32"), - iAnnotation(annotationKubernetesWhiteListUseXForwardedFor, "true"), + iAnnotation(annotationKubernetesWhiteListIPStrategyExcludedIPs, "1.1.1.1/24, 1234:abcd::42/32"), + iAnnotation(annotationKubernetesWhiteListIPStrategyDepth, "5"), iRules( iRule( iHost("test"), iPaths(onePath(iPath("/whitelist-source-range"), iBackend("service1", intstr.FromInt(80))))), ), ), + buildIngress( + iNamespace("testing"), + iAnnotation(annotationKubernetesWhiteListSourceRange, "1.1.1.1/24, 1234:abcd::42/32"), + iAnnotation(annotationKubernetesWhiteListIPStrategy, "true"), + iRules( + iRule( + iHost("test"), + iPaths(onePath(iPath("/whitelist-remote-addr"), iBackend("service1", intstr.FromInt(80))))), + ), + ), buildIngress( iNamespace("testing"), iAnnotation(annotationKubernetesRewriteTarget, "/"), @@ -1357,6 +1368,12 @@ rateset: server("http://example.com", weight(1))), lbMethod("wrr"), ), + backend("test/whitelist-remote-addr", + servers( + server("http://example.com", weight(1)), + server("http://example.com", weight(1))), + lbMethod("wrr"), + ), backend("rewrite/api", servers( server("http://example.com", weight(1)), @@ -1467,11 +1484,22 @@ rateset: ), frontend("test/whitelist-source-range", passHostHeader(), - whiteList(true, "1.1.1.1/24", "1234:abcd::42/32"), + whiteList( + whiteListRange("1.1.1.1/24", "1234:abcd::42/32"), + whiteListIPStrategy(5, "1.1.1.1/24", "1234:abcd::42/32")), routes( route("/whitelist-source-range", "PathPrefix:/whitelist-source-range"), route("test", "Host:test")), ), + frontend("test/whitelist-remote-addr", + passHostHeader(), + whiteList( + whiteListRange("1.1.1.1/24", "1234:abcd::42/32"), + whiteListIPStrategy(0)), + routes( + route("/whitelist-remote-addr", "PathPrefix:/whitelist-remote-addr"), + route("test", "Host:test")), + ), frontend("rewrite/api", passHostHeader(), routes( diff --git a/provider/kv/keynames.go b/provider/kv/keynames.go index 6e2d8537d..b714e3f1e 100644 --- a/provider/kv/keynames.go +++ b/provider/kv/keynames.go @@ -24,13 +24,15 @@ const ( pathBackendBufferingMemRequestBodyBytes = pathBackendBuffering + "memrequestbodybytes" pathBackendBufferingRetryExpression = pathBackendBuffering + "retryexpression" - pathFrontends = "/frontends/" - pathFrontendBackend = "/backend" - pathFrontendPriority = "/priority" - pathFrontendPassHostHeader = "/passhostheader" - pathFrontendPassTLSCert = "/passtlscert" - pathFrontendWhiteListSourceRange = "/whitelist/sourcerange" - pathFrontendWhiteListUseXForwardedFor = "/whitelist/usexforwardedfor" + pathFrontends = "/frontends/" + pathFrontendBackend = "/backend" + pathFrontendPriority = "/priority" + pathFrontendPassHostHeader = "/passhostheader" + pathFrontendPassTLSCert = "/passtlscert" + pathFrontendWhiteListSourceRange = "/whitelist/sourcerange" + pathFrontendWhiteListIPStrategy = "/whitelist/ipstrategy" + pathFrontendWhiteListIPStrategyDepth = pathFrontendWhiteListIPStrategy + "/depth" + pathFrontendWhiteListIPStrategyExcludedIPs = pathFrontendWhiteListIPStrategy + "/excludedips" pathFrontendAuth = "/auth/" pathFrontendAuthBasic = pathFrontendAuth + "basic/" diff --git a/provider/kv/kv_config.go b/provider/kv/kv_config.go index 0067c5936..a154544cc 100644 --- a/provider/kv/kv_config.go +++ b/provider/kv/kv_config.go @@ -80,14 +80,29 @@ func (p *Provider) buildConfiguration() *types.Configuration { func (p *Provider) getWhiteList(rootPath string) *types.WhiteList { ranges := p.getList(rootPath, pathFrontendWhiteListSourceRange) - if len(ranges) > 0 { - return &types.WhiteList{ - SourceRange: ranges, - UseXForwardedFor: p.getBool(false, rootPath, pathFrontendWhiteListUseXForwardedFor), - } + if len(ranges) == 0 { + return nil } - return nil + return &types.WhiteList{ + SourceRange: ranges, + IPStrategy: p.getIPStrategy(rootPath), + } +} + +func (p *Provider) getIPStrategy(rootPath string) *types.IPStrategy { + ipStrategy := p.getBool(false, rootPath, pathFrontendWhiteListIPStrategy) + depth := p.getInt(0, rootPath, pathFrontendWhiteListIPStrategyDepth) + excludedIPs := p.getList(rootPath, pathFrontendWhiteListIPStrategyExcludedIPs) + + if depth == 0 && len(excludedIPs) == 0 && !ipStrategy { + return nil + } + + return &types.IPStrategy{ + Depth: depth, + ExcludedIPs: excludedIPs, + } } func (p *Provider) getRedirect(rootPath string) *types.Redirect { diff --git a/provider/kv/kv_config_test.go b/provider/kv/kv_config_test.go index 64ff91eaf..bba8be163 100644 --- a/provider/kv/kv_config_test.go +++ b/provider/kv/kv_config_test.go @@ -215,6 +215,39 @@ func TestProviderBuildConfiguration(t *testing.T) { }, }, }, + { + desc: "forward auth", + kvPairs: filler("traefik", + frontend("frontend", + withPair(pathFrontendBackend, "backend"), + withList(pathFrontendWhiteListSourceRange, "1.1.1.1/24", "1234:abcd::42/32"), + withPair(pathFrontendWhiteListIPStrategy, "true"), + ), + backend("backend"), + ), + expected: &types.Configuration{ + Backends: map[string]*types.Backend{ + "backend": { + LoadBalancer: &types.LoadBalancer{ + Method: "wrr", + }, + }, + }, + Frontends: map[string]*types.Frontend{ + "frontend": { + Backend: "backend", + PassHostHeader: true, + EntryPoints: []string{}, + WhiteList: &types.WhiteList{ + SourceRange: []string{"1.1.1.1/24", "1234:abcd::42/32"}, + IPStrategy: &types.IPStrategy{ + ExcludedIPs: []string{}, + }, + }, + }, + }, + }, + }, { desc: "all parameters", kvPairs: filler("traefik", @@ -247,7 +280,8 @@ func TestProviderBuildConfiguration(t *testing.T) { withPair(pathFrontendPassTLSCert, "true"), withList(pathFrontendEntryPoints, "http", "https"), withList(pathFrontendWhiteListSourceRange, "1.1.1.1/24", "1234:abcd::42/32"), - withPair(pathFrontendWhiteListUseXForwardedFor, "true"), + withPair(pathFrontendWhiteListIPStrategyDepth, "5"), + withList(pathFrontendWhiteListIPStrategyExcludedIPs, "1.1.1.1/24", "1234:abcd::42/32"), withPair(pathFrontendAuthBasicRemoveHeader, "true"), withList(pathFrontendAuthBasicUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), @@ -363,8 +397,11 @@ func TestProviderBuildConfiguration(t *testing.T) { Backend: "backend1", PassTLSCert: true, WhiteList: &types.WhiteList{ - SourceRange: []string{"1.1.1.1/24", "1234:abcd::42/32"}, - UseXForwardedFor: true, + SourceRange: []string{"1.1.1.1/24", "1234:abcd::42/32"}, + IPStrategy: &types.IPStrategy{ + Depth: 5, + ExcludedIPs: []string{"1.1.1.1/24", "1234:abcd::42/32"}, + }, }, Auth: &types.Auth{ HeaderField: "X-WebAuth-User", @@ -1240,31 +1277,8 @@ func TestWhiteList(t *testing.T) { SourceRange: []string{ "10.10.10.10", }, - UseXForwardedFor: false, }, }, - { - desc: "should return a struct when range and UseXForwardedFor", - rootPath: "traefik/frontends/foo", - kvPairs: filler("traefik", - frontend("foo", - withPair(pathFrontendWhiteListSourceRange, "10.10.10.10"), - withPair(pathFrontendWhiteListUseXForwardedFor, "true"))), - expected: &types.WhiteList{ - SourceRange: []string{ - "10.10.10.10", - }, - UseXForwardedFor: true, - }, - }, - { - desc: "should return nil when only UseXForwardedFor", - rootPath: "traefik/frontends/foo", - kvPairs: filler("traefik", - frontend("foo", - withPair(pathFrontendWhiteListUseXForwardedFor, "true"))), - expected: nil, - }, } for _, test := range testCases { diff --git a/provider/label/names.go b/provider/label/names.go index 45410340c..894c05df4 100644 --- a/provider/label/names.go +++ b/provider/label/names.go @@ -86,10 +86,11 @@ const ( SuffixFrontendRedirectReplacement = "frontend.redirect.replacement" SuffixFrontendRedirectPermanent = "frontend.redirect.permanent" SuffixFrontendRule = "frontend.rule" - SuffixFrontendWhitelistSourceRange = "frontend.whitelistSourceRange" // Deprecated SuffixFrontendWhiteList = "frontend.whiteList." SuffixFrontendWhiteListSourceRange = SuffixFrontendWhiteList + "sourceRange" - SuffixFrontendWhiteListUseXForwardedFor = SuffixFrontendWhiteList + "useXForwardedFor" + SuffixFrontendWhiteListIPStrategy = SuffixFrontendWhiteList + "ipStrategy" + SuffixFrontendWhiteListIPStrategyDepth = SuffixFrontendWhiteListIPStrategy + ".depth" + SuffixFrontendWhiteListIPStrategyExcludedIPS = SuffixFrontendWhiteListIPStrategy + ".excludedIPs" TraefikDomain = Prefix + SuffixDomain TraefikEnable = Prefix + SuffixEnable TraefikPort = Prefix + SuffixPort @@ -150,9 +151,10 @@ const ( TraefikFrontendRedirectReplacement = Prefix + SuffixFrontendRedirectReplacement TraefikFrontendRedirectPermanent = Prefix + SuffixFrontendRedirectPermanent TraefikFrontendRule = Prefix + SuffixFrontendRule - TraefikFrontendWhitelistSourceRange = Prefix + SuffixFrontendWhitelistSourceRange // Deprecated TraefikFrontendWhiteListSourceRange = Prefix + SuffixFrontendWhiteListSourceRange - TraefikFrontendWhiteListUseXForwardedFor = Prefix + SuffixFrontendWhiteListUseXForwardedFor + TraefikFrontendWhiteListIPStrategy = Prefix + SuffixFrontendWhiteListIPStrategy + TraefikFrontendWhiteListIPStrategyDepth = Prefix + SuffixFrontendWhiteListIPStrategyDepth + TraefikFrontendWhiteListIPStrategyExcludedIPS = Prefix + SuffixFrontendWhiteListIPStrategyExcludedIPS TraefikFrontendRequestHeaders = Prefix + SuffixFrontendRequestHeaders TraefikFrontendResponseHeaders = Prefix + SuffixFrontendResponseHeaders TraefikFrontendAllowedHosts = Prefix + SuffixFrontendHeadersAllowedHosts diff --git a/provider/label/partial.go b/provider/label/partial.go index cb65040dd..e01a137de 100644 --- a/provider/label/partial.go +++ b/provider/label/partial.go @@ -13,28 +13,30 @@ import ( // GetWhiteList Create white list from labels func GetWhiteList(labels map[string]string) *types.WhiteList { - if Has(labels, TraefikFrontendWhitelistSourceRange) { - log.Warnf("Deprecated configuration found: %s. Please use %s.", TraefikFrontendWhitelistSourceRange, TraefikFrontendWhiteListSourceRange) - } - ranges := GetSliceStringValue(labels, TraefikFrontendWhiteListSourceRange) - if len(ranges) > 0 { - return &types.WhiteList{ - SourceRange: ranges, - UseXForwardedFor: GetBoolValue(labels, TraefikFrontendWhiteListUseXForwardedFor, false), - } + if len(ranges) == 0 { + return nil } - // TODO: Deprecated - values := GetSliceStringValue(labels, TraefikFrontendWhitelistSourceRange) - if len(values) > 0 { - return &types.WhiteList{ - SourceRange: values, - UseXForwardedFor: false, - } + return &types.WhiteList{ + SourceRange: ranges, + IPStrategy: getIPStrategy(labels), + } +} + +func getIPStrategy(labels map[string]string) *types.IPStrategy { + ipStrategy := GetBoolValue(labels, TraefikFrontendWhiteListIPStrategy, false) + depth := GetIntValue(labels, TraefikFrontendWhiteListIPStrategyDepth, 0) + excludedIPs := GetSliceStringValue(labels, TraefikFrontendWhiteListIPStrategyExcludedIPS) + + if depth == 0 && len(excludedIPs) == 0 && !ipStrategy { + return nil } - return nil + return &types.IPStrategy{ + Depth: depth, + ExcludedIPs: excludedIPs, + } } // GetRedirect Create redirect from labels diff --git a/provider/label/partial_test.go b/provider/label/partial_test.go index 0202b1003..ff17ffebb 100644 --- a/provider/label/partial_test.go +++ b/provider/label/partial_test.go @@ -134,18 +134,6 @@ func TestWhiteList(t *testing.T) { labels: map[string]string{}, expected: nil, }, - { - desc: "should return a struct when deprecated label", - labels: map[string]string{ - TraefikFrontendWhitelistSourceRange: "10.10.10.10", - }, - expected: &types.WhiteList{ - SourceRange: []string{ - "10.10.10.10", - }, - UseXForwardedFor: false, - }, - }, { desc: "should return a struct when only range", labels: map[string]string{ @@ -155,42 +143,75 @@ func TestWhiteList(t *testing.T) { SourceRange: []string{ "10.10.10.10", }, - UseXForwardedFor: false, }, }, { - desc: "should return a struct when range and UseXForwardedFor", + desc: "should return a struct with ip strategy depth", labels: map[string]string{ - TraefikFrontendWhiteListSourceRange: "10.10.10.10", - TraefikFrontendWhiteListUseXForwardedFor: "true", + TraefikFrontendWhiteListSourceRange: "10.10.10.10", + TraefikFrontendWhiteListIPStrategyDepth: "5", }, expected: &types.WhiteList{ SourceRange: []string{ "10.10.10.10", }, - UseXForwardedFor: true, + IPStrategy: &types.IPStrategy{ + Depth: 5, + }, }, }, { - desc: "should return a struct when mix deprecated label and new labels", + desc: "should return a struct with ip strategy depth and excluded ips", labels: map[string]string{ - TraefikFrontendWhitelistSourceRange: "20.20.20.20", - TraefikFrontendWhiteListSourceRange: "10.10.10.10", - TraefikFrontendWhiteListUseXForwardedFor: "true", + TraefikFrontendWhiteListSourceRange: "10.10.10.10", + TraefikFrontendWhiteListIPStrategyDepth: "5", + TraefikFrontendWhiteListIPStrategyExcludedIPS: "10.10.10.10,10.10.10.11", }, expected: &types.WhiteList{ SourceRange: []string{ "10.10.10.10", }, - UseXForwardedFor: true, + IPStrategy: &types.IPStrategy{ + Depth: 5, + ExcludedIPs: []string{ + "10.10.10.10", + "10.10.10.11", + }, + }, }, }, { - desc: "should return nil when only UseXForwardedFor", + desc: "should return a struct with ip strategy (remoteAddr) with no depth and no excludedIPs", labels: map[string]string{ - TraefikFrontendWhiteListUseXForwardedFor: "true", + TraefikFrontendWhiteListSourceRange: "10.10.10.10", + TraefikFrontendWhiteListIPStrategy: "true", + }, + expected: &types.WhiteList{ + SourceRange: []string{ + "10.10.10.10", + }, + IPStrategy: &types.IPStrategy{ + Depth: 0, + ExcludedIPs: nil, + }, + }, + }, + { + desc: "should return a struct with ip strategy with depth", + labels: map[string]string{ + TraefikFrontendWhiteListSourceRange: "10.10.10.10", + TraefikFrontendWhiteListIPStrategy: "true", + TraefikFrontendWhiteListIPStrategyDepth: "5", + }, + expected: &types.WhiteList{ + SourceRange: []string{ + "10.10.10.10", + }, + IPStrategy: &types.IPStrategy{ + Depth: 5, + ExcludedIPs: nil, + }, }, - expected: nil, }, } diff --git a/provider/marathon/config_test.go b/provider/marathon/config_test.go index b4dd22350..06236cd83 100644 --- a/provider/marathon/config_test.go +++ b/provider/marathon/config_test.go @@ -399,7 +399,8 @@ func TestBuildConfiguration(t *testing.T) { withLabel(label.TraefikFrontendRedirectPermanent, "true"), withLabel(label.TraefikFrontendRule, "Host:traefik.io"), withLabel(label.TraefikFrontendWhiteListSourceRange, "10.10.10.10"), - withLabel(label.TraefikFrontendWhiteListUseXForwardedFor, "true"), + withLabel(label.TraefikFrontendWhiteListIPStrategyExcludedIPS, "10.10.10.10,10.10.10.11"), + withLabel(label.TraefikFrontendWhiteListIPStrategyDepth, "5"), withLabel(label.TraefikFrontendRequestHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8"), withLabel(label.TraefikFrontendResponseHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8"), @@ -464,8 +465,11 @@ func TestBuildConfiguration(t *testing.T) { }, }, WhiteList: &types.WhiteList{ - SourceRange: []string{"10.10.10.10"}, - UseXForwardedFor: true, + SourceRange: []string{"10.10.10.10"}, + IPStrategy: &types.IPStrategy{ + Depth: 5, + ExcludedIPs: []string{"10.10.10.10", "10.10.10.11"}, + }, }, Headers: &types.Headers{ CustomRequestHeaders: map[string]string{ @@ -789,7 +793,6 @@ func TestBuildConfigurationSegments(t *testing.T) { withSegmentLabel(label.TraefikFrontendRedirectPermanent, "true", "containous"), withSegmentLabel(label.TraefikFrontendRule, "Host:traefik.io", "containous"), withSegmentLabel(label.TraefikFrontendWhiteListSourceRange, "10.10.10.10", "containous"), - withSegmentLabel(label.TraefikFrontendWhiteListUseXForwardedFor, "true", "containous"), withSegmentLabel(label.TraefikFrontendRequestHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", "containous"), withSegmentLabel(label.TraefikFrontendResponseHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", "containous"), @@ -854,8 +857,7 @@ func TestBuildConfigurationSegments(t *testing.T) { }, }, WhiteList: &types.WhiteList{ - SourceRange: []string{"10.10.10.10"}, - UseXForwardedFor: true, + SourceRange: []string{"10.10.10.10"}, }, Headers: &types.Headers{ CustomRequestHeaders: map[string]string{ diff --git a/provider/mesos/config_test.go b/provider/mesos/config_test.go index b79f84193..e37e2406d 100644 --- a/provider/mesos/config_test.go +++ b/provider/mesos/config_test.go @@ -356,7 +356,8 @@ func TestBuildConfiguration(t *testing.T) { withLabel(label.TraefikFrontendRedirectPermanent, "true"), withLabel(label.TraefikFrontendRule, "Host:traefik.io"), withLabel(label.TraefikFrontendWhiteListSourceRange, "10.10.10.10"), - withLabel(label.TraefikFrontendWhiteListUseXForwardedFor, "true"), + withLabel(label.TraefikFrontendWhiteListIPStrategyExcludedIPS, "10.10.10.10,10.10.10.11"), + withLabel(label.TraefikFrontendWhiteListIPStrategyDepth, "5"), withLabel(label.TraefikFrontendRequestHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type:application/json; charset=utf-8"), withLabel(label.TraefikFrontendResponseHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type:application/json; charset=utf-8"), @@ -427,8 +428,11 @@ func TestBuildConfiguration(t *testing.T) { }, }, WhiteList: &types.WhiteList{ - SourceRange: []string{"10.10.10.10"}, - UseXForwardedFor: true, + SourceRange: []string{"10.10.10.10"}, + IPStrategy: &types.IPStrategy{ + Depth: 5, + ExcludedIPs: []string{"10.10.10.10", "10.10.10.11"}, + }, }, Headers: &types.Headers{ CustomRequestHeaders: map[string]string{ @@ -709,7 +713,6 @@ func TestBuildConfigurationSegments(t *testing.T) { withSegmentLabel(label.TraefikFrontendRedirectPermanent, "true", "containous"), withSegmentLabel(label.TraefikFrontendRule, "Host:traefik.io", "containous"), withSegmentLabel(label.TraefikFrontendWhiteListSourceRange, "10.10.10.10", "containous"), - withSegmentLabel(label.TraefikFrontendWhiteListUseXForwardedFor, "true", "containous"), withSegmentLabel(label.TraefikFrontendRequestHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", "containous"), withSegmentLabel(label.TraefikFrontendResponseHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", "containous"), @@ -776,8 +779,7 @@ func TestBuildConfigurationSegments(t *testing.T) { }, WhiteList: &types.WhiteList{ - SourceRange: []string{"10.10.10.10"}, - UseXForwardedFor: true, + SourceRange: []string{"10.10.10.10"}, }, Headers: &types.Headers{ CustomRequestHeaders: map[string]string{ diff --git a/provider/rancher/config_test.go b/provider/rancher/config_test.go index c672dde41..250071f1e 100644 --- a/provider/rancher/config_test.go +++ b/provider/rancher/config_test.go @@ -74,17 +74,18 @@ func TestProviderBuildConfiguration(t *testing.T) { label.TraefikFrontendAuthForwardTLSInsecureSkipVerify: "true", label.TraefikFrontendAuthHeaderField: "X-WebAuth-User", - label.TraefikFrontendEntryPoints: "http,https", - label.TraefikFrontendPassHostHeader: "true", - label.TraefikFrontendPassTLSCert: "true", - label.TraefikFrontendPriority: "666", - label.TraefikFrontendRedirectEntryPoint: "https", - label.TraefikFrontendRedirectRegex: "nope", - label.TraefikFrontendRedirectReplacement: "nope", - label.TraefikFrontendRedirectPermanent: "true", - label.TraefikFrontendRule: "Host:traefik.io", - label.TraefikFrontendWhiteListSourceRange: "10.10.10.10", - label.TraefikFrontendWhiteListUseXForwardedFor: "true", + label.TraefikFrontendEntryPoints: "http,https", + label.TraefikFrontendPassHostHeader: "true", + label.TraefikFrontendPassTLSCert: "true", + label.TraefikFrontendPriority: "666", + label.TraefikFrontendRedirectEntryPoint: "https", + label.TraefikFrontendRedirectRegex: "nope", + label.TraefikFrontendRedirectReplacement: "nope", + label.TraefikFrontendRedirectPermanent: "true", + label.TraefikFrontendRule: "Host:traefik.io", + label.TraefikFrontendWhiteListSourceRange: "10.10.10.10", + label.TraefikFrontendWhiteListIPStrategyExcludedIPS: "10.10.10.10,10.10.10.11", + label.TraefikFrontendWhiteListIPStrategyDepth: "5", label.TraefikFrontendRequestHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", label.TraefikFrontendResponseHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", @@ -156,7 +157,10 @@ func TestProviderBuildConfiguration(t *testing.T) { SourceRange: []string{ "10.10.10.10", }, - UseXForwardedFor: true, + IPStrategy: &types.IPStrategy{ + Depth: 5, + ExcludedIPs: []string{"10.10.10.10", "10.10.10.11"}, + }, }, Headers: &types.Headers{ CustomRequestHeaders: map[string]string{ @@ -305,16 +309,15 @@ func TestProviderBuildConfiguration(t *testing.T) { label.Prefix + "sauternes." + label.SuffixFrontendAuthForwardTLSInsecureSkipVerify: "true", label.Prefix + "sauternes." + label.SuffixFrontendAuthHeaderField: "X-WebAuth-User", - label.Prefix + "sauternes." + label.SuffixFrontendEntryPoints: "http,https", - label.Prefix + "sauternes." + label.SuffixFrontendPassHostHeader: "true", - label.Prefix + "sauternes." + label.SuffixFrontendPassTLSCert: "true", - label.Prefix + "sauternes." + label.SuffixFrontendPriority: "666", - label.Prefix + "sauternes." + label.SuffixFrontendRedirectEntryPoint: "https", - label.Prefix + "sauternes." + label.SuffixFrontendRedirectRegex: "nope", - label.Prefix + "sauternes." + label.SuffixFrontendRedirectReplacement: "nope", - label.Prefix + "sauternes." + label.SuffixFrontendRedirectPermanent: "true", - label.Prefix + "sauternes." + label.SuffixFrontendWhiteListSourceRange: "10.10.10.10", - label.Prefix + "sauternes." + label.SuffixFrontendWhiteListUseXForwardedFor: "true", + label.Prefix + "sauternes." + label.SuffixFrontendEntryPoints: "http,https", + label.Prefix + "sauternes." + label.SuffixFrontendPassHostHeader: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSCert: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPriority: "666", + label.Prefix + "sauternes." + label.SuffixFrontendRedirectEntryPoint: "https", + label.Prefix + "sauternes." + label.SuffixFrontendRedirectRegex: "nope", + label.Prefix + "sauternes." + label.SuffixFrontendRedirectReplacement: "nope", + label.Prefix + "sauternes." + label.SuffixFrontendRedirectPermanent: "true", + label.Prefix + "sauternes." + label.SuffixFrontendWhiteListSourceRange: "10.10.10.10", label.Prefix + "sauternes." + label.SuffixFrontendRequestHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", label.Prefix + "sauternes." + label.SuffixFrontendResponseHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", @@ -383,7 +386,6 @@ func TestProviderBuildConfiguration(t *testing.T) { SourceRange: []string{ "10.10.10.10", }, - UseXForwardedFor: true, }, Headers: &types.Headers{ CustomRequestHeaders: map[string]string{ diff --git a/server/header_rewriter.go b/server/header_rewriter.go deleted file mode 100644 index b4b46b2d7..000000000 --- a/server/header_rewriter.go +++ /dev/null @@ -1,53 +0,0 @@ -package server - -import ( - "net/http" - "os" - - "github.com/containous/traefik/log" - "github.com/containous/traefik/whitelist" - "github.com/vulcand/oxy/forward" -) - -// NewHeaderRewriter Create a header rewriter -func NewHeaderRewriter(trustedIPs []string, insecure bool) (forward.ReqRewriter, error) { - ips, err := whitelist.NewIP(trustedIPs, insecure, true) - if err != nil { - return nil, err - } - - hostname, err := os.Hostname() - if err != nil { - hostname = "localhost" - } - - return &headerRewriter{ - secureRewriter: &forward.HeaderRewriter{TrustForwardHeader: false, Hostname: hostname}, - insecureRewriter: &forward.HeaderRewriter{TrustForwardHeader: true, Hostname: hostname}, - ips: ips, - insecure: insecure, - }, nil -} - -type headerRewriter struct { - secureRewriter forward.ReqRewriter - insecureRewriter forward.ReqRewriter - insecure bool - ips *whitelist.IP -} - -func (h *headerRewriter) Rewrite(req *http.Request) { - if h.insecure { - h.insecureRewriter.Rewrite(req) - return - } - - err := h.ips.IsAuthorized(req) - if err != nil { - log.Debug(err) - h.secureRewriter.Rewrite(req) - return - } - - h.insecureRewriter.Rewrite(req) -} diff --git a/server/header_rewriter_test.go b/server/header_rewriter_test.go deleted file mode 100644 index 7e5df3bbf..000000000 --- a/server/header_rewriter_test.go +++ /dev/null @@ -1,104 +0,0 @@ -package server - -import ( - "net/http" - "net/http/httptest" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestHeaderRewriter_Rewrite(t *testing.T) { - testCases := []struct { - desc string - remoteAddr string - trustedIPs []string - insecure bool - expected map[string]string - }{ - { - desc: "Secure & authorized", - remoteAddr: "10.10.10.10:80", - trustedIPs: []string{"10.10.10.10"}, - insecure: false, - expected: map[string]string{ - "X-Foo": "bar", - "X-Forwarded-For": "30.30.30.30", - }, - }, - { - desc: "Secure & unauthorized", - remoteAddr: "50.50.50.50:80", - trustedIPs: []string{"10.10.10.10"}, - insecure: false, - expected: map[string]string{ - "X-Foo": "bar", - "X-Forwarded-For": "", - }, - }, - { - desc: "Secure & authorized error", - remoteAddr: "10.10.10.10", - trustedIPs: []string{"10.10.10.10"}, - insecure: false, - expected: map[string]string{ - "X-Foo": "bar", - "X-Forwarded-For": "", - }, - }, - { - desc: "insecure & authorized", - remoteAddr: "10.10.10.10:80", - trustedIPs: []string{"10.10.10.10"}, - insecure: true, - expected: map[string]string{ - "X-Foo": "bar", - "X-Forwarded-For": "30.30.30.30", - }, - }, - { - desc: "insecure & unauthorized", - remoteAddr: "50.50.50.50:80", - trustedIPs: []string{"10.10.10.10"}, - insecure: true, - expected: map[string]string{ - "X-Foo": "bar", - "X-Forwarded-For": "30.30.30.30", - }, - }, - { - desc: "insecure & authorized error", - remoteAddr: "10.10.10.10", - trustedIPs: []string{"10.10.10.10"}, - insecure: true, - expected: map[string]string{ - "X-Foo": "bar", - "X-Forwarded-For": "30.30.30.30", - }, - }, - } - - for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - - rewriter, err := NewHeaderRewriter(test.trustedIPs, test.insecure) - require.NoError(t, err) - - req := httptest.NewRequest(http.MethodGet, "http://20.20.20.20/foo", nil) - require.NoError(t, err) - req.RemoteAddr = test.remoteAddr - - req.Header.Set("X-Foo", "bar") - req.Header.Set("X-Forwarded-For", "30.30.30.30") - - rewriter.Rewrite(req) - - for key, value := range test.expected { - assert.Equal(t, value, req.Header.Get(key)) - } - }) - } -} diff --git a/server/server.go b/server/server.go index b7800f1f9..9c458debc 100644 --- a/server/server.go +++ b/server/server.go @@ -22,6 +22,7 @@ import ( "github.com/containous/traefik/cluster" "github.com/containous/traefik/configuration" "github.com/containous/traefik/h2c" + "github.com/containous/traefik/ip" "github.com/containous/traefik/log" "github.com/containous/traefik/metrics" "github.com/containous/traefik/middlewares" @@ -31,7 +32,6 @@ import ( "github.com/containous/traefik/safe" traefiktls "github.com/containous/traefik/tls" "github.com/containous/traefik/types" - "github.com/containous/traefik/whitelist" "github.com/sirupsen/logrus" "github.com/urfave/negroni" "github.com/xenolf/lego/acme" @@ -552,7 +552,7 @@ func (s *Server) prepareServer(entryPointName string, entryPoint *configuration. if entryPoint.ProxyProtocol != nil { listener, err = buildProxyProtocolListener(entryPoint, listener) if err != nil { - return nil, nil, err + return nil, nil, fmt.Errorf("error creating proxy protocol listener: %v", err) } } @@ -572,23 +572,32 @@ func (s *Server) prepareServer(entryPointName string, entryPoint *configuration. } func buildProxyProtocolListener(entryPoint *configuration.EntryPoint, listener net.Listener) (net.Listener, error) { - IPs, err := whitelist.NewIP(entryPoint.ProxyProtocol.TrustedIPs, entryPoint.ProxyProtocol.Insecure, false) - if err != nil { - return nil, fmt.Errorf("error creating whitelist: %s", err) + var sourceCheck func(addr net.Addr) (bool, error) + if entryPoint.ProxyProtocol.Insecure { + sourceCheck = func(_ net.Addr) (bool, error) { + return true, nil + } + } else { + checker, err := ip.NewChecker(entryPoint.ProxyProtocol.TrustedIPs) + if err != nil { + return nil, err + } + + sourceCheck = func(addr net.Addr) (bool, error) { + ipAddr, ok := addr.(*net.TCPAddr) + if !ok { + return false, fmt.Errorf("type error %v", addr) + } + + return checker.ContainsIP(ipAddr.IP), nil + } } log.Infof("Enabling ProxyProtocol for trusted IPs %v", entryPoint.ProxyProtocol.TrustedIPs) return &proxyproto.Listener{ - Listener: listener, - SourceCheck: func(addr net.Addr) (bool, error) { - ip, ok := addr.(*net.TCPAddr) - if !ok { - return false, fmt.Errorf("type error %v", addr) - } - - return IPs.ContainsIP(ip.IP), nil - }, + Listener: listener, + SourceCheck: sourceCheck, }, nil } diff --git a/server/server_configuration.go b/server/server_configuration.go index 0e9135e1a..70d44eafc 100644 --- a/server/server_configuration.go +++ b/server/server_configuration.go @@ -233,17 +233,11 @@ func (s *Server) buildForwarder(entryPointName string, entryPoint *configuration return nil, fmt.Errorf("failed to create RoundTripper for frontend %s: %v", frontendName, err) } - rewriter, err := NewHeaderRewriter(entryPoint.ForwardedHeaders.TrustedIPs, entryPoint.ForwardedHeaders.Insecure) - if err != nil { - return nil, fmt.Errorf("error creating rewriter for frontend %s: %v", frontendName, err) - } - var fwd http.Handler fwd, err = forward.New( forward.Stream(true), forward.PassHostHeader(frontend.PassHostHeader), forward.RoundTripper(roundTripper), - forward.Rewriter(rewriter), forward.ResponseModifier(responseModifier), forward.BufferPool(s.bufferPool), forward.WebsocketConnectionClosedHook(func(req *http.Request, conn net.Conn) { diff --git a/server/server_middlewares.go b/server/server_middlewares.go index 95befea62..2061dddeb 100644 --- a/server/server_middlewares.go +++ b/server/server_middlewares.go @@ -9,6 +9,7 @@ import ( "github.com/containous/traefik/middlewares/accesslog" mauth "github.com/containous/traefik/middlewares/auth" "github.com/containous/traefik/middlewares/errorpages" + "github.com/containous/traefik/middlewares/forwardedheaders" "github.com/containous/traefik/middlewares/redirect" "github.com/containous/traefik/types" thoas_stats "github.com/thoas/stats" @@ -47,7 +48,7 @@ func (s *Server) buildMiddlewares(frontendName string, frontend *types.Frontend, } // Whitelist - ipWhitelistMiddleware, err := buildIPWhiteLister(frontend.WhiteList, frontend.WhitelistSourceRange) + ipWhitelistMiddleware, err := buildIPWhiteLister(frontend.WhiteList, s.entryPoints[entryPointName].Configuration.ClientIPStrategy) if err != nil { return nil, nil, nil, fmt.Errorf("error creating IP Whitelister: %s", err) } @@ -146,9 +147,18 @@ func (s *Server) buildServerEntryPointMiddlewares(serverEntryPointName string) ( serverMiddlewares = append(serverMiddlewares, &middlewares.Compress{}) } - ipWhitelistMiddleware, err := buildIPWhiteLister( - s.entryPoints[serverEntryPointName].Configuration.WhiteList, - s.entryPoints[serverEntryPointName].Configuration.WhitelistSourceRange) + if s.entryPoints[serverEntryPointName].Configuration.ForwardedHeaders != nil { + xForwardedMiddleware, err := forwardedheaders.NewXforwarded( + s.entryPoints[serverEntryPointName].Configuration.ForwardedHeaders.Insecure, + s.entryPoints[serverEntryPointName].Configuration.ForwardedHeaders.TrustedIPs, + ) + if err != nil { + return nil, fmt.Errorf("failed to create xforwarded headers middleware: %v", err) + } + serverMiddlewares = append(serverMiddlewares, xForwardedMiddleware) + } + + ipWhitelistMiddleware, err := buildIPWhiteLister(s.entryPoints[serverEntryPointName].Configuration.WhiteList, s.entryPoints[serverEntryPointName].Configuration.ClientIPStrategy) if err != nil { return nil, fmt.Errorf("failed to create ip whitelist middleware: %v", err) } @@ -268,14 +278,21 @@ func (s *Server) buildRedirectHandler(srcEntryPointName string, opt *types.Redir return redirection, nil } -func buildIPWhiteLister(whiteList *types.WhiteList, wlRange []string) (*middlewares.IPWhiteLister, error) { - if whiteList != nil && - len(whiteList.SourceRange) > 0 { - return middlewares.NewIPWhiteLister(whiteList.SourceRange, whiteList.UseXForwardedFor) - } else if len(wlRange) > 0 { - return middlewares.NewIPWhiteLister(wlRange, false) +func buildIPWhiteLister(whiteList *types.WhiteList, ipStrategy *types.IPStrategy) (*middlewares.IPWhiteLister, error) { + if whiteList == nil { + return nil, nil } - return nil, nil + + if whiteList.IPStrategy != nil { + ipStrategy = whiteList.IPStrategy + } + + strategy, err := ipStrategy.Get() + if err != nil { + return nil, err + } + + return middlewares.NewIPWhiteLister(whiteList.SourceRange, strategy) } func (s *Server) wrapNegroniHandlerWithAccessLog(handler negroni.Handler, frontendName string) negroni.Handler { diff --git a/server/server_middlewares_test.go b/server/server_middlewares_test.go index 6877c25d9..488085990 100644 --- a/server/server_middlewares_test.go +++ b/server/server_middlewares_test.go @@ -36,8 +36,10 @@ func TestServerEntryPointWhitelistConfig(t *testing.T) { desc: "whitelist middleware should be added if configured on entrypoint", entrypoint: &configuration.EntryPoint{ Address: ":0", - WhitelistSourceRange: []string{ - "127.0.0.1/32", + WhiteList: &types.WhiteList{ + SourceRange: []string{ + "127.0.0.1/32", + }, }, ForwardedHeaders: &configuration.ForwardedHeaders{Insecure: true}, }, @@ -96,23 +98,6 @@ func TestBuildIPWhiteLister(t *testing.T) { middlewareConfigured: false, errMessage: "", }, - { - desc: "whitelists configured (deprecated)", - whitelistSourceRange: []string{ - "1.2.3.4/24", - "fe80::/16", - }, - middlewareConfigured: true, - errMessage: "", - }, - { - desc: "invalid whitelists configured (deprecated)", - whitelistSourceRange: []string{ - "foo", - }, - middlewareConfigured: false, - errMessage: "parsing CIDR whitelist [foo]: parsing CIDR white list : invalid CIDR address: foo", - }, { desc: "whitelists configured", whiteList: &types.WhiteList{ @@ -120,22 +105,10 @@ func TestBuildIPWhiteLister(t *testing.T) { "1.2.3.4/24", "fe80::/16", }, - UseXForwardedFor: false, }, middlewareConfigured: true, errMessage: "", }, - { - desc: "invalid whitelists configured (deprecated)", - whiteList: &types.WhiteList{ - SourceRange: []string{ - "foo", - }, - UseXForwardedFor: false, - }, - middlewareConfigured: false, - errMessage: "parsing CIDR whitelist [foo]: parsing CIDR white list : invalid CIDR address: foo", - }, } for _, test := range testCases { @@ -143,7 +116,7 @@ func TestBuildIPWhiteLister(t *testing.T) { t.Run(test.desc, func(t *testing.T) { t.Parallel() - middleware, err := buildIPWhiteLister(test.whiteList, test.whitelistSourceRange) + middleware, err := buildIPWhiteLister(test.whiteList, nil) if test.errMessage != "" { require.EqualError(t, err, test.errMessage) @@ -258,6 +231,11 @@ func TestServerGenericFrontendAuthFail(t *testing.T) { "http": &configuration.EntryPoint{ForwardedHeaders: &configuration.ForwardedHeaders{Insecure: true}}, }, } + entryPoints := map[string]EntryPoint{ + "http": { + Configuration: globalConfig.EntryPoints["http"], + }, + } dynamicConfigs := types.Configurations{ "config": &types.Configuration{ @@ -286,7 +264,7 @@ func TestServerGenericFrontendAuthFail(t *testing.T) { }, } - srv := NewServer(globalConfig, nil, nil) + srv := NewServer(globalConfig, nil, entryPoints) _, err := srv.loadConfig(dynamicConfigs, globalConfig) require.NoError(t, err) diff --git a/templates/consul_catalog.tmpl b/templates/consul_catalog.tmpl index a576970fa..fa50ee39f 100644 --- a/templates/consul_catalog.tmpl +++ b/templates/consul_catalog.tmpl @@ -123,7 +123,13 @@ sourceRange = [{{range $whitelist.SourceRange }} "{{.}}", {{end}}] - useXForwardedFor = {{ $whitelist.UseXForwardedFor }} + {{if $whitelist.IPStrategy }} + [frontends."frontend-{{ $service.ServiceName }}".whiteList.IPStrategy] + depth = {{ $whitelist.IPStrategy.Depth }} + excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }} + "{{.}}", + {{end}}] + {{end}} {{end}} {{ $redirect := getRedirect $service.TraefikLabels }} diff --git a/templates/docker.tmpl b/templates/docker.tmpl index 0e49ed06c..1e79af1dd 100644 --- a/templates/docker.tmpl +++ b/templates/docker.tmpl @@ -123,7 +123,13 @@ sourceRange = [{{range $whitelist.SourceRange }} "{{.}}", {{end}}] - useXForwardedFor = {{ $whitelist.UseXForwardedFor }} + {{if $whitelist.IPStrategy }} + [frontends."frontend-{{ $frontendName }}".whiteList.IPStrategy] + depth = {{ $whitelist.IPStrategy.Depth }} + excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }} + "{{.}}", + {{end}}] + {{end}} {{end}} {{ $redirect := getRedirect $container.SegmentLabels }} diff --git a/templates/ecs.tmpl b/templates/ecs.tmpl index 7da7d5254..d43fd4f6a 100644 --- a/templates/ecs.tmpl +++ b/templates/ecs.tmpl @@ -122,7 +122,13 @@ sourceRange = [{{range $whitelist.SourceRange }} "{{.}}", {{end}}] - useXForwardedFor = {{ $whitelist.UseXForwardedFor }} + {{if $whitelist.IPStrategy }} + [frontends."frontend-{{ $serviceName }}".whiteList.IPStrategy] + depth = {{ $whitelist.IPStrategy.Depth }} + excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }} + "{{.}}", + {{end}}] + {{end}} {{end}} {{ $redirect := getRedirect $instance.TraefikLabels }} diff --git a/templates/kubernetes.tmpl b/templates/kubernetes.tmpl index 11e0b4904..39207eefa 100644 --- a/templates/kubernetes.tmpl +++ b/templates/kubernetes.tmpl @@ -90,10 +90,16 @@ {{if $frontend.WhiteList }} [frontends."{{ $frontendName }}".whiteList] - sourceRange = [{{range $frontend.WhiteList.SourceRange }} + sourceRange = [{{range $frontend.Whitelist.SourceRange }} "{{.}}", {{end}}] - useXForwardedFor = {{ $frontend.WhiteList.UseXForwardedFor }} + {{if $frontend.Whitelist.IPStrategy }} + [frontends."{{ $frontendName }}".whiteList.IPStrategy] + depth = {{ $frontend.Whitelist.IPStrategy.Depth }} + excludedIPs = [{{range $frontend.Whitelist.IPStrategy.ExcludedIPs }} + "{{.}}", + {{end}}] + {{end}} {{end}} {{if $frontend.Redirect }} diff --git a/templates/kv.tmpl b/templates/kv.tmpl index 87778a173..68edeeb16 100644 --- a/templates/kv.tmpl +++ b/templates/kv.tmpl @@ -122,7 +122,13 @@ sourceRange = [{{range $whitelist.SourceRange }} "{{.}}", {{end}}] - useXForwardedFor = {{ $whitelist.UseXForwardedFor }} + {{if $whitelist.IPStrategy }} + [frontends."{{ $frontendName }}".whiteList.IPStrategy] + depth = {{ $whitelist.IPStrategy.Depth }} + excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }} + "{{.}}", + {{end}}] + {{end}} {{end}} {{ $redirect := getRedirect $frontend }} diff --git a/templates/marathon.tmpl b/templates/marathon.tmpl index a1b336c83..f750486f7 100644 --- a/templates/marathon.tmpl +++ b/templates/marathon.tmpl @@ -125,7 +125,13 @@ sourceRange = [{{range $whitelist.SourceRange }} "{{.}}", {{end}}] - useXForwardedFor = {{ $whitelist.UseXForwardedFor }} + {{if $whitelist.IPStrategy }} + [frontends."{{ $frontendName }}".whiteList.IPStrategy] + depth = {{ $whitelist.IPStrategy.Depth }} + excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }} + "{{.}}", + {{end}}] + {{end}} {{end}} {{ $redirect := getRedirect $app.SegmentLabels }} diff --git a/templates/mesos.tmpl b/templates/mesos.tmpl index 2b6eec1c6..e7ec3d83a 100644 --- a/templates/mesos.tmpl +++ b/templates/mesos.tmpl @@ -125,7 +125,13 @@ sourceRange = [{{range $whitelist.SourceRange }} "{{.}}", {{end}}] - useXForwardedFor = {{ $whitelist.UseXForwardedFor }} + {{if $whitelist.IPStrategy }} + [frontends."frontend-{{ $frontendName }}".whiteList.IPStrategy] + depth = {{ $whitelist.IPStrategy.Depth }} + excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }} + "{{.}}", + {{end}}] + {{end}} {{end}} {{ $redirect := getRedirect $app.TraefikLabels }} diff --git a/templates/rancher.tmpl b/templates/rancher.tmpl index 09ac82af2..44b7f57ef 100644 --- a/templates/rancher.tmpl +++ b/templates/rancher.tmpl @@ -123,7 +123,13 @@ sourceRange = [{{range $whitelist.SourceRange }} "{{.}}", {{end}}] - useXForwardedFor = {{ $whitelist.UseXForwardedFor }} + {{if $whitelist.IPStrategy }} + [frontends."frontend-{{ $frontendName }}".whiteList.IPStrategy] + depth = {{ $whitelist.IPStrategy.Depth }} + excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }} + "{{.}}", + {{end}}] + {{end}} {{end}} {{ $redirect := getRedirect $service.SegmentLabels }} diff --git a/types/types.go b/types/types.go index 89e43fae1..625242927 100644 --- a/types/types.go +++ b/types/types.go @@ -14,6 +14,7 @@ import ( "github.com/abronan/valkeyrie/store" "github.com/containous/flaeg/parse" "github.com/containous/mux" + "github.com/containous/traefik/ip" "github.com/containous/traefik/log" traefiktls "github.com/containous/traefik/tls" "github.com/mitchellh/hashstructure" @@ -63,8 +64,8 @@ type Buffering struct { // WhiteList contains white list configuration. type WhiteList struct { - SourceRange []string `json:"sourceRange,omitempty"` - UseXForwardedFor bool `json:"useXForwardedFor,omitempty" export:"true"` + SourceRange []string `json:"sourceRange,omitempty"` + IPStrategy *IPStrategy `json:"ipStrategy,omitempty"` } // HealthCheck holds HealthCheck configuration @@ -177,19 +178,18 @@ func (h *Headers) HasSecureHeadersDefined() bool { // Frontend holds frontend configuration. type Frontend struct { - EntryPoints []string `json:"entryPoints,omitempty" hash:"ignore"` - Backend string `json:"backend,omitempty"` - Routes map[string]Route `json:"routes,omitempty" hash:"ignore"` - PassHostHeader bool `json:"passHostHeader,omitempty"` - PassTLSCert bool `json:"passTLSCert,omitempty"` - Priority int `json:"priority"` - WhitelistSourceRange []string `json:"whitelistSourceRange,omitempty"` // Deprecated - WhiteList *WhiteList `json:"whiteList,omitempty"` - Headers *Headers `json:"headers,omitempty"` - Errors map[string]*ErrorPage `json:"errors,omitempty"` - RateLimit *RateLimit `json:"ratelimit,omitempty"` - Redirect *Redirect `json:"redirect,omitempty"` - Auth *Auth `json:"auth,omitempty"` + EntryPoints []string `json:"entryPoints,omitempty" hash:"ignore"` + Backend string `json:"backend,omitempty"` + Routes map[string]Route `json:"routes,omitempty" hash:"ignore"` + PassHostHeader bool `json:"passHostHeader,omitempty"` + PassTLSCert bool `json:"passTLSCert,omitempty"` + Priority int `json:"priority"` + WhiteList *WhiteList `json:"whiteList,omitempty"` + Headers *Headers `json:"headers,omitempty"` + Errors map[string]*ErrorPage `json:"errors,omitempty"` + RateLimit *RateLimit `json:"ratelimit,omitempty"` + Redirect *Redirect `json:"redirect,omitempty"` + Auth *Auth `json:"auth,omitempty"` } // Hash returns the hash value of a Frontend struct. @@ -611,3 +611,37 @@ func (h HTTPCodeRanges) Contains(statusCode int) bool { } return false } + +// IPStrategy Configuration to choose the IP selection strategy. +type IPStrategy struct { + Depth int `json:"depth,omitempty" export:"true"` + ExcludedIPs []string `json:"excludedIPs,omitempty"` +} + +// Get an IP selection strategy +// if nil return the RemoteAddr strategy +// else return a strategy base on the configuration using the X-Forwarded-For Header. +// Depth override the ExcludedIPs +func (s *IPStrategy) Get() (ip.Strategy, error) { + if s == nil { + return &ip.RemoteAddrStrategy{}, nil + } + + if s.Depth > 0 { + return &ip.DepthStrategy{ + Depth: s.Depth, + }, nil + } + + if len(s.ExcludedIPs) > 0 { + checker, err := ip.NewChecker(s.ExcludedIPs) + if err != nil { + return nil, err + } + return &ip.CheckerStrategy{ + Checker: checker, + }, nil + } + + return &ip.RemoteAddrStrategy{}, nil +} diff --git a/types/types_test.go b/types/types_test.go index 3973e025d..c6f19b54e 100644 --- a/types/types_test.go +++ b/types/types_test.go @@ -4,7 +4,9 @@ import ( "fmt" "testing" + "github.com/containous/traefik/ip" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestHeaders_ShouldReturnFalseWhenNotHasCustomHeadersDefined(t *testing.T) { @@ -136,3 +138,49 @@ func TestHTTPCodeRanges_Contains(t *testing.T) { }) } } + +func TestIPStrategy_Get(t *testing.T) { + testCases := []struct { + desc string + ipStrategy *IPStrategy + expected ip.Strategy + }{ + { + desc: "IPStrategy is nil", + expected: &ip.RemoteAddrStrategy{}, + }, + { + desc: "IPStrategy is not nil but with no values", + ipStrategy: &IPStrategy{}, + expected: &ip.RemoteAddrStrategy{}, + }, + { + desc: "IPStrategy with Depth", + ipStrategy: &IPStrategy{Depth: 3}, + expected: &ip.DepthStrategy{}, + }, + { + desc: "IPStrategy with ExcludedIPs", + ipStrategy: &IPStrategy{ExcludedIPs: []string{"10.0.0.1"}}, + expected: &ip.CheckerStrategy{}, + }, + { + desc: "IPStrategy with ExcludedIPs and Depth", + ipStrategy: &IPStrategy{Depth: 4, ExcludedIPs: []string{"10.0.0.1"}}, + expected: &ip.DepthStrategy{}, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + strategy, err := test.ipStrategy.Get() + require.NoError(t, err) + + assert.IsType(t, test.expected, strategy) + + }) + } +} diff --git a/whitelist/ip.go b/whitelist/ip.go deleted file mode 100644 index bfb74e007..000000000 --- a/whitelist/ip.go +++ /dev/null @@ -1,145 +0,0 @@ -package whitelist - -import ( - "errors" - "fmt" - "net" - "net/http" - "strings" -) - -const ( - // XForwardedFor Header name - XForwardedFor = "X-Forwarded-For" -) - -// IP allows to check that addresses are in a white list -type IP struct { - whiteListsIPs []*net.IP - whiteListsNet []*net.IPNet - insecure bool - useXForwardedFor bool -} - -// NewIP builds a new IP given a list of CIDR-Strings to white list -func NewIP(whiteList []string, insecure bool, useXForwardedFor bool) (*IP, error) { - if len(whiteList) == 0 && !insecure { - return nil, errors.New("no white list provided") - } - - ip := IP{ - insecure: insecure, - useXForwardedFor: useXForwardedFor, - } - - if !insecure { - for _, ipMask := range whiteList { - if ipAddr := net.ParseIP(ipMask); ipAddr != nil { - ip.whiteListsIPs = append(ip.whiteListsIPs, &ipAddr) - } else { - _, ipAddr, err := net.ParseCIDR(ipMask) - if err != nil { - return nil, fmt.Errorf("parsing CIDR white list %s: %v", ipAddr, err) - } - ip.whiteListsNet = append(ip.whiteListsNet, ipAddr) - } - } - } - - return &ip, nil -} - -// IsAuthorized checks if provided request is authorized by the white list -func (ip *IP) IsAuthorized(req *http.Request) error { - if ip.insecure { - return nil - } - - var invalidMatches []string - - if ip.useXForwardedFor { - xFFs := req.Header[XForwardedFor] - if len(xFFs) > 0 { - for _, xFF := range xFFs { - xffs := strings.Split(xFF, ",") - for _, xff := range xffs { - ok, err := ip.contains(parseHost(xff)) - if err != nil { - return err - } - - if ok { - return nil - } - - invalidMatches = append(invalidMatches, xff) - } - } - } - } - - host, _, err := net.SplitHostPort(req.RemoteAddr) - if err != nil { - return err - } - - ok, err := ip.contains(host) - if err != nil { - return err - } - - if !ok { - invalidMatches = append(invalidMatches, req.RemoteAddr) - return fmt.Errorf("%q matched none of the white list", strings.Join(invalidMatches, ", ")) - } - - return nil -} - -// contains checks if provided address is in the white list -func (ip *IP) contains(addr string) (bool, error) { - ipAddr, err := parseIP(addr) - if err != nil { - return false, fmt.Errorf("unable to parse address: %s: %s", addr, err) - } - - return ip.ContainsIP(ipAddr), nil -} - -// ContainsIP checks if provided address is in the white list -func (ip *IP) ContainsIP(addr net.IP) bool { - if ip.insecure { - return true - } - - for _, whiteListIP := range ip.whiteListsIPs { - if whiteListIP.Equal(addr) { - return true - } - } - - for _, whiteListNet := range ip.whiteListsNet { - if whiteListNet.Contains(addr) { - return true - } - } - - return false -} - -func parseIP(addr string) (net.IP, error) { - userIP := net.ParseIP(addr) - if userIP == nil { - return nil, fmt.Errorf("can't parse IP from address %s", addr) - } - - return userIP, nil -} - -func parseHost(addr string) string { - host, _, err := net.SplitHostPort(addr) - if err != nil { - return addr - } - return host -} diff --git a/whitelist/ip_test.go b/whitelist/ip_test.go deleted file mode 100644 index 0b0efefc0..000000000 --- a/whitelist/ip_test.go +++ /dev/null @@ -1,452 +0,0 @@ -package whitelist - -import ( - "net" - "net/http" - "net/http/httptest" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestIsAuthorized(t *testing.T) { - testCases := []struct { - desc string - whiteList []string - allowXForwardedFor bool - remoteAddr string - xForwardedForValues []string - authorized bool - }{ - { - desc: "allow UseXForwardedFor, remoteAddr not in range, UseXForwardedFor in range", - whiteList: []string{"1.2.3.4/24"}, - allowXForwardedFor: true, - remoteAddr: "10.2.3.1:123", - xForwardedForValues: []string{"1.2.3.1", "10.2.3.1"}, - authorized: true, - }, - { - desc: "allow UseXForwardedFor, remoteAddr not in range, UseXForwardedFor in range (compact XFF)", - whiteList: []string{"1.2.3.4/24"}, - allowXForwardedFor: true, - remoteAddr: "10.2.3.1:123", - xForwardedForValues: []string{"1.2.3.1, 10.2.3.1"}, - authorized: true, - }, - { - desc: "allow UseXForwardedFor, remoteAddr in range, UseXForwardedFor in range", - whiteList: []string{"1.2.3.4/24"}, - allowXForwardedFor: true, - remoteAddr: "1.2.3.1:123", - xForwardedForValues: []string{"1.2.3.1", "10.2.3.1"}, - authorized: true, - }, - { - desc: "allow UseXForwardedFor, remoteAddr in range, UseXForwardedFor not in range", - whiteList: []string{"1.2.3.4/24"}, - allowXForwardedFor: true, - remoteAddr: "1.2.3.1:123", - xForwardedForValues: []string{"10.2.3.1", "10.2.3.1"}, - authorized: true, - }, - { - desc: "allow UseXForwardedFor, remoteAddr not in range, UseXForwardedFor not in range", - whiteList: []string{"1.2.3.4/24"}, - allowXForwardedFor: true, - remoteAddr: "10.2.3.1:123", - xForwardedForValues: []string{"10.2.3.1", "10.2.3.1"}, - authorized: false, - }, - { - desc: "don't allow UseXForwardedFor, remoteAddr not in range, UseXForwardedFor in range", - whiteList: []string{"1.2.3.4/24"}, - allowXForwardedFor: false, - remoteAddr: "10.2.3.1:123", - xForwardedForValues: []string{"1.2.3.1", "10.2.3.1"}, - authorized: false, - }, - { - desc: "don't allow UseXForwardedFor, remoteAddr in range, UseXForwardedFor in range", - whiteList: []string{"1.2.3.4/24"}, - allowXForwardedFor: false, - remoteAddr: "1.2.3.1:123", - xForwardedForValues: []string{"1.2.3.1", "10.2.3.1"}, - authorized: true, - }, - { - desc: "don't allow UseXForwardedFor, remoteAddr in range, UseXForwardedFor not in range", - whiteList: []string{"1.2.3.4/24"}, - allowXForwardedFor: false, - remoteAddr: "1.2.3.1:123", - xForwardedForValues: []string{"10.2.3.1", "10.2.3.1"}, - authorized: true, - }, - { - desc: "don't allow UseXForwardedFor, remoteAddr not in range, UseXForwardedFor not in range", - whiteList: []string{"1.2.3.4/24"}, - allowXForwardedFor: false, - remoteAddr: "10.2.3.1:123", - xForwardedForValues: []string{"10.2.3.1", "10.2.3.1"}, - authorized: false, - }, - } - - for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - - req := NewRequest(test.remoteAddr, test.xForwardedForValues) - - whiteLister, err := NewIP(test.whiteList, false, test.allowXForwardedFor) - require.NoError(t, err) - - err = whiteLister.IsAuthorized(req) - if test.authorized { - require.NoError(t, err) - } else { - require.Error(t, err) - } - }) - } -} - -func TestNew(t *testing.T) { - cases := []struct { - desc string - whiteList []string - expectedWhitelists []*net.IPNet - errMessage string - }{ - { - desc: "nil whitelist", - whiteList: nil, - expectedWhitelists: nil, - errMessage: "no white list provided", - }, { - desc: "empty whitelist", - whiteList: []string{}, - expectedWhitelists: nil, - errMessage: "no white list provided", - }, { - desc: "whitelist containing empty string", - whiteList: []string{ - "1.2.3.4/24", - "", - "fe80::/16", - }, - expectedWhitelists: nil, - errMessage: "parsing CIDR white list : invalid CIDR address: ", - }, { - desc: "whitelist containing only an empty string", - whiteList: []string{ - "", - }, - expectedWhitelists: nil, - errMessage: "parsing CIDR white list : invalid CIDR address: ", - }, { - desc: "whitelist containing an invalid string", - whiteList: []string{ - "foo", - }, - expectedWhitelists: nil, - errMessage: "parsing CIDR white list : invalid CIDR address: foo", - }, { - desc: "IPv4 & IPv6 whitelist", - whiteList: []string{ - "1.2.3.4/24", - "fe80::/16", - }, - expectedWhitelists: []*net.IPNet{ - {IP: net.IPv4(1, 2, 3, 0).To4(), Mask: net.IPv4Mask(255, 255, 255, 0)}, - {IP: net.ParseIP("fe80::"), Mask: net.IPMask(net.ParseIP("ffff::"))}, - }, - errMessage: "", - }, { - desc: "IPv4 only", - whiteList: []string{ - "127.0.0.1/8", - }, - expectedWhitelists: []*net.IPNet{ - {IP: net.IPv4(127, 0, 0, 0).To4(), Mask: net.IPv4Mask(255, 0, 0, 0)}, - }, - errMessage: "", - }, - } - - for _, test := range cases { - test := test - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - whiteLister, err := NewIP(test.whiteList, false, false) - if test.errMessage != "" { - require.EqualError(t, err, test.errMessage) - } else { - require.NoError(t, err) - for index, actual := range whiteLister.whiteListsNet { - expected := test.expectedWhitelists[index] - assert.Equal(t, expected.IP, actual.IP) - assert.Equal(t, expected.Mask.String(), actual.Mask.String()) - } - } - }) - } -} - -func TestContainsIsAllowed(t *testing.T) { - cases := []struct { - desc string - whitelistStrings []string - passIPs []string - rejectIPs []string - }{ - { - desc: "IPv4", - whitelistStrings: []string{"1.2.3.4/24"}, - passIPs: []string{ - "1.2.3.1", - "1.2.3.32", - "1.2.3.156", - "1.2.3.255", - }, - rejectIPs: []string{ - "1.2.16.1", - "1.2.32.1", - "127.0.0.1", - "8.8.8.8", - }, - }, - { - desc: "IPv4 single IP", - whitelistStrings: []string{"8.8.8.8"}, - passIPs: []string{"8.8.8.8"}, - rejectIPs: []string{ - "8.8.8.7", - "8.8.8.9", - "8.8.8.0", - "8.8.8.255", - "4.4.4.4", - "127.0.0.1", - }, - }, - { - desc: "IPv4 Net single IP", - whitelistStrings: []string{"8.8.8.8/32"}, - passIPs: []string{"8.8.8.8"}, - rejectIPs: []string{ - "8.8.8.7", - "8.8.8.9", - "8.8.8.0", - "8.8.8.255", - "4.4.4.4", - "127.0.0.1", - }, - }, - { - desc: "multiple IPv4", - whitelistStrings: []string{"1.2.3.4/24", "8.8.8.8/8"}, - passIPs: []string{ - "1.2.3.1", - "1.2.3.32", - "1.2.3.156", - "1.2.3.255", - "8.8.4.4", - "8.0.0.1", - "8.32.42.128", - "8.255.255.255", - }, - rejectIPs: []string{ - "1.2.16.1", - "1.2.32.1", - "127.0.0.1", - "4.4.4.4", - "4.8.8.8", - }, - }, - { - desc: "IPv6", - whitelistStrings: []string{"2a03:4000:6:d080::/64"}, - passIPs: []string{ - "2a03:4000:6:d080::", - "2a03:4000:6:d080::1", - "2a03:4000:6:d080:dead:beef:ffff:ffff", - "2a03:4000:6:d080::42", - }, - rejectIPs: []string{ - "2a03:4000:7:d080::", - "2a03:4000:7:d080::1", - "fe80::", - "4242::1", - }, - }, - { - desc: "IPv6 single IP", - whitelistStrings: []string{"2a03:4000:6:d080::42/128"}, - passIPs: []string{"2a03:4000:6:d080::42"}, - rejectIPs: []string{ - "2a03:4000:6:d080::1", - "2a03:4000:6:d080:dead:beef:ffff:ffff", - "2a03:4000:6:d080::43", - }, - }, - { - desc: "multiple IPv6", - whitelistStrings: []string{"2a03:4000:6:d080::/64", "fe80::/16"}, - passIPs: []string{ - "2a03:4000:6:d080::", - "2a03:4000:6:d080::1", - "2a03:4000:6:d080:dead:beef:ffff:ffff", - "2a03:4000:6:d080::42", - "fe80::1", - "fe80:aa00:00bb:4232:ff00:eeee:00ff:1111", - "fe80::fe80", - }, - rejectIPs: []string{ - "2a03:4000:7:d080::", - "2a03:4000:7:d080::1", - "4242::1", - }, - }, - { - desc: "multiple IPv6 & IPv4", - whitelistStrings: []string{"2a03:4000:6:d080::/64", "fe80::/16", "1.2.3.4/24", "8.8.8.8/8"}, - passIPs: []string{ - "2a03:4000:6:d080::", - "2a03:4000:6:d080::1", - "2a03:4000:6:d080:dead:beef:ffff:ffff", - "2a03:4000:6:d080::42", - "fe80::1", - "fe80:aa00:00bb:4232:ff00:eeee:00ff:1111", - "fe80::fe80", - "1.2.3.1", - "1.2.3.32", - "1.2.3.156", - "1.2.3.255", - "8.8.4.4", - "8.0.0.1", - "8.32.42.128", - "8.255.255.255", - }, - rejectIPs: []string{ - "2a03:4000:7:d080::", - "2a03:4000:7:d080::1", - "4242::1", - "1.2.16.1", - "1.2.32.1", - "127.0.0.1", - "4.4.4.4", - "4.8.8.8", - }, - }, - { - desc: "broken IP-addresses", - whitelistStrings: []string{"127.0.0.1/32"}, - passIPs: nil, - }, - } - - for _, test := range cases { - test := test - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - - whiteLister, err := NewIP(test.whitelistStrings, false, false) - - require.NoError(t, err) - require.NotNil(t, whiteLister) - - for _, testIP := range test.passIPs { - allowed, err := whiteLister.contains(testIP) - require.NoError(t, err) - assert.Truef(t, allowed, "%s should have passed.", testIP) - } - - for _, testIP := range test.rejectIPs { - allowed, err := whiteLister.contains(testIP) - require.NoError(t, err) - assert.Falsef(t, allowed, "%s should not have passed.", testIP) - } - }) - } -} - -func TestContainsInsecure(t *testing.T) { - mustNewIP := func(whitelistStrings []string, insecure bool) *IP { - ip, err := NewIP(whitelistStrings, insecure, false) - if err != nil { - t.Fatal(err) - } - return ip - } - - testCases := []struct { - desc string - whiteLister *IP - ip string - expected bool - }{ - { - desc: "valid ip and insecure", - whiteLister: mustNewIP([]string{"1.2.3.4/24"}, true), - ip: "1.2.3.1", - expected: true, - }, - { - desc: "invalid ip and insecure", - whiteLister: mustNewIP([]string{"1.2.3.4/24"}, true), - ip: "10.2.3.1", - expected: true, - }, - { - desc: "invalid ip and secure", - whiteLister: mustNewIP([]string{"1.2.3.4/24"}, false), - ip: "10.2.3.1", - expected: false, - }, - } - - for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - - ok, err := test.whiteLister.contains(test.ip) - require.NoError(t, err) - - assert.Equal(t, test.expected, ok) - }) - } -} - -func TestContainsBrokenIPs(t *testing.T) { - brokenIPs := []string{ - "foo", - "10.0.0.350", - "fe:::80", - "", - "\\&$§&/(", - } - - whiteLister, err := NewIP([]string{"1.2.3.4/24"}, false, false) - require.NoError(t, err) - - for _, testIP := range brokenIPs { - _, err := whiteLister.contains(testIP) - assert.Error(t, err) - } -} - -func NewRequest(remoteAddr string, xForwardedFor []string) *http.Request { - req := httptest.NewRequest(http.MethodGet, "http://example.com/foo", nil) - if len(remoteAddr) > 0 { - req.RemoteAddr = remoteAddr - } - if len(xForwardedFor) > 0 { - for _, xff := range xForwardedFor { - req.Header.Add(XForwardedFor, xff) - } - } - return req -}