Ability to use "X-Forwarded-For" as a source of IP for white list.
This commit is contained in:
parent
4802484729
commit
d2766b1b4f
50 changed files with 1496 additions and 599 deletions
|
@ -123,17 +123,19 @@ var _templatesConsul_catalogTmpl = []byte(`[backends]
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
{{ $whitelistSourceRange := getWhitelistSourceRange $service.Attributes }}
|
|
||||||
{{if $whitelistSourceRange }}
|
|
||||||
whitelistSourceRange = [{{range $whitelistSourceRange }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
basicAuth = [{{range getBasicAuth $service.Attributes }}
|
basicAuth = [{{range getBasicAuth $service.Attributes }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
|
{{ $whitelist := getWhiteList $service.Attributes }}
|
||||||
|
{{if $whitelist }}
|
||||||
|
[frontends."frontend-{{ $service.ServiceName }}".whiteList]
|
||||||
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $service.Attributes }}
|
{{ $redirect := getRedirect $service.Attributes }}
|
||||||
{{if $redirect }}
|
{{if $redirect }}
|
||||||
[frontends."frontend-{{ $service.ServiceName }}".redirect]
|
[frontends."frontend-{{ $service.ServiceName }}".redirect]
|
||||||
|
@ -523,17 +525,19 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
{{ $whitelistSourceRange := getWhitelistSourceRange $container.SegmentLabels }}
|
|
||||||
{{if $whitelistSourceRange }}
|
|
||||||
whitelistSourceRange = [{{range $whitelistSourceRange }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
basicAuth = [{{range getBasicAuth $container.SegmentLabels }}
|
basicAuth = [{{range getBasicAuth $container.SegmentLabels }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
|
{{ $whitelist := getWhiteList $container.SegmentLabels }}
|
||||||
|
{{if $whitelist }}
|
||||||
|
[frontends."frontend-{{ $frontendName }}".whiteList]
|
||||||
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $container.SegmentLabels }}
|
{{ $redirect := getRedirect $container.SegmentLabels }}
|
||||||
{{if $redirect }}
|
{{if $redirect }}
|
||||||
[frontends."frontend-{{ $frontendName }}".redirect]
|
[frontends."frontend-{{ $frontendName }}".redirect]
|
||||||
|
@ -713,17 +717,18 @@ var _templatesEcsTmpl = []byte(`[backends]
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
{{ $whitelistSourceRange := getWhitelistSourceRange $instance }}
|
|
||||||
{{if $whitelistSourceRange }}
|
|
||||||
whitelistSourceRange = [{{range $whitelistSourceRange }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
basicAuth = [{{range getBasicAuth $instance }}
|
basicAuth = [{{range getBasicAuth $instance }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
|
{{ $whitelist := getWhiteList $instance }}
|
||||||
|
{{if $whitelist }}
|
||||||
|
[frontends."frontend-{{ $serviceName }}".whiteList]
|
||||||
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $instance }}
|
{{ $redirect := getRedirect $instance }}
|
||||||
{{if $redirect }}
|
{{if $redirect }}
|
||||||
|
@ -934,9 +939,13 @@ var _templatesKubernetesTmpl = []byte(`[backends]
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
whitelistSourceRange = [{{range $frontend.WhitelistSourceRange }}
|
{{if $frontend.WhiteList }}
|
||||||
|
[frontends."{{ $frontendName }}".whiteList]
|
||||||
|
sourceRange = [{{range $frontend.WhiteList.SourceRange }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
useXForwardedFor = {{ $frontend.WhiteList.UseXForwardedFor }}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{if $frontend.Redirect }}
|
{{if $frontend.Redirect }}
|
||||||
[frontends."{{ $frontendName }}".redirect]
|
[frontends."{{ $frontendName }}".redirect]
|
||||||
|
@ -1118,17 +1127,19 @@ var _templatesKvTmpl = []byte(`[backends]
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
{{ $whitelistSourceRange := getWhitelistSourceRange $frontend }}
|
|
||||||
{{if $whitelistSourceRange }}
|
|
||||||
whitelistSourceRange = [{{range $whitelistSourceRange }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
basicAuth = [{{range getBasicAuth $frontend }}
|
basicAuth = [{{range getBasicAuth $frontend }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
|
{{ $whitelist := getWhiteList $frontend }}
|
||||||
|
{{if $whitelist }}
|
||||||
|
[frontends."{{ $frontendName }}".whiteList]
|
||||||
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $frontend }}
|
{{ $redirect := getRedirect $frontend }}
|
||||||
{{if $redirect }}
|
{{if $redirect }}
|
||||||
[frontends."{{ $frontendName }}".redirect]
|
[frontends."{{ $frontendName }}".redirect]
|
||||||
|
@ -1329,17 +1340,19 @@ var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
{{ $whitelistSourceRange := getWhitelistSourceRange $app $serviceName }}
|
|
||||||
{{if $whitelistSourceRange }}
|
|
||||||
whitelistSourceRange = [{{range $whitelistSourceRange }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
basicAuth = [{{range getBasicAuth $app $serviceName }}
|
basicAuth = [{{range getBasicAuth $app $serviceName }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
|
{{ $whitelist := getWhiteList $app $serviceName }}
|
||||||
|
{{if $whitelist }}
|
||||||
|
[frontends."{{ $frontendName }}".whiteList]
|
||||||
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $app $serviceName }}
|
{{ $redirect := getRedirect $app $serviceName }}
|
||||||
{{if $redirect }}
|
{{if $redirect }}
|
||||||
[frontends."{{ $frontendName }}".redirect]
|
[frontends."{{ $frontendName }}".redirect]
|
||||||
|
@ -1522,17 +1535,19 @@ var _templatesMesosTmpl = []byte(`[backends]
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
{{ $whitelistSourceRange := getWhitelistSourceRange $app }}
|
|
||||||
{{if $whitelistSourceRange }}
|
|
||||||
whitelistSourceRange = [{{range $whitelistSourceRange }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
basicAuth = [{{range getBasicAuth $app }}
|
basicAuth = [{{range getBasicAuth $app }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
|
{{ $whitelist := getWhiteList $app }}
|
||||||
|
{{if $whitelist }}
|
||||||
|
[frontends."frontend-{{ $frontendName }}".whiteList]
|
||||||
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $app }}
|
{{ $redirect := getRedirect $app }}
|
||||||
{{if $redirect }}
|
{{if $redirect }}
|
||||||
[frontends."frontend-{{ $frontendName }}".redirect]
|
[frontends."frontend-{{ $frontendName }}".redirect]
|
||||||
|
@ -1736,17 +1751,19 @@ var _templatesRancherTmpl = []byte(`{{ $backendServers := .Backends }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
{{ $whitelistSourceRange := getWhitelistSourceRange $service }}
|
|
||||||
{{if $whitelistSourceRange }}
|
|
||||||
whitelistSourceRange = [{{range $whitelistSourceRange }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
basicAuth = [{{range getBasicAuth $service }}
|
basicAuth = [{{range getBasicAuth $service }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
|
{{ $whitelist := getWhiteList $service }}
|
||||||
|
{{if $whitelist }}
|
||||||
|
[frontends."frontend-{{ $frontendName }}".whiteList]
|
||||||
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $service }}
|
{{ $redirect := getRedirect $service }}
|
||||||
{{if $redirect }}
|
{{if $redirect }}
|
||||||
[frontends."frontend-{{ $frontendName }}".redirect]
|
[frontends."frontend-{{ $frontendName }}".redirect]
|
||||||
|
|
|
@ -182,12 +182,23 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForwardedHeaders must be remove in the next breaking version
|
|
||||||
for entryPointName := range gc.EntryPoints {
|
for entryPointName := range gc.EntryPoints {
|
||||||
entryPoint := gc.EntryPoints[entryPointName]
|
entryPoint := gc.EntryPoints[entryPointName]
|
||||||
|
// 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{Insecure: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(entryPoint.WhitelistSourceRange) > 0 {
|
||||||
|
log.Warnf("Deprecated configuration found: %s. Please use %s.", "whiteListSourceRange", "whiteList.sourceRange")
|
||||||
|
|
||||||
|
if entryPoint.WhiteList == nil {
|
||||||
|
entryPoint.WhiteList = &types.WhiteList{
|
||||||
|
SourceRange: entryPoint.WhitelistSourceRange,
|
||||||
|
}
|
||||||
|
entryPoint.WhitelistSourceRange = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure LifeCycle isn't nil to spare nil checks elsewhere.
|
// Make sure LifeCycle isn't nil to spare nil checks elsewhere.
|
||||||
|
@ -378,18 +389,6 @@ type ForwardingTimeouts struct {
|
||||||
ResponseHeaderTimeout flaeg.Duration `description:"The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists" export:"true"`
|
ResponseHeaderTimeout flaeg.Duration `description:"The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists" export:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProxyProtocol contains Proxy-Protocol configuration
|
|
||||||
type ProxyProtocol struct {
|
|
||||||
Insecure bool
|
|
||||||
TrustedIPs []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ForwardedHeaders Trust client forwarding headers
|
|
||||||
type ForwardedHeaders struct {
|
|
||||||
Insecure bool
|
|
||||||
TrustedIPs []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// LifeCycle contains configurations relevant to the lifecycle (such as the
|
// LifeCycle contains configurations relevant to the lifecycle (such as the
|
||||||
// shutdown phase) of Traefik.
|
// shutdown phase) of Traefik.
|
||||||
type LifeCycle struct {
|
type LifeCycle struct {
|
||||||
|
|
|
@ -15,12 +15,25 @@ type EntryPoint struct {
|
||||||
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
|
WhitelistSourceRange []string // Deprecated
|
||||||
|
WhiteList *types.WhiteList `export:"true"`
|
||||||
Compress bool `export:"true"`
|
Compress bool `export:"true"`
|
||||||
ProxyProtocol *ProxyProtocol `export:"true"`
|
ProxyProtocol *ProxyProtocol `export:"true"`
|
||||||
ForwardedHeaders *ForwardedHeaders `export:"true"`
|
ForwardedHeaders *ForwardedHeaders `export:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProxyProtocol contains Proxy-Protocol configuration
|
||||||
|
type ProxyProtocol struct {
|
||||||
|
Insecure bool `export:"true"`
|
||||||
|
TrustedIPs []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForwardedHeaders Trust client forwarding headers
|
||||||
|
type ForwardedHeaders struct {
|
||||||
|
Insecure bool `export:"true"`
|
||||||
|
TrustedIPs []string
|
||||||
|
}
|
||||||
|
|
||||||
// EntryPoints holds entry points configuration of the reverse proxy (ip, port, TLS...)
|
// EntryPoints holds entry points configuration of the reverse proxy (ip, port, TLS...)
|
||||||
type EntryPoints map[string]*EntryPoint
|
type EntryPoints map[string]*EntryPoint
|
||||||
|
|
||||||
|
@ -70,6 +83,7 @@ func (ep *EntryPoints) Set(value string) error {
|
||||||
Redirect: makeEntryPointRedirect(result),
|
Redirect: makeEntryPointRedirect(result),
|
||||||
Compress: compress,
|
Compress: compress,
|
||||||
WhitelistSourceRange: whiteListSourceRange,
|
WhitelistSourceRange: whiteListSourceRange,
|
||||||
|
WhiteList: makeWhiteList(result),
|
||||||
ProxyProtocol: makeEntryPointProxyProtocol(result),
|
ProxyProtocol: makeEntryPointProxyProtocol(result),
|
||||||
ForwardedHeaders: makeEntryPointForwardedHeaders(result),
|
ForwardedHeaders: makeEntryPointForwardedHeaders(result),
|
||||||
}
|
}
|
||||||
|
@ -77,6 +91,17 @@ func (ep *EntryPoints) Set(value string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeWhiteList(result map[string]string) *types.WhiteList {
|
||||||
|
var wl *types.WhiteList
|
||||||
|
if rawRange, ok := result["whitelist_sourcerange"]; ok {
|
||||||
|
wl = &types.WhiteList{
|
||||||
|
SourceRange: strings.Split(rawRange, ","),
|
||||||
|
UseXForwardedFor: toBool(result, "whitelist_usexforwardedfor"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return wl
|
||||||
|
}
|
||||||
|
|
||||||
func makeEntryPointAuth(result map[string]string) *types.Auth {
|
func makeEntryPointAuth(result map[string]string) *types.Auth {
|
||||||
var basic *types.Basic
|
var basic *types.Basic
|
||||||
if v, ok := result["auth_basic_users"]; ok {
|
if v, ok := result["auth_basic_users"]; ok {
|
||||||
|
|
|
@ -28,7 +28,6 @@ func Test_parseEntryPointsConfiguration(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 " +
|
|
||||||
"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 " +
|
||||||
|
@ -40,7 +39,10 @@ func Test_parseEntryPointsConfiguration(t *testing.T) {
|
||||||
"Auth.Forward.TLS.CAOptional:true " +
|
"Auth.Forward.TLS.CAOptional:true " +
|
||||||
"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.useXForwardedFor:true ",
|
||||||
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",
|
||||||
|
@ -66,6 +68,8 @@ func Test_parseEntryPointsConfiguration(t *testing.T) {
|
||||||
"tls": "goo,gii",
|
"tls": "goo,gii",
|
||||||
"tls_acme": "TLS",
|
"tls_acme": "TLS",
|
||||||
"whitelistsourcerange": "10.42.0.0/16,152.89.1.33/32,afed:be44::/16",
|
"whitelistsourcerange": "10.42.0.0/16,152.89.1.33/32,afed:be44::/16",
|
||||||
|
"whitelist_sourcerange": "10.42.0.0/16,152.89.1.33/32,afed:be44::/16",
|
||||||
|
"whitelist_usexforwardedfor": "true",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -175,7 +179,6 @@ 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 " +
|
|
||||||
"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 " +
|
||||||
|
@ -187,7 +190,10 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||||
"Auth.Forward.TLS.CAOptional:true " +
|
"Auth.Forward.TLS.CAOptional:true " +
|
||||||
"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.useXForwardedFor:true ",
|
||||||
expectedEntryPointName: "foo",
|
expectedEntryPointName: "foo",
|
||||||
expectedEntryPoint: &EntryPoint{
|
expectedEntryPoint: &EntryPoint{
|
||||||
Address: ":8000",
|
Address: ":8000",
|
||||||
|
@ -240,6 +246,14 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||||
"152.89.1.33/32",
|
"152.89.1.33/32",
|
||||||
"afed:be44::/16",
|
"afed:be44::/16",
|
||||||
},
|
},
|
||||||
|
WhiteList: &types.WhiteList{
|
||||||
|
SourceRange: []string{
|
||||||
|
"10.42.0.0/16",
|
||||||
|
"152.89.1.33/32",
|
||||||
|
"afed:be44::/16",
|
||||||
|
},
|
||||||
|
UseXForwardedFor: true,
|
||||||
|
},
|
||||||
Compress: true,
|
Compress: true,
|
||||||
ProxyProtocol: &ProxyProtocol{
|
ProxyProtocol: &ProxyProtocol{
|
||||||
Insecure: false,
|
Insecure: false,
|
||||||
|
|
|
@ -109,7 +109,8 @@ Additional settings can be defined using Consul Catalog tags.
|
||||||
| `<prefix>.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
| `<prefix>.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||||
| `<prefix>.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
| `<prefix>.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
||||||
| `<prefix>.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{{.ServiceName}}.{{.Domain}}`. |
|
| `<prefix>.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{{.ServiceName}}.{{.Domain}}`. |
|
||||||
| `<prefix>.frontend.whitelistSourceRange=RANGE` | 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` | 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` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||||
|
|
||||||
### Custom Headers
|
### Custom Headers
|
||||||
|
|
||||||
|
|
|
@ -194,7 +194,7 @@ 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` | Set the docker network to use for connections to this container. [1] |
|
| `traefik.docker.network` | Set the docker network to use for connections to this container. [1] |
|
||||||
| `traefik.enable=false` | Disable this container in Træfik |
|
| `traefik.enable=false` | Disable this container in Træfik |
|
||||||
| `traefik.port=80` | Register this port. Useful when the container exposes multiples ports. |
|
| `traefik.port=80` | Register this port. Useful when the container exposes multiples ports. |
|
||||||
|
@ -234,7 +234,8 @@ Labels can be used on containers to override default behavior.
|
||||||
| `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. Default: `Host:{containerName}.{domain}` or `Host:{service}.{project_name}.{domain}` if you are using `docker-compose`. |
|
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{containerName}.{domain}` or `Host:{service}.{project_name}.{domain}` if you are using `docker-compose`. |
|
||||||
| `traefik.frontend.whitelistSourceRange=RANGE` | 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` | 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. |
|
||||||
|
|
||||||
[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).
|
||||||
|
@ -303,7 +304,8 @@ Segment labels override the default behavior.
|
||||||
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Overrides `traefik.frontend.redirect.replacement`. |
|
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Overrides `traefik.frontend.redirect.replacement`. |
|
||||||
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
||||||
| `traefik.<segment_name>.frontend.rule` | Overrides `traefik.frontend.rule`. |
|
| `traefik.<segment_name>.frontend.rule` | Overrides `traefik.frontend.rule`. |
|
||||||
| `traefik.<segment_name>.frontend.whitelistSourceRange=RANGE` | Overrides `traefik.frontend.whitelistSourceRange`. |
|
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Overrides `traefik.frontend.whiteList.sourceRange`. |
|
||||||
|
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Overrides `traefik.frontend.whiteList.useXForwardedFor`. |
|
||||||
|
|
||||||
#### Custom Headers
|
#### Custom Headers
|
||||||
|
|
||||||
|
|
|
@ -163,7 +163,8 @@ Labels can be used on task containers to override default behaviour:
|
||||||
| `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. Default: `Host:{instance_name}.{domain}`. |
|
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{instance_name}.{domain}`. |
|
||||||
| `traefik.frontend.whitelistSourceRange=RANGE` | 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` | 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` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||||
|
|
||||||
### Custom Headers
|
### Custom Headers
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,10 @@ Træfik can be configured with a file.
|
||||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
]
|
]
|
||||||
whitelistSourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
|
|
||||||
|
[frontends.frontend1.whiteList]
|
||||||
|
sourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
|
||||||
|
useXForwardedFor = true
|
||||||
|
|
||||||
[frontends.frontend1.routes]
|
[frontends.frontend1.routes]
|
||||||
[frontends.frontend1.routes.route0]
|
[frontends.frontend1.routes.route0]
|
||||||
|
|
|
@ -200,7 +200,8 @@ The following labels can be defined on Marathon applications. They adjust the be
|
||||||
| `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. Default: `Host:{sub_domain}.{domain}`. |
|
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{sub_domain}.{domain}`. |
|
||||||
| `traefik.frontend.whitelistSourceRange=RANGE` | 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` | 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` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||||
|
|
||||||
#### Custom Headers
|
#### Custom Headers
|
||||||
|
|
||||||
|
@ -265,6 +266,8 @@ You can define as many segments as ports exposed in an application.
|
||||||
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
||||||
| `traefik.<segment_name>.frontend.rule=EXP` | Overrides `traefik.frontend.rule`. Default: `{service_name}.{sub_domain}.{domain}` |
|
| `traefik.<segment_name>.frontend.rule=EXP` | Overrides `traefik.frontend.rule`. Default: `{service_name}.{sub_domain}.{domain}` |
|
||||||
| `traefik.<segment_name>.frontend.whitelistSourceRange=RANGE` | Overrides `traefik.frontend.whitelistSourceRange`. |
|
| `traefik.<segment_name>.frontend.whitelistSourceRange=RANGE` | Overrides `traefik.frontend.whitelistSourceRange`. |
|
||||||
|
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Overrides `traefik.frontend.whiteList.sourceRange`. |
|
||||||
|
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||||
|
|
||||||
#### Custom Headers
|
#### Custom Headers
|
||||||
|
|
||||||
|
|
|
@ -135,7 +135,8 @@ The following labels can be defined on Mesos tasks. They adjust the behaviour fo
|
||||||
| `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. Default: `Host:{discovery_name}.{domain}`. |
|
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{discovery_name}.{domain}`. |
|
||||||
| `traefik.frontend.whitelistSourceRange=RANGE` | 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` | 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` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||||
|
|
||||||
### Custom Headers
|
### Custom Headers
|
||||||
|
|
||||||
|
|
|
@ -159,7 +159,8 @@ Labels can be used on task containers to override default behaviour:
|
||||||
| `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. Default: `Host:{service_name}.{stack_name}.{domain}`. |
|
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{service_name}.{stack_name}.{domain}`. |
|
||||||
| `traefik.frontend.whitelistSourceRange=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. |
|
||||||
|
|
||||||
### Custom Headers
|
### Custom Headers
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,12 @@
|
||||||
[entryPoints]
|
[entryPoints]
|
||||||
[entryPoints.http]
|
[entryPoints.http]
|
||||||
address = ":80"
|
address = ":80"
|
||||||
whitelistSourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
|
|
||||||
compress = true
|
compress = true
|
||||||
|
|
||||||
|
[entryPoints.http.whitelist]
|
||||||
|
sourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
|
||||||
|
useXForwardedFor = true
|
||||||
|
|
||||||
[entryPoints.http.tls]
|
[entryPoints.http.tls]
|
||||||
minVersion = "VersionTLS12"
|
minVersion = "VersionTLS12"
|
||||||
cipherSuites = [
|
cipherSuites = [
|
||||||
|
@ -112,7 +115,8 @@ Redirect.Regex:http://localhost/(.*)
|
||||||
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
|
||||||
|
WhiteList.UseXForwardedFor:true
|
||||||
ProxyProtocol.TrustedIPs:192.168.0.1
|
ProxyProtocol.TrustedIPs:192.168.0.1
|
||||||
ProxyProtocol.Insecure:tue
|
ProxyProtocol.Insecure:tue
|
||||||
ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24
|
ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24
|
||||||
|
@ -352,15 +356,18 @@ Responses are compressed when:
|
||||||
* And the `Accept-Encoding` request header contains `gzip`
|
* And the `Accept-Encoding` request header contains `gzip`
|
||||||
* And the response is not already compressed, i.e. the `Content-Encoding` response header is not already set.
|
* And the response is not already compressed, i.e. the `Content-Encoding` response header is not already set.
|
||||||
|
|
||||||
## Whitelisting
|
## White Listing
|
||||||
|
|
||||||
To enable IP whitelisting at the entrypoint level.
|
To enable IP white listing at the entry point level.
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[entryPoints]
|
[entryPoints]
|
||||||
[entryPoints.http]
|
[entryPoints.http]
|
||||||
address = ":80"
|
address = ":80"
|
||||||
whiteListSourceRange = ["127.0.0.1/32", "192.168.1.7"]
|
|
||||||
|
[entryPoints.http]
|
||||||
|
sourceRange = ["127.0.0.1/32", "192.168.1.7"]
|
||||||
|
# useXForwardedFor = true
|
||||||
```
|
```
|
||||||
|
|
||||||
## ProxyProtocol
|
## ProxyProtocol
|
||||||
|
|
|
@ -2,7 +2,6 @@ package middlewares
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/containous/traefik/log"
|
"github.com/containous/traefik/log"
|
||||||
|
@ -18,49 +17,41 @@ type IPWhiteLister struct {
|
||||||
whiteLister *whitelist.IP
|
whiteLister *whitelist.IP
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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(whitelistStrings []string) (*IPWhiteLister, error) {
|
func NewIPWhiteLister(whiteList []string, useXForwardedFor bool) (*IPWhiteLister, error) {
|
||||||
|
if len(whiteList) == 0 {
|
||||||
if len(whitelistStrings) == 0 {
|
return nil, errors.New("no white list provided")
|
||||||
return nil, errors.New("no whitelists provided")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
whiteLister := IPWhiteLister{}
|
whiteLister := IPWhiteLister{}
|
||||||
|
|
||||||
ip, err := whitelist.NewIP(whitelistStrings, false)
|
ip, err := whitelist.NewIP(whiteList, false, useXForwardedFor)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("parsing CIDR whitelist %s: %v", whitelistStrings, err)
|
return nil, fmt.Errorf("parsing CIDR whitelist %s: %v", whiteList, err)
|
||||||
}
|
}
|
||||||
whiteLister.whiteLister = ip
|
whiteLister.whiteLister = ip
|
||||||
|
|
||||||
whiteLister.handler = negroni.HandlerFunc(whiteLister.handle)
|
whiteLister.handler = negroni.HandlerFunc(whiteLister.handle)
|
||||||
log.Debugf("configured %u IP whitelists: %s", len(whitelistStrings), whitelistStrings)
|
log.Debugf("configured %u IP white list: %s", len(whiteList), whiteList)
|
||||||
|
|
||||||
return &whiteLister, nil
|
return &whiteLister, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
ipAddress, _, err := net.SplitHostPort(r.RemoteAddr)
|
allowed, ip, err := wl.whiteLister.IsAuthorized(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tracing.SetErrorAndWarnLog(r, "unable to parse remote-address from header: %s - rejecting", r.RemoteAddr)
|
tracing.SetErrorAndDebugLog(r, "request %+v matched none of the white list - rejecting", r)
|
||||||
reject(w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
allowed, ip, err := wl.whiteLister.Contains(ipAddress)
|
|
||||||
if err != nil {
|
|
||||||
tracing.SetErrorAndDebugLog(r, "source-IP %s matched none of the whitelists - rejecting", ipAddress)
|
|
||||||
reject(w)
|
reject(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if allowed {
|
if allowed {
|
||||||
tracing.SetErrorAndDebugLog(r, "source-IP %s matched whitelist %s - passing", ipAddress, wl.whiteLister)
|
tracing.SetErrorAndDebugLog(r, "request %+v matched white list %s - passing", r, wl.whiteLister)
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tracing.SetErrorAndDebugLog(r, "source-IP %s matched none of the whitelists - rejecting", ip)
|
tracing.SetErrorAndDebugLog(r, "source-IP %s matched none of the white list - rejecting", ip)
|
||||||
reject(w)
|
reject(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ func (p *Provider) buildConfiguration(catalog []catalogUpdate) *types.Configurat
|
||||||
"getPriority": p.getFuncIntAttribute(label.SuffixFrontendPriority, label.DefaultFrontendPriorityInt),
|
"getPriority": p.getFuncIntAttribute(label.SuffixFrontendPriority, label.DefaultFrontendPriorityInt),
|
||||||
"getPassHostHeader": p.getFuncBoolAttribute(label.SuffixFrontendPassHostHeader, label.DefaultPassHostHeaderBool),
|
"getPassHostHeader": p.getFuncBoolAttribute(label.SuffixFrontendPassHostHeader, label.DefaultPassHostHeaderBool),
|
||||||
"getPassTLSCert": p.getFuncBoolAttribute(label.SuffixFrontendPassTLSCert, label.DefaultPassTLSCert),
|
"getPassTLSCert": p.getFuncBoolAttribute(label.SuffixFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||||
"getWhitelistSourceRange": p.getFuncSliceAttribute(label.SuffixFrontendWhitelistSourceRange),
|
"getWhiteList": p.getWhiteList,
|
||||||
"getRedirect": p.getRedirect,
|
"getRedirect": p.getRedirect,
|
||||||
"hasErrorPages": p.getFuncHasAttributePrefix(label.BaseFrontendErrorPage),
|
"hasErrorPages": p.getFuncHasAttributePrefix(label.BaseFrontendErrorPage),
|
||||||
"getErrorPages": p.getErrorPages,
|
"getErrorPages": p.getErrorPages,
|
||||||
|
@ -311,6 +311,19 @@ func (p *Provider) getBuffering(tags []string) *types.Buffering {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Provider) getWhiteList(tags []string) *types.WhiteList {
|
||||||
|
ranges := p.getSliceAttribute(label.SuffixFrontendWhiteListSourceRange, tags)
|
||||||
|
|
||||||
|
if len(ranges) > 0 {
|
||||||
|
return &types.WhiteList{
|
||||||
|
SourceRange: ranges,
|
||||||
|
UseXForwardedFor: p.getBoolAttribute(label.SuffixFrontendWhiteListUseXForwardedFor, tags, false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Provider) getRedirect(tags []string) *types.Redirect {
|
func (p *Provider) getRedirect(tags []string) *types.Redirect {
|
||||||
permanent := p.getBoolAttribute(label.SuffixFrontendRedirectPermanent, tags, false)
|
permanent := p.getBoolAttribute(label.SuffixFrontendRedirectPermanent, tags, false)
|
||||||
|
|
||||||
|
|
|
@ -1048,6 +1048,65 @@ func TestProviderGetBuffering(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestProviderWhiteList(t *testing.T) {
|
||||||
|
p := &Provider{
|
||||||
|
Prefix: "traefik",
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
tags []string
|
||||||
|
expected *types.WhiteList
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "should return nil when no white list labels",
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return a struct when only range",
|
||||||
|
tags: []string{
|
||||||
|
label.TraefikFrontendWhiteListSourceRange + "=10.10.10.10",
|
||||||
|
},
|
||||||
|
expected: &types.WhiteList{
|
||||||
|
SourceRange: []string{
|
||||||
|
"10.10.10.10",
|
||||||
|
},
|
||||||
|
UseXForwardedFor: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return a struct when range and UseXForwardedFor",
|
||||||
|
tags: []string{
|
||||||
|
label.TraefikFrontendWhiteListSourceRange + "=10.10.10.10",
|
||||||
|
label.TraefikFrontendWhiteListUseXForwardedFor + "=true",
|
||||||
|
},
|
||||||
|
expected: &types.WhiteList{
|
||||||
|
SourceRange: []string{
|
||||||
|
"10.10.10.10",
|
||||||
|
},
|
||||||
|
UseXForwardedFor: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return nil when only UseXForwardedFor",
|
||||||
|
tags: []string{
|
||||||
|
label.TraefikFrontendWhiteListUseXForwardedFor + "=true",
|
||||||
|
},
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
actual := p.getWhiteList(test.tags)
|
||||||
|
assert.Equal(t, test.expected, actual)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestProviderGetRedirect(t *testing.T) {
|
func TestProviderGetRedirect(t *testing.T) {
|
||||||
p := &Provider{
|
p := &Provider{
|
||||||
Prefix: "traefik",
|
Prefix: "traefik",
|
||||||
|
|
|
@ -46,12 +46,12 @@ func (p *Provider) buildConfigurationV2(containersInspected []dockerData) *types
|
||||||
"getPassTLSCert": getFuncBoolLabel(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
"getPassTLSCert": getFuncBoolLabel(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||||
"getEntryPoints": getFuncSliceStringLabel(label.TraefikFrontendEntryPoints),
|
"getEntryPoints": getFuncSliceStringLabel(label.TraefikFrontendEntryPoints),
|
||||||
"getBasicAuth": getFuncSliceStringLabel(label.TraefikFrontendAuthBasic),
|
"getBasicAuth": getFuncSliceStringLabel(label.TraefikFrontendAuthBasic),
|
||||||
"getWhitelistSourceRange": getFuncSliceStringLabel(label.TraefikFrontendWhitelistSourceRange),
|
|
||||||
"getFrontendRule": p.getFrontendRule,
|
"getFrontendRule": p.getFrontendRule,
|
||||||
"getRedirect": getRedirect,
|
"getRedirect": getRedirect,
|
||||||
"getErrorPages": getErrorPages,
|
"getErrorPages": getErrorPages,
|
||||||
"getRateLimit": getRateLimit,
|
"getRateLimit": getRateLimit,
|
||||||
"getHeaders": getHeaders,
|
"getHeaders": getHeaders,
|
||||||
|
"getWhiteList": getWhiteList,
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter containers
|
// filter containers
|
||||||
|
@ -276,6 +276,31 @@ func getBackendName(container dockerData) string {
|
||||||
return getDefaultBackendName(container)
|
return getDefaultBackendName(container)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getWhiteList(labels map[string]string) *types.WhiteList {
|
||||||
|
if label.Has(labels, label.TraefikFrontendWhitelistSourceRange) {
|
||||||
|
log.Warnf("Deprecated configuration found: %s. Please use %s.", label.TraefikFrontendWhitelistSourceRange, label.TraefikFrontendWhiteListSourceRange)
|
||||||
|
}
|
||||||
|
|
||||||
|
ranges := label.GetSliceStringValue(labels, label.TraefikFrontendWhiteListSourceRange)
|
||||||
|
if len(ranges) > 0 {
|
||||||
|
return &types.WhiteList{
|
||||||
|
SourceRange: ranges,
|
||||||
|
UseXForwardedFor: label.GetBoolValue(labels, label.TraefikFrontendWhiteListUseXForwardedFor, false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Deprecated
|
||||||
|
values := label.GetSliceStringValue(labels, label.TraefikFrontendWhitelistSourceRange)
|
||||||
|
if len(values) > 0 {
|
||||||
|
return &types.WhiteList{
|
||||||
|
SourceRange: values,
|
||||||
|
UseXForwardedFor: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func getRedirect(labels map[string]string) *types.Redirect {
|
func getRedirect(labels map[string]string) *types.Redirect {
|
||||||
permanent := label.GetBoolValue(labels, label.TraefikFrontendRedirectPermanent, false)
|
permanent := label.GetBoolValue(labels, label.TraefikFrontendRedirectPermanent, false)
|
||||||
|
|
||||||
|
|
|
@ -123,7 +123,8 @@ func TestDockerBuildConfiguration(t *testing.T) {
|
||||||
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.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",
|
||||||
|
@ -187,8 +188,9 @@ func TestDockerBuildConfiguration(t *testing.T) {
|
||||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
},
|
},
|
||||||
WhitelistSourceRange: []string{
|
WhiteList: &types.WhiteList{
|
||||||
"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{
|
||||||
|
@ -692,17 +694,17 @@ func TestDockerGetSliceStringLabel(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "whitelist-label with empty string",
|
desc: "whitelist-label with empty string",
|
||||||
labels: map[string]string{
|
labels: map[string]string{
|
||||||
label.TraefikFrontendWhitelistSourceRange: "",
|
label.TraefikFrontendWhiteListSourceRange: "",
|
||||||
},
|
},
|
||||||
labelName: label.TraefikFrontendWhitelistSourceRange,
|
labelName: label.TraefikFrontendWhiteListSourceRange,
|
||||||
expected: nil,
|
expected: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "whitelist-label with IPv4 mask",
|
desc: "whitelist-label with IPv4 mask",
|
||||||
labels: map[string]string{
|
labels: map[string]string{
|
||||||
label.TraefikFrontendWhitelistSourceRange: "1.2.3.4/16",
|
label.TraefikFrontendWhiteListSourceRange: "1.2.3.4/16",
|
||||||
},
|
},
|
||||||
labelName: label.TraefikFrontendWhitelistSourceRange,
|
labelName: label.TraefikFrontendWhiteListSourceRange,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"1.2.3.4/16",
|
"1.2.3.4/16",
|
||||||
},
|
},
|
||||||
|
@ -710,9 +712,9 @@ func TestDockerGetSliceStringLabel(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "whitelist-label with IPv6 mask",
|
desc: "whitelist-label with IPv6 mask",
|
||||||
labels: map[string]string{
|
labels: map[string]string{
|
||||||
label.TraefikFrontendWhitelistSourceRange: "fe80::/16",
|
label.TraefikFrontendWhiteListSourceRange: "fe80::/16",
|
||||||
},
|
},
|
||||||
labelName: label.TraefikFrontendWhitelistSourceRange,
|
labelName: label.TraefikFrontendWhiteListSourceRange,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"fe80::/16",
|
"fe80::/16",
|
||||||
},
|
},
|
||||||
|
@ -720,9 +722,9 @@ func TestDockerGetSliceStringLabel(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "whitelist-label with multiple masks",
|
desc: "whitelist-label with multiple masks",
|
||||||
labels: map[string]string{
|
labels: map[string]string{
|
||||||
label.TraefikFrontendWhitelistSourceRange: "1.1.1.1/24, 1234:abcd::42/32",
|
label.TraefikFrontendWhiteListSourceRange: "1.1.1.1/24, 1234:abcd::42/32",
|
||||||
},
|
},
|
||||||
labelName: label.TraefikFrontendWhitelistSourceRange,
|
labelName: label.TraefikFrontendWhiteListSourceRange,
|
||||||
expected: []string{
|
expected: []string{
|
||||||
"1.1.1.1/24",
|
"1.1.1.1/24",
|
||||||
"1234:abcd::42/32",
|
"1234:abcd::42/32",
|
||||||
|
@ -1025,3 +1027,85 @@ func TestDockerGetPort(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWhiteList(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
labels map[string]string
|
||||||
|
expected *types.WhiteList
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "should return nil when no white list labels",
|
||||||
|
labels: map[string]string{},
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return a struct when deprecated label",
|
||||||
|
labels: map[string]string{
|
||||||
|
label.TraefikFrontendWhitelistSourceRange: "10.10.10.10",
|
||||||
|
},
|
||||||
|
expected: &types.WhiteList{
|
||||||
|
SourceRange: []string{
|
||||||
|
"10.10.10.10",
|
||||||
|
},
|
||||||
|
UseXForwardedFor: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return a struct when only range",
|
||||||
|
labels: map[string]string{
|
||||||
|
label.TraefikFrontendWhiteListSourceRange: "10.10.10.10",
|
||||||
|
},
|
||||||
|
expected: &types.WhiteList{
|
||||||
|
SourceRange: []string{
|
||||||
|
"10.10.10.10",
|
||||||
|
},
|
||||||
|
UseXForwardedFor: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return a struct when range and UseXForwardedFor",
|
||||||
|
labels: map[string]string{
|
||||||
|
label.TraefikFrontendWhiteListSourceRange: "10.10.10.10",
|
||||||
|
label.TraefikFrontendWhiteListUseXForwardedFor: "true",
|
||||||
|
},
|
||||||
|
expected: &types.WhiteList{
|
||||||
|
SourceRange: []string{
|
||||||
|
"10.10.10.10",
|
||||||
|
},
|
||||||
|
UseXForwardedFor: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return a struct when mix deprecated label and new labels",
|
||||||
|
labels: map[string]string{
|
||||||
|
label.TraefikFrontendWhitelistSourceRange: "20.20.20.20",
|
||||||
|
label.TraefikFrontendWhiteListSourceRange: "10.10.10.10",
|
||||||
|
label.TraefikFrontendWhiteListUseXForwardedFor: "true",
|
||||||
|
},
|
||||||
|
expected: &types.WhiteList{
|
||||||
|
SourceRange: []string{
|
||||||
|
"10.10.10.10",
|
||||||
|
},
|
||||||
|
UseXForwardedFor: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return nil when only UseXForwardedFor",
|
||||||
|
labels: map[string]string{
|
||||||
|
label.TraefikFrontendWhiteListUseXForwardedFor: "true",
|
||||||
|
},
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
actual := getWhiteList(test.labels)
|
||||||
|
assert.Equal(t, test.expected, actual)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -131,7 +131,8 @@ func TestSwarmBuildConfiguration(t *testing.T) {
|
||||||
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.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",
|
||||||
|
@ -193,8 +194,9 @@ func TestSwarmBuildConfiguration(t *testing.T) {
|
||||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
},
|
},
|
||||||
WhitelistSourceRange: []string{
|
WhiteList: &types.WhiteList{
|
||||||
"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{
|
||||||
|
|
|
@ -85,7 +85,8 @@ func TestSegmentBuildConfiguration(t *testing.T) {
|
||||||
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",
|
||||||
|
@ -144,8 +145,9 @@ func TestSegmentBuildConfiguration(t *testing.T) {
|
||||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
},
|
},
|
||||||
WhitelistSourceRange: []string{
|
WhiteList: &types.WhiteList{
|
||||||
"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{
|
||||||
|
|
|
@ -103,7 +103,7 @@ func (p *Provider) buildConfigurationV1(containersInspected []dockerData) *types
|
||||||
"getServiceWeight": getFuncServiceStringLabelV1(label.SuffixWeight, label.DefaultWeight),
|
"getServiceWeight": getFuncServiceStringLabelV1(label.SuffixWeight, label.DefaultWeight),
|
||||||
// Services - Frontend functions
|
// Services - Frontend functions
|
||||||
"getServiceEntryPoints": getFuncServiceSliceStringLabelV1(label.SuffixFrontendEntryPoints),
|
"getServiceEntryPoints": getFuncServiceSliceStringLabelV1(label.SuffixFrontendEntryPoints),
|
||||||
"getServiceWhitelistSourceRange": getFuncServiceSliceStringLabelV1(label.SuffixFrontendWhitelistSourceRange),
|
"getServiceWhitelistSourceRange": getFuncServiceSliceStringLabelV1(label.SuffixFrontendWhiteListSourceRange),
|
||||||
"getServiceBasicAuth": getFuncServiceSliceStringLabelV1(label.SuffixFrontendAuthBasic),
|
"getServiceBasicAuth": getFuncServiceSliceStringLabelV1(label.SuffixFrontendAuthBasic),
|
||||||
"getServiceFrontendRule": p.getServiceFrontendRuleV1,
|
"getServiceFrontendRule": p.getServiceFrontendRuleV1,
|
||||||
"getServicePassHostHeader": getFuncServiceBoolLabelV1(label.SuffixFrontendPassHostHeader, label.DefaultPassHostHeaderBool),
|
"getServicePassHostHeader": getFuncServiceBoolLabelV1(label.SuffixFrontendPassHostHeader, label.DefaultPassHostHeaderBool),
|
||||||
|
|
|
@ -55,11 +55,11 @@ func (p *Provider) buildConfiguration(services map[string][]ecsInstance) (*types
|
||||||
"getPriority": getFuncIntValue(label.TraefikFrontendPriority, label.DefaultFrontendPriorityInt),
|
"getPriority": getFuncIntValue(label.TraefikFrontendPriority, label.DefaultFrontendPriorityInt),
|
||||||
"getBasicAuth": getFuncSliceString(label.TraefikFrontendAuthBasic),
|
"getBasicAuth": getFuncSliceString(label.TraefikFrontendAuthBasic),
|
||||||
"getEntryPoints": getFuncSliceString(label.TraefikFrontendEntryPoints),
|
"getEntryPoints": getFuncSliceString(label.TraefikFrontendEntryPoints),
|
||||||
"getWhitelistSourceRange": getFuncSliceString(label.TraefikFrontendWhitelistSourceRange),
|
|
||||||
"getRedirect": getRedirect,
|
"getRedirect": getRedirect,
|
||||||
"getErrorPages": getErrorPages,
|
"getErrorPages": getErrorPages,
|
||||||
"getRateLimit": getRateLimit,
|
"getRateLimit": getRateLimit,
|
||||||
"getHeaders": getHeaders,
|
"getHeaders": getHeaders,
|
||||||
|
"getWhiteList": getWhiteList,
|
||||||
}
|
}
|
||||||
return p.GetConfiguration("templates/ecs.tmpl", ecsFuncMap, struct {
|
return p.GetConfiguration("templates/ecs.tmpl", ecsFuncMap, struct {
|
||||||
Services map[string][]ecsInstance
|
Services map[string][]ecsInstance
|
||||||
|
@ -211,6 +211,18 @@ func getServers(instances []ecsInstance) map[string]types.Server {
|
||||||
return servers
|
return servers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getWhiteList(instance ecsInstance) *types.WhiteList {
|
||||||
|
ranges := getSliceString(instance, label.TraefikFrontendWhiteListSourceRange)
|
||||||
|
if len(ranges) > 0 {
|
||||||
|
return &types.WhiteList{
|
||||||
|
SourceRange: ranges,
|
||||||
|
UseXForwardedFor: getBoolValue(instance, label.TraefikFrontendWhiteListUseXForwardedFor, false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func getRedirect(instance ecsInstance) *types.Redirect {
|
func getRedirect(instance ecsInstance) *types.Redirect {
|
||||||
permanent := getBoolValue(instance, label.TraefikFrontendRedirectPermanent, false)
|
permanent := getBoolValue(instance, label.TraefikFrontendRedirectPermanent, false)
|
||||||
|
|
||||||
|
|
|
@ -152,7 +152,8 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
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"),
|
||||||
|
@ -257,8 +258,9 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
},
|
},
|
||||||
WhitelistSourceRange: []string{
|
WhiteList: &types.WhiteList{
|
||||||
"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{
|
||||||
|
@ -1115,6 +1117,74 @@ func TestGetServers(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWhiteList(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
instance ecsInstance
|
||||||
|
expected *types.WhiteList
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "should return nil when no white list labels",
|
||||||
|
instance: ecsInstance{
|
||||||
|
containerDefinition: &ecs.ContainerDefinition{
|
||||||
|
DockerLabels: map[string]*string{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return a struct when only range",
|
||||||
|
instance: ecsInstance{
|
||||||
|
containerDefinition: &ecs.ContainerDefinition{
|
||||||
|
DockerLabels: map[string]*string{
|
||||||
|
label.TraefikFrontendWhiteListSourceRange: aws.String("10.10.10.10"),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
expected: &types.WhiteList{
|
||||||
|
SourceRange: []string{
|
||||||
|
"10.10.10.10",
|
||||||
|
},
|
||||||
|
UseXForwardedFor: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return a struct when range and UseXForwardedFor",
|
||||||
|
instance: ecsInstance{
|
||||||
|
containerDefinition: &ecs.ContainerDefinition{
|
||||||
|
DockerLabels: map[string]*string{
|
||||||
|
label.TraefikFrontendWhiteListSourceRange: aws.String("10.10.10.10"),
|
||||||
|
label.TraefikFrontendWhiteListUseXForwardedFor: aws.String("true"),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
expected: &types.WhiteList{
|
||||||
|
SourceRange: []string{
|
||||||
|
"10.10.10.10",
|
||||||
|
},
|
||||||
|
UseXForwardedFor: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return nil when only UseXForwardedFor",
|
||||||
|
instance: ecsInstance{
|
||||||
|
containerDefinition: &ecs.ContainerDefinition{
|
||||||
|
DockerLabels: map[string]*string{
|
||||||
|
label.TraefikFrontendWhiteListUseXForwardedFor: aws.String("true"),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
actual := getWhiteList(test.instance)
|
||||||
|
assert.Equal(t, test.expected, actual)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetRedirect(t *testing.T) {
|
func TestGetRedirect(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
|
|
@ -10,7 +10,8 @@ const (
|
||||||
annotationKubernetesAuthType = "ingress.kubernetes.io/auth-type"
|
annotationKubernetesAuthType = "ingress.kubernetes.io/auth-type"
|
||||||
annotationKubernetesAuthSecret = "ingress.kubernetes.io/auth-secret"
|
annotationKubernetesAuthSecret = "ingress.kubernetes.io/auth-secret"
|
||||||
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"
|
||||||
annotationKubernetesPreserveHost = "ingress.kubernetes.io/preserve-host"
|
annotationKubernetesPreserveHost = "ingress.kubernetes.io/preserve-host"
|
||||||
annotationKubernetesPassTLSCert = "ingress.kubernetes.io/pass-tls-cert"
|
annotationKubernetesPassTLSCert = "ingress.kubernetes.io/pass-tls-cert"
|
||||||
annotationKubernetesFrontendEntryPoints = "ingress.kubernetes.io/frontend-entry-points"
|
annotationKubernetesFrontendEntryPoints = "ingress.kubernetes.io/frontend-entry-points"
|
||||||
|
|
|
@ -202,9 +202,13 @@ func basicAuth(auth ...string) func(*types.Frontend) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func whitelistSourceRange(ranges ...string) func(*types.Frontend) {
|
func whiteList(useXFF bool, ranges ...string) func(*types.Frontend) {
|
||||||
return func(f *types.Frontend) {
|
return func(f *types.Frontend) {
|
||||||
f.WhitelistSourceRange = ranges
|
if f.WhiteList == nil {
|
||||||
|
f.WhiteList = &types.WhiteList{}
|
||||||
|
}
|
||||||
|
f.WhiteList.UseXForwardedFor = useXFF
|
||||||
|
f.WhiteList.SourceRange = ranges
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -200,7 +200,6 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error)
|
||||||
passTLSCert := getBoolValue(i.Annotations, annotationKubernetesPassTLSCert, p.EnablePassTLSCert)
|
passTLSCert := getBoolValue(i.Annotations, annotationKubernetesPassTLSCert, p.EnablePassTLSCert)
|
||||||
priority := getIntValue(i.Annotations, annotationKubernetesPriority, 0)
|
priority := getIntValue(i.Annotations, annotationKubernetesPriority, 0)
|
||||||
entryPoints := getSliceStringValue(i.Annotations, annotationKubernetesFrontendEntryPoints)
|
entryPoints := getSliceStringValue(i.Annotations, annotationKubernetesFrontendEntryPoints)
|
||||||
whitelistSourceRange := getSliceStringValue(i.Annotations, annotationKubernetesWhitelistSourceRange)
|
|
||||||
|
|
||||||
templateObjects.Frontends[baseName] = &types.Frontend{
|
templateObjects.Frontends[baseName] = &types.Frontend{
|
||||||
Backend: baseName,
|
Backend: baseName,
|
||||||
|
@ -209,7 +208,7 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error)
|
||||||
Routes: make(map[string]types.Route),
|
Routes: make(map[string]types.Route),
|
||||||
Priority: priority,
|
Priority: priority,
|
||||||
BasicAuth: basicAuthCreds,
|
BasicAuth: basicAuthCreds,
|
||||||
WhitelistSourceRange: whitelistSourceRange,
|
WhiteList: getWhiteList(i),
|
||||||
Redirect: getFrontendRedirect(i),
|
Redirect: getFrontendRedirect(i),
|
||||||
EntryPoints: entryPoints,
|
EntryPoints: entryPoints,
|
||||||
Headers: getHeader(i),
|
Headers: getHeader(i),
|
||||||
|
@ -457,7 +456,7 @@ func getTLS(ingress *extensionsv1beta1.Ingress, k8sClient Client) ([]*tls.Config
|
||||||
|
|
||||||
func endpointPortNumber(servicePort corev1.ServicePort, endpointPorts []corev1.EndpointPort) int {
|
func endpointPortNumber(servicePort corev1.ServicePort, endpointPorts []corev1.EndpointPort) int {
|
||||||
if len(endpointPorts) > 0 {
|
if len(endpointPorts) > 0 {
|
||||||
//name is optional if there is only one port
|
// name is optional if there is only one port
|
||||||
port := endpointPorts[0]
|
port := endpointPorts[0]
|
||||||
for _, endpointPort := range endpointPorts {
|
for _, endpointPort := range endpointPorts {
|
||||||
if servicePort.Name == endpointPort.Name {
|
if servicePort.Name == endpointPort.Name {
|
||||||
|
@ -510,6 +509,18 @@ func getFrontendRedirect(i *extensionsv1beta1.Ingress) *types.Redirect {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getWhiteList(i *extensionsv1beta1.Ingress) *types.WhiteList {
|
||||||
|
ranges := getSliceStringValue(i.Annotations, annotationKubernetesWhiteListSourceRange)
|
||||||
|
if len(ranges) <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &types.WhiteList{
|
||||||
|
SourceRange: ranges,
|
||||||
|
UseXForwardedFor: getBoolValue(i.Annotations, annotationKubernetesWhiteListUseXForwardedFor, false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func getBuffering(service *corev1.Service) *types.Buffering {
|
func getBuffering(service *corev1.Service) *types.Buffering {
|
||||||
var buffering *types.Buffering
|
var buffering *types.Buffering
|
||||||
|
|
||||||
|
|
|
@ -665,7 +665,8 @@ 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"),
|
||||||
iRules(
|
iRules(
|
||||||
iRule(
|
iRule(
|
||||||
iHost("test"),
|
iHost("test"),
|
||||||
|
@ -984,7 +985,7 @@ rateset:
|
||||||
),
|
),
|
||||||
frontend("test/whitelist-source-range",
|
frontend("test/whitelist-source-range",
|
||||||
passHostHeader(),
|
passHostHeader(),
|
||||||
whitelistSourceRange("1.1.1.1/24", "1234:abcd::42/32"),
|
whiteList(true, "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")),
|
||||||
|
|
|
@ -28,7 +28,8 @@ const (
|
||||||
pathFrontendPassHostHeaderDeprecated = "/passHostHeader" // Deprecated
|
pathFrontendPassHostHeaderDeprecated = "/passHostHeader" // Deprecated
|
||||||
pathFrontendPassHostHeader = "/passhostheader"
|
pathFrontendPassHostHeader = "/passhostheader"
|
||||||
pathFrontendPassTLSCert = "/passtlscert"
|
pathFrontendPassTLSCert = "/passtlscert"
|
||||||
pathFrontendWhiteListSourceRange = "/whitelistsourcerange"
|
pathFrontendWhiteListSourceRange = "/whitelist/sourcerange"
|
||||||
|
pathFrontendWhiteListUseXForwardedFor = "/whitelist/usexforwardedfor"
|
||||||
pathFrontendBasicAuth = "/basicauth"
|
pathFrontendBasicAuth = "/basicauth"
|
||||||
pathFrontendEntryPoints = "/entrypoints"
|
pathFrontendEntryPoints = "/entrypoints"
|
||||||
pathFrontendRedirectEntryPoint = "/redirect/entrypoint"
|
pathFrontendRedirectEntryPoint = "/redirect/entrypoint"
|
||||||
|
|
|
@ -46,13 +46,13 @@ func (p *Provider) buildConfiguration() *types.Configuration {
|
||||||
"getPassHostHeader": p.getPassHostHeader(),
|
"getPassHostHeader": p.getPassHostHeader(),
|
||||||
"getPassTLSCert": p.getFuncBool(pathFrontendPassTLSCert, label.DefaultPassTLSCert),
|
"getPassTLSCert": p.getFuncBool(pathFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||||
"getEntryPoints": p.getFuncList(pathFrontendEntryPoints),
|
"getEntryPoints": p.getFuncList(pathFrontendEntryPoints),
|
||||||
"getWhitelistSourceRange": p.getFuncList(pathFrontendWhiteListSourceRange),
|
|
||||||
"getBasicAuth": p.getFuncList(pathFrontendBasicAuth),
|
"getBasicAuth": p.getFuncList(pathFrontendBasicAuth),
|
||||||
"getRoutes": p.getRoutes,
|
"getRoutes": p.getRoutes,
|
||||||
"getRedirect": p.getRedirect,
|
"getRedirect": p.getRedirect,
|
||||||
"getErrorPages": p.getErrorPages,
|
"getErrorPages": p.getErrorPages,
|
||||||
"getRateLimit": p.getRateLimit,
|
"getRateLimit": p.getRateLimit,
|
||||||
"getHeaders": p.getHeaders,
|
"getHeaders": p.getHeaders,
|
||||||
|
"getWhiteList": p.getWhiteList,
|
||||||
|
|
||||||
// Backend functions
|
// Backend functions
|
||||||
"getServers": p.getServers,
|
"getServers": p.getServers,
|
||||||
|
@ -125,6 +125,19 @@ func (p *Provider) getStickinessCookieName(rootPath string) string {
|
||||||
return p.get("", rootPath, pathBackendLoadBalancerStickinessCookieName)
|
return p.get("", rootPath, pathBackendLoadBalancerStickinessCookieName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Provider) getWhiteList(rootPath string) *types.WhiteList {
|
||||||
|
ranges := p.getList(rootPath, pathFrontendWhiteListSourceRange)
|
||||||
|
|
||||||
|
if len(ranges) > 0 {
|
||||||
|
return &types.WhiteList{
|
||||||
|
SourceRange: ranges,
|
||||||
|
UseXForwardedFor: p.getBool(false, rootPath, pathFrontendWhiteListUseXForwardedFor),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Provider) getRedirect(rootPath string) *types.Redirect {
|
func (p *Provider) getRedirect(rootPath string) *types.Redirect {
|
||||||
permanent := p.getBool(false, rootPath, pathFrontendRedirectPermanent)
|
permanent := p.getBool(false, rootPath, pathFrontendRedirectPermanent)
|
||||||
|
|
||||||
|
|
|
@ -91,6 +91,7 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
||||||
withPair(pathFrontendPassTLSCert, "true"),
|
withPair(pathFrontendPassTLSCert, "true"),
|
||||||
withPair(pathFrontendEntryPoints, "http,https"),
|
withPair(pathFrontendEntryPoints, "http,https"),
|
||||||
withPair(pathFrontendWhiteListSourceRange, "1.1.1.1/24, 1234:abcd::42/32"),
|
withPair(pathFrontendWhiteListSourceRange, "1.1.1.1/24, 1234:abcd::42/32"),
|
||||||
|
withPair(pathFrontendWhiteListUseXForwardedFor, "true"),
|
||||||
withPair(pathFrontendBasicAuth, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/, test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
withPair(pathFrontendBasicAuth, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/, test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
||||||
withPair(pathFrontendRedirectEntryPoint, "https"),
|
withPair(pathFrontendRedirectEntryPoint, "https"),
|
||||||
withPair(pathFrontendRedirectRegex, "nope"),
|
withPair(pathFrontendRedirectRegex, "nope"),
|
||||||
|
@ -184,7 +185,10 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
||||||
EntryPoints: []string{"http", "https"},
|
EntryPoints: []string{"http", "https"},
|
||||||
Backend: "backend1",
|
Backend: "backend1",
|
||||||
PassTLSCert: true,
|
PassTLSCert: true,
|
||||||
WhitelistSourceRange: []string{"1.1.1.1/24", "1234:abcd::42/32"},
|
WhiteList: &types.WhiteList{
|
||||||
|
SourceRange: []string{"1.1.1.1/24", "1234:abcd::42/32"},
|
||||||
|
UseXForwardedFor: true,
|
||||||
|
},
|
||||||
BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"},
|
BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"},
|
||||||
Redirect: &types.Redirect{
|
Redirect: &types.Redirect{
|
||||||
EntryPoint: "https",
|
EntryPoint: "https",
|
||||||
|
@ -1031,6 +1035,68 @@ func TestProviderHasStickinessLabel(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWhiteList(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
rootPath string
|
||||||
|
kvPairs []*store.KVPair
|
||||||
|
expected *types.WhiteList
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "should return nil when no white list labels",
|
||||||
|
rootPath: "traefik/frontends/foo",
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return a struct when only range",
|
||||||
|
rootPath: "traefik/frontends/foo",
|
||||||
|
kvPairs: filler("traefik",
|
||||||
|
frontend("foo",
|
||||||
|
withPair(pathFrontendWhiteListSourceRange, "10.10.10.10"))),
|
||||||
|
expected: &types.WhiteList{
|
||||||
|
SourceRange: []string{
|
||||||
|
"10.10.10.10",
|
||||||
|
},
|
||||||
|
UseXForwardedFor: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return a struct when range and UseXForwardedFor",
|
||||||
|
rootPath: "traefik/frontends/foo",
|
||||||
|
kvPairs: filler("traefik",
|
||||||
|
frontend("foo",
|
||||||
|
withPair(pathFrontendWhiteListSourceRange, "10.10.10.10"),
|
||||||
|
withPair(pathFrontendWhiteListUseXForwardedFor, "true"))),
|
||||||
|
expected: &types.WhiteList{
|
||||||
|
SourceRange: []string{
|
||||||
|
"10.10.10.10",
|
||||||
|
},
|
||||||
|
UseXForwardedFor: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return nil when only UseXForwardedFor",
|
||||||
|
rootPath: "traefik/frontends/foo",
|
||||||
|
kvPairs: filler("traefik",
|
||||||
|
frontend("foo",
|
||||||
|
withPair(pathFrontendWhiteListUseXForwardedFor, "true"))),
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
p := newProviderMock(test.kvPairs)
|
||||||
|
|
||||||
|
actual := p.getWhiteList(test.rootPath)
|
||||||
|
assert.Equal(t, test.expected, actual)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestProviderGetRedirect(t *testing.T) {
|
func TestProviderGetRedirect(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
|
|
@ -65,7 +65,10 @@ 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"
|
SuffixFrontendWhitelistSourceRange = "frontend.whitelistSourceRange" // Deprecated
|
||||||
|
SuffixFrontendWhiteList = "frontend.whiteList."
|
||||||
|
SuffixFrontendWhiteListSourceRange = SuffixFrontendWhiteList + "sourceRange"
|
||||||
|
SuffixFrontendWhiteListUseXForwardedFor = SuffixFrontendWhiteList + "useXForwardedFor"
|
||||||
TraefikDomain = Prefix + SuffixDomain
|
TraefikDomain = Prefix + SuffixDomain
|
||||||
TraefikEnable = Prefix + SuffixEnable
|
TraefikEnable = Prefix + SuffixEnable
|
||||||
TraefikPort = Prefix + SuffixPort
|
TraefikPort = Prefix + SuffixPort
|
||||||
|
@ -106,7 +109,9 @@ const (
|
||||||
TraefikFrontendRedirectReplacement = Prefix + SuffixFrontendRedirectReplacement
|
TraefikFrontendRedirectReplacement = Prefix + SuffixFrontendRedirectReplacement
|
||||||
TraefikFrontendRedirectPermanent = Prefix + SuffixFrontendRedirectPermanent
|
TraefikFrontendRedirectPermanent = Prefix + SuffixFrontendRedirectPermanent
|
||||||
TraefikFrontendRule = Prefix + SuffixFrontendRule
|
TraefikFrontendRule = Prefix + SuffixFrontendRule
|
||||||
TraefikFrontendWhitelistSourceRange = Prefix + SuffixFrontendWhitelistSourceRange
|
TraefikFrontendWhitelistSourceRange = Prefix + SuffixFrontendWhitelistSourceRange // Deprecated
|
||||||
|
TraefikFrontendWhiteListSourceRange = Prefix + SuffixFrontendWhiteListSourceRange
|
||||||
|
TraefikFrontendWhiteListUseXForwardedFor = Prefix + SuffixFrontendWhiteListUseXForwardedFor
|
||||||
TraefikFrontendRequestHeaders = Prefix + SuffixFrontendRequestHeaders
|
TraefikFrontendRequestHeaders = Prefix + SuffixFrontendRequestHeaders
|
||||||
TraefikFrontendResponseHeaders = Prefix + SuffixFrontendResponseHeaders
|
TraefikFrontendResponseHeaders = Prefix + SuffixFrontendResponseHeaders
|
||||||
TraefikFrontendAllowedHosts = Prefix + SuffixFrontendHeadersAllowedHosts
|
TraefikFrontendAllowedHosts = Prefix + SuffixFrontendHeadersAllowedHosts
|
||||||
|
|
|
@ -76,11 +76,14 @@ func (p *Provider) buildConfiguration() *types.Configuration {
|
||||||
"getFrontendRule": p.getFrontendRule,
|
"getFrontendRule": p.getFrontendRule,
|
||||||
"getFrontendName": p.getFrontendName,
|
"getFrontendName": p.getFrontendName,
|
||||||
"getBasicAuth": getFuncSliceStringService(label.SuffixFrontendAuthBasic),
|
"getBasicAuth": getFuncSliceStringService(label.SuffixFrontendAuthBasic),
|
||||||
"getWhitelistSourceRange": getFuncSliceStringService(label.SuffixFrontendWhitelistSourceRange),
|
|
||||||
"getRedirect": getRedirect,
|
"getRedirect": getRedirect,
|
||||||
"getErrorPages": getErrorPages,
|
"getErrorPages": getErrorPages,
|
||||||
"getRateLimit": getRateLimit,
|
"getRateLimit": getRateLimit,
|
||||||
"getHeaders": getHeaders,
|
"getHeaders": getHeaders,
|
||||||
|
"getWhiteList": getWhiteList,
|
||||||
|
|
||||||
|
// TODO Deprecated [breaking]
|
||||||
|
"getWhitelistSourceRange": getFuncSliceStringService(label.SuffixFrontendWhitelistSourceRange),
|
||||||
}
|
}
|
||||||
|
|
||||||
v := url.Values{}
|
v := url.Values{}
|
||||||
|
@ -486,6 +489,20 @@ func (p *Provider) getServers(application marathon.Application, serviceName stri
|
||||||
return servers
|
return servers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getWhiteList(application marathon.Application, serviceName string) *types.WhiteList {
|
||||||
|
labels := getLabels(application, serviceName)
|
||||||
|
|
||||||
|
ranges := label.GetSliceStringValue(labels, getLabelName(serviceName, label.SuffixFrontendWhiteListSourceRange))
|
||||||
|
if len(ranges) > 0 {
|
||||||
|
return &types.WhiteList{
|
||||||
|
SourceRange: ranges,
|
||||||
|
UseXForwardedFor: label.GetBoolValue(labels, getLabelName(serviceName, label.SuffixFrontendWhiteListUseXForwardedFor), false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func getRedirect(application marathon.Application, serviceName string) *types.Redirect {
|
func getRedirect(application marathon.Application, serviceName string) *types.Redirect {
|
||||||
labels := getLabels(application, serviceName)
|
labels := getLabels(application, serviceName)
|
||||||
|
|
||||||
|
|
|
@ -206,7 +206,8 @@ func TestBuildConfigurationNonAPIErrors(t *testing.T) {
|
||||||
withLabel(label.TraefikFrontendRedirectReplacement, "nope"),
|
withLabel(label.TraefikFrontendRedirectReplacement, "nope"),
|
||||||
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.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"),
|
||||||
|
@ -268,8 +269,9 @@ func TestBuildConfigurationNonAPIErrors(t *testing.T) {
|
||||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
},
|
},
|
||||||
WhitelistSourceRange: []string{
|
WhiteList: &types.WhiteList{
|
||||||
"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{
|
||||||
|
@ -498,7 +500,7 @@ func TestBuildConfigurationServicesNonAPIErrors(t *testing.T) {
|
||||||
application: application(
|
application: application(
|
||||||
appPorts(80, 81),
|
appPorts(80, 81),
|
||||||
|
|
||||||
//withLabel(label.TraefikBackend, "foobar"),
|
// withLabel(label.TraefikBackend, "foobar"),
|
||||||
|
|
||||||
withLabel(label.TraefikBackendCircuitBreakerExpression, "NetworkErrorRatio() > 0.5"),
|
withLabel(label.TraefikBackendCircuitBreakerExpression, "NetworkErrorRatio() > 0.5"),
|
||||||
withLabel(label.TraefikBackendHealthCheckPath, "/health"),
|
withLabel(label.TraefikBackendHealthCheckPath, "/health"),
|
||||||
|
@ -530,7 +532,8 @@ func TestBuildConfigurationServicesNonAPIErrors(t *testing.T) {
|
||||||
withServiceLabel(label.TraefikFrontendRedirectReplacement, "nope", "containous"),
|
withServiceLabel(label.TraefikFrontendRedirectReplacement, "nope", "containous"),
|
||||||
withServiceLabel(label.TraefikFrontendRedirectPermanent, "true", "containous"),
|
withServiceLabel(label.TraefikFrontendRedirectPermanent, "true", "containous"),
|
||||||
withServiceLabel(label.TraefikFrontendRule, "Host:traefik.io", "containous"),
|
withServiceLabel(label.TraefikFrontendRule, "Host:traefik.io", "containous"),
|
||||||
withServiceLabel(label.TraefikFrontendWhitelistSourceRange, "10.10.10.10", "containous"),
|
withServiceLabel(label.TraefikFrontendWhiteListSourceRange, "10.10.10.10", "containous"),
|
||||||
|
withServiceLabel(label.TraefikFrontendWhiteListUseXForwardedFor, "true", "containous"),
|
||||||
|
|
||||||
withServiceLabel(label.TraefikFrontendRequestHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", "containous"),
|
withServiceLabel(label.TraefikFrontendRequestHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", "containous"),
|
||||||
withServiceLabel(label.TraefikFrontendResponseHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", "containous"),
|
withServiceLabel(label.TraefikFrontendResponseHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", "containous"),
|
||||||
|
@ -591,8 +594,9 @@ func TestBuildConfigurationServicesNonAPIErrors(t *testing.T) {
|
||||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
},
|
},
|
||||||
WhitelistSourceRange: []string{
|
WhiteList: &types.WhiteList{
|
||||||
"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{
|
||||||
|
@ -1627,6 +1631,107 @@ func TestGetServers(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWhiteList(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
application marathon.Application
|
||||||
|
serviceName string
|
||||||
|
expected *types.WhiteList
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "should return nil when no white list labels",
|
||||||
|
application: application(
|
||||||
|
appPorts(80),
|
||||||
|
),
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return a struct when only range",
|
||||||
|
application: application(
|
||||||
|
appPorts(80),
|
||||||
|
withLabel(label.TraefikFrontendWhiteListSourceRange, "10.10.10.10"),
|
||||||
|
),
|
||||||
|
expected: &types.WhiteList{
|
||||||
|
SourceRange: []string{
|
||||||
|
"10.10.10.10",
|
||||||
|
},
|
||||||
|
UseXForwardedFor: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return a struct when range and UseXForwardedFor",
|
||||||
|
application: application(
|
||||||
|
appPorts(80),
|
||||||
|
withLabel(label.TraefikFrontendWhiteListSourceRange, "10.10.10.10"),
|
||||||
|
withLabel(label.TraefikFrontendWhiteListUseXForwardedFor, "true"),
|
||||||
|
),
|
||||||
|
expected: &types.WhiteList{
|
||||||
|
SourceRange: []string{
|
||||||
|
"10.10.10.10",
|
||||||
|
},
|
||||||
|
UseXForwardedFor: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return nil when only UseXForwardedFor",
|
||||||
|
application: application(
|
||||||
|
appPorts(80),
|
||||||
|
withLabel(label.TraefikFrontendWhiteListUseXForwardedFor, "true"),
|
||||||
|
),
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
// Service
|
||||||
|
{
|
||||||
|
desc: "should return a struct when only range on service",
|
||||||
|
application: application(
|
||||||
|
appPorts(80),
|
||||||
|
withLabel(label.Prefix+"containous."+label.SuffixFrontendWhiteListSourceRange, "10.10.10.10"),
|
||||||
|
),
|
||||||
|
serviceName: "containous",
|
||||||
|
expected: &types.WhiteList{
|
||||||
|
SourceRange: []string{
|
||||||
|
"10.10.10.10",
|
||||||
|
},
|
||||||
|
UseXForwardedFor: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return a struct when range and UseXForwardedFor on service",
|
||||||
|
application: application(
|
||||||
|
appPorts(80),
|
||||||
|
withLabel(label.Prefix+"containous."+label.SuffixFrontendWhiteListSourceRange, "10.10.10.10"),
|
||||||
|
withLabel(label.Prefix+"containous."+label.SuffixFrontendWhiteListUseXForwardedFor, "true"),
|
||||||
|
),
|
||||||
|
serviceName: "containous",
|
||||||
|
expected: &types.WhiteList{
|
||||||
|
SourceRange: []string{
|
||||||
|
"10.10.10.10",
|
||||||
|
},
|
||||||
|
UseXForwardedFor: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return nil when only UseXForwardedFor on service",
|
||||||
|
application: application(
|
||||||
|
appPorts(80),
|
||||||
|
withLabel(label.Prefix+"containous."+label.SuffixFrontendWhiteListUseXForwardedFor, "true"),
|
||||||
|
),
|
||||||
|
serviceName: "containous",
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
actual := getWhiteList(test.application, test.serviceName)
|
||||||
|
assert.Equal(t, test.expected, actual)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetRedirect(t *testing.T) {
|
func TestGetRedirect(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
|
|
@ -44,7 +44,6 @@ func (p *Provider) buildConfiguration(tasks []state.Task) *types.Configuration {
|
||||||
"getFrontEndName": getFrontendName,
|
"getFrontEndName": getFrontendName,
|
||||||
"getEntryPoints": getFuncSliceStringValue(label.TraefikFrontendEntryPoints),
|
"getEntryPoints": getFuncSliceStringValue(label.TraefikFrontendEntryPoints),
|
||||||
"getBasicAuth": getFuncSliceStringValue(label.TraefikFrontendAuthBasic),
|
"getBasicAuth": getFuncSliceStringValue(label.TraefikFrontendAuthBasic),
|
||||||
"getWhitelistSourceRange": getFuncSliceStringValue(label.TraefikFrontendWhitelistSourceRange),
|
|
||||||
"getPriority": getFuncStringValue(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
"getPriority": getFuncStringValue(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
||||||
"getPassHostHeader": getFuncBoolValue(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeaderBool),
|
"getPassHostHeader": getFuncBoolValue(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeaderBool),
|
||||||
"getPassTLSCert": getFuncBoolValue(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
"getPassTLSCert": getFuncBoolValue(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||||
|
@ -53,6 +52,7 @@ func (p *Provider) buildConfiguration(tasks []state.Task) *types.Configuration {
|
||||||
"getErrorPages": getErrorPages,
|
"getErrorPages": getErrorPages,
|
||||||
"getRateLimit": getRateLimit,
|
"getRateLimit": getRateLimit,
|
||||||
"getHeaders": getHeaders,
|
"getHeaders": getHeaders,
|
||||||
|
"getWhiteList": getWhiteList,
|
||||||
|
|
||||||
// TODO Deprecated [breaking]
|
// TODO Deprecated [breaking]
|
||||||
"getFrontendBackend": getBackendName,
|
"getFrontendBackend": getBackendName,
|
||||||
|
@ -337,6 +337,18 @@ func (p *Provider) getServers(tasks []state.Task) map[string]types.Server {
|
||||||
return servers
|
return servers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getWhiteList(task state.Task) *types.WhiteList {
|
||||||
|
ranges := getSliceStringValue(task, label.TraefikFrontendWhiteListSourceRange)
|
||||||
|
if len(ranges) > 0 {
|
||||||
|
return &types.WhiteList{
|
||||||
|
SourceRange: ranges,
|
||||||
|
UseXForwardedFor: getBoolValue(task, label.TraefikFrontendWhiteListUseXForwardedFor, false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func getRedirect(task state.Task) *types.Redirect {
|
func getRedirect(task state.Task) *types.Redirect {
|
||||||
permanent := getBoolValue(task, label.TraefikFrontendRedirectPermanent, false)
|
permanent := getBoolValue(task, label.TraefikFrontendRedirectPermanent, false)
|
||||||
|
|
||||||
|
|
|
@ -148,7 +148,8 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
withLabel(label.TraefikFrontendRedirectReplacement, "nope"),
|
withLabel(label.TraefikFrontendRedirectReplacement, "nope"),
|
||||||
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.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"),
|
||||||
|
@ -212,8 +213,9 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
},
|
},
|
||||||
WhitelistSourceRange: []string{
|
WhiteList: &types.WhiteList{
|
||||||
"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{
|
||||||
|
@ -953,6 +955,75 @@ func TestGetServers(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWhiteList(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
task state.Task
|
||||||
|
expected *types.WhiteList
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "should return nil when no white list labels",
|
||||||
|
task: aTask("ID1",
|
||||||
|
withIP("10.10.10.10"),
|
||||||
|
withInfo("name1", withPorts(withPort("TCP", 80, "WEB"))),
|
||||||
|
withDefaultStatus(),
|
||||||
|
),
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return a struct when only range",
|
||||||
|
task: aTask("ID1",
|
||||||
|
withLabel(label.TraefikFrontendWhiteListSourceRange, "10.10.10.10"),
|
||||||
|
withIP("10.10.10.10"),
|
||||||
|
withInfo("name1", withPorts(withPort("TCP", 80, "WEB"))),
|
||||||
|
withDefaultStatus(),
|
||||||
|
),
|
||||||
|
expected: &types.WhiteList{
|
||||||
|
SourceRange: []string{
|
||||||
|
"10.10.10.10",
|
||||||
|
},
|
||||||
|
UseXForwardedFor: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return a struct when range and UseXForwardedFor",
|
||||||
|
task: aTask("ID1",
|
||||||
|
withLabel(label.TraefikFrontendWhiteListSourceRange, "10.10.10.10"),
|
||||||
|
withLabel(label.TraefikFrontendWhiteListUseXForwardedFor, "true"),
|
||||||
|
withIP("10.10.10.10"),
|
||||||
|
withInfo("name1", withPorts(withPort("TCP", 80, "WEB"))),
|
||||||
|
withDefaultStatus(),
|
||||||
|
),
|
||||||
|
expected: &types.WhiteList{
|
||||||
|
SourceRange: []string{
|
||||||
|
"10.10.10.10",
|
||||||
|
},
|
||||||
|
UseXForwardedFor: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return nil when only UseXForwardedFor",
|
||||||
|
task: aTask("ID1",
|
||||||
|
withLabel(label.TraefikFrontendWhiteListUseXForwardedFor, "true"),
|
||||||
|
withIP("10.10.10.10"),
|
||||||
|
withInfo("name1", withPorts(withPort("TCP", 80, "WEB"))),
|
||||||
|
withDefaultStatus(),
|
||||||
|
),
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
actual := getWhiteList(test.task)
|
||||||
|
assert.Equal(t, test.expected, actual)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetRedirect(t *testing.T) {
|
func TestGetRedirect(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
|
|
@ -63,12 +63,14 @@ func (p *Provider) buildConfiguration(services []rancherData) *types.Configurati
|
||||||
"getPassTLSCert": getFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
"getPassTLSCert": getFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||||
"getEntryPoints": getFuncSliceString(label.TraefikFrontendEntryPoints),
|
"getEntryPoints": getFuncSliceString(label.TraefikFrontendEntryPoints),
|
||||||
"getBasicAuth": getFuncSliceString(label.TraefikFrontendAuthBasic),
|
"getBasicAuth": getFuncSliceString(label.TraefikFrontendAuthBasic),
|
||||||
"getWhitelistSourceRange": getFuncSliceString(label.TraefikFrontendWhitelistSourceRange),
|
|
||||||
|
|
||||||
"getErrorPages": getErrorPages,
|
"getErrorPages": getErrorPages,
|
||||||
"getRateLimit": getRateLimit,
|
"getRateLimit": getRateLimit,
|
||||||
"getRedirect": getRedirect,
|
"getRedirect": getRedirect,
|
||||||
"getHeaders": getHeaders,
|
"getHeaders": getHeaders,
|
||||||
|
"getWhiteList": getWhiteList,
|
||||||
|
|
||||||
|
// TODO Deprecated [breaking]
|
||||||
|
"getWhitelistSourceRange": getFuncSliceString(label.TraefikFrontendWhitelistSourceRange),
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter services
|
// filter services
|
||||||
|
@ -271,6 +273,19 @@ func getServers(service rancherData) map[string]types.Server {
|
||||||
return servers
|
return servers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getWhiteList(service rancherData) *types.WhiteList {
|
||||||
|
ranges := label.GetSliceStringValue(service.Labels, label.TraefikFrontendWhiteListSourceRange)
|
||||||
|
|
||||||
|
if len(ranges) > 0 {
|
||||||
|
return &types.WhiteList{
|
||||||
|
SourceRange: ranges,
|
||||||
|
UseXForwardedFor: label.GetBoolValue(service.Labels, label.TraefikFrontendWhiteListUseXForwardedFor, false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func getRedirect(service rancherData) *types.Redirect {
|
func getRedirect(service rancherData) *types.Redirect {
|
||||||
permanent := label.GetBoolValue(service.Labels, label.TraefikFrontendRedirectPermanent, false)
|
permanent := label.GetBoolValue(service.Labels, label.TraefikFrontendRedirectPermanent, false)
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,8 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
||||||
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.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",
|
||||||
|
@ -128,9 +129,12 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
||||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
},
|
},
|
||||||
WhitelistSourceRange: []string{
|
WhiteList: &types.WhiteList{
|
||||||
|
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{
|
||||||
"Access-Control-Allow-Methods": "POST,GET,OPTIONS",
|
"Access-Control-Allow-Methods": "POST,GET,OPTIONS",
|
||||||
|
@ -1001,6 +1005,78 @@ func TestGetServers(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWhiteList(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
service rancherData
|
||||||
|
expected *types.WhiteList
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "should return nil when no white list labels",
|
||||||
|
service: rancherData{
|
||||||
|
Labels: map[string]string{},
|
||||||
|
Health: "healthy",
|
||||||
|
State: "active",
|
||||||
|
},
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return a struct when only range",
|
||||||
|
service: rancherData{
|
||||||
|
Labels: map[string]string{
|
||||||
|
label.TraefikFrontendWhiteListSourceRange: "10.10.10.10",
|
||||||
|
},
|
||||||
|
Health: "healthy",
|
||||||
|
State: "active",
|
||||||
|
},
|
||||||
|
expected: &types.WhiteList{
|
||||||
|
SourceRange: []string{
|
||||||
|
"10.10.10.10",
|
||||||
|
},
|
||||||
|
UseXForwardedFor: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return a struct when range and UseXForwardedFor",
|
||||||
|
service: rancherData{
|
||||||
|
Labels: map[string]string{
|
||||||
|
label.TraefikFrontendWhiteListSourceRange: "10.10.10.10",
|
||||||
|
label.TraefikFrontendWhiteListUseXForwardedFor: "true",
|
||||||
|
},
|
||||||
|
Health: "healthy",
|
||||||
|
State: "active",
|
||||||
|
},
|
||||||
|
expected: &types.WhiteList{
|
||||||
|
SourceRange: []string{
|
||||||
|
"10.10.10.10",
|
||||||
|
},
|
||||||
|
UseXForwardedFor: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "should return nil when only UseXForwardedFor",
|
||||||
|
service: rancherData{
|
||||||
|
Labels: map[string]string{
|
||||||
|
label.TraefikFrontendWhiteListUseXForwardedFor: "true",
|
||||||
|
},
|
||||||
|
Health: "healthy",
|
||||||
|
State: "active",
|
||||||
|
},
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
actual := getWhiteList(test.service)
|
||||||
|
assert.Equal(t, test.expected, actual)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetRedirect(t *testing.T) {
|
func TestGetRedirect(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
@ -12,7 +11,7 @@ import (
|
||||||
|
|
||||||
// NewHeaderRewriter Create a header rewriter
|
// NewHeaderRewriter Create a header rewriter
|
||||||
func NewHeaderRewriter(trustedIPs []string, insecure bool) (forward.ReqRewriter, error) {
|
func NewHeaderRewriter(trustedIPs []string, insecure bool) (forward.ReqRewriter, error) {
|
||||||
IPs, err := whitelist.NewIP(trustedIPs, insecure)
|
IPs, err := whitelist.NewIP(trustedIPs, insecure, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -38,14 +37,7 @@ type headerRewriter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *headerRewriter) Rewrite(req *http.Request) {
|
func (h *headerRewriter) Rewrite(req *http.Request) {
|
||||||
clientIP, _, err := net.SplitHostPort(req.RemoteAddr)
|
authorized, _, err := h.ips.IsAuthorized(req)
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
h.secureRewriter.Rewrite(req)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
authorized, _, err := h.ips.Contains(clientIP)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
h.secureRewriter.Rewrite(req)
|
h.secureRewriter.Rewrite(req)
|
||||||
|
|
|
@ -326,8 +326,8 @@ func (s *Server) setupServerEntryPoint(newServerEntryPointName string, newServer
|
||||||
}
|
}
|
||||||
serverMiddlewares = append(serverMiddlewares, s.globalConfiguration.API.StatsRecorder)
|
serverMiddlewares = append(serverMiddlewares, s.globalConfiguration.API.StatsRecorder)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.globalConfiguration.EntryPoints[newServerEntryPointName].Auth != nil {
|
if s.globalConfiguration.EntryPoints[newServerEntryPointName].Auth != nil {
|
||||||
authMiddleware, err := mauth.NewAuthenticator(s.globalConfiguration.EntryPoints[newServerEntryPointName].Auth, s.tracingMiddleware)
|
authMiddleware, err := mauth.NewAuthenticator(s.globalConfiguration.EntryPoints[newServerEntryPointName].Auth, s.tracingMiddleware)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -336,17 +336,22 @@ func (s *Server) setupServerEntryPoint(newServerEntryPointName string, newServer
|
||||||
serverMiddlewares = append(serverMiddlewares, s.wrapNegroniHandlerWithAccessLog(authMiddleware, fmt.Sprintf("Auth for entrypoint %s", newServerEntryPointName)))
|
serverMiddlewares = append(serverMiddlewares, s.wrapNegroniHandlerWithAccessLog(authMiddleware, fmt.Sprintf("Auth for entrypoint %s", newServerEntryPointName)))
|
||||||
serverInternalMiddlewares = append(serverInternalMiddlewares, authMiddleware)
|
serverInternalMiddlewares = append(serverInternalMiddlewares, authMiddleware)
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.globalConfiguration.EntryPoints[newServerEntryPointName].Compress {
|
if s.globalConfiguration.EntryPoints[newServerEntryPointName].Compress {
|
||||||
serverMiddlewares = append(serverMiddlewares, &middlewares.Compress{})
|
serverMiddlewares = append(serverMiddlewares, &middlewares.Compress{})
|
||||||
}
|
}
|
||||||
if len(s.globalConfiguration.EntryPoints[newServerEntryPointName].WhitelistSourceRange) > 0 {
|
|
||||||
ipWhitelistMiddleware, err := middlewares.NewIPWhitelister(s.globalConfiguration.EntryPoints[newServerEntryPointName].WhitelistSourceRange)
|
ipWhitelistMiddleware, err := buildIPWhiteLister(
|
||||||
|
s.globalConfiguration.EntryPoints[newServerEntryPointName].WhiteList,
|
||||||
|
s.globalConfiguration.EntryPoints[newServerEntryPointName].WhitelistSourceRange)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error starting server: ", err)
|
log.Fatal("Error starting server: ", err)
|
||||||
}
|
}
|
||||||
|
if ipWhitelistMiddleware != nil {
|
||||||
serverMiddlewares = append(serverMiddlewares, s.wrapNegroniHandlerWithAccessLog(ipWhitelistMiddleware, fmt.Sprintf("ipwhitelister for entrypoint %s", newServerEntryPointName)))
|
serverMiddlewares = append(serverMiddlewares, s.wrapNegroniHandlerWithAccessLog(ipWhitelistMiddleware, fmt.Sprintf("ipwhitelister for entrypoint %s", newServerEntryPointName)))
|
||||||
serverInternalMiddlewares = append(serverInternalMiddlewares, ipWhitelistMiddleware)
|
serverInternalMiddlewares = append(serverInternalMiddlewares, ipWhitelistMiddleware)
|
||||||
}
|
}
|
||||||
|
|
||||||
newSrv, listener, err := s.prepareServer(newServerEntryPointName, s.globalConfiguration.EntryPoints[newServerEntryPointName], newServerEntryPoint.httpRouter, serverMiddlewares, serverInternalMiddlewares)
|
newSrv, listener, err := s.prepareServer(newServerEntryPointName, s.globalConfiguration.EntryPoints[newServerEntryPointName], newServerEntryPoint.httpRouter, serverMiddlewares, serverInternalMiddlewares)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error preparing server: ", err)
|
log.Fatal("Error preparing server: ", err)
|
||||||
|
@ -794,7 +799,7 @@ func (s *Server) prepareServer(entryPointName string, entryPoint *configuration.
|
||||||
}
|
}
|
||||||
|
|
||||||
if entryPoint.ProxyProtocol != nil {
|
if entryPoint.ProxyProtocol != nil {
|
||||||
IPs, err := whitelist.NewIP(entryPoint.ProxyProtocol.TrustedIPs, entryPoint.ProxyProtocol.Insecure)
|
IPs, err := whitelist.NewIP(entryPoint.ProxyProtocol.TrustedIPs, entryPoint.ProxyProtocol.Insecure, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("error creating whitelist: %s", err)
|
return nil, nil, fmt.Errorf("error creating whitelist: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -1137,13 +1142,16 @@ func (s *Server) loadConfig(configurations types.Configurations, globalConfigura
|
||||||
n.Use(middlewares.NewBackendMetricsMiddleware(s.metricsRegistry, frontend.Backend))
|
n.Use(middlewares.NewBackendMetricsMiddleware(s.metricsRegistry, frontend.Backend))
|
||||||
}
|
}
|
||||||
|
|
||||||
ipWhitelistMiddleware, err := configureIPWhitelistMiddleware(frontend.WhitelistSourceRange)
|
ipWhitelistMiddleware, err := buildIPWhiteLister(frontend.WhiteList, frontend.WhitelistSourceRange)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error creating IP Whitelister: %s", err)
|
log.Errorf("Error creating IP Whitelister: %s", err)
|
||||||
} else if ipWhitelistMiddleware != nil {
|
} else if ipWhitelistMiddleware != nil {
|
||||||
ipWhitelistMiddleware = s.wrapNegroniHandlerWithAccessLog(ipWhitelistMiddleware, fmt.Sprintf("ipwhitelister for %s", frontendName))
|
n.Use(
|
||||||
n.Use(s.tracingMiddleware.NewNegroniHandlerWrapper("IP whitelist", ipWhitelistMiddleware, false))
|
s.tracingMiddleware.NewNegroniHandlerWrapper(
|
||||||
log.Infof("Configured IP Whitelists: %s", frontend.WhitelistSourceRange)
|
"IP whitelist",
|
||||||
|
s.wrapNegroniHandlerWithAccessLog(ipWhitelistMiddleware, fmt.Sprintf("ipwhitelister for %s", frontendName)),
|
||||||
|
false))
|
||||||
|
log.Debugf("Configured IP Whitelists: %s", frontend.WhitelistSourceRange)
|
||||||
}
|
}
|
||||||
|
|
||||||
if frontend.Redirect != nil && entryPointName != frontend.Redirect.EntryPoint {
|
if frontend.Redirect != nil && entryPointName != frontend.Redirect.EntryPoint {
|
||||||
|
@ -1256,18 +1264,13 @@ func (s *Server) configureLBServers(lb healthcheck.LoadBalancer, config *types.C
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func configureIPWhitelistMiddleware(whitelistSourceRanges []string) (negroni.Handler, error) {
|
func buildIPWhiteLister(whiteList *types.WhiteList, wlRange []string) (*middlewares.IPWhiteLister, error) {
|
||||||
if len(whitelistSourceRanges) > 0 {
|
if whiteList != nil &&
|
||||||
ipSourceRanges := whitelistSourceRanges
|
len(whiteList.SourceRange) > 0 {
|
||||||
ipWhitelistMiddleware, err := middlewares.NewIPWhitelister(ipSourceRanges)
|
return middlewares.NewIPWhiteLister(whiteList.SourceRange, whiteList.UseXForwardedFor)
|
||||||
|
} else if len(wlRange) > 0 {
|
||||||
if err != nil {
|
return middlewares.NewIPWhiteLister(wlRange, false)
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ipWhitelistMiddleware, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -571,48 +571,75 @@ func TestServerParseHealthCheckOptions(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewServerWithWhitelistSourceRange(t *testing.T) {
|
func TestBuildIPWhiteLister(t *testing.T) {
|
||||||
cases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
whitelistStrings []string
|
whitelistSourceRange []string
|
||||||
|
whiteList *types.WhiteList
|
||||||
middlewareConfigured bool
|
middlewareConfigured bool
|
||||||
errMessage string
|
errMessage string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "no whitelists configued",
|
desc: "no whitelists configured",
|
||||||
whitelistStrings: nil,
|
whitelistSourceRange: nil,
|
||||||
middlewareConfigured: false,
|
middlewareConfigured: false,
|
||||||
errMessage: "",
|
errMessage: "",
|
||||||
}, {
|
},
|
||||||
desc: "whitelists configued",
|
{
|
||||||
whitelistStrings: []string{
|
desc: "whitelists configured (deprecated)",
|
||||||
|
whitelistSourceRange: []string{
|
||||||
"1.2.3.4/24",
|
"1.2.3.4/24",
|
||||||
"fe80::/16",
|
"fe80::/16",
|
||||||
},
|
},
|
||||||
middlewareConfigured: true,
|
middlewareConfigured: true,
|
||||||
errMessage: "",
|
errMessage: "",
|
||||||
}, {
|
},
|
||||||
desc: "invalid whitelists configued",
|
{
|
||||||
whitelistStrings: []string{
|
desc: "invalid whitelists configured (deprecated)",
|
||||||
|
whitelistSourceRange: []string{
|
||||||
"foo",
|
"foo",
|
||||||
},
|
},
|
||||||
middlewareConfigured: false,
|
middlewareConfigured: false,
|
||||||
errMessage: "parsing CIDR whitelist [foo]: parsing CIDR whitelist <nil>: invalid CIDR address: foo",
|
errMessage: "parsing CIDR whitelist [foo]: parsing CIDR white list <nil>: invalid CIDR address: foo",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "whitelists configured",
|
||||||
|
whiteList: &types.WhiteList{
|
||||||
|
SourceRange: []string{
|
||||||
|
"1.2.3.4/24",
|
||||||
|
"fe80::/16",
|
||||||
|
},
|
||||||
|
UseXForwardedFor: false,
|
||||||
|
},
|
||||||
|
middlewareConfigured: true,
|
||||||
|
errMessage: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "invalid whitelists configured (deprecated)",
|
||||||
|
whiteList: &types.WhiteList{
|
||||||
|
SourceRange: []string{
|
||||||
|
"foo",
|
||||||
|
},
|
||||||
|
UseXForwardedFor: false,
|
||||||
|
},
|
||||||
|
middlewareConfigured: false,
|
||||||
|
errMessage: "parsing CIDR whitelist [foo]: parsing CIDR white list <nil>: invalid CIDR address: foo",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, test := range testCases {
|
||||||
tc := tc
|
test := test
|
||||||
t.Run(tc.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
middleware, err := configureIPWhitelistMiddleware(tc.whitelistStrings)
|
|
||||||
|
|
||||||
if tc.errMessage != "" {
|
middleware, err := buildIPWhiteLister(test.whiteList, test.whitelistSourceRange)
|
||||||
require.EqualError(t, err, tc.errMessage)
|
|
||||||
|
if test.errMessage != "" {
|
||||||
|
require.EqualError(t, err, test.errMessage)
|
||||||
} else {
|
} else {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
if tc.middlewareConfigured {
|
if test.middlewareConfigured {
|
||||||
require.NotNil(t, middleware, "not expected middleware to be configured")
|
require.NotNil(t, middleware, "not expected middleware to be configured")
|
||||||
} else {
|
} else {
|
||||||
require.Nil(t, middleware, "expected middleware to be configured")
|
require.Nil(t, middleware, "expected middleware to be configured")
|
||||||
|
|
|
@ -66,17 +66,19 @@
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
{{ $whitelistSourceRange := getWhitelistSourceRange $service.Attributes }}
|
|
||||||
{{if $whitelistSourceRange }}
|
|
||||||
whitelistSourceRange = [{{range $whitelistSourceRange }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
basicAuth = [{{range getBasicAuth $service.Attributes }}
|
basicAuth = [{{range getBasicAuth $service.Attributes }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
|
{{ $whitelist := getWhiteList $service.Attributes }}
|
||||||
|
{{if $whitelist }}
|
||||||
|
[frontends."frontend-{{ $service.ServiceName }}".whiteList]
|
||||||
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $service.Attributes }}
|
{{ $redirect := getRedirect $service.Attributes }}
|
||||||
{{if $redirect }}
|
{{if $redirect }}
|
||||||
[frontends."frontend-{{ $service.ServiceName }}".redirect]
|
[frontends."frontend-{{ $service.ServiceName }}".redirect]
|
||||||
|
|
|
@ -67,17 +67,19 @@
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
{{ $whitelistSourceRange := getWhitelistSourceRange $container.SegmentLabels }}
|
|
||||||
{{if $whitelistSourceRange }}
|
|
||||||
whitelistSourceRange = [{{range $whitelistSourceRange }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
basicAuth = [{{range getBasicAuth $container.SegmentLabels }}
|
basicAuth = [{{range getBasicAuth $container.SegmentLabels }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
|
{{ $whitelist := getWhiteList $container.SegmentLabels }}
|
||||||
|
{{if $whitelist }}
|
||||||
|
[frontends."frontend-{{ $frontendName }}".whiteList]
|
||||||
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $container.SegmentLabels }}
|
{{ $redirect := getRedirect $container.SegmentLabels }}
|
||||||
{{if $redirect }}
|
{{if $redirect }}
|
||||||
[frontends."frontend-{{ $frontendName }}".redirect]
|
[frontends."frontend-{{ $frontendName }}".redirect]
|
||||||
|
|
|
@ -66,17 +66,18 @@
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
{{ $whitelistSourceRange := getWhitelistSourceRange $instance }}
|
|
||||||
{{if $whitelistSourceRange }}
|
|
||||||
whitelistSourceRange = [{{range $whitelistSourceRange }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
basicAuth = [{{range getBasicAuth $instance }}
|
basicAuth = [{{range getBasicAuth $instance }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
|
{{ $whitelist := getWhiteList $instance }}
|
||||||
|
{{if $whitelist }}
|
||||||
|
[frontends."frontend-{{ $serviceName }}".whiteList]
|
||||||
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $instance }}
|
{{ $redirect := getRedirect $instance }}
|
||||||
{{if $redirect }}
|
{{if $redirect }}
|
||||||
|
|
|
@ -56,9 +56,13 @@
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
whitelistSourceRange = [{{range $frontend.WhitelistSourceRange }}
|
{{if $frontend.WhiteList }}
|
||||||
|
[frontends."{{ $frontendName }}".whiteList]
|
||||||
|
sourceRange = [{{range $frontend.WhiteList.SourceRange }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
useXForwardedFor = {{ $frontend.WhiteList.UseXForwardedFor }}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{if $frontend.Redirect }}
|
{{if $frontend.Redirect }}
|
||||||
[frontends."{{ $frontendName }}".redirect]
|
[frontends."{{ $frontendName }}".redirect]
|
||||||
|
|
|
@ -66,17 +66,19 @@
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
{{ $whitelistSourceRange := getWhitelistSourceRange $frontend }}
|
|
||||||
{{if $whitelistSourceRange }}
|
|
||||||
whitelistSourceRange = [{{range $whitelistSourceRange }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
basicAuth = [{{range getBasicAuth $frontend }}
|
basicAuth = [{{range getBasicAuth $frontend }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
|
{{ $whitelist := getWhiteList $frontend }}
|
||||||
|
{{if $whitelist }}
|
||||||
|
[frontends."{{ $frontendName }}".whiteList]
|
||||||
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $frontend }}
|
{{ $redirect := getRedirect $frontend }}
|
||||||
{{if $redirect }}
|
{{if $redirect }}
|
||||||
[frontends."{{ $frontendName }}".redirect]
|
[frontends."{{ $frontendName }}".redirect]
|
||||||
|
|
|
@ -73,17 +73,19 @@
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
{{ $whitelistSourceRange := getWhitelistSourceRange $app $serviceName }}
|
|
||||||
{{if $whitelistSourceRange }}
|
|
||||||
whitelistSourceRange = [{{range $whitelistSourceRange }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
basicAuth = [{{range getBasicAuth $app $serviceName }}
|
basicAuth = [{{range getBasicAuth $app $serviceName }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
|
{{ $whitelist := getWhiteList $app $serviceName }}
|
||||||
|
{{if $whitelist }}
|
||||||
|
[frontends."{{ $frontendName }}".whiteList]
|
||||||
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $app $serviceName }}
|
{{ $redirect := getRedirect $app $serviceName }}
|
||||||
{{if $redirect }}
|
{{if $redirect }}
|
||||||
[frontends."{{ $frontendName }}".redirect]
|
[frontends."{{ $frontendName }}".redirect]
|
||||||
|
|
|
@ -69,17 +69,19 @@
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
{{ $whitelistSourceRange := getWhitelistSourceRange $app }}
|
|
||||||
{{if $whitelistSourceRange }}
|
|
||||||
whitelistSourceRange = [{{range $whitelistSourceRange }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
basicAuth = [{{range getBasicAuth $app }}
|
basicAuth = [{{range getBasicAuth $app }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
|
{{ $whitelist := getWhiteList $app }}
|
||||||
|
{{if $whitelist }}
|
||||||
|
[frontends."frontend-{{ $frontendName }}".whiteList]
|
||||||
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $app }}
|
{{ $redirect := getRedirect $app }}
|
||||||
{{if $redirect }}
|
{{if $redirect }}
|
||||||
[frontends."frontend-{{ $frontendName }}".redirect]
|
[frontends."frontend-{{ $frontendName }}".redirect]
|
||||||
|
|
|
@ -67,17 +67,19 @@
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
{{ $whitelistSourceRange := getWhitelistSourceRange $service }}
|
|
||||||
{{if $whitelistSourceRange }}
|
|
||||||
whitelistSourceRange = [{{range $whitelistSourceRange }}
|
|
||||||
"{{.}}",
|
|
||||||
{{end}}]
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
basicAuth = [{{range getBasicAuth $service }}
|
basicAuth = [{{range getBasicAuth $service }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
|
{{ $whitelist := getWhiteList $service }}
|
||||||
|
{{if $whitelist }}
|
||||||
|
[frontends."frontend-{{ $frontendName }}".whiteList]
|
||||||
|
sourceRange = [{{range $whitelist.SourceRange }}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{ $redirect := getRedirect $service }}
|
{{ $redirect := getRedirect $service }}
|
||||||
{{if $redirect }}
|
{{if $redirect }}
|
||||||
[frontends."frontend-{{ $frontendName }}".redirect]
|
[frontends."frontend-{{ $frontendName }}".redirect]
|
||||||
|
|
|
@ -61,6 +61,12 @@ type Buffering struct {
|
||||||
RetryExpression string `json:"retryExpression,omitempty"`
|
RetryExpression string `json:"retryExpression,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WhiteList contains white list configuration.
|
||||||
|
type WhiteList struct {
|
||||||
|
SourceRange []string `json:"sourceRange,omitempty"`
|
||||||
|
UseXForwardedFor bool `json:"useXForwardedFor,omitempty" export:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
// HealthCheck holds HealthCheck configuration
|
// HealthCheck holds HealthCheck configuration
|
||||||
type HealthCheck struct {
|
type HealthCheck struct {
|
||||||
Path string `json:"path,omitempty"`
|
Path string `json:"path,omitempty"`
|
||||||
|
@ -89,7 +95,7 @@ type ServerRoute struct {
|
||||||
ReplacePathRegex string
|
ReplacePathRegex string
|
||||||
}
|
}
|
||||||
|
|
||||||
//ErrorPage holds custom error page configuration
|
// ErrorPage holds custom error page configuration
|
||||||
type ErrorPage struct {
|
type ErrorPage struct {
|
||||||
Status []string `json:"status,omitempty"`
|
Status []string `json:"status,omitempty"`
|
||||||
Backend string `json:"backend,omitempty"`
|
Backend string `json:"backend,omitempty"`
|
||||||
|
@ -172,7 +178,8 @@ type Frontend struct {
|
||||||
PassTLSCert bool `json:"passTLSCert,omitempty"`
|
PassTLSCert bool `json:"passTLSCert,omitempty"`
|
||||||
Priority int `json:"priority"`
|
Priority int `json:"priority"`
|
||||||
BasicAuth []string `json:"basicAuth"`
|
BasicAuth []string `json:"basicAuth"`
|
||||||
WhitelistSourceRange []string `json:"whitelistSourceRange,omitempty"`
|
WhitelistSourceRange []string `json:"whitelistSourceRange,omitempty"` // Deprecated
|
||||||
|
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"`
|
||||||
|
@ -547,7 +554,7 @@ func NewHTTPCodeRanges(strBlocks []string) (HTTPCodeRanges, error) {
|
||||||
var blocks HTTPCodeRanges
|
var blocks HTTPCodeRanges
|
||||||
for _, block := range strBlocks {
|
for _, block := range strBlocks {
|
||||||
codes := strings.Split(block, "-")
|
codes := strings.Split(block, "-")
|
||||||
//if only a single HTTP code was configured, assume the best and create the correct configuration on the user's behalf
|
// if only a single HTTP code was configured, assume the best and create the correct configuration on the user's behalf
|
||||||
if len(codes) == 1 {
|
if len(codes) == 1 {
|
||||||
codes = append(codes, codes[0])
|
codes = append(codes, codes[0])
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,36 +3,45 @@ package whitelist
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// XForwardedFor Header name
|
||||||
|
XForwardedFor = "X-Forwarded-For"
|
||||||
|
)
|
||||||
|
|
||||||
// IP allows to check that addresses are in a white list
|
// IP allows to check that addresses are in a white list
|
||||||
type IP struct {
|
type IP struct {
|
||||||
whiteListsIPs []*net.IP
|
whiteListsIPs []*net.IP
|
||||||
whiteListsNet []*net.IPNet
|
whiteListsNet []*net.IPNet
|
||||||
insecure bool
|
insecure bool
|
||||||
|
useXForwardedFor bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIP builds a new IP given a list of CIDR-Strings to whitelist
|
// NewIP builds a new IP given a list of CIDR-Strings to white list
|
||||||
func NewIP(whitelistStrings []string, insecure bool) (*IP, error) {
|
func NewIP(whiteList []string, insecure bool, useXForwardedFor bool) (*IP, error) {
|
||||||
if len(whitelistStrings) == 0 && !insecure {
|
if len(whiteList) == 0 && !insecure {
|
||||||
return nil, errors.New("no white list provided")
|
return nil, errors.New("no white list provided")
|
||||||
}
|
}
|
||||||
|
|
||||||
ip := IP{insecure: insecure}
|
ip := IP{
|
||||||
|
insecure: insecure,
|
||||||
|
useXForwardedFor: useXForwardedFor,
|
||||||
|
}
|
||||||
|
|
||||||
if !insecure {
|
if !insecure {
|
||||||
for _, whitelistString := range whitelistStrings {
|
for _, ipMask := range whiteList {
|
||||||
ipAddr := net.ParseIP(whitelistString)
|
if ipAddr := net.ParseIP(ipMask); ipAddr != nil {
|
||||||
if ipAddr != nil {
|
|
||||||
ip.whiteListsIPs = append(ip.whiteListsIPs, &ipAddr)
|
ip.whiteListsIPs = append(ip.whiteListsIPs, &ipAddr)
|
||||||
} else {
|
} else {
|
||||||
_, whitelist, err := net.ParseCIDR(whitelistString)
|
_, ipAddr, err := net.ParseCIDR(ipMask)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("parsing CIDR whitelist %s: %v", whitelist, err)
|
return nil, fmt.Errorf("parsing CIDR white list %s: %v", ipAddr, err)
|
||||||
}
|
}
|
||||||
ip.whiteListsNet = append(ip.whiteListsNet, whitelist)
|
ip.whiteListsNet = append(ip.whiteListsNet, ipAddr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,13 +49,38 @@ func NewIP(whitelistStrings []string, insecure bool) (*IP, error) {
|
||||||
return &ip, nil
|
return &ip, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contains checks if provided address is in the white list
|
// IsAuthorized checks if provided request is authorized by the white list
|
||||||
func (ip *IP) Contains(addr string) (bool, net.IP, error) {
|
func (ip *IP) IsAuthorized(req *http.Request) (bool, net.IP, error) {
|
||||||
if ip.insecure {
|
if ip.insecure {
|
||||||
return true, nil, nil
|
return true, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ipAddr, err := ipFromRemoteAddr(addr)
|
if ip.useXForwardedFor {
|
||||||
|
xFFs := req.Header[XForwardedFor]
|
||||||
|
if len(xFFs) > 1 {
|
||||||
|
for _, xFF := range xFFs {
|
||||||
|
ok, i, err := ip.contains(parseHost(xFF))
|
||||||
|
if err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
return ok, i, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
host, _, err := net.SplitHostPort(req.RemoteAddr)
|
||||||
|
if err != nil {
|
||||||
|
return false, nil, err
|
||||||
|
}
|
||||||
|
return ip.contains(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
// contains checks if provided address is in the white list
|
||||||
|
func (ip *IP) contains(addr string) (bool, net.IP, error) {
|
||||||
|
ipAddr, err := parseIP(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil, fmt.Errorf("unable to parse address: %s: %s", addr, err)
|
return false, nil, fmt.Errorf("unable to parse address: %s: %s", addr, err)
|
||||||
}
|
}
|
||||||
|
@ -76,7 +110,7 @@ func (ip *IP) ContainsIP(addr net.IP) (bool, error) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ipFromRemoteAddr(addr string) (net.IP, error) {
|
func parseIP(addr string) (net.IP, error) {
|
||||||
userIP := net.ParseIP(addr)
|
userIP := net.ParseIP(addr)
|
||||||
if userIP == nil {
|
if userIP == nil {
|
||||||
return nil, fmt.Errorf("can't parse IP from address %s", addr)
|
return nil, fmt.Errorf("can't parse IP from address %s", addr)
|
||||||
|
@ -84,3 +118,11 @@ func ipFromRemoteAddr(addr string) (net.IP, error) {
|
||||||
|
|
||||||
return userIP, nil
|
return userIP, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseHost(addr string) string {
|
||||||
|
host, _, err := net.SplitHostPort(addr)
|
||||||
|
if err != nil {
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
return host
|
||||||
|
}
|
||||||
|
|
|
@ -2,55 +2,151 @@ package whitelist
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestIsAuthorized(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
whiteList []string
|
||||||
|
allowXForwardedFor bool
|
||||||
|
remoteAddr string
|
||||||
|
xForwardedForValues []string
|
||||||
|
expected 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"},
|
||||||
|
expected: 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"},
|
||||||
|
expected: 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"},
|
||||||
|
expected: 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"},
|
||||||
|
expected: 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"},
|
||||||
|
expected: 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"},
|
||||||
|
expected: 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"},
|
||||||
|
expected: 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"},
|
||||||
|
expected: 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)
|
||||||
|
|
||||||
|
authorized, ips, err := whiteLister.IsAuthorized(req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.NotNil(t, ips)
|
||||||
|
|
||||||
|
assert.Equal(t, test.expected, authorized)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestNew(t *testing.T) {
|
func TestNew(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
desc string
|
desc string
|
||||||
whitelistStrings []string
|
whiteList []string
|
||||||
expectedWhitelists []*net.IPNet
|
expectedWhitelists []*net.IPNet
|
||||||
errMessage string
|
errMessage string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "nil whitelist",
|
desc: "nil whitelist",
|
||||||
whitelistStrings: nil,
|
whiteList: nil,
|
||||||
expectedWhitelists: nil,
|
expectedWhitelists: nil,
|
||||||
errMessage: "no white list provided",
|
errMessage: "no white list provided",
|
||||||
}, {
|
}, {
|
||||||
desc: "empty whitelist",
|
desc: "empty whitelist",
|
||||||
whitelistStrings: []string{},
|
whiteList: []string{},
|
||||||
expectedWhitelists: nil,
|
expectedWhitelists: nil,
|
||||||
errMessage: "no white list provided",
|
errMessage: "no white list provided",
|
||||||
}, {
|
}, {
|
||||||
desc: "whitelist containing empty string",
|
desc: "whitelist containing empty string",
|
||||||
whitelistStrings: []string{
|
whiteList: []string{
|
||||||
"1.2.3.4/24",
|
"1.2.3.4/24",
|
||||||
"",
|
"",
|
||||||
"fe80::/16",
|
"fe80::/16",
|
||||||
},
|
},
|
||||||
expectedWhitelists: nil,
|
expectedWhitelists: nil,
|
||||||
errMessage: "parsing CIDR whitelist <nil>: invalid CIDR address: ",
|
errMessage: "parsing CIDR white list <nil>: invalid CIDR address: ",
|
||||||
}, {
|
}, {
|
||||||
desc: "whitelist containing only an empty string",
|
desc: "whitelist containing only an empty string",
|
||||||
whitelistStrings: []string{
|
whiteList: []string{
|
||||||
"",
|
"",
|
||||||
},
|
},
|
||||||
expectedWhitelists: nil,
|
expectedWhitelists: nil,
|
||||||
errMessage: "parsing CIDR whitelist <nil>: invalid CIDR address: ",
|
errMessage: "parsing CIDR white list <nil>: invalid CIDR address: ",
|
||||||
}, {
|
}, {
|
||||||
desc: "whitelist containing an invalid string",
|
desc: "whitelist containing an invalid string",
|
||||||
whitelistStrings: []string{
|
whiteList: []string{
|
||||||
"foo",
|
"foo",
|
||||||
},
|
},
|
||||||
expectedWhitelists: nil,
|
expectedWhitelists: nil,
|
||||||
errMessage: "parsing CIDR whitelist <nil>: invalid CIDR address: foo",
|
errMessage: "parsing CIDR white list <nil>: invalid CIDR address: foo",
|
||||||
}, {
|
}, {
|
||||||
desc: "IPv4 & IPv6 whitelist",
|
desc: "IPv4 & IPv6 whitelist",
|
||||||
whitelistStrings: []string{
|
whiteList: []string{
|
||||||
"1.2.3.4/24",
|
"1.2.3.4/24",
|
||||||
"fe80::/16",
|
"fe80::/16",
|
||||||
},
|
},
|
||||||
|
@ -61,7 +157,7 @@ func TestNew(t *testing.T) {
|
||||||
errMessage: "",
|
errMessage: "",
|
||||||
}, {
|
}, {
|
||||||
desc: "IPv4 only",
|
desc: "IPv4 only",
|
||||||
whitelistStrings: []string{
|
whiteList: []string{
|
||||||
"127.0.0.1/8",
|
"127.0.0.1/8",
|
||||||
},
|
},
|
||||||
expectedWhitelists: []*net.IPNet{
|
expectedWhitelists: []*net.IPNet{
|
||||||
|
@ -75,12 +171,12 @@ func TestNew(t *testing.T) {
|
||||||
test := test
|
test := test
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
whitelister, err := NewIP(test.whitelistStrings, false)
|
whiteLister, err := NewIP(test.whiteList, false, false)
|
||||||
if test.errMessage != "" {
|
if test.errMessage != "" {
|
||||||
require.EqualError(t, err, test.errMessage)
|
require.EqualError(t, err, test.errMessage)
|
||||||
} else {
|
} else {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
for index, actual := range whitelister.whiteListsNet {
|
for index, actual := range whiteLister.whiteListsNet {
|
||||||
expected := test.expectedWhitelists[index]
|
expected := test.expectedWhitelists[index]
|
||||||
assert.Equal(t, expected.IP, actual.IP)
|
assert.Equal(t, expected.IP, actual.IP)
|
||||||
assert.Equal(t, expected.Mask.String(), actual.Mask.String())
|
assert.Equal(t, expected.Mask.String(), actual.Mask.String())
|
||||||
|
@ -99,9 +195,7 @@ func TestContainsIsAllowed(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "IPv4",
|
desc: "IPv4",
|
||||||
whitelistStrings: []string{
|
whitelistStrings: []string{"1.2.3.4/24"},
|
||||||
"1.2.3.4/24",
|
|
||||||
},
|
|
||||||
passIPs: []string{
|
passIPs: []string{
|
||||||
"1.2.3.1",
|
"1.2.3.1",
|
||||||
"1.2.3.32",
|
"1.2.3.32",
|
||||||
|
@ -117,12 +211,8 @@ func TestContainsIsAllowed(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "IPv4 single IP",
|
desc: "IPv4 single IP",
|
||||||
whitelistStrings: []string{
|
whitelistStrings: []string{"8.8.8.8"},
|
||||||
"8.8.8.8",
|
passIPs: []string{"8.8.8.8"},
|
||||||
},
|
|
||||||
passIPs: []string{
|
|
||||||
"8.8.8.8",
|
|
||||||
},
|
|
||||||
rejectIPs: []string{
|
rejectIPs: []string{
|
||||||
"8.8.8.7",
|
"8.8.8.7",
|
||||||
"8.8.8.9",
|
"8.8.8.9",
|
||||||
|
@ -134,12 +224,8 @@ func TestContainsIsAllowed(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "IPv4 Net single IP",
|
desc: "IPv4 Net single IP",
|
||||||
whitelistStrings: []string{
|
whitelistStrings: []string{"8.8.8.8/32"},
|
||||||
"8.8.8.8/32",
|
passIPs: []string{"8.8.8.8"},
|
||||||
},
|
|
||||||
passIPs: []string{
|
|
||||||
"8.8.8.8",
|
|
||||||
},
|
|
||||||
rejectIPs: []string{
|
rejectIPs: []string{
|
||||||
"8.8.8.7",
|
"8.8.8.7",
|
||||||
"8.8.8.9",
|
"8.8.8.9",
|
||||||
|
@ -151,10 +237,7 @@ func TestContainsIsAllowed(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "multiple IPv4",
|
desc: "multiple IPv4",
|
||||||
whitelistStrings: []string{
|
whitelistStrings: []string{"1.2.3.4/24", "8.8.8.8/8"},
|
||||||
"1.2.3.4/24",
|
|
||||||
"8.8.8.8/8",
|
|
||||||
},
|
|
||||||
passIPs: []string{
|
passIPs: []string{
|
||||||
"1.2.3.1",
|
"1.2.3.1",
|
||||||
"1.2.3.32",
|
"1.2.3.32",
|
||||||
|
@ -175,9 +258,7 @@ func TestContainsIsAllowed(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "IPv6",
|
desc: "IPv6",
|
||||||
whitelistStrings: []string{
|
whitelistStrings: []string{"2a03:4000:6:d080::/64"},
|
||||||
"2a03:4000:6:d080::/64",
|
|
||||||
},
|
|
||||||
passIPs: []string{
|
passIPs: []string{
|
||||||
"2a03:4000:6:d080::",
|
"2a03:4000:6:d080::",
|
||||||
"2a03:4000:6:d080::1",
|
"2a03:4000:6:d080::1",
|
||||||
|
@ -193,12 +274,8 @@ func TestContainsIsAllowed(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "IPv6 single IP",
|
desc: "IPv6 single IP",
|
||||||
whitelistStrings: []string{
|
whitelistStrings: []string{"2a03:4000:6:d080::42/128"},
|
||||||
"2a03:4000:6:d080::42/128",
|
passIPs: []string{"2a03:4000:6:d080::42"},
|
||||||
},
|
|
||||||
passIPs: []string{
|
|
||||||
"2a03:4000:6:d080::42",
|
|
||||||
},
|
|
||||||
rejectIPs: []string{
|
rejectIPs: []string{
|
||||||
"2a03:4000:6:d080::1",
|
"2a03:4000:6:d080::1",
|
||||||
"2a03:4000:6:d080:dead:beef:ffff:ffff",
|
"2a03:4000:6:d080:dead:beef:ffff:ffff",
|
||||||
|
@ -207,10 +284,7 @@ func TestContainsIsAllowed(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "multiple IPv6",
|
desc: "multiple IPv6",
|
||||||
whitelistStrings: []string{
|
whitelistStrings: []string{"2a03:4000:6:d080::/64", "fe80::/16"},
|
||||||
"2a03:4000:6:d080::/64",
|
|
||||||
"fe80::/16",
|
|
||||||
},
|
|
||||||
passIPs: []string{
|
passIPs: []string{
|
||||||
"2a03:4000:6:d080::",
|
"2a03:4000:6:d080::",
|
||||||
"2a03:4000:6:d080::1",
|
"2a03:4000:6:d080::1",
|
||||||
|
@ -228,12 +302,7 @@ func TestContainsIsAllowed(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "multiple IPv6 & IPv4",
|
desc: "multiple IPv6 & IPv4",
|
||||||
whitelistStrings: []string{
|
whitelistStrings: []string{"2a03:4000:6:d080::/64", "fe80::/16", "1.2.3.4/24", "8.8.8.8/8"},
|
||||||
"2a03:4000:6:d080::/64",
|
|
||||||
"fe80::/16",
|
|
||||||
"1.2.3.4/24",
|
|
||||||
"8.8.8.8/8",
|
|
||||||
},
|
|
||||||
passIPs: []string{
|
passIPs: []string{
|
||||||
"2a03:4000:6:d080::",
|
"2a03:4000:6:d080::",
|
||||||
"2a03:4000:6:d080::1",
|
"2a03:4000:6:d080::1",
|
||||||
|
@ -264,9 +333,7 @@ func TestContainsIsAllowed(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "broken IP-addresses",
|
desc: "broken IP-addresses",
|
||||||
whitelistStrings: []string{
|
whitelistStrings: []string{"127.0.0.1/32"},
|
||||||
"127.0.0.1/32",
|
|
||||||
},
|
|
||||||
passIPs: nil,
|
passIPs: nil,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -276,23 +343,23 @@ func TestContainsIsAllowed(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 := NewIP(test.whitelistStrings, false)
|
whiteLister, err := NewIP(test.whitelistStrings, false, false)
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, whiteLister)
|
require.NotNil(t, whiteLister)
|
||||||
|
|
||||||
for _, testIP := range test.passIPs {
|
for _, testIP := range test.passIPs {
|
||||||
allowed, ip, err := whiteLister.Contains(testIP)
|
allowed, ip, err := whiteLister.contains(testIP)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, ip, err)
|
require.NotNil(t, ip, err)
|
||||||
assert.True(t, allowed, testIP+" should have passed "+test.desc)
|
assert.Truef(t, allowed, "%s should have passed.", testIP)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testIP := range test.rejectIPs {
|
for _, testIP := range test.rejectIPs {
|
||||||
allowed, ip, err := whiteLister.Contains(testIP)
|
allowed, ip, err := whiteLister.contains(testIP)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NotNil(t, ip, err)
|
require.NotNil(t, ip, err)
|
||||||
assert.False(t, allowed, testIP+" should not have passed "+test.desc)
|
assert.Falsef(t, allowed, "%s should not have passed.", testIP)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -300,7 +367,7 @@ func TestContainsIsAllowed(t *testing.T) {
|
||||||
|
|
||||||
func TestContainsInsecure(t *testing.T) {
|
func TestContainsInsecure(t *testing.T) {
|
||||||
mustNewIP := func(whitelistStrings []string, insecure bool) *IP {
|
mustNewIP := func(whitelistStrings []string, insecure bool) *IP {
|
||||||
ip, err := NewIP(whitelistStrings, insecure)
|
ip, err := NewIP(whitelistStrings, insecure, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -338,7 +405,7 @@ func TestContainsInsecure(t *testing.T) {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
ok, _, err := test.whiteLister.Contains(test.ip)
|
ok, _, err := test.whiteLister.contains(test.ip)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, test.expected, ok)
|
assert.Equal(t, test.expected, ok)
|
||||||
|
@ -355,13 +422,25 @@ func TestContainsBrokenIPs(t *testing.T) {
|
||||||
"\\&$§&/(",
|
"\\&$§&/(",
|
||||||
}
|
}
|
||||||
|
|
||||||
whiteLister, err := NewIP([]string{"1.2.3.4/24"}, false)
|
whiteLister, err := NewIP([]string{"1.2.3.4/24"}, false, false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for _, testIP := range brokenIPs {
|
for _, testIP := range brokenIPs {
|
||||||
_, ip, err := whiteLister.Contains(testIP)
|
_, ip, err := whiteLister.contains(testIP)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
require.Nil(t, ip, err)
|
require.Nil(t, ip, 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…
Add table
Reference in a new issue