IPStrategy for selecting IP in whitelist
This commit is contained in:
parent
1ec4e03738
commit
00728e711c
65 changed files with 2444 additions and 1837 deletions
|
@ -80,8 +80,12 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||||
TrustForwardHeader: true,
|
TrustForwardHeader: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
WhitelistSourceRange: []string{"foo WhitelistSourceRange 1", "foo WhitelistSourceRange 2", "foo WhitelistSourceRange 3"},
|
WhiteList: &types.WhiteList{
|
||||||
Compress: &configuration.Compress{},
|
SourceRange: []string{
|
||||||
|
"127.0.0.1/32",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Compress: &configuration.Compress{},
|
||||||
ProxyProtocol: &configuration.ProxyProtocol{
|
ProxyProtocol: &configuration.ProxyProtocol{
|
||||||
TrustedIPs: []string{"127.0.0.1/32", "192.168.0.1"},
|
TrustedIPs: []string{"127.0.0.1/32", "192.168.0.1"},
|
||||||
},
|
},
|
||||||
|
@ -125,8 +129,12 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||||
TrustForwardHeader: true,
|
TrustForwardHeader: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
WhitelistSourceRange: []string{"fii WhitelistSourceRange 1", "fii WhitelistSourceRange 2", "fii WhitelistSourceRange 3"},
|
WhiteList: &types.WhiteList{
|
||||||
Compress: &configuration.Compress{},
|
SourceRange: []string{
|
||||||
|
"127.0.0.1/32",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Compress: &configuration.Compress{},
|
||||||
ProxyProtocol: &configuration.ProxyProtocol{
|
ProxyProtocol: &configuration.ProxyProtocol{
|
||||||
TrustedIPs: []string{"127.0.0.1/32", "192.168.0.1"},
|
TrustedIPs: []string{"127.0.0.1/32", "192.168.0.1"},
|
||||||
},
|
},
|
||||||
|
|
|
@ -179,7 +179,13 @@ var _templatesConsul_catalogTmpl = []byte(`[backends]
|
||||||
sourceRange = [{{range $whitelist.SourceRange }}
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{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}}
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $service.TraefikLabels }}
|
{{ $redirect := getRedirect $service.TraefikLabels }}
|
||||||
|
@ -418,7 +424,13 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
|
||||||
sourceRange = [{{range $whitelist.SourceRange }}
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
{{if $whitelist.IPStrategy }}
|
||||||
|
[frontends."frontend-{{ $frontendName }}".whiteList.IPStrategy]
|
||||||
|
depth = {{ $whitelist.IPStrategy.Depth }}
|
||||||
|
excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $container.SegmentLabels }}
|
{{ $redirect := getRedirect $container.SegmentLabels }}
|
||||||
|
@ -657,7 +669,13 @@ var _templatesEcsTmpl = []byte(`[backends]
|
||||||
sourceRange = [{{range $whitelist.SourceRange }}
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
{{if $whitelist.IPStrategy }}
|
||||||
|
[frontends."frontend-{{ $serviceName }}".whiteList.IPStrategy]
|
||||||
|
depth = {{ $whitelist.IPStrategy.Depth }}
|
||||||
|
excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $instance.TraefikLabels }}
|
{{ $redirect := getRedirect $instance.TraefikLabels }}
|
||||||
|
@ -904,10 +922,16 @@ var _templatesKubernetesTmpl = []byte(`[backends]
|
||||||
|
|
||||||
{{if $frontend.WhiteList }}
|
{{if $frontend.WhiteList }}
|
||||||
[frontends."{{ $frontendName }}".whiteList]
|
[frontends."{{ $frontendName }}".whiteList]
|
||||||
sourceRange = [{{range $frontend.WhiteList.SourceRange }}
|
sourceRange = [{{range $frontend.Whitelist.SourceRange }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{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}}
|
{{end}}
|
||||||
|
|
||||||
{{if $frontend.Redirect }}
|
{{if $frontend.Redirect }}
|
||||||
|
@ -1148,7 +1172,13 @@ var _templatesKvTmpl = []byte(`[backends]
|
||||||
sourceRange = [{{range $whitelist.SourceRange }}
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
{{if $whitelist.IPStrategy }}
|
||||||
|
[frontends."{{ $frontendName }}".whiteList.IPStrategy]
|
||||||
|
depth = {{ $whitelist.IPStrategy.Depth }}
|
||||||
|
excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $frontend }}
|
{{ $redirect := getRedirect $frontend }}
|
||||||
|
@ -1404,7 +1434,13 @@ var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }}
|
||||||
sourceRange = [{{range $whitelist.SourceRange }}
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
{{if $whitelist.IPStrategy }}
|
||||||
|
[frontends."{{ $frontendName }}".whiteList.IPStrategy]
|
||||||
|
depth = {{ $whitelist.IPStrategy.Depth }}
|
||||||
|
excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $app.SegmentLabels }}
|
{{ $redirect := getRedirect $app.SegmentLabels }}
|
||||||
|
@ -1645,7 +1681,13 @@ var _templatesMesosTmpl = []byte(`[backends]
|
||||||
sourceRange = [{{range $whitelist.SourceRange }}
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
{{if $whitelist.IPStrategy }}
|
||||||
|
[frontends."frontend-{{ $frontendName }}".whiteList.IPStrategy]
|
||||||
|
depth = {{ $whitelist.IPStrategy.Depth }}
|
||||||
|
excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $app.TraefikLabels }}
|
{{ $redirect := getRedirect $app.TraefikLabels }}
|
||||||
|
@ -1908,7 +1950,13 @@ var _templatesRancherTmpl = []byte(`{{ $backendServers := .Backends }}
|
||||||
sourceRange = [{{range $whitelist.SourceRange }}
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
{{if $whitelist.IPStrategy }}
|
||||||
|
[frontends."frontend-{{ $frontendName }}".whiteList.IPStrategy]
|
||||||
|
depth = {{ $whitelist.IPStrategy.Depth }}
|
||||||
|
excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $service.SegmentLabels }}
|
{{ $redirect := getRedirect $service.SegmentLabels }}
|
||||||
|
|
|
@ -108,7 +108,7 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
|
||||||
if len(gc.EntryPoints) == 0 {
|
if len(gc.EntryPoints) == 0 {
|
||||||
gc.EntryPoints = map[string]*EntryPoint{"http": {
|
gc.EntryPoints = map[string]*EntryPoint{"http": {
|
||||||
Address: ":80",
|
Address: ":80",
|
||||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
ForwardedHeaders: &ForwardedHeaders{},
|
||||||
}}
|
}}
|
||||||
gc.DefaultEntryPoints = []string{"http"}
|
gc.DefaultEntryPoints = []string{"http"}
|
||||||
}
|
}
|
||||||
|
@ -126,18 +126,7 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
|
||||||
entryPoint := gc.EntryPoints[entryPointName]
|
entryPoint := gc.EntryPoints[entryPointName]
|
||||||
// ForwardedHeaders must be remove in the next breaking version
|
// ForwardedHeaders must be remove in the next breaking version
|
||||||
if entryPoint.ForwardedHeaders == nil {
|
if entryPoint.ForwardedHeaders == nil {
|
||||||
entryPoint.ForwardedHeaders = &ForwardedHeaders{Insecure: true}
|
entryPoint.ForwardedHeaders = &ForwardedHeaders{}
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package configuration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containous/traefik/log"
|
"github.com/containous/traefik/log"
|
||||||
|
@ -11,20 +12,19 @@ import (
|
||||||
|
|
||||||
// EntryPoint holds an entry point configuration of the reverse proxy (ip, port, TLS...)
|
// EntryPoint holds an entry point configuration of the reverse proxy (ip, port, TLS...)
|
||||||
type EntryPoint struct {
|
type EntryPoint struct {
|
||||||
Address string
|
Address string
|
||||||
TLS *tls.TLS `export:"true"`
|
TLS *tls.TLS `export:"true"`
|
||||||
Redirect *types.Redirect `export:"true"`
|
Redirect *types.Redirect `export:"true"`
|
||||||
Auth *types.Auth `export:"true"`
|
Auth *types.Auth `export:"true"`
|
||||||
WhitelistSourceRange []string // Deprecated
|
WhiteList *types.WhiteList `export:"true"`
|
||||||
WhiteList *types.WhiteList `export:"true"`
|
Compress *Compress `export:"true"`
|
||||||
Compress *Compress `export:"true"`
|
ProxyProtocol *ProxyProtocol `export:"true"`
|
||||||
ProxyProtocol *ProxyProtocol `export:"true"`
|
ForwardedHeaders *ForwardedHeaders `export:"true"`
|
||||||
ForwardedHeaders *ForwardedHeaders `export:"true"`
|
ClientIPStrategy *types.IPStrategy `export:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compress contains compress configuration
|
// Compress contains compress configuration
|
||||||
type Compress struct {
|
type Compress struct{}
|
||||||
}
|
|
||||||
|
|
||||||
// ProxyProtocol contains Proxy-Protocol configuration
|
// ProxyProtocol contains Proxy-Protocol configuration
|
||||||
type ProxyProtocol struct {
|
type ProxyProtocol struct {
|
||||||
|
@ -68,11 +68,6 @@ func (ep *EntryPoints) Type() string {
|
||||||
func (ep *EntryPoints) Set(value string) error {
|
func (ep *EntryPoints) Set(value string) error {
|
||||||
result := parseEntryPointsConfiguration(value)
|
result := parseEntryPointsConfiguration(value)
|
||||||
|
|
||||||
var whiteListSourceRange []string
|
|
||||||
if len(result["whitelistsourcerange"]) > 0 {
|
|
||||||
whiteListSourceRange = strings.Split(result["whitelistsourcerange"], ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
var compress *Compress
|
var compress *Compress
|
||||||
if len(result["compress"]) > 0 {
|
if len(result["compress"]) > 0 {
|
||||||
compress = &Compress{}
|
compress = &Compress{}
|
||||||
|
@ -84,29 +79,42 @@ func (ep *EntryPoints) Set(value string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
(*ep)[result["name"]] = &EntryPoint{
|
(*ep)[result["name"]] = &EntryPoint{
|
||||||
Address: result["address"],
|
Address: result["address"],
|
||||||
TLS: configTLS,
|
TLS: configTLS,
|
||||||
Auth: makeEntryPointAuth(result),
|
Auth: makeEntryPointAuth(result),
|
||||||
Redirect: makeEntryPointRedirect(result),
|
Redirect: makeEntryPointRedirect(result),
|
||||||
Compress: compress,
|
Compress: compress,
|
||||||
WhitelistSourceRange: whiteListSourceRange,
|
WhiteList: makeWhiteList(result),
|
||||||
WhiteList: makeWhiteList(result),
|
ProxyProtocol: makeEntryPointProxyProtocol(result),
|
||||||
ProxyProtocol: makeEntryPointProxyProtocol(result),
|
ForwardedHeaders: makeEntryPointForwardedHeaders(result),
|
||||||
ForwardedHeaders: makeEntryPointForwardedHeaders(result),
|
ClientIPStrategy: makeIPStrategy("clientipstrategy", result),
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeWhiteList(result map[string]string) *types.WhiteList {
|
func makeWhiteList(result map[string]string) *types.WhiteList {
|
||||||
var wl *types.WhiteList
|
|
||||||
if rawRange, ok := result["whitelist_sourcerange"]; ok {
|
if rawRange, ok := result["whitelist_sourcerange"]; ok {
|
||||||
wl = &types.WhiteList{
|
return &types.WhiteList{
|
||||||
SourceRange: strings.Split(rawRange, ","),
|
SourceRange: strings.Split(rawRange, ","),
|
||||||
UseXForwardedFor: toBool(result, "whitelist_usexforwardedfor"),
|
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 {
|
func makeEntryPointAuth(result map[string]string) *types.Auth {
|
||||||
|
@ -184,15 +192,14 @@ func makeEntryPointProxyProtocol(result map[string]string) *ProxyProtocol {
|
||||||
}
|
}
|
||||||
|
|
||||||
if proxyProtocol != nil && proxyProtocol.Insecure {
|
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
|
return proxyProtocol
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeEntryPointForwardedHeaders(result map[string]string) *ForwardedHeaders {
|
func makeEntryPointForwardedHeaders(result map[string]string) *ForwardedHeaders {
|
||||||
// TODO must be changed to false by default in the next breaking version.
|
forwardedHeaders := &ForwardedHeaders{}
|
||||||
forwardedHeaders := &ForwardedHeaders{Insecure: true}
|
|
||||||
if _, ok := result["forwardedheaders_insecure"]; ok {
|
if _, ok := result["forwardedheaders_insecure"]; ok {
|
||||||
forwardedHeaders.Insecure = toBool(result, "forwardedheaders_insecure")
|
forwardedHeaders.Insecure = toBool(result, "forwardedheaders_insecure")
|
||||||
}
|
}
|
||||||
|
@ -300,3 +307,14 @@ func toBool(conf map[string]string, key string) bool {
|
||||||
}
|
}
|
||||||
return false
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -45,9 +45,11 @@ func Test_parseEntryPointsConfiguration(t *testing.T) {
|
||||||
"Auth.Forward.TLS.Cert:path/to/foo.cert " +
|
"Auth.Forward.TLS.Cert:path/to/foo.cert " +
|
||||||
"Auth.Forward.TLS.Key:path/to/foo.key " +
|
"Auth.Forward.TLS.Key:path/to/foo.key " +
|
||||||
"Auth.Forward.TLS.InsecureSkipVerify:true " +
|
"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.sourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " +
|
"WhiteList.IPStrategy.depth:3 " +
|
||||||
"whiteList.useXForwardedFor:true ",
|
"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{
|
expectedResult: map[string]string{
|
||||||
"address": ":8000",
|
"address": ":8000",
|
||||||
"auth_basic_users": "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
"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_permanent": "true",
|
||||||
"redirect_regex": "http://localhost/(.*)",
|
"redirect_regex": "http://localhost/(.*)",
|
||||||
"redirect_replacement": "http://mydomain/$1",
|
"redirect_replacement": "http://mydomain/$1",
|
||||||
"tls": "goo,gii",
|
"tls": "goo,gii",
|
||||||
"tls_acme": "TLS",
|
"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_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",
|
"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_sourcerange": "10.42.0.0/16,152.89.1.33/32,afed:be44::/16",
|
"whitelist_ipstrategy_depth": "3",
|
||||||
"whitelist_usexforwardedfor": "true",
|
"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.Cert:path/to/foo.cert " +
|
||||||
"Auth.Forward.TLS.Key:path/to/foo.key " +
|
"Auth.Forward.TLS.Key:path/to/foo.key " +
|
||||||
"Auth.Forward.TLS.InsecureSkipVerify:true " +
|
"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.sourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " +
|
"WhiteList.IPStrategy.depth:3 " +
|
||||||
"whiteList.useXForwardedFor:true ",
|
"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",
|
expectedEntryPointName: "foo",
|
||||||
expectedEntryPoint: &EntryPoint{
|
expectedEntryPoint: &EntryPoint{
|
||||||
Address: ":8000",
|
Address: ":8000",
|
||||||
|
@ -265,18 +271,19 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||||
},
|
},
|
||||||
HeaderField: "X-WebAuth-User",
|
HeaderField: "X-WebAuth-User",
|
||||||
},
|
},
|
||||||
WhitelistSourceRange: []string{
|
|
||||||
"10.42.0.0/16",
|
|
||||||
"152.89.1.33/32",
|
|
||||||
"afed:be44::/16",
|
|
||||||
},
|
|
||||||
WhiteList: &types.WhiteList{
|
WhiteList: &types.WhiteList{
|
||||||
SourceRange: []string{
|
SourceRange: []string{
|
||||||
"10.42.0.0/16",
|
"10.42.0.0/16",
|
||||||
"152.89.1.33/32",
|
"152.89.1.33/32",
|
||||||
"afed:be44::/16",
|
"afed:be44::/16",
|
||||||
},
|
},
|
||||||
UseXForwardedFor: true,
|
IPStrategy: &types.IPStrategy{
|
||||||
|
Depth: 3,
|
||||||
|
ExcludedIPs: []string{
|
||||||
|
"10.0.0.3/24",
|
||||||
|
"20.0.0.3/24",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Compress: &Compress{},
|
Compress: &Compress{},
|
||||||
ProxyProtocol: &ProxyProtocol{
|
ProxyProtocol: &ProxyProtocol{
|
||||||
|
@ -290,6 +297,13 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||||
"20.0.0.3/24",
|
"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.replacement:http://mydomain/$1 " +
|
||||||
"redirect.permanent:true " +
|
"redirect.permanent:true " +
|
||||||
"compress: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 " +
|
"proxyProtocol.TrustedIPs:192.168.0.1 " +
|
||||||
"forwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24 " +
|
"forwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24 " +
|
||||||
"auth.basic.users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0 " +
|
"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",
|
HeaderField: "X-WebAuth-User",
|
||||||
},
|
},
|
||||||
WhitelistSourceRange: []string{
|
WhiteList: &types.WhiteList{
|
||||||
"10.42.0.0/16",
|
SourceRange: []string{
|
||||||
"152.89.1.33/32",
|
"10.42.0.0/16",
|
||||||
"afed:be44::/16",
|
"152.89.1.33/32",
|
||||||
|
"afed:be44::/16",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Compress: &Compress{},
|
Compress: &Compress{},
|
||||||
ProxyProtocol: &ProxyProtocol{
|
ProxyProtocol: &ProxyProtocol{
|
||||||
|
@ -399,12 +415,12 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||||
expression: "Name:foo",
|
expression: "Name:foo",
|
||||||
expectedEntryPointName: "foo",
|
expectedEntryPointName: "foo",
|
||||||
expectedEntryPoint: &EntryPoint{
|
expectedEntryPoint: &EntryPoint{
|
||||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
ForwardedHeaders: &ForwardedHeaders{Insecure: false},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ForwardedHeaders insecure true",
|
name: "ForwardedHeaders insecure true",
|
||||||
expression: "Name:foo ForwardedHeaders.Insecure:true",
|
expression: "Name:foo ForwardedHeaders.insecure:true",
|
||||||
expectedEntryPointName: "foo",
|
expectedEntryPointName: "foo",
|
||||||
expectedEntryPoint: &EntryPoint{
|
expectedEntryPoint: &EntryPoint{
|
||||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||||
|
@ -412,7 +428,7 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ForwardedHeaders insecure false",
|
name: "ForwardedHeaders insecure false",
|
||||||
expression: "Name:foo ForwardedHeaders.Insecure:false",
|
expression: "Name:foo ForwardedHeaders.insecure:false",
|
||||||
expectedEntryPointName: "foo",
|
expectedEntryPointName: "foo",
|
||||||
expectedEntryPoint: &EntryPoint{
|
expectedEntryPoint: &EntryPoint{
|
||||||
ForwardedHeaders: &ForwardedHeaders{Insecure: false},
|
ForwardedHeaders: &ForwardedHeaders{Insecure: false},
|
||||||
|
@ -430,19 +446,19 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ProxyProtocol insecure true",
|
name: "ProxyProtocol insecure true",
|
||||||
expression: "Name:foo ProxyProtocol.Insecure:true",
|
expression: "Name:foo ProxyProtocol.insecure:true",
|
||||||
expectedEntryPointName: "foo",
|
expectedEntryPointName: "foo",
|
||||||
expectedEntryPoint: &EntryPoint{
|
expectedEntryPoint: &EntryPoint{
|
||||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
ForwardedHeaders: &ForwardedHeaders{},
|
||||||
ProxyProtocol: &ProxyProtocol{Insecure: true},
|
ProxyProtocol: &ProxyProtocol{Insecure: true},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ProxyProtocol insecure false",
|
name: "ProxyProtocol insecure false",
|
||||||
expression: "Name:foo ProxyProtocol.Insecure:false",
|
expression: "Name:foo ProxyProtocol.insecure:false",
|
||||||
expectedEntryPointName: "foo",
|
expectedEntryPointName: "foo",
|
||||||
expectedEntryPoint: &EntryPoint{
|
expectedEntryPoint: &EntryPoint{
|
||||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
ForwardedHeaders: &ForwardedHeaders{},
|
||||||
ProxyProtocol: &ProxyProtocol{},
|
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",
|
expression: "Name:foo ProxyProtocol.TrustedIPs:10.0.0.3/24,20.0.0.3/24",
|
||||||
expectedEntryPointName: "foo",
|
expectedEntryPointName: "foo",
|
||||||
expectedEntryPoint: &EntryPoint{
|
expectedEntryPoint: &EntryPoint{
|
||||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
ForwardedHeaders: &ForwardedHeaders{},
|
||||||
ProxyProtocol: &ProxyProtocol{
|
ProxyProtocol: &ProxyProtocol{
|
||||||
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
|
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
|
||||||
},
|
},
|
||||||
|
@ -463,7 +479,7 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||||
expectedEntryPointName: "foo",
|
expectedEntryPointName: "foo",
|
||||||
expectedEntryPoint: &EntryPoint{
|
expectedEntryPoint: &EntryPoint{
|
||||||
Compress: &Compress{},
|
Compress: &Compress{},
|
||||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
ForwardedHeaders: &ForwardedHeaders{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -472,7 +488,7 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||||
expectedEntryPointName: "foo",
|
expectedEntryPointName: "foo",
|
||||||
expectedEntryPoint: &EntryPoint{
|
expectedEntryPoint: &EntryPoint{
|
||||||
Compress: &Compress{},
|
Compress: &Compress{},
|
||||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
ForwardedHeaders: &ForwardedHeaders{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,17 @@ func NewInternalRouterAggregator(globalConfiguration configuration.GlobalConfigu
|
||||||
var serverMiddlewares []negroni.Handler
|
var serverMiddlewares []negroni.Handler
|
||||||
|
|
||||||
if globalConfiguration.EntryPoints[entryPointName].WhiteList != nil {
|
if globalConfiguration.EntryPoints[entryPointName].WhiteList != nil {
|
||||||
ipWhitelistMiddleware, err := middlewares.NewIPWhiteLister(
|
ipStrategy := globalConfiguration.EntryPoints[entryPointName].ClientIPStrategy
|
||||||
globalConfiguration.EntryPoints[entryPointName].WhiteList.SourceRange,
|
if globalConfiguration.EntryPoints[entryPointName].WhiteList.IPStrategy != nil {
|
||||||
globalConfiguration.EntryPoints[entryPointName].WhiteList.UseXForwardedFor)
|
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 {
|
if err != nil {
|
||||||
log.Fatalf("Error creating whitelist middleware: %s", err)
|
log.Fatalf("Error creating whitelist middleware: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,61 +94,63 @@ Additional settings can be defined using Consul Catalog tags.
|
||||||
!!! note
|
!!! note
|
||||||
The default prefix is `traefik`.
|
The default prefix is `traefik`.
|
||||||
|
|
||||||
| Label | Description |
|
| Label | Description |
|
||||||
|-------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|--------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `<prefix>.enable=false` | Disables this container in Træfik. |
|
| `<prefix>.enable=false` | Disables this container in Træfik. |
|
||||||
| `<prefix>.protocol=https` | Overrides the default `http` protocol. |
|
| `<prefix>.protocol=https` | Overrides the default `http` protocol. |
|
||||||
| `<prefix>.weight=10` | Assigns this weight to the container. |
|
| `<prefix>.weight=10` | Assigns this weight to the container. |
|
||||||
| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
| `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.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||||
| `traefik.backend.buffering.memRequestBodyBytes=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.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||||
| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
|
| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
|
||||||
| `<prefix>.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend. ex: `NetworkErrorRatio() > 0.` |
|
| `<prefix>.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend. ex: `NetworkErrorRatio() > 0.` |
|
||||||
| `<prefix>.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. |
|
| `<prefix>.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. |
|
||||||
| `<prefix>.backend.healthcheck.interval=1s` | Defines the health check interval. |
|
| `<prefix>.backend.healthcheck.interval=1s` | Defines the health check interval. |
|
||||||
| `<prefix>.backend.healthcheck.port=8080` | Sets a different port for the health check. |
|
| `<prefix>.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.scheme=http` | Overrides the server URL scheme. |
|
||||||
| `<prefix>.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
|
| `<prefix>.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
|
||||||
| `<prefix>.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
| `<prefix>.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||||
| `<prefix>.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm. |
|
| `<prefix>.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm. |
|
||||||
| `<prefix>.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions. |
|
| `<prefix>.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions. |
|
||||||
| `<prefix>.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions. |
|
| `<prefix>.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions. |
|
||||||
| `<prefix>.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
| `<prefix>.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||||
| `<prefix>.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.<br>Must be used in conjunction with the above label to take effect. |
|
| `<prefix>.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.<br>Must be used in conjunction with the above label to take effect. |
|
||||||
| `<prefix>.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). |
|
| `<prefix>.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). |
|
||||||
| `<prefix>.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
|
| `<prefix>.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
|
||||||
| `<prefix>.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. |
|
| `<prefix>.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. |
|
||||||
| `<prefix>.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. |
|
| `<prefix>.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. |
|
||||||
| `<prefix>.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
|
| `<prefix>.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
|
||||||
| `<prefix>.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. |
|
| `<prefix>.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. |
|
||||||
| `<prefix>.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. |
|
| `<prefix>.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. |
|
||||||
| `<prefix>.frontend.auth.forward.address=https://example.com`| Sets the URL of the authentication server. |
|
| `<prefix>.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. |
|
||||||
| `<prefix>.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. |
|
| `<prefix>.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. |
|
||||||
| `<prefix>.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). |
|
| `<prefix>.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). |
|
||||||
| `<prefix>.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. |
|
| `<prefix>.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||||
| `<prefix>.frontend.auth.forward.tls.insecureSkipVerify=true`| If set to true invalid SSL certificates are accepted. |
|
| `<prefix>.frontend.auth.forward.tls.insecureSkipVerify=true` | If set to true invalid SSL certificates are accepted. |
|
||||||
| `<prefix>.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. |
|
| `<prefix>.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||||
| `<prefix>.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. |
|
| `<prefix>.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. |
|
||||||
| `<prefix>.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. |
|
| `<prefix>.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. |
|
||||||
| `<prefix>.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
| `<prefix>.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||||
| `<prefix>.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `<prefix>.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||||
| `<prefix>.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `<prefix>.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||||
| `<prefix>.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `<prefix>.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||||
| `<prefix>.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
| `<prefix>.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
||||||
| `<prefix>.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
| `<prefix>.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
||||||
| `<prefix>.frontend.priority=10` | Overrides default frontend priority. |
|
| `<prefix>.frontend.priority=10` | Overrides default frontend priority. |
|
||||||
| `<prefix>.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `<prefix>.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||||
| `<prefix>.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `<prefix>.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||||
| `<prefix>.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `<prefix>.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||||
| `<prefix>.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `<prefix>.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||||
| `<prefix>.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS). |
|
| `<prefix>.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS). |
|
||||||
| `<prefix>.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
| `<prefix>.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||||
| `<prefix>.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
| `<prefix>.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||||
| `<prefix>.frontend.redirect.permanent=true` | Returns 301 instead of 302. |
|
| `<prefix>.frontend.redirect.permanent=true` | Returns 301 instead of 302. |
|
||||||
| `<prefix>.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{{.ServiceName}}.{{.Domain}}`. |
|
| `<prefix>.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{{.ServiceName}}.{{.Domain}}`. |
|
||||||
| `<prefix>.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.<br>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. |
|
| `<prefix>.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.<br>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. |
|
||||||
| `<prefix>.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. |
|
| `<prefix>.frontend.whiteList.ipStrategy=true` | Uses the default IPStrategy.<br>Can be used when there is an existing `clientIPStrategy` but you want the remote address for whitelisting. |
|
||||||
|
| `<prefix>.frontend.whiteList.ipStrategy.depth=5` | See [whitelist](/configuration/entrypoints/#white-listing) |
|
||||||
|
| `<prefix>.frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | See [whitelist](/configuration/entrypoints/#white-listing) |
|
||||||
|
|
||||||
### Custom Headers
|
### Custom Headers
|
||||||
|
|
||||||
|
|
|
@ -207,66 +207,68 @@ services:
|
||||||
|
|
||||||
Labels can be used on containers to override default behavior.
|
Labels can be used on containers to override default behavior.
|
||||||
|
|
||||||
| Label | Description |
|
| Label | Description |
|
||||||
|------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `traefik.docker.network` | Overrides the default docker network to use for connections to the container. [1] |
|
| `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.domain` | Sets the default domain for the frontend rules. |
|
||||||
| `traefik.enable=false` | Disables this container in Træfik. |
|
| `traefik.enable=false` | Disables this container in Træfik. |
|
||||||
| `traefik.port=80` | Registers this port. Useful when the container exposes multiples ports. |
|
| `traefik.port=80` | Registers this port. Useful when the container exposes multiples ports. |
|
||||||
| `traefik.protocol=https` | Overrides the default `http` protocol |
|
| `traefik.protocol=https` | Overrides the default `http` protocol |
|
||||||
| `traefik.weight=10` | Assigns this weight to the container |
|
| `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=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.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||||
| `traefik.backend.buffering.maxResponseBodyBytes=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.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||||
| `traefik.backend.buffering.memResponseBodyBytes=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.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.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.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.interval=1s` | Defines the health check interval. |
|
||||||
| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. |
|
| `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.scheme=http` | Overrides the server URL scheme. |
|
||||||
| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
|
| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
|
||||||
| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||||
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
|
| `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=true` | Enables backend sticky sessions |
|
||||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for 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.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.<br>Must be used in conjunction with the below label to take effect. |
|
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>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.<br>Must be used in conjunction with the above 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.<br>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=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.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.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.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.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.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.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.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.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.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.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.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.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.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.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`.<br>Overrides `defaultEntryPoints` |
|
| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.frontend.errors.<name>.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.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
||||||
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
||||||
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
||||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||||
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||||
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.frontend.rateLimit.rateSet.<name>.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.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.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||||
| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. |
|
| `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.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.<br>An unset or empty list allows all Source-IPs to access.<br>If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access.<br>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. |
|
| `traefik.frontend.whiteList.ipStrategy=true` | Uses the default IPStrategy.<br>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`:
|
[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 <container_id>`) otherwise it will randomly pick one (depending on how docker is returning them).
|
If a container is linked to several networks, be sure to set the proper network name (you can check with `docker inspect <container_id>`) 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`.
|
`echo $(htpasswd -nb user password) | sed -e s/\\$/\\$\\$/g`.
|
||||||
The result will be `user:$$apr1$$9Cv/OMGj$$ZomWQzuQbL.3TRCS81A1g/`, note additional symbol `$` makes escaping.
|
The result will be `user:$$apr1$$9Cv/OMGj$$ZomWQzuQbL.3TRCS81A1g/`, note additional symbol `$` makes escaping.
|
||||||
|
|
||||||
|
|
||||||
#### Custom Headers
|
#### Custom Headers
|
||||||
|
|
||||||
| Label | Description |
|
| Label | Description |
|
||||||
|
@ -319,46 +320,48 @@ You can define as many segments as ports exposed in a container.
|
||||||
|
|
||||||
Segment labels override the default behavior.
|
Segment labels override the default behavior.
|
||||||
|
|
||||||
| Label | Description |
|
| Label | Description |
|
||||||
|---------------------------------------------------------------------------|---------------------------------------------------------------|
|
|------------------------------------------------------------------------------|----------------------------------------------------------------|
|
||||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||||
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
||||||
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
||||||
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
||||||
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
||||||
| `traefik.<segment_name>.frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` |
|
| `traefik.<segment_name>.frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` |
|
||||||
| `traefik.<segment_name>.frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` |
|
| `traefik.<segment_name>.frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` |
|
||||||
| `traefik.<segment_name>.frontend.auth.basic.usersfile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersfile` |
|
| `traefik.<segment_name>.frontend.auth.basic.usersfile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersfile` |
|
||||||
| `traefik.<segment_name>.frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` |
|
| `traefik.<segment_name>.frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` |
|
||||||
| `traefik.<segment_name>.frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` |
|
| `traefik.<segment_name>.frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` |
|
||||||
| `traefik.<segment_name>.frontend.auth.digest.usersfile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersfile` |
|
| `traefik.<segment_name>.frontend.auth.digest.usersfile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersfile` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.address=https://example.com`| Same as `traefik.frontend.auth.forward.address` |
|
| `traefik.<segment_name>.frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` |
|
| `traefik.<segment_name>.frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` |
|
| `traefik.<segment_name>.frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` |
|
| `traefik.<segment_name>.frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.tls.insecureSkipVerify=true`| Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify`|
|
| `traefik.<segment_name>.frontend.auth.forward.tls.insecureSkipVerify=true` | Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` |
|
| `traefik.<segment_name>.frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` |
|
| `traefik.<segment_name>.frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` |
|
||||||
| `traefik.<segment_name>.frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` |
|
| `traefik.<segment_name>.frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` |
|
||||||
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
||||||
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
||||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||||
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
||||||
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
||||||
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
||||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
||||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
|
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
|
||||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
|
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
|
||||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
|
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
|
||||||
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
|
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
|
||||||
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
|
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
|
||||||
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
|
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
|
||||||
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
|
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
|
||||||
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
|
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
|
||||||
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
|
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
|
||||||
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
|
| `traefik.<segment_name>.frontend.whiteList.ipStrategy=true` | Same as `traefik.frontend.whiteList.ipStrategy` |
|
||||||
|
| `traefik.<segment_name>.frontend.whiteList.ipStrategy.depth=5` | Same as `traefik.frontend.whiteList.ipStrategy.depth` |
|
||||||
|
| `traefik.<segment_name>.frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | Same as `traefik.frontend.whiteList.ipStrategy.excludedIPs` |
|
||||||
|
|
||||||
#### Custom Headers
|
#### Custom Headers
|
||||||
|
|
||||||
|
|
|
@ -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:
|
Labels can be used on task containers to override default behavior:
|
||||||
|
|
||||||
| Label | Description |
|
| Label | Description |
|
||||||
|------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|-------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `traefik.domain` | Sets the default domain for frontend rules. |
|
| `traefik.domain` | Sets the default domain for frontend rules. |
|
||||||
| `traefik.enable=false` | Disables this container in Træfik. |
|
| `traefik.enable=false` | Disables this container in Træfik. |
|
||||||
| `traefik.port=80` | Overrides the default `port` value. Overrides `NetworkBindings` from Docker Container |
|
| `traefik.port=80` | Overrides the default `port` value. Overrides `NetworkBindings` from Docker Container |
|
||||||
| `traefik.protocol=https` | Overrides the default `http` protocol |
|
| `traefik.protocol=https` | Overrides the default `http` protocol |
|
||||||
| `traefik.weight=10` | Assigns this weight to the container |
|
| `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=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.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||||
| `traefik.backend.buffering.maxResponseBodyBytes=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.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||||
| `traefik.backend.buffering.memResponseBodyBytes=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.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.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.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.interval=1s` | Defines the health check interval. (Default: 30s) |
|
||||||
| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. |
|
| `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.port=8080` | Sets a different port for the health check. |
|
||||||
| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
|
| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
|
||||||
| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||||
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
|
| `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=true` | Enables backend sticky sessions |
|
||||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie manually name for 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.<br>Must be used in conjunction with the below label to take effect. |
|
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>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.<br>Must be used in conjunction with the above 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.<br>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=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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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`.<br>Overrides `defaultEntryPoints` |
|
| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.frontend.errors.<name>.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.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
||||||
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
||||||
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
||||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||||
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||||
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.frontend.rateLimit.rateSet.<name>.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.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.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||||
| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. |
|
| `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.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.<br>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.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.<br>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. |
|
| `traefik.frontend.whiteList.ipStrategy=true` | Uses the default IPStrategy.<br>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
|
### Custom Headers
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,9 @@ Træfik can be configured with a file.
|
||||||
|
|
||||||
[frontends.frontend1.whiteList]
|
[frontends.frontend1.whiteList]
|
||||||
sourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
|
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]
|
||||||
[frontends.frontend1.routes.route0]
|
[frontends.frontend1.routes.route0]
|
||||||
|
|
|
@ -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:
|
The following general annotations are applicable on the Ingress object:
|
||||||
|
|
||||||
| Annotation | Description |
|
| Annotation | Description |
|
||||||
|---------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------|
|
|---------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `traefik.ingress.kubernetes.io/buffering: <YML>` | (3) See [buffering](/configuration/commons/#buffering) section. |
|
| `traefik.ingress.kubernetes.io/buffering: <YML>` | (3) See [buffering](/configuration/commons/#buffering) section. |
|
||||||
| `traefik.ingress.kubernetes.io/error-pages: <YML>` | (1) See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.ingress.kubernetes.io/error-pages: <YML>` | (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/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/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/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/priority: "3"` | Override the default frontend rule priority. |
|
||||||
| `traefik.ingress.kubernetes.io/rate-limit: <YML>` | (2) See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.ingress.kubernetes.io/rate-limit: <YML>` | (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-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-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-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/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/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/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/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-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/whiteList-ipstrategy=true` | Uses the default IPStrategy.<br>Can be used when there is an existing `clientIPStrategy` but you want the remote address for whitelisting. |
|
||||||
| `traefik.ingress.kubernetes.io/app-root: "/index.html"` | Redirects all requests for `/` to the defined path. (4) |
|
| `traefik.ingress.kubernetes.io/whiteList-ipstrategy-depth=5` | See [whitelist](/configuration/entrypoints/#white-listing) |
|
||||||
| `traefik.ingress.kubernetes.io/service-weights: <YML>` | Set ingress backend weights specified as percentage or decimal numbers in YAML. (5)
|
| `traefik.ingress.kubernetes.io/whiteList-ipstrategy-excludedIPs=127.0.0. 1` | See [whitelist](/configuration/entrypoints/#white-listing) |
|
||||||
| `ingress.kubernetes.io/protocol: <NAME>` | Set the protocol Traefik will use to communicate with pods.
|
| `traefik.ingress.kubernetes.io/app-root: "/index.html"` | Redirects all requests for `/` to the defined path. (4) |
|
||||||
|
| `traefik.ingress.kubernetes.io/service-weights: <YML>` | Set ingress backend weights specified as percentage or decimal numbers in YAML. (5) |
|
||||||
|
| `ingress.kubernetes.io/protocol: <NAME>` | Set the protocol Traefik will use to communicate with pods. |
|
||||||
|
|
||||||
|
|
||||||
<1> `traefik.ingress.kubernetes.io/error-pages` example:
|
<1> `traefik.ingress.kubernetes.io/error-pages` example:
|
||||||
|
|
|
@ -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.
|
The following labels can be defined on Marathon applications. They adjust the behavior for the entire application.
|
||||||
|
|
||||||
| Label | Description |
|
| Label | Description |
|
||||||
|------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|-----------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `traefik.domain` | Sets the default domain used for the frontend rules. |
|
| `traefik.domain` | Sets the default domain used for the frontend rules. |
|
||||||
| `traefik.enable=false` | Disables this container in Træfik. |
|
| `traefik.enable=false` | Disables this container in Træfik. |
|
||||||
| `traefik.port=80` | Registers this port. Useful when the container exposes multiples ports. |
|
| `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.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.protocol=https` | Overrides the default `http` protocol. |
|
||||||
| `traefik.weight=10` | Assigns this weight to the container. |
|
| `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=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.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||||
| `traefik.backend.buffering.maxResponseBodyBytes=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.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||||
| `traefik.backend.buffering.memResponseBodyBytes=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.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.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.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.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.port=8080` | Sets a different port for the health check. |
|
||||||
| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. |
|
| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. |
|
||||||
| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
|
| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
|
||||||
| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||||
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
|
| `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=true` | Enables backend sticky sessions |
|
||||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for 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.<br>Must be used in conjunction with the below label to take effect. |
|
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>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.<br>Must be used in conjunction with the above 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.<br>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=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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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`.<br>Overrides `defaultEntryPoints` |
|
| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.frontend.errors.<name>.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.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
||||||
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
||||||
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
||||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||||
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||||
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.frontend.rateLimit.rateSet.<name>.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.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.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||||
| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. |
|
| `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.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.<br>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.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.<br>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. |
|
| `traefik.frontend.whiteList.ipStrategy=true` | Uses the default IPStrategy.<br>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
|
#### Custom Headers
|
||||||
|
|
||||||
|
@ -295,48 +297,50 @@ You can define as many segments as ports exposed in an application.
|
||||||
|
|
||||||
Segment labels override the default behavior.
|
Segment labels override the default behavior.
|
||||||
|
|
||||||
| Label | Description |
|
| Label | Description |
|
||||||
|---------------------------------------------------------------------------|----------------------------------------------------------------|
|
|------------------------------------------------------------------------------ |----------------------------------------------------------------|
|
||||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||||
| `traefik.<segment_name>.portIndex=1` | Same as `traefik.portIndex` |
|
| `traefik.<segment_name>.portIndex=1` | Same as `traefik.portIndex` |
|
||||||
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
||||||
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
||||||
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
||||||
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
||||||
| `traefik.<segment_name>.frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` |
|
| `traefik.<segment_name>.frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` |
|
||||||
| `traefik.<segment_name>.frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` |
|
| `traefik.<segment_name>.frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` |
|
||||||
| `traefik.<segment_name>.frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` |
|
| `traefik.<segment_name>.frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` |
|
||||||
| `traefik.<segment_name>.frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` |
|
| `traefik.<segment_name>.frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` |
|
||||||
| `traefik.<segment_name>.frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` |
|
| `traefik.<segment_name>.frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` |
|
||||||
| `traefik.<segment_name>.frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` |
|
| `traefik.<segment_name>.frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.address=https://example.com`| Same as `traefik.frontend.auth.forward.address` |
|
| `traefik.<segment_name>.frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` |
|
| `traefik.<segment_name>.frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` |
|
| `traefik.<segment_name>.frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` |
|
| `traefik.<segment_name>.frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.tls.insecureSkipVerify=true`| Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` |
|
| `traefik.<segment_name>.frontend.auth.forward.tls.insecureSkipVerify=true` | Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` |
|
| `traefik.<segment_name>.frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` |
|
| `traefik.<segment_name>.frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` |
|
||||||
| `traefik.<segment_name>.frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` |
|
| `traefik.<segment_name>.frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` |
|
||||||
| `traefik.<segment_name>.frontend.auth.removeHeader=true` | Same as `traefik.frontend.auth.removeHeader` |
|
| `traefik.<segment_name>.frontend.auth.removeHeader=true` | Same as `traefik.frontend.auth.removeHeader` |
|
||||||
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
||||||
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
||||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||||
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
||||||
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
||||||
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
||||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
||||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
|
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
|
||||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
|
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
|
||||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
|
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
|
||||||
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
|
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
|
||||||
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
|
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
|
||||||
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
|
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
|
||||||
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
|
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
|
||||||
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
|
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
|
||||||
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
|
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
|
||||||
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
|
| `traefik.<segment_name>.frontend.whiteList.ipStrategy=true` | Same as `traefik.frontend.whiteList.ipStrategy` |
|
||||||
|
| `traefik.<segment_name>.frontend.whiteList.ipStrategy.depth=5` | Same as `traefik.frontend.whiteList.ipStrategy.depth` |
|
||||||
|
| `traefik.<segment_name>.frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | Same as `traefik.frontend.whiteList.ipStrategy.excludedIPs` |
|
||||||
|
|
||||||
#### Custom Headers
|
#### Custom Headers
|
||||||
|
|
||||||
|
|
|
@ -106,67 +106,69 @@ domain = "mesos.localhost"
|
||||||
|
|
||||||
The following labels can be defined on Mesos tasks. They adjust the behavior for the entire application.
|
The following labels can be defined on Mesos tasks. They adjust the behavior for the entire application.
|
||||||
|
|
||||||
| Label | Description |
|
| Label | Description |
|
||||||
|------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|-----------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `traefik.domain` | Sets the default domain for the frontend rules. |
|
| `traefik.domain` | Sets the default domain for the frontend rules. |
|
||||||
| `traefik.enable=false` | Disables this container in Træfik. |
|
| `traefik.enable=false` | Disables this container in Træfik. |
|
||||||
| `traefik.port=80` | Registers this port. Useful when the application exposes multiple ports. |
|
| `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.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.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.protocol=https` | Overrides the default `http` protocol |
|
||||||
| `traefik.weight=10` | Assigns this weight to the container |
|
| `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=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.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||||
| `traefik.backend.buffering.maxResponseBodyBytes=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.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||||
| `traefik.backend.buffering.memResponseBodyBytes=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.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.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.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.interval=1s` | Defines the health check interval. (Default: 30s) |
|
||||||
| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. |
|
| `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.port=8080` | Sets a different port for the health check. |
|
||||||
| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
|
| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
|
||||||
| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||||
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
|
| `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=true` | Enables backend sticky sessions |
|
||||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie manually name for 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.<br>Must be used in conjunction with the below label to take effect. |
|
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>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.<br>Must be used in conjunction with the above 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.<br>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=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.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.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.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.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.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.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.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.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.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.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.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.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.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.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.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`.<br>Overrides `defaultEntryPoints` |
|
| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.frontend.errors.<name>.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.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
||||||
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
||||||
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
||||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||||
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||||
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.frontend.rateLimit.rateSet.<name>.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.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.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||||
| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. |
|
| `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.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.<br>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.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.<br>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. |
|
| `traefik.frontend.whiteList.ipStrategy=true` | Uses the default IPStrategy.<br>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
|
### 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.
|
Segment labels override the default behavior.
|
||||||
|
|
||||||
| Label | Description |
|
| Label | Description |
|
||||||
|---------------------------------------------------------------------------|----------------------------------------------------------------|
|
|------------------------------------------------------------------------------|----------------------------------------------------------------|
|
||||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||||
| `traefik.<segment_name>.portIndex=1` | Same as `traefik.portIndex` |
|
| `traefik.<segment_name>.portIndex=1` | Same as `traefik.portIndex` |
|
||||||
| `traefik.<segment_name>.portName=web` | Same as `traefik.portName` |
|
| `traefik.<segment_name>.portName=web` | Same as `traefik.portName` |
|
||||||
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
||||||
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
||||||
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
||||||
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
||||||
| `traefik.<segment_name>.frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` |
|
| `traefik.<segment_name>.frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` |
|
||||||
| `traefik.<segment_name>.frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` |
|
| `traefik.<segment_name>.frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` |
|
||||||
| `traefik.<segment_name>.frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` |
|
| `traefik.<segment_name>.frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` |
|
||||||
| `traefik.<segment_name>.frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` |
|
| `traefik.<segment_name>.frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` |
|
||||||
| `traefik.<segment_name>.frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` |
|
| `traefik.<segment_name>.frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` |
|
||||||
| `traefik.<segment_name>.frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` |
|
| `traefik.<segment_name>.frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.address=https://example.com`| Same as `traefik.frontend.auth.forward.address` |
|
| `traefik.<segment_name>.frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` |
|
| `traefik.<segment_name>.frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` |
|
| `traefik.<segment_name>.frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` |
|
| `traefik.<segment_name>.frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.tls.insecureSkipVerify=true`| Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` |
|
| `traefik.<segment_name>.frontend.auth.forward.tls.insecureSkipVerify=true` | Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` |
|
| `traefik.<segment_name>.frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` |
|
| `traefik.<segment_name>.frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` |
|
||||||
| `traefik.<segment_name>.frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` |
|
| `traefik.<segment_name>.frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` |
|
||||||
| `traefik.<segment_name>.frontend.auth.removeHeader=true` | Same as `traefik.frontend.auth.removeHeader` |
|
| `traefik.<segment_name>.frontend.auth.removeHeader=true` | Same as `traefik.frontend.auth.removeHeader` |
|
||||||
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
||||||
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
||||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||||
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
||||||
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
||||||
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
||||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
||||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
|
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
|
||||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
|
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
|
||||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
|
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
|
||||||
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
|
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
|
||||||
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
|
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
|
||||||
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
|
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
|
||||||
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
|
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
|
||||||
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
|
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
|
||||||
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
|
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
|
||||||
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
|
| `traefik.<segment_name>.frontend.whiteList.ipStrategy=true` | Same as `traefik.frontend.whiteList.ipStrategy` |
|
||||||
|
| `traefik.<segment_name>.frontend.whiteList.ipStrategy.depth=5` | Same as `traefik.frontend.whiteList.ipStrategy.depth` |
|
||||||
|
| `traefik.<segment_name>.frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | Same as `traefik.frontend.whiteList.ipStrategy.excludedIPs` |
|
||||||
|
|
||||||
#### Custom Headers
|
#### Custom Headers
|
||||||
|
|
||||||
|
|
|
@ -138,64 +138,66 @@ secretKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||||
|
|
||||||
Labels can be used on task containers to override default behavior:
|
Labels can be used on task containers to override default behavior:
|
||||||
|
|
||||||
| Label | Description |
|
| Label | Description |
|
||||||
|------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|-----------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `traefik.domain` | Sets the default domain for the frontend rules. |
|
| `traefik.domain` | Sets the default domain for the frontend rules. |
|
||||||
| `traefik.enable=false` | Disables this container in Træfik. |
|
| `traefik.enable=false` | Disables this container in Træfik. |
|
||||||
| `traefik.port=80` | Registers this port. Useful when the container exposes multiple ports. |
|
| `traefik.port=80` | Registers this port. Useful when the container exposes multiple ports. |
|
||||||
| `traefik.protocol=https` | Overrides the default `http` protocol. |
|
| `traefik.protocol=https` | Overrides the default `http` protocol. |
|
||||||
| `traefik.weight=10` | Assigns this weight to the container. |
|
| `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=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.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||||
| `traefik.backend.buffering.maxResponseBodyBytes=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.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||||
| `traefik.backend.buffering.memResponseBodyBytes=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.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.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.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.interval=1s` | Defines the health check interval. |
|
||||||
| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. |
|
| `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.scheme=http` | Overrides the server URL scheme. |
|
||||||
| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
|
| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
|
||||||
| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||||
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
|
| `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=true` | Enables backend sticky sessions |
|
||||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for 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.<br>Must be used in conjunction with the below label to take effect. |
|
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>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.<br>Must be used in conjunction with the above 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.<br>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=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.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.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.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.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.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.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.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.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.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.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.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.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.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.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`.<br>Overrides `defaultEntryPoints` |
|
| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.frontend.errors.<name>.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.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
||||||
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
||||||
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
||||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||||
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||||
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.frontend.rateLimit.rateSet.<name>.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.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.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||||
| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. |
|
| `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.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.<br>An unset or empty list allows all Source-IPs to access.<br>If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access.<br>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. |
|
| `traefik.frontend.whiteList.ipStrategy=true` | Uses the default IPStrategy.<br>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
|
#### Custom Headers
|
||||||
|
|
||||||
|
@ -237,46 +239,48 @@ You can define as many segments as ports exposed in a container.
|
||||||
|
|
||||||
Segment labels override the default behavior.
|
Segment labels override the default behavior.
|
||||||
|
|
||||||
| Label | Description |
|
| Label | Description |
|
||||||
|---------------------------------------------------------------------------|---------------------------------------------------------------|
|
|------------------------------------------------------------------------------|----------------------------------------------------------------|
|
||||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||||
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
||||||
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
||||||
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
||||||
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
||||||
| `traefik.<segment_name>.frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` |
|
| `traefik.<segment_name>.frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` |
|
||||||
| `traefik.<segment_name>.frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` |
|
| `traefik.<segment_name>.frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` |
|
||||||
| `traefik.<segment_name>.frontend.auth.basic.usersfile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersfile` |
|
| `traefik.<segment_name>.frontend.auth.basic.usersfile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersfile` |
|
||||||
| `traefik.<segment_name>.frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` |
|
| `traefik.<segment_name>.frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` |
|
||||||
| `traefik.<segment_name>.frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` |
|
| `traefik.<segment_name>.frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` |
|
||||||
| `traefik.<segment_name>.frontend.auth.digest.usersfile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersfile` |
|
| `traefik.<segment_name>.frontend.auth.digest.usersfile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersfile` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.address=https://example.com`| Same as `traefik.frontend.auth.forward.address` |
|
| `traefik.<segment_name>.frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` |
|
| `traefik.<segment_name>.frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` |
|
| `traefik.<segment_name>.frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` |
|
| `traefik.<segment_name>.frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.tls.insecureSkipVerify=true`| Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify`|
|
| `traefik.<segment_name>.frontend.auth.forward.tls.insecureSkipVerify=true` | Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` |
|
| `traefik.<segment_name>.frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` |
|
||||||
| `traefik.<segment_name>.frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` |
|
| `traefik.<segment_name>.frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` |
|
||||||
| `traefik.<segment_name>.frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` |
|
| `traefik.<segment_name>.frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` |
|
||||||
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
||||||
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
||||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||||
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
||||||
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
||||||
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
||||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
||||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
|
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
|
||||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
|
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
|
||||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
|
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
|
||||||
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
|
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
|
||||||
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
|
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
|
||||||
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
|
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
|
||||||
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
|
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
|
||||||
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
|
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
|
||||||
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
|
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
|
||||||
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
|
| `traefik.<segment_name>.frontend.whiteList.ipStrategy=true` | Same as `traefik.frontend.whiteList.ipStrategy` |
|
||||||
|
| `traefik.<segment_name>.frontend.whiteList.ipStrategy.depth=5` | Same as `traefik.frontend.whiteList.ipStrategy.depth` |
|
||||||
|
| `traefik.<segment_name>.frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | Same as `traefik.frontend.whiteList.ipStrategy.excludedIPs` |
|
||||||
|
|
||||||
#### Custom Headers
|
#### Custom Headers
|
||||||
|
|
||||||
|
|
|
@ -94,36 +94,38 @@ curl -X PUT \
|
||||||
|
|
||||||
Labels, set through extensions or the property manager, can be used on services to override default behavior.
|
Labels, set through extensions or the property manager, can be used on services to override default behavior.
|
||||||
|
|
||||||
| Label | Description |
|
| Label | Description |
|
||||||
|------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|-----------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `traefik.enable=false` | Disable this container in Træfik |
|
| `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.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.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.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.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.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.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.interval=1s` | Define the health check interval. |
|
||||||
| `traefik.backend.healthcheck.hostname=foobar.com` | Define the health check hostname. |
|
| `traefik.backend.healthcheck.hostname=foobar.com` | Define the health check hostname. |
|
||||||
| `traefik.backend.healthcheck.headers=EXPR` | Define the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
| `traefik.backend.healthcheck.headers=EXPR` | Define the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||||
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
|
| `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=true` | Enable backend sticky sessions |
|
||||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for 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.<br>Must be used in conjunction with the below label to take effect. |
|
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.<br>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.<br>Must be used in conjunction with the above 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.<br>Must be used in conjunction with the above label to take effect. |
|
||||||
| `traefik.backend.weight=10` | Assign this weight to the container |
|
| `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.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`.<br>Overrides `defaultEntryPoints` |
|
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||||
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
|
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
|
||||||
| `traefik.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. |
|
| `traefik.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. |
|
||||||
| `traefik.frontend.priority=10` | Override default frontend priority |
|
| `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.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.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||||
| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
| `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.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.<br>An unset or empty list allows all Source-IPs to access.<br>If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
| `traefik.frontend.whiteList.sourceRange=RANGE` | List of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access.<br>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.frontend.whiteList.ipStrategy=true` | Uses the default IPStrategy.<br>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
|
### Custom Headers
|
||||||
|
|
||||||
|
|
|
@ -14,11 +14,17 @@ defaultEntryPoints = ["http", "https"]
|
||||||
[entryPoints.http]
|
[entryPoints.http]
|
||||||
address = ":80"
|
address = ":80"
|
||||||
[entryPoints.http.compress]
|
[entryPoints.http.compress]
|
||||||
|
|
||||||
|
[entryPoints.http.clientIPStrategy]
|
||||||
|
depth = 5
|
||||||
|
excludedIPs = ["127.0.0.1/32", "192.168.1.7"]
|
||||||
|
|
||||||
[entryPoints.http.whitelist]
|
[entryPoints.http.whitelist]
|
||||||
sourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
|
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]
|
[entryPoints.http.tls]
|
||||||
minVersion = "VersionTLS12"
|
minVersion = "VersionTLS12"
|
||||||
cipherSuites = [
|
cipherSuites = [
|
||||||
|
@ -75,6 +81,7 @@ defaultEntryPoints = ["http", "https"]
|
||||||
|
|
||||||
[entryPoints.http.forwardedHeaders]
|
[entryPoints.http.forwardedHeaders]
|
||||||
trustedIPs = ["10.10.10.1", "10.10.10.2"]
|
trustedIPs = ["10.10.10.1", "10.10.10.2"]
|
||||||
|
insecure = false
|
||||||
|
|
||||||
[entryPoints.https]
|
[entryPoints.https]
|
||||||
# ...
|
# ...
|
||||||
|
@ -129,7 +136,8 @@ Redirect.Replacement:http://mydomain/$1
|
||||||
Redirect.Permanent:true
|
Redirect.Permanent:true
|
||||||
Compress:true
|
Compress:true
|
||||||
WhiteList.SourceRange: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.IPStrategy.depth:3
|
||||||
|
WhiteList.IPStrategy.ExcludedIPs:10.0.0.3/24,20.0.0.3/24
|
||||||
ProxyProtocol.TrustedIPs:192.168.0.1
|
ProxyProtocol.TrustedIPs:192.168.0.1
|
||||||
ProxyProtocol.Insecure:true
|
ProxyProtocol.Insecure:true
|
||||||
ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24
|
ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24
|
||||||
|
@ -464,7 +472,9 @@ Responses are compressed when:
|
||||||
|
|
||||||
## White Listing
|
## 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
|
```toml
|
||||||
[entryPoints]
|
[entryPoints]
|
||||||
|
@ -473,9 +483,97 @@ To enable IP white listing at the entry point level.
|
||||||
|
|
||||||
[entryPoints.http.whiteList]
|
[entryPoints.http.whiteList]
|
||||||
sourceRange = ["127.0.0.1/32", "192.168.1.7"]
|
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
|
## ProxyProtocol
|
||||||
|
|
||||||
To enable [ProxyProtocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) support.
|
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: []
|
# Default: []
|
||||||
#
|
#
|
||||||
trustedIPs = ["127.0.0.1/32", "192.168.1.7"]
|
trustedIPs = ["127.0.0.1/32", "192.168.1.7"]
|
||||||
|
|
||||||
|
# Insecure mode
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
# insecure = true
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -3,6 +3,7 @@ package integration
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
@ -253,7 +254,6 @@ func (s *SimpleSuite) TestNoAuthOnPing(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SimpleSuite) TestDefaultEntrypointHTTP(c *check.C) {
|
func (s *SimpleSuite) TestDefaultEntrypointHTTP(c *check.C) {
|
||||||
|
|
||||||
s.createComposeProject(c, "base")
|
s.createComposeProject(c, "base")
|
||||||
s.composeProject.Start(c)
|
s.composeProject.Start(c)
|
||||||
|
|
||||||
|
@ -272,7 +272,6 @@ func (s *SimpleSuite) TestDefaultEntrypointHTTP(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SimpleSuite) TestWithUnexistingEntrypoint(c *check.C) {
|
func (s *SimpleSuite) TestWithUnexistingEntrypoint(c *check.C) {
|
||||||
|
|
||||||
s.createComposeProject(c, "base")
|
s.createComposeProject(c, "base")
|
||||||
s.composeProject.Start(c)
|
s.composeProject.Start(c)
|
||||||
|
|
||||||
|
@ -291,7 +290,6 @@ func (s *SimpleSuite) TestWithUnexistingEntrypoint(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SimpleSuite) TestMetricsPrometheusDefaultEntrypoint(c *check.C) {
|
func (s *SimpleSuite) TestMetricsPrometheusDefaultEntrypoint(c *check.C) {
|
||||||
|
|
||||||
s.createComposeProject(c, "base")
|
s.createComposeProject(c, "base")
|
||||||
s.composeProject.Start(c)
|
s.composeProject.Start(c)
|
||||||
|
|
||||||
|
@ -313,15 +311,16 @@ func (s *SimpleSuite) TestMetricsPrometheusDefaultEntrypoint(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SimpleSuite) TestMultipleProviderSameBackendName(c *check.C) {
|
func (s *SimpleSuite) TestMultipleProviderSameBackendName(c *check.C) {
|
||||||
|
|
||||||
s.createComposeProject(c, "base")
|
s.createComposeProject(c, "base")
|
||||||
s.composeProject.Start(c)
|
s.composeProject.Start(c)
|
||||||
|
|
||||||
ipWhoami01 := s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress
|
ipWhoami01 := s.composeProject.Container(c, "whoami1").NetworkSettings.IPAddress
|
||||||
ipWhoami02 := s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress
|
ipWhoami02 := s.composeProject.Container(c, "whoami2").NetworkSettings.IPAddress
|
||||||
file := s.adaptFile(c, "fixtures/multiple_provider.toml", struct{ IP string }{
|
file := s.adaptFile(c, "fixtures/multiple_provider.toml", struct{ IP string }{
|
||||||
IP: ipWhoami02,
|
IP: ipWhoami02,
|
||||||
})
|
})
|
||||||
defer os.Remove(file)
|
defer os.Remove(file)
|
||||||
|
|
||||||
cmd, output := s.traefikCmd(withConfigFile(file))
|
cmd, output := s.traefikCmd(withConfigFile(file))
|
||||||
defer output(c)
|
defer output(c)
|
||||||
|
|
||||||
|
@ -339,3 +338,80 @@ func (s *SimpleSuite) TestMultipleProviderSameBackendName(c *check.C) {
|
||||||
c.Assert(err, checker.IsNil)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -17,7 +17,8 @@ checkNewVersion = false
|
||||||
entryPoint = "http"
|
entryPoint = "http"
|
||||||
[entryPoints.httpWhitelistReject]
|
[entryPoints.httpWhitelistReject]
|
||||||
address = ":8002"
|
address = ":8002"
|
||||||
whiteListSourceRange = ["8.8.8.8/32"]
|
[entryPoints.httpWhitelistReject.whiteList]
|
||||||
|
sourceRange = ["8.8.8.8/32"]
|
||||||
[entryPoints.httpAuth]
|
[entryPoints.httpAuth]
|
||||||
address = ":8004"
|
address = ":8004"
|
||||||
[entryPoints.httpAuth.auth.basic]
|
[entryPoints.httpAuth.auth.basic]
|
||||||
|
|
13
integration/fixtures/simple_whitelist.toml
Normal file
13
integration/fixtures/simple_whitelist.toml
Normal file
|
@ -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]
|
|
@ -102,6 +102,6 @@ frontendWhitelist:
|
||||||
- traefik.enable=true
|
- traefik.enable=true
|
||||||
- traefik.port=80
|
- traefik.port=80
|
||||||
- traefik.backend=backend3
|
- 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.entryPoints=http
|
||||||
- traefik.frontend.rule=Host:frontend.whitelist.docker.local
|
- traefik.frontend.rule=Host:frontend.whitelist.docker.local
|
||||||
|
|
34
integration/resources/compose/whitelist.yml
Normal file
34
integration/resources/compose/whitelist.yml
Normal file
|
@ -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
|
99
ip/checker.go
Normal file
99
ip/checker.go
Normal file
|
@ -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
|
||||||
|
}
|
326
ip/checker_test.go
Normal file
326
ip/checker_test.go
Normal file
|
@ -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 <nil>: invalid CIDR address: ",
|
||||||
|
}, {
|
||||||
|
desc: "trusted IPs containing only an empty string",
|
||||||
|
trustedIPs: []string{
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
expectedAuthorizedIPs: nil,
|
||||||
|
errMessage: "parsing CIDR trusted IPs <nil>: invalid CIDR address: ",
|
||||||
|
}, {
|
||||||
|
desc: "trusted IPs containing an invalid string",
|
||||||
|
trustedIPs: []string{
|
||||||
|
"foo",
|
||||||
|
},
|
||||||
|
expectedAuthorizedIPs: nil,
|
||||||
|
errMessage: "parsing CIDR trusted IPs <nil>: 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)
|
||||||
|
}
|
||||||
|
}
|
62
ip/strategy.go
Normal file
62
ip/strategy.go
Normal file
|
@ -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 ""
|
||||||
|
}
|
125
ip/strategy_test.go
Normal file
125
ip/strategy_test.go
Normal file
|
@ -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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
52
middlewares/forwardedheaders/forwarded_header.go
Normal file
52
middlewares/forwardedheaders/forwarded_header.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
128
middlewares/forwardedheaders/forwarded_header_test.go
Normal file
128
middlewares/forwardedheaders/forwarded_header_test.go
Normal file
|
@ -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))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,9 +4,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/containous/traefik/ip"
|
||||||
"github.com/containous/traefik/log"
|
"github.com/containous/traefik/log"
|
||||||
"github.com/containous/traefik/middlewares/tracing"
|
"github.com/containous/traefik/middlewares/tracing"
|
||||||
"github.com/containous/traefik/whitelist"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/negroni"
|
"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
|
// IPWhiteLister is a middleware that provides Checks of the Requesting IP against a set of Whitelists
|
||||||
type IPWhiteLister struct {
|
type IPWhiteLister struct {
|
||||||
handler negroni.Handler
|
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
|
// 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 {
|
if len(whiteList) == 0 {
|
||||||
return nil, errors.New("no white list provided")
|
return nil, errors.New("no white list provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
whiteLister := IPWhiteLister{}
|
checker, err := ip.NewChecker(whiteList)
|
||||||
|
|
||||||
ip, err := whitelist.NewIP(whiteList, false, useXForwardedFor)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("parsing CIDR whitelist %s: %v", whiteList, err)
|
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)
|
whiteLister.handler = negroni.HandlerFunc(whiteLister.handle)
|
||||||
log.Debugf("configured IP white list: %s", whiteList)
|
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) {
|
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 {
|
if err != nil {
|
||||||
tracing.SetErrorAndDebugLog(r, "request %+v - rejecting: %v", r, err)
|
tracing.SetErrorAndDebugLog(r, "request %+v - rejecting: %v", r, err)
|
||||||
reject(w)
|
reject(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
log.Debugf("Accept %s: %+v", wl.strategy.GetIP(r), r)
|
||||||
tracing.SetErrorAndDebugLog(r, "request %+v matched white list %s - passing", r, wl.whiteLister)
|
tracing.SetErrorAndDebugLog(r, "request %+v matched white list %s - passing", r, wl.whiteLister)
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,29 +5,26 @@ import (
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/containous/traefik/whitelist"
|
"github.com/containous/traefik/ip"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewIPWhiteLister(t *testing.T) {
|
func TestNewIPWhiteLister(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
whiteList []string
|
whiteList []string
|
||||||
useXForwardedFor bool
|
expectedError string
|
||||||
expectedError string
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "invalid IP",
|
desc: "invalid IP",
|
||||||
whiteList: []string{"foo"},
|
whiteList: []string{"foo"},
|
||||||
useXForwardedFor: false,
|
expectedError: "parsing CIDR whitelist [foo]: parsing CIDR trusted IPs <nil>: invalid CIDR address: foo",
|
||||||
expectedError: "parsing CIDR whitelist [foo]: parsing CIDR white list <nil>: invalid CIDR address: foo",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "valid IP",
|
desc: "valid IP",
|
||||||
whiteList: []string{"10.10.10.10"},
|
whiteList: []string{"10.10.10.10"},
|
||||||
useXForwardedFor: false,
|
expectedError: "",
|
||||||
expectedError: "",
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +33,7 @@ func TestNewIPWhiteLister(t *testing.T) {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
whiteLister, err := NewIPWhiteLister(test.whiteList, test.useXForwardedFor)
|
whiteLister, err := NewIPWhiteLister(test.whiteList, &ip.RemoteAddrStrategy{})
|
||||||
|
|
||||||
if len(test.expectedError) > 0 {
|
if len(test.expectedError) > 0 {
|
||||||
assert.EqualError(t, err, test.expectedError)
|
assert.EqualError(t, err, test.expectedError)
|
||||||
|
@ -50,57 +47,22 @@ func TestNewIPWhiteLister(t *testing.T) {
|
||||||
|
|
||||||
func TestIPWhiteLister_ServeHTTP(t *testing.T) {
|
func TestIPWhiteLister_ServeHTTP(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
whiteList []string
|
whiteList []string
|
||||||
useXForwardedFor bool
|
remoteAddr string
|
||||||
remoteAddr string
|
expected int
|
||||||
xForwardedFor []string
|
|
||||||
expected int
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "authorized with remote address",
|
desc: "authorized with remote address",
|
||||||
whiteList: []string{"20.20.20.20"},
|
whiteList: []string{"20.20.20.20"},
|
||||||
useXForwardedFor: false,
|
remoteAddr: "20.20.20.20:1234",
|
||||||
remoteAddr: "20.20.20.20:1234",
|
expected: 200,
|
||||||
xForwardedFor: nil,
|
|
||||||
expected: 200,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "non authorized with remote address",
|
desc: "non authorized with remote address",
|
||||||
whiteList: []string{"20.20.20.20"},
|
whiteList: []string{"20.20.20.20"},
|
||||||
useXForwardedFor: false,
|
remoteAddr: "20.20.20.21:1234",
|
||||||
remoteAddr: "20.20.20.21:1234",
|
expected: 403,
|
||||||
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,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +71,7 @@ func TestIPWhiteLister_ServeHTTP(t *testing.T) {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
whiteLister, err := NewIPWhiteLister(test.whiteList, test.useXForwardedFor)
|
whiteLister, err := NewIPWhiteLister(test.whiteList, &ip.RemoteAddrStrategy{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
@ -120,12 +82,6 @@ func TestIPWhiteLister_ServeHTTP(t *testing.T) {
|
||||||
req.RemoteAddr = test.remoteAddr
|
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) {})
|
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||||
|
|
||||||
whiteLister.ServeHTTP(recorder, req, next)
|
whiteLister.ServeHTTP(recorder, req, next)
|
||||||
|
|
|
@ -375,7 +375,8 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
||||||
label.TraefikFrontendRedirectPermanent + "=true",
|
label.TraefikFrontendRedirectPermanent + "=true",
|
||||||
label.TraefikFrontendRule + "=Host:traefik.io",
|
label.TraefikFrontendRule + "=Host:traefik.io",
|
||||||
label.TraefikFrontendWhiteListSourceRange + "=10.10.10.10",
|
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.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",
|
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{
|
SourceRange: []string{
|
||||||
"10.10.10.10",
|
"10.10.10.10",
|
||||||
},
|
},
|
||||||
UseXForwardedFor: true,
|
IPStrategy: &types.IPStrategy{
|
||||||
|
Depth: 5,
|
||||||
|
ExcludedIPs: []string{"10.10.10.10", "10.10.10.11"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Headers: &types.Headers{
|
Headers: &types.Headers{
|
||||||
CustomRequestHeaders: map[string]string{
|
CustomRequestHeaders: map[string]string{
|
||||||
|
|
|
@ -403,17 +403,18 @@ func TestDockerBuildConfiguration(t *testing.T) {
|
||||||
label.TraefikFrontendAuthForwardTLSInsecureSkipVerify: "true",
|
label.TraefikFrontendAuthForwardTLSInsecureSkipVerify: "true",
|
||||||
label.TraefikFrontendAuthHeaderField: "X-WebAuth-User",
|
label.TraefikFrontendAuthHeaderField: "X-WebAuth-User",
|
||||||
|
|
||||||
label.TraefikFrontendEntryPoints: "http,https",
|
label.TraefikFrontendEntryPoints: "http,https",
|
||||||
label.TraefikFrontendPassHostHeader: "true",
|
label.TraefikFrontendPassHostHeader: "true",
|
||||||
label.TraefikFrontendPassTLSCert: "true",
|
label.TraefikFrontendPassTLSCert: "true",
|
||||||
label.TraefikFrontendPriority: "666",
|
label.TraefikFrontendPriority: "666",
|
||||||
label.TraefikFrontendRedirectEntryPoint: "https",
|
label.TraefikFrontendRedirectEntryPoint: "https",
|
||||||
label.TraefikFrontendRedirectRegex: "nope",
|
label.TraefikFrontendRedirectRegex: "nope",
|
||||||
label.TraefikFrontendRedirectReplacement: "nope",
|
label.TraefikFrontendRedirectReplacement: "nope",
|
||||||
label.TraefikFrontendRedirectPermanent: "true",
|
label.TraefikFrontendRedirectPermanent: "true",
|
||||||
label.TraefikFrontendRule: "Host:traefik.io",
|
label.TraefikFrontendRule: "Host:traefik.io",
|
||||||
label.TraefikFrontendWhiteListSourceRange: "10.10.10.10",
|
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.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",
|
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{
|
WhiteList: &types.WhiteList{
|
||||||
SourceRange: []string{"10.10.10.10"},
|
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{
|
Headers: &types.Headers{
|
||||||
CustomRequestHeaders: map[string]string{
|
CustomRequestHeaders: map[string]string{
|
||||||
|
|
|
@ -347,17 +347,18 @@ func TestSwarmBuildConfiguration(t *testing.T) {
|
||||||
label.TraefikFrontendAuthForwardTLSInsecureSkipVerify: "true",
|
label.TraefikFrontendAuthForwardTLSInsecureSkipVerify: "true",
|
||||||
label.TraefikFrontendAuthHeaderField: "X-WebAuth-User",
|
label.TraefikFrontendAuthHeaderField: "X-WebAuth-User",
|
||||||
|
|
||||||
label.TraefikFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
label.TraefikFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
label.TraefikFrontendEntryPoints: "http,https",
|
label.TraefikFrontendEntryPoints: "http,https",
|
||||||
label.TraefikFrontendPassHostHeader: "true",
|
label.TraefikFrontendPassHostHeader: "true",
|
||||||
label.TraefikFrontendPassTLSCert: "true",
|
label.TraefikFrontendPassTLSCert: "true",
|
||||||
label.TraefikFrontendPriority: "666",
|
label.TraefikFrontendPriority: "666",
|
||||||
label.TraefikFrontendRedirectEntryPoint: "https",
|
label.TraefikFrontendRedirectEntryPoint: "https",
|
||||||
label.TraefikFrontendRedirectRegex: "nope",
|
label.TraefikFrontendRedirectRegex: "nope",
|
||||||
label.TraefikFrontendRedirectReplacement: "nope",
|
label.TraefikFrontendRedirectReplacement: "nope",
|
||||||
label.TraefikFrontendRule: "Host:traefik.io",
|
label.TraefikFrontendRule: "Host:traefik.io",
|
||||||
label.TraefikFrontendWhiteListSourceRange: "10.10.10.10",
|
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.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",
|
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{
|
WhiteList: &types.WhiteList{
|
||||||
SourceRange: []string{"10.10.10.10"},
|
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{
|
Headers: &types.Headers{
|
||||||
CustomRequestHeaders: map[string]string{
|
CustomRequestHeaders: map[string]string{
|
||||||
|
|
|
@ -301,17 +301,18 @@ func TestSegmentBuildConfiguration(t *testing.T) {
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendAuthForwardTLSInsecureSkipVerify: "true",
|
label.Prefix + "sauternes." + label.SuffixFrontendAuthForwardTLSInsecureSkipVerify: "true",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendAuthHeaderField: "X-WebAuth-User",
|
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.SuffixFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendEntryPoints: "http,https",
|
label.Prefix + "sauternes." + label.SuffixFrontendEntryPoints: "http,https",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendPassHostHeader: "true",
|
label.Prefix + "sauternes." + label.SuffixFrontendPassHostHeader: "true",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSCert: "true",
|
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSCert: "true",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendPriority: "666",
|
label.Prefix + "sauternes." + label.SuffixFrontendPriority: "666",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendRedirectEntryPoint: "https",
|
label.Prefix + "sauternes." + label.SuffixFrontendRedirectEntryPoint: "https",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendRedirectRegex: "nope",
|
label.Prefix + "sauternes." + label.SuffixFrontendRedirectRegex: "nope",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendRedirectReplacement: "nope",
|
label.Prefix + "sauternes." + label.SuffixFrontendRedirectReplacement: "nope",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendRedirectPermanent: "true",
|
label.Prefix + "sauternes." + label.SuffixFrontendRedirectPermanent: "true",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendWhiteListSourceRange: "10.10.10.10",
|
label.Prefix + "sauternes." + label.SuffixFrontendWhiteListSourceRange: "10.10.10.10",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendWhiteListUseXForwardedFor: "true",
|
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.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",
|
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{
|
WhiteList: &types.WhiteList{
|
||||||
SourceRange: []string{"10.10.10.10"},
|
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{
|
Headers: &types.Headers{
|
||||||
CustomRequestHeaders: map[string]string{
|
CustomRequestHeaders: map[string]string{
|
||||||
|
|
|
@ -368,17 +368,18 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
label.TraefikFrontendAuthForwardTLSInsecureSkipVerify: aws.String("true"),
|
label.TraefikFrontendAuthForwardTLSInsecureSkipVerify: aws.String("true"),
|
||||||
label.TraefikFrontendAuthHeaderField: aws.String("X-WebAuth-User"),
|
label.TraefikFrontendAuthHeaderField: aws.String("X-WebAuth-User"),
|
||||||
|
|
||||||
label.TraefikFrontendEntryPoints: aws.String("http,https"),
|
label.TraefikFrontendEntryPoints: aws.String("http,https"),
|
||||||
label.TraefikFrontendPassHostHeader: aws.String("true"),
|
label.TraefikFrontendPassHostHeader: aws.String("true"),
|
||||||
label.TraefikFrontendPassTLSCert: aws.String("true"),
|
label.TraefikFrontendPassTLSCert: aws.String("true"),
|
||||||
label.TraefikFrontendPriority: aws.String("666"),
|
label.TraefikFrontendPriority: aws.String("666"),
|
||||||
label.TraefikFrontendRedirectEntryPoint: aws.String("https"),
|
label.TraefikFrontendRedirectEntryPoint: aws.String("https"),
|
||||||
label.TraefikFrontendRedirectRegex: aws.String("nope"),
|
label.TraefikFrontendRedirectRegex: aws.String("nope"),
|
||||||
label.TraefikFrontendRedirectReplacement: aws.String("nope"),
|
label.TraefikFrontendRedirectReplacement: aws.String("nope"),
|
||||||
label.TraefikFrontendRedirectPermanent: aws.String("true"),
|
label.TraefikFrontendRedirectPermanent: aws.String("true"),
|
||||||
label.TraefikFrontendRule: aws.String("Host:traefik.io"),
|
label.TraefikFrontendRule: aws.String("Host:traefik.io"),
|
||||||
label.TraefikFrontendWhiteListSourceRange: aws.String("10.10.10.10"),
|
label.TraefikFrontendWhiteListSourceRange: aws.String("10.10.10.10"),
|
||||||
label.TraefikFrontendWhiteListUseXForwardedFor: aws.String("true"),
|
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.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"),
|
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{
|
WhiteList: &types.WhiteList{
|
||||||
SourceRange: []string{"10.10.10.10"},
|
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{
|
Headers: &types.Headers{
|
||||||
CustomRequestHeaders: map[string]string{
|
CustomRequestHeaders: map[string]string{
|
||||||
|
@ -610,18 +614,17 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
label.TraefikBackendBufferingMemRequestBodyBytes: aws.String("2097152"),
|
label.TraefikBackendBufferingMemRequestBodyBytes: aws.String("2097152"),
|
||||||
label.TraefikBackendBufferingRetryExpression: aws.String("IsNetworkError() && Attempts() <= 2"),
|
label.TraefikBackendBufferingRetryExpression: aws.String("IsNetworkError() && Attempts() <= 2"),
|
||||||
|
|
||||||
label.TraefikFrontendAuthBasicUsers: aws.String("test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
label.TraefikFrontendAuthBasicUsers: aws.String("test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
||||||
label.TraefikFrontendEntryPoints: aws.String("http,https"),
|
label.TraefikFrontendEntryPoints: aws.String("http,https"),
|
||||||
label.TraefikFrontendPassHostHeader: aws.String("true"),
|
label.TraefikFrontendPassHostHeader: aws.String("true"),
|
||||||
label.TraefikFrontendPassTLSCert: aws.String("true"),
|
label.TraefikFrontendPassTLSCert: aws.String("true"),
|
||||||
label.TraefikFrontendPriority: aws.String("666"),
|
label.TraefikFrontendPriority: aws.String("666"),
|
||||||
label.TraefikFrontendRedirectEntryPoint: aws.String("https"),
|
label.TraefikFrontendRedirectEntryPoint: aws.String("https"),
|
||||||
label.TraefikFrontendRedirectRegex: aws.String("nope"),
|
label.TraefikFrontendRedirectRegex: aws.String("nope"),
|
||||||
label.TraefikFrontendRedirectReplacement: aws.String("nope"),
|
label.TraefikFrontendRedirectReplacement: aws.String("nope"),
|
||||||
label.TraefikFrontendRedirectPermanent: aws.String("true"),
|
label.TraefikFrontendRedirectPermanent: aws.String("true"),
|
||||||
label.TraefikFrontendRule: aws.String("Host:traefik.io"),
|
label.TraefikFrontendRule: aws.String("Host:traefik.io"),
|
||||||
label.TraefikFrontendWhiteListSourceRange: aws.String("10.10.10.10"),
|
label.TraefikFrontendWhiteListSourceRange: aws.String("10.10.10.10"),
|
||||||
label.TraefikFrontendWhiteListUseXForwardedFor: aws.String("true"),
|
|
||||||
|
|
||||||
label.TraefikFrontendRequestHeaders: aws.String("Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8"),
|
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"),
|
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.TraefikBackendBufferingMemRequestBodyBytes: aws.String("2097152"),
|
||||||
label.TraefikBackendBufferingRetryExpression: aws.String("IsNetworkError() && Attempts() <= 2"),
|
label.TraefikBackendBufferingRetryExpression: aws.String("IsNetworkError() && Attempts() <= 2"),
|
||||||
|
|
||||||
label.TraefikFrontendAuthBasic: aws.String("test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
label.TraefikFrontendAuthBasic: aws.String("test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
||||||
label.TraefikFrontendEntryPoints: aws.String("http,https"),
|
label.TraefikFrontendEntryPoints: aws.String("http,https"),
|
||||||
label.TraefikFrontendPassHostHeader: aws.String("true"),
|
label.TraefikFrontendPassHostHeader: aws.String("true"),
|
||||||
label.TraefikFrontendPassTLSCert: aws.String("true"),
|
label.TraefikFrontendPassTLSCert: aws.String("true"),
|
||||||
label.TraefikFrontendPriority: aws.String("666"),
|
label.TraefikFrontendPriority: aws.String("666"),
|
||||||
label.TraefikFrontendRedirectEntryPoint: aws.String("https"),
|
label.TraefikFrontendRedirectEntryPoint: aws.String("https"),
|
||||||
label.TraefikFrontendRedirectRegex: aws.String("nope"),
|
label.TraefikFrontendRedirectRegex: aws.String("nope"),
|
||||||
label.TraefikFrontendRedirectReplacement: aws.String("nope"),
|
label.TraefikFrontendRedirectReplacement: aws.String("nope"),
|
||||||
label.TraefikFrontendRedirectPermanent: aws.String("true"),
|
label.TraefikFrontendRedirectPermanent: aws.String("true"),
|
||||||
label.TraefikFrontendRule: aws.String("Host:traefik.io"),
|
label.TraefikFrontendRule: aws.String("Host:traefik.io"),
|
||||||
label.TraefikFrontendWhiteListSourceRange: aws.String("10.10.10.10"),
|
label.TraefikFrontendWhiteListSourceRange: aws.String("10.10.10.10"),
|
||||||
label.TraefikFrontendWhiteListUseXForwardedFor: aws.String("true"),
|
|
||||||
|
|
||||||
label.TraefikFrontendRequestHeaders: aws.String("Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8"),
|
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"),
|
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{
|
WhiteList: &types.WhiteList{
|
||||||
SourceRange: []string{"10.10.10.10"},
|
SourceRange: []string{"10.10.10.10"},
|
||||||
UseXForwardedFor: true,
|
|
||||||
},
|
},
|
||||||
Headers: &types.Headers{
|
Headers: &types.Headers{
|
||||||
CustomRequestHeaders: map[string]string{
|
CustomRequestHeaders: map[string]string{
|
||||||
|
|
|
@ -5,41 +5,43 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
annotationKubernetesIngressClass = "kubernetes.io/ingress.class"
|
annotationKubernetesIngressClass = "kubernetes.io/ingress.class"
|
||||||
annotationKubernetesAuthRealm = "ingress.kubernetes.io/auth-realm"
|
annotationKubernetesAuthRealm = "ingress.kubernetes.io/auth-realm"
|
||||||
annotationKubernetesAuthType = "ingress.kubernetes.io/auth-type"
|
annotationKubernetesAuthType = "ingress.kubernetes.io/auth-type"
|
||||||
annotationKubernetesAuthSecret = "ingress.kubernetes.io/auth-secret"
|
annotationKubernetesAuthSecret = "ingress.kubernetes.io/auth-secret"
|
||||||
annotationKubernetesAuthHeaderField = "ingress.kubernetes.io/auth-header-field"
|
annotationKubernetesAuthHeaderField = "ingress.kubernetes.io/auth-header-field"
|
||||||
annotationKubernetesAuthForwardResponseHeaders = "ingress.kubernetes.io/auth-response-headers"
|
annotationKubernetesAuthForwardResponseHeaders = "ingress.kubernetes.io/auth-response-headers"
|
||||||
annotationKubernetesAuthRemoveHeader = "ingress.kubernetes.io/auth-remove-header"
|
annotationKubernetesAuthRemoveHeader = "ingress.kubernetes.io/auth-remove-header"
|
||||||
annotationKubernetesAuthForwardURL = "ingress.kubernetes.io/auth-url"
|
annotationKubernetesAuthForwardURL = "ingress.kubernetes.io/auth-url"
|
||||||
annotationKubernetesAuthForwardTrustHeaders = "ingress.kubernetes.io/auth-trust-headers"
|
annotationKubernetesAuthForwardTrustHeaders = "ingress.kubernetes.io/auth-trust-headers"
|
||||||
annotationKubernetesAuthForwardTLSSecret = "ingress.kubernetes.io/auth-tls-secret"
|
annotationKubernetesAuthForwardTLSSecret = "ingress.kubernetes.io/auth-tls-secret"
|
||||||
annotationKubernetesAuthForwardTLSInsecure = "ingress.kubernetes.io/auth-tls-insecure"
|
annotationKubernetesAuthForwardTLSInsecure = "ingress.kubernetes.io/auth-tls-insecure"
|
||||||
annotationKubernetesRewriteTarget = "ingress.kubernetes.io/rewrite-target"
|
annotationKubernetesRewriteTarget = "ingress.kubernetes.io/rewrite-target"
|
||||||
annotationKubernetesWhiteListSourceRange = "ingress.kubernetes.io/whitelist-source-range"
|
annotationKubernetesWhiteListSourceRange = "ingress.kubernetes.io/whitelist-source-range"
|
||||||
annotationKubernetesWhiteListUseXForwardedFor = "ingress.kubernetes.io/whitelist-x-forwarded-for"
|
annotationKubernetesWhiteListIPStrategy = "ingress.kubernetes.io/whitelist-ipstrategy"
|
||||||
annotationKubernetesPreserveHost = "ingress.kubernetes.io/preserve-host"
|
annotationKubernetesWhiteListIPStrategyDepth = "ingress.kubernetes.io/whitelist-ipstrategy-depth"
|
||||||
annotationKubernetesPassTLSCert = "ingress.kubernetes.io/pass-tls-cert"
|
annotationKubernetesWhiteListIPStrategyExcludedIPs = "ingress.kubernetes.io/whitelist-ipstrategy-excluded-ips"
|
||||||
annotationKubernetesFrontendEntryPoints = "ingress.kubernetes.io/frontend-entry-points"
|
annotationKubernetesPreserveHost = "ingress.kubernetes.io/preserve-host"
|
||||||
annotationKubernetesPriority = "ingress.kubernetes.io/priority"
|
annotationKubernetesPassTLSCert = "ingress.kubernetes.io/pass-tls-cert"
|
||||||
annotationKubernetesCircuitBreakerExpression = "ingress.kubernetes.io/circuit-breaker-expression"
|
annotationKubernetesFrontendEntryPoints = "ingress.kubernetes.io/frontend-entry-points"
|
||||||
annotationKubernetesLoadBalancerMethod = "ingress.kubernetes.io/load-balancer-method"
|
annotationKubernetesPriority = "ingress.kubernetes.io/priority"
|
||||||
annotationKubernetesAffinity = "ingress.kubernetes.io/affinity"
|
annotationKubernetesCircuitBreakerExpression = "ingress.kubernetes.io/circuit-breaker-expression"
|
||||||
annotationKubernetesSessionCookieName = "ingress.kubernetes.io/session-cookie-name"
|
annotationKubernetesLoadBalancerMethod = "ingress.kubernetes.io/load-balancer-method"
|
||||||
annotationKubernetesRuleType = "ingress.kubernetes.io/rule-type"
|
annotationKubernetesAffinity = "ingress.kubernetes.io/affinity"
|
||||||
annotationKubernetesRedirectEntryPoint = "ingress.kubernetes.io/redirect-entry-point"
|
annotationKubernetesSessionCookieName = "ingress.kubernetes.io/session-cookie-name"
|
||||||
annotationKubernetesRedirectPermanent = "ingress.kubernetes.io/redirect-permanent"
|
annotationKubernetesRuleType = "ingress.kubernetes.io/rule-type"
|
||||||
annotationKubernetesRedirectRegex = "ingress.kubernetes.io/redirect-regex"
|
annotationKubernetesRedirectEntryPoint = "ingress.kubernetes.io/redirect-entry-point"
|
||||||
annotationKubernetesRedirectReplacement = "ingress.kubernetes.io/redirect-replacement"
|
annotationKubernetesRedirectPermanent = "ingress.kubernetes.io/redirect-permanent"
|
||||||
annotationKubernetesMaxConnAmount = "ingress.kubernetes.io/max-conn-amount"
|
annotationKubernetesRedirectRegex = "ingress.kubernetes.io/redirect-regex"
|
||||||
annotationKubernetesMaxConnExtractorFunc = "ingress.kubernetes.io/max-conn-extractor-func"
|
annotationKubernetesRedirectReplacement = "ingress.kubernetes.io/redirect-replacement"
|
||||||
annotationKubernetesRateLimit = "ingress.kubernetes.io/rate-limit"
|
annotationKubernetesMaxConnAmount = "ingress.kubernetes.io/max-conn-amount"
|
||||||
annotationKubernetesErrorPages = "ingress.kubernetes.io/error-pages"
|
annotationKubernetesMaxConnExtractorFunc = "ingress.kubernetes.io/max-conn-extractor-func"
|
||||||
annotationKubernetesBuffering = "ingress.kubernetes.io/buffering"
|
annotationKubernetesRateLimit = "ingress.kubernetes.io/rate-limit"
|
||||||
annotationKubernetesAppRoot = "ingress.kubernetes.io/app-root"
|
annotationKubernetesErrorPages = "ingress.kubernetes.io/error-pages"
|
||||||
annotationKubernetesServiceWeights = "ingress.kubernetes.io/service-weights"
|
annotationKubernetesBuffering = "ingress.kubernetes.io/buffering"
|
||||||
annotationKubernetesRequestModifier = "ingress.kubernetes.io/request-modifier"
|
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"
|
annotationKubernetesSSLForceHost = "ingress.kubernetes.io/ssl-force-host"
|
||||||
annotationKubernetesSSLRedirect = "ingress.kubernetes.io/ssl-redirect"
|
annotationKubernetesSSLRedirect = "ingress.kubernetes.io/ssl-redirect"
|
||||||
|
|
|
@ -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) {
|
return func(f *types.Frontend) {
|
||||||
if f.WhiteList == nil {
|
if f.WhiteList == nil {
|
||||||
f.WhiteList = &types.WhiteList{}
|
f.WhiteList = &types.WhiteList{}
|
||||||
}
|
}
|
||||||
f.WhiteList.UseXForwardedFor = useXFF
|
for _, opt := range opts {
|
||||||
f.WhiteList.SourceRange = ranges
|
opt(f.WhiteList)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -902,8 +902,23 @@ func getWhiteList(i *extensionsv1beta1.Ingress) *types.WhiteList {
|
||||||
}
|
}
|
||||||
|
|
||||||
return &types.WhiteList{
|
return &types.WhiteList{
|
||||||
SourceRange: ranges,
|
SourceRange: ranges,
|
||||||
UseXForwardedFor: getBoolValue(i.Annotations, annotationKubernetesWhiteListUseXForwardedFor, false),
|
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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1080,13 +1080,24 @@ func TestIngressAnnotations(t *testing.T) {
|
||||||
buildIngress(
|
buildIngress(
|
||||||
iNamespace("testing"),
|
iNamespace("testing"),
|
||||||
iAnnotation(annotationKubernetesWhiteListSourceRange, "1.1.1.1/24, 1234:abcd::42/32"),
|
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(
|
iRules(
|
||||||
iRule(
|
iRule(
|
||||||
iHost("test"),
|
iHost("test"),
|
||||||
iPaths(onePath(iPath("/whitelist-source-range"), iBackend("service1", intstr.FromInt(80))))),
|
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(
|
buildIngress(
|
||||||
iNamespace("testing"),
|
iNamespace("testing"),
|
||||||
iAnnotation(annotationKubernetesRewriteTarget, "/"),
|
iAnnotation(annotationKubernetesRewriteTarget, "/"),
|
||||||
|
@ -1357,6 +1368,12 @@ rateset:
|
||||||
server("http://example.com", weight(1))),
|
server("http://example.com", weight(1))),
|
||||||
lbMethod("wrr"),
|
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",
|
backend("rewrite/api",
|
||||||
servers(
|
servers(
|
||||||
server("http://example.com", weight(1)),
|
server("http://example.com", weight(1)),
|
||||||
|
@ -1467,11 +1484,22 @@ rateset:
|
||||||
),
|
),
|
||||||
frontend("test/whitelist-source-range",
|
frontend("test/whitelist-source-range",
|
||||||
passHostHeader(),
|
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(
|
routes(
|
||||||
route("/whitelist-source-range", "PathPrefix:/whitelist-source-range"),
|
route("/whitelist-source-range", "PathPrefix:/whitelist-source-range"),
|
||||||
route("test", "Host:test")),
|
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",
|
frontend("rewrite/api",
|
||||||
passHostHeader(),
|
passHostHeader(),
|
||||||
routes(
|
routes(
|
||||||
|
|
|
@ -24,13 +24,15 @@ const (
|
||||||
pathBackendBufferingMemRequestBodyBytes = pathBackendBuffering + "memrequestbodybytes"
|
pathBackendBufferingMemRequestBodyBytes = pathBackendBuffering + "memrequestbodybytes"
|
||||||
pathBackendBufferingRetryExpression = pathBackendBuffering + "retryexpression"
|
pathBackendBufferingRetryExpression = pathBackendBuffering + "retryexpression"
|
||||||
|
|
||||||
pathFrontends = "/frontends/"
|
pathFrontends = "/frontends/"
|
||||||
pathFrontendBackend = "/backend"
|
pathFrontendBackend = "/backend"
|
||||||
pathFrontendPriority = "/priority"
|
pathFrontendPriority = "/priority"
|
||||||
pathFrontendPassHostHeader = "/passhostheader"
|
pathFrontendPassHostHeader = "/passhostheader"
|
||||||
pathFrontendPassTLSCert = "/passtlscert"
|
pathFrontendPassTLSCert = "/passtlscert"
|
||||||
pathFrontendWhiteListSourceRange = "/whitelist/sourcerange"
|
pathFrontendWhiteListSourceRange = "/whitelist/sourcerange"
|
||||||
pathFrontendWhiteListUseXForwardedFor = "/whitelist/usexforwardedfor"
|
pathFrontendWhiteListIPStrategy = "/whitelist/ipstrategy"
|
||||||
|
pathFrontendWhiteListIPStrategyDepth = pathFrontendWhiteListIPStrategy + "/depth"
|
||||||
|
pathFrontendWhiteListIPStrategyExcludedIPs = pathFrontendWhiteListIPStrategy + "/excludedips"
|
||||||
|
|
||||||
pathFrontendAuth = "/auth/"
|
pathFrontendAuth = "/auth/"
|
||||||
pathFrontendAuthBasic = pathFrontendAuth + "basic/"
|
pathFrontendAuthBasic = pathFrontendAuth + "basic/"
|
||||||
|
|
|
@ -80,14 +80,29 @@ func (p *Provider) buildConfiguration() *types.Configuration {
|
||||||
func (p *Provider) getWhiteList(rootPath string) *types.WhiteList {
|
func (p *Provider) getWhiteList(rootPath string) *types.WhiteList {
|
||||||
ranges := p.getList(rootPath, pathFrontendWhiteListSourceRange)
|
ranges := p.getList(rootPath, pathFrontendWhiteListSourceRange)
|
||||||
|
|
||||||
if len(ranges) > 0 {
|
if len(ranges) == 0 {
|
||||||
return &types.WhiteList{
|
return nil
|
||||||
SourceRange: ranges,
|
|
||||||
UseXForwardedFor: p.getBool(false, rootPath, pathFrontendWhiteListUseXForwardedFor),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
func (p *Provider) getRedirect(rootPath string) *types.Redirect {
|
||||||
|
|
|
@ -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",
|
desc: "all parameters",
|
||||||
kvPairs: filler("traefik",
|
kvPairs: filler("traefik",
|
||||||
|
@ -247,7 +280,8 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
||||||
withPair(pathFrontendPassTLSCert, "true"),
|
withPair(pathFrontendPassTLSCert, "true"),
|
||||||
withList(pathFrontendEntryPoints, "http", "https"),
|
withList(pathFrontendEntryPoints, "http", "https"),
|
||||||
withList(pathFrontendWhiteListSourceRange, "1.1.1.1/24", "1234:abcd::42/32"),
|
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"),
|
withPair(pathFrontendAuthBasicRemoveHeader, "true"),
|
||||||
withList(pathFrontendAuthBasicUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
withList(pathFrontendAuthBasicUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
||||||
|
@ -363,8 +397,11 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
||||||
Backend: "backend1",
|
Backend: "backend1",
|
||||||
PassTLSCert: true,
|
PassTLSCert: true,
|
||||||
WhiteList: &types.WhiteList{
|
WhiteList: &types.WhiteList{
|
||||||
SourceRange: []string{"1.1.1.1/24", "1234:abcd::42/32"},
|
SourceRange: []string{"1.1.1.1/24", "1234:abcd::42/32"},
|
||||||
UseXForwardedFor: true,
|
IPStrategy: &types.IPStrategy{
|
||||||
|
Depth: 5,
|
||||||
|
ExcludedIPs: []string{"1.1.1.1/24", "1234:abcd::42/32"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Auth: &types.Auth{
|
Auth: &types.Auth{
|
||||||
HeaderField: "X-WebAuth-User",
|
HeaderField: "X-WebAuth-User",
|
||||||
|
@ -1240,31 +1277,8 @@ func TestWhiteList(t *testing.T) {
|
||||||
SourceRange: []string{
|
SourceRange: []string{
|
||||||
"10.10.10.10",
|
"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 {
|
for _, test := range testCases {
|
||||||
|
|
|
@ -86,10 +86,11 @@ const (
|
||||||
SuffixFrontendRedirectReplacement = "frontend.redirect.replacement"
|
SuffixFrontendRedirectReplacement = "frontend.redirect.replacement"
|
||||||
SuffixFrontendRedirectPermanent = "frontend.redirect.permanent"
|
SuffixFrontendRedirectPermanent = "frontend.redirect.permanent"
|
||||||
SuffixFrontendRule = "frontend.rule"
|
SuffixFrontendRule = "frontend.rule"
|
||||||
SuffixFrontendWhitelistSourceRange = "frontend.whitelistSourceRange" // Deprecated
|
|
||||||
SuffixFrontendWhiteList = "frontend.whiteList."
|
SuffixFrontendWhiteList = "frontend.whiteList."
|
||||||
SuffixFrontendWhiteListSourceRange = SuffixFrontendWhiteList + "sourceRange"
|
SuffixFrontendWhiteListSourceRange = SuffixFrontendWhiteList + "sourceRange"
|
||||||
SuffixFrontendWhiteListUseXForwardedFor = SuffixFrontendWhiteList + "useXForwardedFor"
|
SuffixFrontendWhiteListIPStrategy = SuffixFrontendWhiteList + "ipStrategy"
|
||||||
|
SuffixFrontendWhiteListIPStrategyDepth = SuffixFrontendWhiteListIPStrategy + ".depth"
|
||||||
|
SuffixFrontendWhiteListIPStrategyExcludedIPS = SuffixFrontendWhiteListIPStrategy + ".excludedIPs"
|
||||||
TraefikDomain = Prefix + SuffixDomain
|
TraefikDomain = Prefix + SuffixDomain
|
||||||
TraefikEnable = Prefix + SuffixEnable
|
TraefikEnable = Prefix + SuffixEnable
|
||||||
TraefikPort = Prefix + SuffixPort
|
TraefikPort = Prefix + SuffixPort
|
||||||
|
@ -150,9 +151,10 @@ const (
|
||||||
TraefikFrontendRedirectReplacement = Prefix + SuffixFrontendRedirectReplacement
|
TraefikFrontendRedirectReplacement = Prefix + SuffixFrontendRedirectReplacement
|
||||||
TraefikFrontendRedirectPermanent = Prefix + SuffixFrontendRedirectPermanent
|
TraefikFrontendRedirectPermanent = Prefix + SuffixFrontendRedirectPermanent
|
||||||
TraefikFrontendRule = Prefix + SuffixFrontendRule
|
TraefikFrontendRule = Prefix + SuffixFrontendRule
|
||||||
TraefikFrontendWhitelistSourceRange = Prefix + SuffixFrontendWhitelistSourceRange // Deprecated
|
|
||||||
TraefikFrontendWhiteListSourceRange = Prefix + SuffixFrontendWhiteListSourceRange
|
TraefikFrontendWhiteListSourceRange = Prefix + SuffixFrontendWhiteListSourceRange
|
||||||
TraefikFrontendWhiteListUseXForwardedFor = Prefix + SuffixFrontendWhiteListUseXForwardedFor
|
TraefikFrontendWhiteListIPStrategy = Prefix + SuffixFrontendWhiteListIPStrategy
|
||||||
|
TraefikFrontendWhiteListIPStrategyDepth = Prefix + SuffixFrontendWhiteListIPStrategyDepth
|
||||||
|
TraefikFrontendWhiteListIPStrategyExcludedIPS = Prefix + SuffixFrontendWhiteListIPStrategyExcludedIPS
|
||||||
TraefikFrontendRequestHeaders = Prefix + SuffixFrontendRequestHeaders
|
TraefikFrontendRequestHeaders = Prefix + SuffixFrontendRequestHeaders
|
||||||
TraefikFrontendResponseHeaders = Prefix + SuffixFrontendResponseHeaders
|
TraefikFrontendResponseHeaders = Prefix + SuffixFrontendResponseHeaders
|
||||||
TraefikFrontendAllowedHosts = Prefix + SuffixFrontendHeadersAllowedHosts
|
TraefikFrontendAllowedHosts = Prefix + SuffixFrontendHeadersAllowedHosts
|
||||||
|
|
|
@ -13,28 +13,30 @@ import (
|
||||||
|
|
||||||
// GetWhiteList Create white list from labels
|
// GetWhiteList Create white list from labels
|
||||||
func GetWhiteList(labels map[string]string) *types.WhiteList {
|
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)
|
ranges := GetSliceStringValue(labels, TraefikFrontendWhiteListSourceRange)
|
||||||
if len(ranges) > 0 {
|
if len(ranges) == 0 {
|
||||||
return &types.WhiteList{
|
return nil
|
||||||
SourceRange: ranges,
|
|
||||||
UseXForwardedFor: GetBoolValue(labels, TraefikFrontendWhiteListUseXForwardedFor, false),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Deprecated
|
return &types.WhiteList{
|
||||||
values := GetSliceStringValue(labels, TraefikFrontendWhitelistSourceRange)
|
SourceRange: ranges,
|
||||||
if len(values) > 0 {
|
IPStrategy: getIPStrategy(labels),
|
||||||
return &types.WhiteList{
|
}
|
||||||
SourceRange: values,
|
}
|
||||||
UseXForwardedFor: false,
|
|
||||||
}
|
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
|
// GetRedirect Create redirect from labels
|
||||||
|
|
|
@ -134,18 +134,6 @@ func TestWhiteList(t *testing.T) {
|
||||||
labels: map[string]string{},
|
labels: map[string]string{},
|
||||||
expected: nil,
|
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",
|
desc: "should return a struct when only range",
|
||||||
labels: map[string]string{
|
labels: map[string]string{
|
||||||
|
@ -155,42 +143,75 @@ func TestWhiteList(t *testing.T) {
|
||||||
SourceRange: []string{
|
SourceRange: []string{
|
||||||
"10.10.10.10",
|
"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{
|
labels: map[string]string{
|
||||||
TraefikFrontendWhiteListSourceRange: "10.10.10.10",
|
TraefikFrontendWhiteListSourceRange: "10.10.10.10",
|
||||||
TraefikFrontendWhiteListUseXForwardedFor: "true",
|
TraefikFrontendWhiteListIPStrategyDepth: "5",
|
||||||
},
|
},
|
||||||
expected: &types.WhiteList{
|
expected: &types.WhiteList{
|
||||||
SourceRange: []string{
|
SourceRange: []string{
|
||||||
"10.10.10.10",
|
"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{
|
labels: map[string]string{
|
||||||
TraefikFrontendWhitelistSourceRange: "20.20.20.20",
|
TraefikFrontendWhiteListSourceRange: "10.10.10.10",
|
||||||
TraefikFrontendWhiteListSourceRange: "10.10.10.10",
|
TraefikFrontendWhiteListIPStrategyDepth: "5",
|
||||||
TraefikFrontendWhiteListUseXForwardedFor: "true",
|
TraefikFrontendWhiteListIPStrategyExcludedIPS: "10.10.10.10,10.10.10.11",
|
||||||
},
|
},
|
||||||
expected: &types.WhiteList{
|
expected: &types.WhiteList{
|
||||||
SourceRange: []string{
|
SourceRange: []string{
|
||||||
"10.10.10.10",
|
"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{
|
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,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -399,7 +399,8 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
withLabel(label.TraefikFrontendRedirectPermanent, "true"),
|
withLabel(label.TraefikFrontendRedirectPermanent, "true"),
|
||||||
withLabel(label.TraefikFrontendRule, "Host:traefik.io"),
|
withLabel(label.TraefikFrontendRule, "Host:traefik.io"),
|
||||||
withLabel(label.TraefikFrontendWhiteListSourceRange, "10.10.10.10"),
|
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.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"),
|
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{
|
WhiteList: &types.WhiteList{
|
||||||
SourceRange: []string{"10.10.10.10"},
|
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{
|
Headers: &types.Headers{
|
||||||
CustomRequestHeaders: map[string]string{
|
CustomRequestHeaders: map[string]string{
|
||||||
|
@ -789,7 +793,6 @@ func TestBuildConfigurationSegments(t *testing.T) {
|
||||||
withSegmentLabel(label.TraefikFrontendRedirectPermanent, "true", "containous"),
|
withSegmentLabel(label.TraefikFrontendRedirectPermanent, "true", "containous"),
|
||||||
withSegmentLabel(label.TraefikFrontendRule, "Host:traefik.io", "containous"),
|
withSegmentLabel(label.TraefikFrontendRule, "Host:traefik.io", "containous"),
|
||||||
withSegmentLabel(label.TraefikFrontendWhiteListSourceRange, "10.10.10.10", "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.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"),
|
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{
|
WhiteList: &types.WhiteList{
|
||||||
SourceRange: []string{"10.10.10.10"},
|
SourceRange: []string{"10.10.10.10"},
|
||||||
UseXForwardedFor: true,
|
|
||||||
},
|
},
|
||||||
Headers: &types.Headers{
|
Headers: &types.Headers{
|
||||||
CustomRequestHeaders: map[string]string{
|
CustomRequestHeaders: map[string]string{
|
||||||
|
|
|
@ -356,7 +356,8 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
withLabel(label.TraefikFrontendRedirectPermanent, "true"),
|
withLabel(label.TraefikFrontendRedirectPermanent, "true"),
|
||||||
withLabel(label.TraefikFrontendRule, "Host:traefik.io"),
|
withLabel(label.TraefikFrontendRule, "Host:traefik.io"),
|
||||||
withLabel(label.TraefikFrontendWhiteListSourceRange, "10.10.10.10"),
|
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.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"),
|
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{
|
WhiteList: &types.WhiteList{
|
||||||
SourceRange: []string{"10.10.10.10"},
|
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{
|
Headers: &types.Headers{
|
||||||
CustomRequestHeaders: map[string]string{
|
CustomRequestHeaders: map[string]string{
|
||||||
|
@ -709,7 +713,6 @@ func TestBuildConfigurationSegments(t *testing.T) {
|
||||||
withSegmentLabel(label.TraefikFrontendRedirectPermanent, "true", "containous"),
|
withSegmentLabel(label.TraefikFrontendRedirectPermanent, "true", "containous"),
|
||||||
withSegmentLabel(label.TraefikFrontendRule, "Host:traefik.io", "containous"),
|
withSegmentLabel(label.TraefikFrontendRule, "Host:traefik.io", "containous"),
|
||||||
withSegmentLabel(label.TraefikFrontendWhiteListSourceRange, "10.10.10.10", "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.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"),
|
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{
|
WhiteList: &types.WhiteList{
|
||||||
SourceRange: []string{"10.10.10.10"},
|
SourceRange: []string{"10.10.10.10"},
|
||||||
UseXForwardedFor: true,
|
|
||||||
},
|
},
|
||||||
Headers: &types.Headers{
|
Headers: &types.Headers{
|
||||||
CustomRequestHeaders: map[string]string{
|
CustomRequestHeaders: map[string]string{
|
||||||
|
|
|
@ -74,17 +74,18 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
||||||
label.TraefikFrontendAuthForwardTLSInsecureSkipVerify: "true",
|
label.TraefikFrontendAuthForwardTLSInsecureSkipVerify: "true",
|
||||||
label.TraefikFrontendAuthHeaderField: "X-WebAuth-User",
|
label.TraefikFrontendAuthHeaderField: "X-WebAuth-User",
|
||||||
|
|
||||||
label.TraefikFrontendEntryPoints: "http,https",
|
label.TraefikFrontendEntryPoints: "http,https",
|
||||||
label.TraefikFrontendPassHostHeader: "true",
|
label.TraefikFrontendPassHostHeader: "true",
|
||||||
label.TraefikFrontendPassTLSCert: "true",
|
label.TraefikFrontendPassTLSCert: "true",
|
||||||
label.TraefikFrontendPriority: "666",
|
label.TraefikFrontendPriority: "666",
|
||||||
label.TraefikFrontendRedirectEntryPoint: "https",
|
label.TraefikFrontendRedirectEntryPoint: "https",
|
||||||
label.TraefikFrontendRedirectRegex: "nope",
|
label.TraefikFrontendRedirectRegex: "nope",
|
||||||
label.TraefikFrontendRedirectReplacement: "nope",
|
label.TraefikFrontendRedirectReplacement: "nope",
|
||||||
label.TraefikFrontendRedirectPermanent: "true",
|
label.TraefikFrontendRedirectPermanent: "true",
|
||||||
label.TraefikFrontendRule: "Host:traefik.io",
|
label.TraefikFrontendRule: "Host:traefik.io",
|
||||||
label.TraefikFrontendWhiteListSourceRange: "10.10.10.10",
|
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.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",
|
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{
|
SourceRange: []string{
|
||||||
"10.10.10.10",
|
"10.10.10.10",
|
||||||
},
|
},
|
||||||
UseXForwardedFor: true,
|
IPStrategy: &types.IPStrategy{
|
||||||
|
Depth: 5,
|
||||||
|
ExcludedIPs: []string{"10.10.10.10", "10.10.10.11"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Headers: &types.Headers{
|
Headers: &types.Headers{
|
||||||
CustomRequestHeaders: map[string]string{
|
CustomRequestHeaders: map[string]string{
|
||||||
|
@ -305,16 +309,15 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendAuthForwardTLSInsecureSkipVerify: "true",
|
label.Prefix + "sauternes." + label.SuffixFrontendAuthForwardTLSInsecureSkipVerify: "true",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendAuthHeaderField: "X-WebAuth-User",
|
label.Prefix + "sauternes." + label.SuffixFrontendAuthHeaderField: "X-WebAuth-User",
|
||||||
|
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendEntryPoints: "http,https",
|
label.Prefix + "sauternes." + label.SuffixFrontendEntryPoints: "http,https",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendPassHostHeader: "true",
|
label.Prefix + "sauternes." + label.SuffixFrontendPassHostHeader: "true",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSCert: "true",
|
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSCert: "true",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendPriority: "666",
|
label.Prefix + "sauternes." + label.SuffixFrontendPriority: "666",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendRedirectEntryPoint: "https",
|
label.Prefix + "sauternes." + label.SuffixFrontendRedirectEntryPoint: "https",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendRedirectRegex: "nope",
|
label.Prefix + "sauternes." + label.SuffixFrontendRedirectRegex: "nope",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendRedirectReplacement: "nope",
|
label.Prefix + "sauternes." + label.SuffixFrontendRedirectReplacement: "nope",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendRedirectPermanent: "true",
|
label.Prefix + "sauternes." + label.SuffixFrontendRedirectPermanent: "true",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendWhiteListSourceRange: "10.10.10.10",
|
label.Prefix + "sauternes." + label.SuffixFrontendWhiteListSourceRange: "10.10.10.10",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendWhiteListUseXForwardedFor: "true",
|
|
||||||
|
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendRequestHeaders: "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8",
|
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",
|
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{
|
SourceRange: []string{
|
||||||
"10.10.10.10",
|
"10.10.10.10",
|
||||||
},
|
},
|
||||||
UseXForwardedFor: true,
|
|
||||||
},
|
},
|
||||||
Headers: &types.Headers{
|
Headers: &types.Headers{
|
||||||
CustomRequestHeaders: map[string]string{
|
CustomRequestHeaders: map[string]string{
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"github.com/containous/traefik/cluster"
|
"github.com/containous/traefik/cluster"
|
||||||
"github.com/containous/traefik/configuration"
|
"github.com/containous/traefik/configuration"
|
||||||
"github.com/containous/traefik/h2c"
|
"github.com/containous/traefik/h2c"
|
||||||
|
"github.com/containous/traefik/ip"
|
||||||
"github.com/containous/traefik/log"
|
"github.com/containous/traefik/log"
|
||||||
"github.com/containous/traefik/metrics"
|
"github.com/containous/traefik/metrics"
|
||||||
"github.com/containous/traefik/middlewares"
|
"github.com/containous/traefik/middlewares"
|
||||||
|
@ -31,7 +32,6 @@ import (
|
||||||
"github.com/containous/traefik/safe"
|
"github.com/containous/traefik/safe"
|
||||||
traefiktls "github.com/containous/traefik/tls"
|
traefiktls "github.com/containous/traefik/tls"
|
||||||
"github.com/containous/traefik/types"
|
"github.com/containous/traefik/types"
|
||||||
"github.com/containous/traefik/whitelist"
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/negroni"
|
"github.com/urfave/negroni"
|
||||||
"github.com/xenolf/lego/acme"
|
"github.com/xenolf/lego/acme"
|
||||||
|
@ -552,7 +552,7 @@ func (s *Server) prepareServer(entryPointName string, entryPoint *configuration.
|
||||||
if entryPoint.ProxyProtocol != nil {
|
if entryPoint.ProxyProtocol != nil {
|
||||||
listener, err = buildProxyProtocolListener(entryPoint, listener)
|
listener, err = buildProxyProtocolListener(entryPoint, listener)
|
||||||
if err != nil {
|
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) {
|
func buildProxyProtocolListener(entryPoint *configuration.EntryPoint, listener net.Listener) (net.Listener, error) {
|
||||||
IPs, err := whitelist.NewIP(entryPoint.ProxyProtocol.TrustedIPs, entryPoint.ProxyProtocol.Insecure, false)
|
var sourceCheck func(addr net.Addr) (bool, error)
|
||||||
if err != nil {
|
if entryPoint.ProxyProtocol.Insecure {
|
||||||
return nil, fmt.Errorf("error creating whitelist: %s", err)
|
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)
|
log.Infof("Enabling ProxyProtocol for trusted IPs %v", entryPoint.ProxyProtocol.TrustedIPs)
|
||||||
|
|
||||||
return &proxyproto.Listener{
|
return &proxyproto.Listener{
|
||||||
Listener: listener,
|
Listener: listener,
|
||||||
SourceCheck: func(addr net.Addr) (bool, error) {
|
SourceCheck: sourceCheck,
|
||||||
ip, ok := addr.(*net.TCPAddr)
|
|
||||||
if !ok {
|
|
||||||
return false, fmt.Errorf("type error %v", addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
return IPs.ContainsIP(ip.IP), nil
|
|
||||||
},
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
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
|
var fwd http.Handler
|
||||||
fwd, err = forward.New(
|
fwd, err = forward.New(
|
||||||
forward.Stream(true),
|
forward.Stream(true),
|
||||||
forward.PassHostHeader(frontend.PassHostHeader),
|
forward.PassHostHeader(frontend.PassHostHeader),
|
||||||
forward.RoundTripper(roundTripper),
|
forward.RoundTripper(roundTripper),
|
||||||
forward.Rewriter(rewriter),
|
|
||||||
forward.ResponseModifier(responseModifier),
|
forward.ResponseModifier(responseModifier),
|
||||||
forward.BufferPool(s.bufferPool),
|
forward.BufferPool(s.bufferPool),
|
||||||
forward.WebsocketConnectionClosedHook(func(req *http.Request, conn net.Conn) {
|
forward.WebsocketConnectionClosedHook(func(req *http.Request, conn net.Conn) {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/containous/traefik/middlewares/accesslog"
|
"github.com/containous/traefik/middlewares/accesslog"
|
||||||
mauth "github.com/containous/traefik/middlewares/auth"
|
mauth "github.com/containous/traefik/middlewares/auth"
|
||||||
"github.com/containous/traefik/middlewares/errorpages"
|
"github.com/containous/traefik/middlewares/errorpages"
|
||||||
|
"github.com/containous/traefik/middlewares/forwardedheaders"
|
||||||
"github.com/containous/traefik/middlewares/redirect"
|
"github.com/containous/traefik/middlewares/redirect"
|
||||||
"github.com/containous/traefik/types"
|
"github.com/containous/traefik/types"
|
||||||
thoas_stats "github.com/thoas/stats"
|
thoas_stats "github.com/thoas/stats"
|
||||||
|
@ -47,7 +48,7 @@ func (s *Server) buildMiddlewares(frontendName string, frontend *types.Frontend,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whitelist
|
// Whitelist
|
||||||
ipWhitelistMiddleware, err := buildIPWhiteLister(frontend.WhiteList, frontend.WhitelistSourceRange)
|
ipWhitelistMiddleware, err := buildIPWhiteLister(frontend.WhiteList, s.entryPoints[entryPointName].Configuration.ClientIPStrategy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, fmt.Errorf("error creating IP Whitelister: %s", err)
|
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{})
|
serverMiddlewares = append(serverMiddlewares, &middlewares.Compress{})
|
||||||
}
|
}
|
||||||
|
|
||||||
ipWhitelistMiddleware, err := buildIPWhiteLister(
|
if s.entryPoints[serverEntryPointName].Configuration.ForwardedHeaders != nil {
|
||||||
s.entryPoints[serverEntryPointName].Configuration.WhiteList,
|
xForwardedMiddleware, err := forwardedheaders.NewXforwarded(
|
||||||
s.entryPoints[serverEntryPointName].Configuration.WhitelistSourceRange)
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create ip whitelist middleware: %v", err)
|
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
|
return redirection, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildIPWhiteLister(whiteList *types.WhiteList, wlRange []string) (*middlewares.IPWhiteLister, error) {
|
func buildIPWhiteLister(whiteList *types.WhiteList, ipStrategy *types.IPStrategy) (*middlewares.IPWhiteLister, error) {
|
||||||
if whiteList != nil &&
|
if whiteList == nil {
|
||||||
len(whiteList.SourceRange) > 0 {
|
return nil, nil
|
||||||
return middlewares.NewIPWhiteLister(whiteList.SourceRange, whiteList.UseXForwardedFor)
|
|
||||||
} else if len(wlRange) > 0 {
|
|
||||||
return middlewares.NewIPWhiteLister(wlRange, false)
|
|
||||||
}
|
}
|
||||||
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 {
|
func (s *Server) wrapNegroniHandlerWithAccessLog(handler negroni.Handler, frontendName string) negroni.Handler {
|
||||||
|
|
|
@ -36,8 +36,10 @@ func TestServerEntryPointWhitelistConfig(t *testing.T) {
|
||||||
desc: "whitelist middleware should be added if configured on entrypoint",
|
desc: "whitelist middleware should be added if configured on entrypoint",
|
||||||
entrypoint: &configuration.EntryPoint{
|
entrypoint: &configuration.EntryPoint{
|
||||||
Address: ":0",
|
Address: ":0",
|
||||||
WhitelistSourceRange: []string{
|
WhiteList: &types.WhiteList{
|
||||||
"127.0.0.1/32",
|
SourceRange: []string{
|
||||||
|
"127.0.0.1/32",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
ForwardedHeaders: &configuration.ForwardedHeaders{Insecure: true},
|
ForwardedHeaders: &configuration.ForwardedHeaders{Insecure: true},
|
||||||
},
|
},
|
||||||
|
@ -96,23 +98,6 @@ func TestBuildIPWhiteLister(t *testing.T) {
|
||||||
middlewareConfigured: false,
|
middlewareConfigured: false,
|
||||||
errMessage: "",
|
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 <nil>: invalid CIDR address: foo",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
desc: "whitelists configured",
|
desc: "whitelists configured",
|
||||||
whiteList: &types.WhiteList{
|
whiteList: &types.WhiteList{
|
||||||
|
@ -120,22 +105,10 @@ func TestBuildIPWhiteLister(t *testing.T) {
|
||||||
"1.2.3.4/24",
|
"1.2.3.4/24",
|
||||||
"fe80::/16",
|
"fe80::/16",
|
||||||
},
|
},
|
||||||
UseXForwardedFor: false,
|
|
||||||
},
|
},
|
||||||
middlewareConfigured: true,
|
middlewareConfigured: true,
|
||||||
errMessage: "",
|
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 <nil>: invalid CIDR address: foo",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range testCases {
|
for _, test := range testCases {
|
||||||
|
@ -143,7 +116,7 @@ func TestBuildIPWhiteLister(t *testing.T) {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
middleware, err := buildIPWhiteLister(test.whiteList, test.whitelistSourceRange)
|
middleware, err := buildIPWhiteLister(test.whiteList, nil)
|
||||||
|
|
||||||
if test.errMessage != "" {
|
if test.errMessage != "" {
|
||||||
require.EqualError(t, err, 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}},
|
"http": &configuration.EntryPoint{ForwardedHeaders: &configuration.ForwardedHeaders{Insecure: true}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
entryPoints := map[string]EntryPoint{
|
||||||
|
"http": {
|
||||||
|
Configuration: globalConfig.EntryPoints["http"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
dynamicConfigs := types.Configurations{
|
dynamicConfigs := types.Configurations{
|
||||||
"config": &types.Configuration{
|
"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)
|
_, err := srv.loadConfig(dynamicConfigs, globalConfig)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
|
@ -123,7 +123,13 @@
|
||||||
sourceRange = [{{range $whitelist.SourceRange }}
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{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}}
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $service.TraefikLabels }}
|
{{ $redirect := getRedirect $service.TraefikLabels }}
|
||||||
|
|
|
@ -123,7 +123,13 @@
|
||||||
sourceRange = [{{range $whitelist.SourceRange }}
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
{{if $whitelist.IPStrategy }}
|
||||||
|
[frontends."frontend-{{ $frontendName }}".whiteList.IPStrategy]
|
||||||
|
depth = {{ $whitelist.IPStrategy.Depth }}
|
||||||
|
excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $container.SegmentLabels }}
|
{{ $redirect := getRedirect $container.SegmentLabels }}
|
||||||
|
|
|
@ -122,7 +122,13 @@
|
||||||
sourceRange = [{{range $whitelist.SourceRange }}
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
{{if $whitelist.IPStrategy }}
|
||||||
|
[frontends."frontend-{{ $serviceName }}".whiteList.IPStrategy]
|
||||||
|
depth = {{ $whitelist.IPStrategy.Depth }}
|
||||||
|
excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $instance.TraefikLabels }}
|
{{ $redirect := getRedirect $instance.TraefikLabels }}
|
||||||
|
|
|
@ -90,10 +90,16 @@
|
||||||
|
|
||||||
{{if $frontend.WhiteList }}
|
{{if $frontend.WhiteList }}
|
||||||
[frontends."{{ $frontendName }}".whiteList]
|
[frontends."{{ $frontendName }}".whiteList]
|
||||||
sourceRange = [{{range $frontend.WhiteList.SourceRange }}
|
sourceRange = [{{range $frontend.Whitelist.SourceRange }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{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}}
|
{{end}}
|
||||||
|
|
||||||
{{if $frontend.Redirect }}
|
{{if $frontend.Redirect }}
|
||||||
|
|
|
@ -122,7 +122,13 @@
|
||||||
sourceRange = [{{range $whitelist.SourceRange }}
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
{{if $whitelist.IPStrategy }}
|
||||||
|
[frontends."{{ $frontendName }}".whiteList.IPStrategy]
|
||||||
|
depth = {{ $whitelist.IPStrategy.Depth }}
|
||||||
|
excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $frontend }}
|
{{ $redirect := getRedirect $frontend }}
|
||||||
|
|
|
@ -125,7 +125,13 @@
|
||||||
sourceRange = [{{range $whitelist.SourceRange }}
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
{{if $whitelist.IPStrategy }}
|
||||||
|
[frontends."{{ $frontendName }}".whiteList.IPStrategy]
|
||||||
|
depth = {{ $whitelist.IPStrategy.Depth }}
|
||||||
|
excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $app.SegmentLabels }}
|
{{ $redirect := getRedirect $app.SegmentLabels }}
|
||||||
|
|
|
@ -125,7 +125,13 @@
|
||||||
sourceRange = [{{range $whitelist.SourceRange }}
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
{{if $whitelist.IPStrategy }}
|
||||||
|
[frontends."frontend-{{ $frontendName }}".whiteList.IPStrategy]
|
||||||
|
depth = {{ $whitelist.IPStrategy.Depth }}
|
||||||
|
excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $app.TraefikLabels }}
|
{{ $redirect := getRedirect $app.TraefikLabels }}
|
||||||
|
|
|
@ -123,7 +123,13 @@
|
||||||
sourceRange = [{{range $whitelist.SourceRange }}
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
{{if $whitelist.IPStrategy }}
|
||||||
|
[frontends."frontend-{{ $frontendName }}".whiteList.IPStrategy]
|
||||||
|
depth = {{ $whitelist.IPStrategy.Depth }}
|
||||||
|
excludedIPs = [{{range $whitelist.IPStrategy.ExcludedIPs }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $service.SegmentLabels }}
|
{{ $redirect := getRedirect $service.SegmentLabels }}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/abronan/valkeyrie/store"
|
"github.com/abronan/valkeyrie/store"
|
||||||
"github.com/containous/flaeg/parse"
|
"github.com/containous/flaeg/parse"
|
||||||
"github.com/containous/mux"
|
"github.com/containous/mux"
|
||||||
|
"github.com/containous/traefik/ip"
|
||||||
"github.com/containous/traefik/log"
|
"github.com/containous/traefik/log"
|
||||||
traefiktls "github.com/containous/traefik/tls"
|
traefiktls "github.com/containous/traefik/tls"
|
||||||
"github.com/mitchellh/hashstructure"
|
"github.com/mitchellh/hashstructure"
|
||||||
|
@ -63,8 +64,8 @@ type Buffering struct {
|
||||||
|
|
||||||
// WhiteList contains white list configuration.
|
// WhiteList contains white list configuration.
|
||||||
type WhiteList struct {
|
type WhiteList struct {
|
||||||
SourceRange []string `json:"sourceRange,omitempty"`
|
SourceRange []string `json:"sourceRange,omitempty"`
|
||||||
UseXForwardedFor bool `json:"useXForwardedFor,omitempty" export:"true"`
|
IPStrategy *IPStrategy `json:"ipStrategy,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// HealthCheck holds HealthCheck configuration
|
// HealthCheck holds HealthCheck configuration
|
||||||
|
@ -177,19 +178,18 @@ func (h *Headers) HasSecureHeadersDefined() bool {
|
||||||
|
|
||||||
// Frontend holds frontend configuration.
|
// Frontend holds frontend configuration.
|
||||||
type Frontend struct {
|
type Frontend struct {
|
||||||
EntryPoints []string `json:"entryPoints,omitempty" hash:"ignore"`
|
EntryPoints []string `json:"entryPoints,omitempty" hash:"ignore"`
|
||||||
Backend string `json:"backend,omitempty"`
|
Backend string `json:"backend,omitempty"`
|
||||||
Routes map[string]Route `json:"routes,omitempty" hash:"ignore"`
|
Routes map[string]Route `json:"routes,omitempty" hash:"ignore"`
|
||||||
PassHostHeader bool `json:"passHostHeader,omitempty"`
|
PassHostHeader bool `json:"passHostHeader,omitempty"`
|
||||||
PassTLSCert bool `json:"passTLSCert,omitempty"`
|
PassTLSCert bool `json:"passTLSCert,omitempty"`
|
||||||
Priority int `json:"priority"`
|
Priority int `json:"priority"`
|
||||||
WhitelistSourceRange []string `json:"whitelistSourceRange,omitempty"` // Deprecated
|
WhiteList *WhiteList `json:"whiteList,omitempty"`
|
||||||
WhiteList *WhiteList `json:"whiteList,omitempty"`
|
Headers *Headers `json:"headers,omitempty"`
|
||||||
Headers *Headers `json:"headers,omitempty"`
|
Errors map[string]*ErrorPage `json:"errors,omitempty"`
|
||||||
Errors map[string]*ErrorPage `json:"errors,omitempty"`
|
RateLimit *RateLimit `json:"ratelimit,omitempty"`
|
||||||
RateLimit *RateLimit `json:"ratelimit,omitempty"`
|
Redirect *Redirect `json:"redirect,omitempty"`
|
||||||
Redirect *Redirect `json:"redirect,omitempty"`
|
Auth *Auth `json:"auth,omitempty"`
|
||||||
Auth *Auth `json:"auth,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash returns the hash value of a Frontend struct.
|
// Hash returns the hash value of a Frontend struct.
|
||||||
|
@ -611,3 +611,37 @@ func (h HTTPCodeRanges) Contains(statusCode int) bool {
|
||||||
}
|
}
|
||||||
return false
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/containous/traefik/ip"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHeaders_ShouldReturnFalseWhenNotHasCustomHeadersDefined(t *testing.T) {
|
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)
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
145
whitelist/ip.go
145
whitelist/ip.go
|
@ -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
|
|
||||||
}
|
|
|
@ -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 <nil>: invalid CIDR address: ",
|
|
||||||
}, {
|
|
||||||
desc: "whitelist containing only an empty string",
|
|
||||||
whiteList: []string{
|
|
||||||
"",
|
|
||||||
},
|
|
||||||
expectedWhitelists: nil,
|
|
||||||
errMessage: "parsing CIDR white list <nil>: invalid CIDR address: ",
|
|
||||||
}, {
|
|
||||||
desc: "whitelist containing an invalid string",
|
|
||||||
whiteList: []string{
|
|
||||||
"foo",
|
|
||||||
},
|
|
||||||
expectedWhitelists: nil,
|
|
||||||
errMessage: "parsing CIDR white list <nil>: 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
|
|
||||||
}
|
|
Loading…
Reference in a new issue