diff --git a/autogen/gentemplates/gen.go b/autogen/gentemplates/gen.go index 81a1a22d5..bc64f14f7 100644 --- a/autogen/gentemplates/gen.go +++ b/autogen/gentemplates/gen.go @@ -241,6 +241,11 @@ var _templatesConsul_catalogTmpl = []byte(`[backends] [frontends."frontend-{{ $service.ServiceName }}".auth.forward] address = "{{ $auth.Forward.Address }}" trustForwardHeader = {{ $auth.Forward.TrustForwardHeader }} + {{if $auth.Forward.AuthResponseHeaders }} + authResponseHeaders = [{{range $auth.Forward.AuthResponseHeaders }} + "{{.}}", + {{end}}] + {{end}} {{if $auth.Forward.TLS }} [frontends."frontend-{{ $service.ServiceName }}".auth.forward.tls] @@ -713,6 +718,11 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}} [frontends."frontend-{{ $frontendName }}".auth.forward] address = "{{ $auth.Forward.Address }}" trustForwardHeader = {{ $auth.Forward.TrustForwardHeader }} + {{if $auth.Forward.AuthResponseHeaders }} + authResponseHeaders = [{{range $auth.Forward.AuthResponseHeaders }} + "{{.}}", + {{end}}] + {{end}} {{if $auth.Forward.TLS }} [frontends."frontend-{{ $frontendName }}".auth.forward.tls] @@ -1038,6 +1048,11 @@ var _templatesEcsTmpl = []byte(`[backends] [frontends."frontend-{{ $frontendName }}".auth.forward] address = "{{ $auth.Forward.Address }}" trustForwardHeader = {{ $auth.Forward.TrustForwardHeader }} + {{if $auth.Forward.AuthResponseHeaders }} + authResponseHeaders = [{{range $auth.Forward.AuthResponseHeaders }} + "{{.}}", + {{end}}] + {{end}} {{if $auth.Forward.TLS }} [frontends."frontend-{{ $frontendName }}".auth.forward.tls] @@ -1554,6 +1569,11 @@ var _templatesKvTmpl = []byte(`[backends] [frontends."{{ $frontendName }}".auth.forward] address = "{{ $auth.Forward.Address }}" trustForwardHeader = {{ $auth.Forward.TrustForwardHeader }} + {{if $auth.Forward.AuthResponseHeaders }} + authResponseHeaders = [{{range $auth.Forward.AuthResponseHeaders }} + "{{.}}", + {{end}}] + {{end}} {{if $auth.Forward.TLS }} [frontends."{{ $frontendName }}".auth.forward.tls] @@ -1919,6 +1939,11 @@ var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }} [frontends."{{ $frontendName }}".auth.forward] address = "{{ $auth.Forward.Address }}" trustForwardHeader = {{ $auth.Forward.TrustForwardHeader }} + {{if $auth.Forward.AuthResponseHeaders }} + authResponseHeaders = [{{range $auth.Forward.AuthResponseHeaders }} + "{{.}}", + {{end}}] + {{end}} {{if $auth.Forward.TLS }} [frontends."{{ $frontendName }}".auth.forward.tls] @@ -2228,6 +2253,11 @@ var _templatesMesosTmpl = []byte(`[backends] [frontends."frontend-{{ $frontendName }}".auth.forward] address = "{{ $auth.Forward.Address }}" trustForwardHeader = {{ $auth.Forward.TrustForwardHeader }} + {{if $auth.Forward.AuthResponseHeaders }} + authResponseHeaders = [{{range $auth.Forward.AuthResponseHeaders }} + "{{.}}", + {{end}}] + {{end}} {{if $auth.Forward.TLS }} [frontends."frontend-{{ $frontendName }}".auth.forward.tls] @@ -2590,6 +2620,11 @@ var _templatesRancherTmpl = []byte(`{{ $backendServers := .Backends }} [frontends."frontend-{{ $frontendName }}".auth.forward] address = "{{ $auth.Forward.Address }}" trustForwardHeader = {{ $auth.Forward.TrustForwardHeader }} + {{if $auth.Forward.AuthResponseHeaders }} + authResponseHeaders = [{{range $auth.Forward.AuthResponseHeaders }} + "{{.}}", + {{end}}] + {{end}} {{if $auth.Forward.TLS }} [frontends."frontend-{{ $frontendName }}".auth.forward.tls] diff --git a/docs/configuration/backends/consulcatalog.md b/docs/configuration/backends/consulcatalog.md index 96e219ed1..82d693d14 100644 --- a/docs/configuration/backends/consulcatalog.md +++ b/docs/configuration/backends/consulcatalog.md @@ -125,6 +125,7 @@ Additional settings can be defined using Consul Catalog tags. | `.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | | `.frontend.auth.digest.usersfile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | | `.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. | +| `.frontend.auth.forward.authResponseHeaders=EXPR` | Sets the forward authentication authResponseHeaders in CSV format: `X-Auth-User,X-Auth-Header` | | `.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | | `.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | | `.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | diff --git a/docs/configuration/backends/docker.md b/docs/configuration/backends/docker.md index 338d10741..c21b815d8 100644 --- a/docs/configuration/backends/docker.md +++ b/docs/configuration/backends/docker.md @@ -244,6 +244,7 @@ Labels can be used on containers to override default behavior. | `traefik.frontend.auth.digest.users=EXPR` | Sets the digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | | `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets the digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | | `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. | +| `traefik.frontend.auth.forward.authResponseHeaders=EXPR` | Sets the forward authentication authResponseHeaders in CSV format: `X-Auth-User,X-Auth-Header` | | `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | | `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | | `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | @@ -346,6 +347,7 @@ Segment labels override the default behavior. | `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | | `traefik..frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` | | `traefik..frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` | +| `traefik..frontend.auth.forward.authResponseHeaders=EXPR` | Same as `traefik.frontend.auth.forward.authResponseHeaders` | | `traefik..frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` | | `traefik..frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` | | `traefik..frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` | diff --git a/docs/configuration/backends/ecs.md b/docs/configuration/backends/ecs.md index a4f7c5d36..81a2d7ba5 100644 --- a/docs/configuration/backends/ecs.md +++ b/docs/configuration/backends/ecs.md @@ -170,6 +170,7 @@ Labels can be used on task containers to override default behaviour: | `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | | `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | | `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. | +| `traefik.frontend.auth.forward.authResponseHeaders=EXPR` | Sets the forward authentication authResponseHeaders in CSV format: `X-Auth-User,X-Auth-Header` | | `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | | `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | | `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | @@ -262,6 +263,7 @@ Segment labels override the default behavior. | `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | | `traefik..frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` | | `traefik..frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` | +| `traefik..frontend.auth.forward.authResponseHeaders=EXPR` | Same as `traefik.frontend.auth.forward.authResponseHeaders` | | `traefik..frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` | | `traefik..frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` | | `traefik..frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` | diff --git a/docs/configuration/backends/marathon.md b/docs/configuration/backends/marathon.md index 0db0de8c7..7241e41ec 100644 --- a/docs/configuration/backends/marathon.md +++ b/docs/configuration/backends/marathon.md @@ -228,6 +228,7 @@ The following labels can be defined on Marathon applications. They adjust the be | `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | | `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | | `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. | +| `traefik.frontend.auth.forward.authResponseHeaders=EXPR` | Sets the forward authentication authResponseHeaders in CSV format: `X-Auth-User,X-Auth-Header` | | `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | | `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | | `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | @@ -322,6 +323,7 @@ Segment labels override the default behavior. | `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | | `traefik..frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` | | `traefik..frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` | +| `traefik..frontend.auth.forward.authResponseHeaders=EXPR` | Same as `traefik.frontend.auth.forward.authResponseHeaders` | | `traefik..frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` | | `traefik..frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` | | `traefik..frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` | diff --git a/docs/configuration/backends/mesos.md b/docs/configuration/backends/mesos.md index 5f4e4730f..6e701bf62 100644 --- a/docs/configuration/backends/mesos.md +++ b/docs/configuration/backends/mesos.md @@ -141,6 +141,7 @@ The following labels can be defined on Mesos tasks. They adjust the behavior for | `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | | `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | | `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. | +| `traefik.frontend.auth.forward.authResponseHeaders=EXPR` | Sets the forward authentication authResponseHeaders in CSV format: `X-Auth-User,X-Auth-Header` | | `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | | `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | | `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | @@ -237,6 +238,7 @@ Segment labels override the default behavior. | `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | | `traefik..frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` | | `traefik..frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` | +| `traefik..frontend.auth.forward.authResponseHeaders=EXPR` | Same as `traefik.frontend.auth.forward.authResponseHeaders` | | `traefik..frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` | | `traefik..frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` | | `traefik..frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` | diff --git a/docs/configuration/backends/rancher.md b/docs/configuration/backends/rancher.md index a9aff127a..bb84fe8ee 100644 --- a/docs/configuration/backends/rancher.md +++ b/docs/configuration/backends/rancher.md @@ -172,6 +172,7 @@ Labels can be used on task containers to override default behavior: | `traefik.frontend.auth.digest.users=EXPR` | Sets the digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | | `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets the digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | | `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. | +| `traefik.frontend.auth.forward.authResponseHeaders=EXPR` | Sets the forward authentication authResponseHeaders in CSV format: `X-Auth-User,X-Auth-Header` | | `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | | `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | | `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | @@ -263,6 +264,7 @@ Segment labels override the default behavior. | `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | | `traefik..frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` | | `traefik..frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` | +| `traefik..frontend.auth.forward.authResponseHeaders=EXPR` | Same as `traefik.frontend.auth.forward.authResponseHeaders` | | `traefik..frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` | | `traefik..frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` | | `traefik..frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` | diff --git a/provider/consulcatalog/config_test.go b/provider/consulcatalog/config_test.go index f45a655fe..9485b925c 100644 --- a/provider/consulcatalog/config_test.go +++ b/provider/consulcatalog/config_test.go @@ -319,6 +319,82 @@ func TestProviderBuildConfiguration(t *testing.T) { }, }, }, + { + desc: "Should build config with a forward auth", + nodes: []catalogUpdate{ + { + Service: &serviceUpdate{ + ServiceName: "test", + Attributes: []string{ + "random.foo=bar", + label.TraefikFrontendAuthForwardAddress + "=auth.server", + label.TraefikFrontendAuthForwardAuthResponseHeaders + "=X-Auth-User,X-Auth-Token", + label.TraefikFrontendAuthForwardTrustForwardHeader + "=true", + label.TraefikFrontendAuthForwardTLSCa + "=ca.crt", + label.TraefikFrontendAuthForwardTLSCaOptional + "=true", + label.TraefikFrontendAuthForwardTLSCert + "=server.crt", + label.TraefikFrontendAuthForwardTLSKey + "=server.key", + label.TraefikFrontendAuthForwardTLSInsecureSkipVerify + "=true", + }, + }, + Nodes: []*api.ServiceEntry{ + { + Service: &api.AgentService{ + Service: "test", + Address: "127.0.0.1", + Port: 80, + Tags: []string{ + "random.foo=bar", + label.Prefix + "backend.weight=42", // Deprecated label + label.TraefikFrontendPassHostHeader + "=true", + label.TraefikProtocol + "=https", + }, + }, + Node: &api.Node{ + Node: "localhost", + Address: "127.0.0.1", + }, + }, + }, + }, + }, + expectedFrontends: map[string]*types.Frontend{ + "frontend-test": { + Backend: "backend-test", + PassHostHeader: true, + Routes: map[string]types.Route{ + "route-host-test": { + Rule: "Host:test.localhost", + }, + }, + Auth: &types.Auth{ + Forward: &types.Forward{ + Address: "auth.server", + TLS: &types.ClientTLS{ + CA: "ca.crt", + CAOptional: true, + Cert: "server.crt", + Key: "server.key", + InsecureSkipVerify: true, + }, + TrustForwardHeader: true, + AuthResponseHeaders: []string{"X-Auth-User", "X-Auth-Token"}, + }, + }, + EntryPoints: []string{}, + }, + }, + expectedBackends: map[string]*types.Backend{ + "backend-test": { + Servers: map[string]types.Server{ + "test-0-us4-27hAOu2ARV7nNrmv6GoKlcA": { + URL: "https://127.0.0.1:80", + Weight: 42, + }, + }, + }, + }, + }, { desc: "when all labels are set", nodes: []catalogUpdate{ @@ -366,6 +442,7 @@ func TestProviderBuildConfiguration(t *testing.T) { label.TraefikFrontendAuthDigestUsers + "=test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", label.TraefikFrontendAuthDigestUsersFile + "=.htpasswd", label.TraefikFrontendAuthForwardAddress + "=auth.server", + label.TraefikFrontendAuthForwardAuthResponseHeaders + "=X-Auth-User,X-Auth-Token", label.TraefikFrontendAuthForwardTrustForwardHeader + "=true", label.TraefikFrontendAuthForwardTLSCa + "=ca.crt", label.TraefikFrontendAuthForwardTLSCaOptional + "=true", diff --git a/provider/docker/config_container_docker_test.go b/provider/docker/config_container_docker_test.go index 89aa7d910..2922e2c2d 100644 --- a/provider/docker/config_container_docker_test.go +++ b/provider/docker/config_container_docker_test.go @@ -63,54 +63,6 @@ func TestDockerBuildConfiguration(t *testing.T) { }, }, }, - { - desc: "when frontend basic auth", - containers: []docker.ContainerJSON{ - containerJSON( - name("test"), - labels(map[string]string{ - label.TraefikFrontendAuthBasicUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", - label.TraefikFrontendAuthBasicUsersFile: ".htpasswd", - label.TraefikFrontendAuthBasicRemoveHeader: "true", - }), - ports(nat.PortMap{ - "80/tcp": {}, - }), - withNetwork("bridge", ipv4("127.0.0.1")), - ), - }, - expectedFrontends: map[string]*types.Frontend{ - "frontend-Host-test-docker-localhost-0": { - Backend: "backend-test", - PassHostHeader: true, - EntryPoints: []string{}, - Auth: &types.Auth{ - Basic: &types.Basic{ - RemoveHeader: true, - Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", - "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, - UsersFile: ".htpasswd", - }, - }, - Routes: map[string]types.Route{ - "route-frontend-Host-test-docker-localhost-0": { - Rule: "Host:test.docker.localhost", - }, - }, - }, - }, - expectedBackends: map[string]*types.Backend{ - "backend-test": { - Servers: map[string]types.Server{ - "server-test-842895ca2aca17f6ee36ddb2f621194d": { - URL: "http://127.0.0.1:80", - Weight: label.DefaultWeight, - }, - }, - CircuitBreaker: nil, - }, - }, - }, { desc: "when pass tls client certificate", containers: []docker.ContainerJSON{ @@ -173,6 +125,53 @@ func TestDockerBuildConfiguration(t *testing.T) { CircuitBreaker: nil, }, }, + }, { + desc: "when frontend basic auth", + containers: []docker.ContainerJSON{ + containerJSON( + name("test"), + labels(map[string]string{ + label.TraefikFrontendAuthBasicUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", + label.TraefikFrontendAuthBasicUsersFile: ".htpasswd", + label.TraefikFrontendAuthBasicRemoveHeader: "true", + }), + ports(nat.PortMap{ + "80/tcp": {}, + }), + withNetwork("bridge", ipv4("127.0.0.1")), + ), + }, + expectedFrontends: map[string]*types.Frontend{ + "frontend-Host-test-docker-localhost-0": { + Backend: "backend-test", + PassHostHeader: true, + EntryPoints: []string{}, + Auth: &types.Auth{ + Basic: &types.Basic{ + RemoveHeader: true, + Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", + "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, + UsersFile: ".htpasswd", + }, + }, + Routes: map[string]types.Route{ + "route-frontend-Host-test-docker-localhost-0": { + Rule: "Host:test.docker.localhost", + }, + }, + }, + }, + expectedBackends: map[string]*types.Backend{ + "backend-test": { + Servers: map[string]types.Server{ + "server-test-842895ca2aca17f6ee36ddb2f621194d": { + URL: "http://127.0.0.1:80", + Weight: label.DefaultWeight, + }, + }, + CircuitBreaker: nil, + }, + }, }, { desc: "when frontend basic auth backward compatibility", @@ -279,6 +278,7 @@ func TestDockerBuildConfiguration(t *testing.T) { label.TraefikFrontendAuthForwardTLSCert: "server.crt", label.TraefikFrontendAuthForwardTLSKey: "server.key", label.TraefikFrontendAuthForwardTLSInsecureSkipVerify: "true", + label.TraefikFrontendAuthForwardAuthResponseHeaders: "X-Auth-User,X-Auth-Token", }), ports(nat.PortMap{ "80/tcp": {}, @@ -293,8 +293,7 @@ func TestDockerBuildConfiguration(t *testing.T) { EntryPoints: []string{}, Auth: &types.Auth{ Forward: &types.Forward{ - Address: "auth.server", - TrustForwardHeader: true, + Address: "auth.server", TLS: &types.ClientTLS{ CA: "ca.crt", CAOptional: true, @@ -302,6 +301,8 @@ func TestDockerBuildConfiguration(t *testing.T) { Cert: "server.crt", Key: "server.key", }, + TrustForwardHeader: true, + AuthResponseHeaders: []string{"X-Auth-User", "X-Auth-Token"}, }, }, Routes: map[string]types.Route{ diff --git a/provider/docker/config_container_swarm_test.go b/provider/docker/config_container_swarm_test.go index 187e5aa41..29e862757 100644 --- a/provider/docker/config_container_swarm_test.go +++ b/provider/docker/config_container_swarm_test.go @@ -322,6 +322,7 @@ func TestSwarmBuildConfiguration(t *testing.T) { label.TraefikFrontendAuthForwardTLSCert: "server.crt", label.TraefikFrontendAuthForwardTLSKey: "server.key", label.TraefikFrontendAuthForwardTLSInsecureSkipVerify: "true", + label.TraefikFrontendAuthForwardAuthResponseHeaders: "X-Auth-User,X-Auth-Token", }), withEndpointSpec(modeVIP), withEndpoint(virtualIP("1", "127.0.0.1/24")), @@ -334,8 +335,7 @@ func TestSwarmBuildConfiguration(t *testing.T) { EntryPoints: []string{}, Auth: &types.Auth{ Forward: &types.Forward{ - Address: "auth.server", - TrustForwardHeader: true, + Address: "auth.server", TLS: &types.ClientTLS{ CA: "ca.crt", CAOptional: true, @@ -343,6 +343,8 @@ func TestSwarmBuildConfiguration(t *testing.T) { Key: "server.key", InsecureSkipVerify: true, }, + TrustForwardHeader: true, + AuthResponseHeaders: []string{"X-Auth-User", "X-Auth-Token"}, }, }, Routes: map[string]types.Route{ diff --git a/provider/docker/config_segment_test.go b/provider/docker/config_segment_test.go index 5a0340c66..8abff9c70 100644 --- a/provider/docker/config_segment_test.go +++ b/provider/docker/config_segment_test.go @@ -296,6 +296,7 @@ func TestSegmentBuildConfiguration(t *testing.T) { label.Prefix + "sauternes." + label.SuffixFrontendAuthForwardTLSCert: "server.crt", label.Prefix + "sauternes." + label.SuffixFrontendAuthForwardTLSKey: "server.key", label.Prefix + "sauternes." + label.SuffixFrontendAuthForwardTLSInsecureSkipVerify: "true", + label.Prefix + "sauternes." + label.SuffixFrontendAuthForwardAuthResponseHeaders: "X-Auth-User,X-Auth-Token", }), ports(nat.PortMap{ "80/tcp": {}, @@ -316,8 +317,7 @@ func TestSegmentBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Forward: &types.Forward{ - Address: "auth.server", - TrustForwardHeader: true, + Address: "auth.server", TLS: &types.ClientTLS{ CA: "ca.crt", CAOptional: true, @@ -325,6 +325,8 @@ func TestSegmentBuildConfiguration(t *testing.T) { Key: "server.key", InsecureSkipVerify: true, }, + TrustForwardHeader: true, + AuthResponseHeaders: []string{"X-Auth-User", "X-Auth-Token"}, }, }, }, diff --git a/provider/ecs/config_segment_test.go b/provider/ecs/config_segment_test.go index cf08747b1..7d72f7215 100644 --- a/provider/ecs/config_segment_test.go +++ b/provider/ecs/config_segment_test.go @@ -251,6 +251,7 @@ func TestSegmentBuildConfiguration(t *testing.T) { label.Prefix + "sauternes." + label.SuffixFrontendAuthForwardTLSCert: "server.crt", label.Prefix + "sauternes." + label.SuffixFrontendAuthForwardTLSKey: "server.key", label.Prefix + "sauternes." + label.SuffixFrontendAuthForwardTLSInsecureSkipVerify: "true", + label.Prefix + "sauternes." + label.SuffixFrontendAuthForwardAuthResponseHeaders: "X-Auth-User,X-Auth-Token", }), iMachine( mName("machine1"), @@ -275,8 +276,7 @@ func TestSegmentBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Forward: &types.Forward{ - Address: "auth.server", - TrustForwardHeader: true, + Address: "auth.server", TLS: &types.ClientTLS{ CA: "ca.crt", CAOptional: true, @@ -284,6 +284,8 @@ func TestSegmentBuildConfiguration(t *testing.T) { Key: "server.key", InsecureSkipVerify: true, }, + TrustForwardHeader: true, + AuthResponseHeaders: []string{"X-Auth-User", "X-Auth-Token"}, }, }, }, diff --git a/provider/ecs/config_test.go b/provider/ecs/config_test.go index 0aa7e2ce6..f120b109f 100644 --- a/provider/ecs/config_test.go +++ b/provider/ecs/config_test.go @@ -276,7 +276,9 @@ func TestBuildConfiguration(t *testing.T) { label.TraefikFrontendAuthForwardTLSCaOptional: aws.String("true"), label.TraefikFrontendAuthForwardTLSCert: aws.String("server.crt"), label.TraefikFrontendAuthForwardTLSKey: aws.String("server.key"), - label.TraefikFrontendAuthForwardTLSInsecureSkipVerify: aws.String("true"), label.TraefikFrontendAuthHeaderField: aws.String("X-WebAuth-User"), + label.TraefikFrontendAuthForwardTLSInsecureSkipVerify: aws.String("true"), + label.TraefikFrontendAuthHeaderField: aws.String("X-WebAuth-User"), + label.TraefikFrontendAuthForwardAuthResponseHeaders: aws.String("X-Auth-User,X-Auth-Token"), }), iMachine( mState(ec2.InstanceStateNameRunning), @@ -309,8 +311,7 @@ func TestBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Forward: &types.Forward{ - Address: "auth.server", - TrustForwardHeader: true, + Address: "auth.server", TLS: &types.ClientTLS{ CA: "ca.crt", CAOptional: true, @@ -318,6 +319,8 @@ func TestBuildConfiguration(t *testing.T) { Cert: "server.crt", Key: "server.key", }, + TrustForwardHeader: true, + AuthResponseHeaders: []string{"X-Auth-User", "X-Auth-Token"}, }, }, PassHostHeader: true, diff --git a/provider/kv/keynames.go b/provider/kv/keynames.go index f021e1eef..38876c797 100644 --- a/provider/kv/keynames.go +++ b/provider/kv/keynames.go @@ -49,6 +49,7 @@ const ( pathFrontendBasicAuth = "/basicauth" // Deprecated pathFrontendAuth = "/auth/" + pathFrontendAuthHeaderField = pathFrontendAuth + "headerfield" pathFrontendAuthBasic = pathFrontendAuth + "basic/" pathFrontendAuthBasicRemoveHeader = pathFrontendAuthBasic + "removeheader" pathFrontendAuthBasicUsers = pathFrontendAuthBasic + "users" @@ -59,6 +60,7 @@ const ( pathFrontendAuthDigestUsersFile = pathFrontendAuthDigest + "usersfile" pathFrontendAuthForward = pathFrontendAuth + "forward/" pathFrontendAuthForwardAddress = pathFrontendAuthForward + "address" + pathFrontendAuthForwardAuthResponseHeaders = pathFrontendAuthForward + ".authresponseheaders" pathFrontendAuthForwardTLS = pathFrontendAuthForward + "tls/" pathFrontendAuthForwardTLSCa = pathFrontendAuthForwardTLS + "ca" pathFrontendAuthForwardTLSCaOptional = pathFrontendAuthForwardTLS + "caoptional" @@ -66,7 +68,6 @@ const ( pathFrontendAuthForwardTLSInsecureSkipVerify = pathFrontendAuthForwardTLS + "insecureskipverify" pathFrontendAuthForwardTLSKey = pathFrontendAuthForwardTLS + "key" pathFrontendAuthForwardTrustForwardHeader = pathFrontendAuthForward + "trustforwardheader" - pathFrontendAuthHeaderField = pathFrontendAuth + "headerfield" pathFrontendEntryPoints = "/entrypoints" pathFrontendRedirectEntryPoint = "/redirect/entrypoint" diff --git a/provider/kv/kv_config.go b/provider/kv/kv_config.go index bf02ce4b7..4883ce108 100644 --- a/provider/kv/kv_config.go +++ b/provider/kv/kv_config.go @@ -459,8 +459,9 @@ func (p *Provider) getAuthDigest(rootPath string) *types.Digest { // getAuthForward Create Forward Auth from path func (p *Provider) getAuthForward(rootPath string) *types.Forward { forwardAuth := &types.Forward{ - Address: p.get("", rootPath, pathFrontendAuthForwardAddress), - TrustForwardHeader: p.getBool(false, rootPath, pathFrontendAuthForwardTrustForwardHeader), + Address: p.get("", rootPath, pathFrontendAuthForwardAddress), + TrustForwardHeader: p.getBool(false, rootPath, pathFrontendAuthForwardTrustForwardHeader), + AuthResponseHeaders: p.getList(rootPath, pathFrontendAuthForwardAuthResponseHeaders), } // TLS configuration diff --git a/provider/kv/kv_config_test.go b/provider/kv/kv_config_test.go index b41e4653e..b719d8474 100644 --- a/provider/kv/kv_config_test.go +++ b/provider/kv/kv_config_test.go @@ -213,6 +213,7 @@ func TestProviderBuildConfiguration(t *testing.T) { withPair(pathFrontendAuthForwardTLSCert, "server.crt"), withPair(pathFrontendAuthForwardTLSKey, "server.key"), withPair(pathFrontendAuthForwardTLSInsecureSkipVerify, "true"), + withPair(pathFrontendAuthForwardAuthResponseHeaders, "X-Auth-User,X-Auth-Token"), ), backend("backend"), ), @@ -232,8 +233,7 @@ func TestProviderBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Forward: &types.Forward{ - Address: "auth.server", - TrustForwardHeader: true, + Address: "auth.server", TLS: &types.ClientTLS{ CA: "ca.crt", CAOptional: true, @@ -241,6 +241,8 @@ func TestProviderBuildConfiguration(t *testing.T) { Cert: "server.crt", Key: "server.key", }, + TrustForwardHeader: true, + AuthResponseHeaders: []string{"X-Auth-User", "X-Auth-Token"}, }, }, }, diff --git a/provider/label/names.go b/provider/label/names.go index 59a8c7684..442a2f890 100644 --- a/provider/label/names.go +++ b/provider/label/names.go @@ -46,6 +46,7 @@ const ( SuffixFrontendAuthDigestUsersFile = SuffixFrontendAuthDigest + ".usersFile" SuffixFrontendAuthForward = SuffixFrontendAuth + ".forward" SuffixFrontendAuthForwardAddress = SuffixFrontendAuthForward + ".address" + SuffixFrontendAuthForwardAuthResponseHeaders = SuffixFrontendAuthForward + ".authResponseHeaders" SuffixFrontendAuthForwardTLS = SuffixFrontendAuthForward + ".tls" SuffixFrontendAuthForwardTLSCa = SuffixFrontendAuthForwardTLS + ".ca" SuffixFrontendAuthForwardTLSCaOptional = SuffixFrontendAuthForwardTLS + ".caOptional" @@ -147,6 +148,7 @@ const ( TraefikFrontendAuthDigestUsersFile = Prefix + SuffixFrontendAuthDigestUsersFile TraefikFrontendAuthForward = Prefix + SuffixFrontendAuthForward TraefikFrontendAuthForwardAddress = Prefix + SuffixFrontendAuthForwardAddress + TraefikFrontendAuthForwardAuthResponseHeaders = Prefix + SuffixFrontendAuthForwardAuthResponseHeaders TraefikFrontendAuthForwardTLS = Prefix + SuffixFrontendAuthForwardTLS TraefikFrontendAuthForwardTLSCa = Prefix + SuffixFrontendAuthForwardTLSCa TraefikFrontendAuthForwardTLSCaOptional = Prefix + SuffixFrontendAuthForwardTLSCaOptional diff --git a/provider/label/partial.go b/provider/label/partial.go index 39289cc3b..a2026743d 100644 --- a/provider/label/partial.go +++ b/provider/label/partial.go @@ -144,8 +144,9 @@ func getAuthDigest(labels map[string]string) *types.Digest { // getAuthForward Create Forward Auth from labels func getAuthForward(labels map[string]string) *types.Forward { forwardAuth := &types.Forward{ - Address: GetStringValue(labels, TraefikFrontendAuthForwardAddress, ""), - TrustForwardHeader: GetBoolValue(labels, TraefikFrontendAuthForwardTrustForwardHeader, false), + Address: GetStringValue(labels, TraefikFrontendAuthForwardAddress, ""), + AuthResponseHeaders: GetSliceStringValue(labels, TraefikFrontendAuthForwardAuthResponseHeaders), + TrustForwardHeader: GetBoolValue(labels, TraefikFrontendAuthForwardTrustForwardHeader, false), } // TLS configuration diff --git a/provider/marathon/config_segment_test.go b/provider/marathon/config_segment_test.go new file mode 100644 index 000000000..7abff18e9 --- /dev/null +++ b/provider/marathon/config_segment_test.go @@ -0,0 +1,372 @@ +package marathon + +import ( + "testing" + "time" + + "github.com/containous/flaeg" + "github.com/containous/traefik/provider/label" + "github.com/containous/traefik/types" + "github.com/gambol99/go-marathon" + "github.com/stretchr/testify/assert" +) + +func TestBuildConfigurationSegments(t *testing.T) { + testCases := []struct { + desc string + applications *marathon.Applications + expectedFrontends map[string]*types.Frontend + expectedBackends map[string]*types.Backend + }{ + { + desc: "multiple ports with segments", + applications: withApplications( + application( + appID("/app"), + appPorts(80, 81), + withTasks(localhostTask(taskPorts(80, 81))), + + withLabel(label.TraefikBackendMaxConnAmount, "1000"), + withLabel(label.TraefikBackendMaxConnExtractorFunc, "client.ip"), + withSegmentLabel(label.TraefikPort, "80", "web"), + withSegmentLabel(label.TraefikPort, "81", "admin"), + withLabel("traefik..port", "82"), // This should be ignored, as it fails to match the segmentPropertiesRegexp regex. + withSegmentLabel(label.TraefikFrontendRule, "Host:web.app.marathon.localhost", "web"), + withSegmentLabel(label.TraefikFrontendRule, "Host:admin.app.marathon.localhost", "admin"), + )), + expectedFrontends: map[string]*types.Frontend{ + "frontend-app-service-web": { + Backend: "backend-app-service-web", + Routes: map[string]types.Route{ + `route-host-app-service-web`: { + Rule: "Host:web.app.marathon.localhost", + }, + }, + PassHostHeader: true, + EntryPoints: []string{}, + }, + "frontend-app-service-admin": { + Backend: "backend-app-service-admin", + Routes: map[string]types.Route{ + `route-host-app-service-admin`: { + Rule: "Host:admin.app.marathon.localhost", + }, + }, + PassHostHeader: true, + EntryPoints: []string{}, + }, + }, + expectedBackends: map[string]*types.Backend{ + "backend-app-service-web": { + Servers: map[string]types.Server{ + "server-app-taskID-service-web": { + URL: "http://localhost:80", + Weight: label.DefaultWeight, + }, + }, + MaxConn: &types.MaxConn{ + Amount: 1000, + ExtractorFunc: "client.ip", + }, + }, + "backend-app-service-admin": { + Servers: map[string]types.Server{ + "server-app-taskID-service-admin": { + URL: "http://localhost:81", + Weight: label.DefaultWeight, + }, + }, + MaxConn: &types.MaxConn{ + Amount: 1000, + ExtractorFunc: "client.ip", + }, + }, + }, + }, + { + desc: "when all labels are set", + applications: withApplications( + application( + appID("/app"), + appPorts(80, 81), + withTasks(localhostTask(taskPorts(80, 81))), + + // withLabel(label.TraefikBackend, "foobar"), + + withLabel(label.TraefikBackendCircuitBreakerExpression, "NetworkErrorRatio() > 0.5"), + withLabel(label.TraefikBackendHealthCheckPath, "/health"), + withLabel(label.TraefikBackendHealthCheckPort, "880"), + withLabel(label.TraefikBackendHealthCheckInterval, "6"), + withLabel(label.TraefikBackendLoadBalancerMethod, "drr"), + withLabel(label.TraefikBackendLoadBalancerSticky, "true"), + withLabel(label.TraefikBackendLoadBalancerStickiness, "true"), + withLabel(label.TraefikBackendLoadBalancerStickinessCookieName, "chocolate"), + withLabel(label.TraefikBackendMaxConnAmount, "666"), + withLabel(label.TraefikBackendMaxConnExtractorFunc, "client.ip"), + withLabel(label.TraefikBackendBufferingMaxResponseBodyBytes, "10485760"), + withLabel(label.TraefikBackendBufferingMemResponseBodyBytes, "2097152"), + withLabel(label.TraefikBackendBufferingMaxRequestBodyBytes, "10485760"), + withLabel(label.TraefikBackendBufferingMemRequestBodyBytes, "2097152"), + withLabel(label.TraefikBackendBufferingRetryExpression, "IsNetworkError() && Attempts() <= 2"), + + withSegmentLabel(label.TraefikPort, "80", "containous"), + withSegmentLabel(label.TraefikProtocol, "https", "containous"), + withSegmentLabel(label.TraefikWeight, "12", "containous"), + + withSegmentLabel(label.TraefikFrontendPassTLSClientCertPem, "true", "containous"), + withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosNotBefore, "true", "containous"), + withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosNotAfter, "true", "containous"), + withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSans, "true", "containous"), + withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectCommonName, "true", "containous"), + withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectCountry, "true", "containous"), + withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectLocality, "true", "containous"), + withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectOrganization, "true", "containous"), + withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectProvince, "true", "containous"), + withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber, "true", "containous"), + + withSegmentLabel(label.TraefikFrontendAuthBasic, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", "containous"), + withSegmentLabel(label.TraefikFrontendAuthBasicRemoveHeader, "true", "containous"), + withSegmentLabel(label.TraefikFrontendAuthBasicUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", "containous"), + withSegmentLabel(label.TraefikFrontendAuthBasicUsersFile, ".htpasswd", "containous"), + withSegmentLabel(label.TraefikFrontendAuthDigestRemoveHeader, "true", "containous"), + withSegmentLabel(label.TraefikFrontendAuthDigestUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", "containous"), + withSegmentLabel(label.TraefikFrontendAuthDigestUsersFile, ".htpasswd", "containous"), + withSegmentLabel(label.TraefikFrontendAuthForwardAddress, "auth.server", "containous"), + withSegmentLabel(label.TraefikFrontendAuthForwardTrustForwardHeader, "true", "containous"), + withSegmentLabel(label.TraefikFrontendAuthForwardTLSCa, "ca.crt", "containous"), + withSegmentLabel(label.TraefikFrontendAuthForwardTLSCaOptional, "true", "containous"), + withSegmentLabel(label.TraefikFrontendAuthForwardTLSCert, "server.crt", "containous"), + withSegmentLabel(label.TraefikFrontendAuthForwardTLSKey, "server.key", "containous"), + withSegmentLabel(label.TraefikFrontendAuthForwardTLSInsecureSkipVerify, "true", "containous"), + withSegmentLabel(label.TraefikFrontendAuthHeaderField, "X-WebAuth-User", "containous"), + + withSegmentLabel(label.TraefikFrontendEntryPoints, "http,https", "containous"), + withSegmentLabel(label.TraefikFrontendPassHostHeader, "true", "containous"), + withSegmentLabel(label.TraefikFrontendPassTLSCert, "true", "containous"), + withSegmentLabel(label.TraefikFrontendPriority, "666", "containous"), + withSegmentLabel(label.TraefikFrontendRedirectEntryPoint, "https", "containous"), + withSegmentLabel(label.TraefikFrontendRedirectRegex, "nope", "containous"), + withSegmentLabel(label.TraefikFrontendRedirectReplacement, "nope", "containous"), + withSegmentLabel(label.TraefikFrontendRedirectPermanent, "true", "containous"), + withSegmentLabel(label.TraefikFrontendRule, "Host:traefik.io", "containous"), + withSegmentLabel(label.TraefikFrontendWhiteListSourceRange, "10.10.10.10", "containous"), + withSegmentLabel(label.TraefikFrontendWhiteListUseXForwardedFor, "true", "containous"), + + withSegmentLabel(label.TraefikFrontendRequestHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", "containous"), + withSegmentLabel(label.TraefikFrontendResponseHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", "containous"), + withSegmentLabel(label.TraefikFrontendSSLProxyHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", "containous"), + withSegmentLabel(label.TraefikFrontendAllowedHosts, "foo,bar,bor", "containous"), + withSegmentLabel(label.TraefikFrontendHostsProxyHeaders, "foo,bar,bor", "containous"), + withSegmentLabel(label.TraefikFrontendSSLForceHost, "true", "containous"), + withSegmentLabel(label.TraefikFrontendSSLHost, "foo", "containous"), + withSegmentLabel(label.TraefikFrontendCustomFrameOptionsValue, "foo", "containous"), + withSegmentLabel(label.TraefikFrontendContentSecurityPolicy, "foo", "containous"), + withSegmentLabel(label.TraefikFrontendPublicKey, "foo", "containous"), + withSegmentLabel(label.TraefikFrontendReferrerPolicy, "foo", "containous"), + withSegmentLabel(label.TraefikFrontendCustomBrowserXSSValue, "foo", "containous"), + withSegmentLabel(label.TraefikFrontendSTSSeconds, "666", "containous"), + withSegmentLabel(label.TraefikFrontendSSLRedirect, "true", "containous"), + withSegmentLabel(label.TraefikFrontendSSLTemporaryRedirect, "true", "containous"), + withSegmentLabel(label.TraefikFrontendSTSIncludeSubdomains, "true", "containous"), + withSegmentLabel(label.TraefikFrontendSTSPreload, "true", "containous"), + withSegmentLabel(label.TraefikFrontendForceSTSHeader, "true", "containous"), + withSegmentLabel(label.TraefikFrontendFrameDeny, "true", "containous"), + withSegmentLabel(label.TraefikFrontendContentTypeNosniff, "true", "containous"), + withSegmentLabel(label.TraefikFrontendBrowserXSSFilter, "true", "containous"), + withSegmentLabel(label.TraefikFrontendIsDevelopment, "true", "containous"), + + withLabel(label.Prefix+"containous."+label.BaseFrontendErrorPage+"foo."+label.SuffixErrorPageStatus, "404"), + withLabel(label.Prefix+"containous."+label.BaseFrontendErrorPage+"foo."+label.SuffixErrorPageBackend, "foobar"), + withLabel(label.Prefix+"containous."+label.BaseFrontendErrorPage+"foo."+label.SuffixErrorPageQuery, "foo_query"), + withLabel(label.Prefix+"containous."+label.BaseFrontendErrorPage+"bar."+label.SuffixErrorPageStatus, "500,600"), + withLabel(label.Prefix+"containous."+label.BaseFrontendErrorPage+"bar."+label.SuffixErrorPageBackend, "foobar"), + withLabel(label.Prefix+"containous."+label.BaseFrontendErrorPage+"bar."+label.SuffixErrorPageQuery, "bar_query"), + + withSegmentLabel(label.TraefikFrontendRateLimitExtractorFunc, "client.ip", "containous"), + withLabel(label.Prefix+"containous."+label.BaseFrontendRateLimit+"foo."+label.SuffixRateLimitPeriod, "6"), + withLabel(label.Prefix+"containous."+label.BaseFrontendRateLimit+"foo."+label.SuffixRateLimitAverage, "12"), + withLabel(label.Prefix+"containous."+label.BaseFrontendRateLimit+"foo."+label.SuffixRateLimitBurst, "18"), + withLabel(label.Prefix+"containous."+label.BaseFrontendRateLimit+"bar."+label.SuffixRateLimitPeriod, "3"), + withLabel(label.Prefix+"containous."+label.BaseFrontendRateLimit+"bar."+label.SuffixRateLimitAverage, "6"), + withLabel(label.Prefix+"containous."+label.BaseFrontendRateLimit+"bar."+label.SuffixRateLimitBurst, "9"), + )), + expectedFrontends: map[string]*types.Frontend{ + "frontend-app-service-containous": { + EntryPoints: []string{ + "http", + "https", + }, + Backend: "backend-app-service-containous", + Routes: map[string]types.Route{ + "route-host-app-service-containous": { + Rule: "Host:traefik.io", + }, + }, + PassHostHeader: true, + PassTLSCert: true, + Priority: 666, + PassTLSClientCert: &types.TLSClientHeaders{ + PEM: true, + Infos: &types.TLSClientCertificateInfos{ + NotBefore: true, + Sans: true, + NotAfter: true, + Subject: &types.TLSCLientCertificateSubjectInfos{ + CommonName: true, + Country: true, + Locality: true, + Organization: true, + Province: true, + SerialNumber: true, + }, + }, + }, + Auth: &types.Auth{ + HeaderField: "X-WebAuth-User", + Basic: &types.Basic{ + RemoveHeader: true, + Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", + "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, + UsersFile: ".htpasswd", + }, + }, + WhiteList: &types.WhiteList{ + SourceRange: []string{"10.10.10.10"}, + UseXForwardedFor: true, + }, + Headers: &types.Headers{ + CustomRequestHeaders: map[string]string{ + "Access-Control-Allow-Methods": "POST,GET,OPTIONS", + "Content-Type": "application/json; charset=utf-8", + }, + CustomResponseHeaders: map[string]string{ + "Access-Control-Allow-Methods": "POST,GET,OPTIONS", + "Content-Type": "application/json; charset=utf-8", + }, + AllowedHosts: []string{ + "foo", + "bar", + "bor", + }, + HostsProxyHeaders: []string{ + "foo", + "bar", + "bor", + }, + SSLRedirect: true, + SSLTemporaryRedirect: true, + SSLForceHost: true, + SSLHost: "foo", + SSLProxyHeaders: map[string]string{ + "Access-Control-Allow-Methods": "POST,GET,OPTIONS", + "Content-Type": "application/json; charset=utf-8", + }, + STSSeconds: 666, + STSIncludeSubdomains: true, + STSPreload: true, + ForceSTSHeader: true, + FrameDeny: true, + CustomFrameOptionsValue: "foo", + ContentTypeNosniff: true, + BrowserXSSFilter: true, + CustomBrowserXSSValue: "foo", + ContentSecurityPolicy: "foo", + PublicKey: "foo", + ReferrerPolicy: "foo", + IsDevelopment: true, + }, + Errors: map[string]*types.ErrorPage{ + "bar": { + Status: []string{ + "500", + "600", + }, + Backend: "backendfoobar", + Query: "bar_query", + }, + "foo": { + Status: []string{ + "404", + }, + Backend: "backendfoobar", + Query: "foo_query", + }, + }, + RateLimit: &types.RateLimit{ + RateSet: map[string]*types.Rate{ + "bar": { + Period: flaeg.Duration(3 * time.Second), + Average: 6, + Burst: 9, + }, + "foo": { + Period: flaeg.Duration(6 * time.Second), + Average: 12, + Burst: 18, + }, + }, + ExtractorFunc: "client.ip", + }, + Redirect: &types.Redirect{ + EntryPoint: "https", + Permanent: true, + }, + }, + }, + expectedBackends: map[string]*types.Backend{ + "backend-app-service-containous": { + Servers: map[string]types.Server{ + "server-app-taskID-service-containous": { + URL: "https://localhost:80", + Weight: 12, + }, + }, + CircuitBreaker: &types.CircuitBreaker{ + Expression: "NetworkErrorRatio() > 0.5", + }, + LoadBalancer: &types.LoadBalancer{ + Method: "drr", + Sticky: true, + Stickiness: &types.Stickiness{ + CookieName: "chocolate", + }, + }, + MaxConn: &types.MaxConn{ + Amount: 666, + ExtractorFunc: "client.ip", + }, + HealthCheck: &types.HealthCheck{ + Path: "/health", + Port: 880, + Interval: "6", + }, + Buffering: &types.Buffering{ + MaxResponseBodyBytes: 10485760, + MemResponseBodyBytes: 2097152, + MaxRequestBodyBytes: 10485760, + MemRequestBodyBytes: 2097152, + RetryExpression: "IsNetworkError() && Attempts() <= 2", + }, + }, + }, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + p := &Provider{ + Domain: "marathon.localhost", + ExposedByDefault: true, + } + + actualConfig := p.buildConfigurationV2(test.applications) + + assert.NotNil(t, actualConfig) + assert.Equal(t, test.expectedBackends, actualConfig.Backends) + assert.Equal(t, test.expectedFrontends, actualConfig.Frontends) + }) + } +} diff --git a/provider/marathon/config_test.go b/provider/marathon/config_test.go index e7471c649..172b88963 100644 --- a/provider/marathon/config_test.go +++ b/provider/marathon/config_test.go @@ -299,6 +299,7 @@ func TestBuildConfiguration(t *testing.T) { withLabel(label.TraefikFrontendAuthForwardTLSCert, "server.crt"), withLabel(label.TraefikFrontendAuthForwardTLSKey, "server.key"), withLabel(label.TraefikFrontendAuthForwardTLSInsecureSkipVerify, "true"), + withLabel(label.TraefikFrontendAuthForwardAuthResponseHeaders, "X-Auth-User,X-Auth-Token"), withTasks(localhostTask(taskPorts(80))), )), @@ -313,8 +314,7 @@ func TestBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Forward: &types.Forward{ - Address: "auth.server", - TrustForwardHeader: true, + Address: "auth.server", TLS: &types.ClientTLS{ CA: "ca.crt", CAOptional: true, @@ -322,6 +322,8 @@ func TestBuildConfiguration(t *testing.T) { Cert: "server.crt", Key: "server.key", }, + TrustForwardHeader: true, + AuthResponseHeaders: []string{"X-Auth-User", "X-Auth-Token"}, }, }, PassHostHeader: true, @@ -690,366 +692,6 @@ func TestBuildConfiguration(t *testing.T) { } } -func TestBuildConfigurationSegments(t *testing.T) { - testCases := []struct { - desc string - applications *marathon.Applications - expectedFrontends map[string]*types.Frontend - expectedBackends map[string]*types.Backend - }{ - { - desc: "multiple ports with segments", - applications: withApplications( - application( - appID("/app"), - appPorts(80, 81), - withTasks(localhostTask(taskPorts(80, 81))), - - withLabel(label.TraefikBackendMaxConnAmount, "1000"), - withLabel(label.TraefikBackendMaxConnExtractorFunc, "client.ip"), - withSegmentLabel(label.TraefikPort, "80", "web"), - withSegmentLabel(label.TraefikPort, "81", "admin"), - withLabel("traefik..port", "82"), // This should be ignored, as it fails to match the segmentPropertiesRegexp regex. - withSegmentLabel(label.TraefikFrontendRule, "Host:web.app.marathon.localhost", "web"), - withSegmentLabel(label.TraefikFrontendRule, "Host:admin.app.marathon.localhost", "admin"), - )), - expectedFrontends: map[string]*types.Frontend{ - "frontend-app-service-web": { - Backend: "backend-app-service-web", - Routes: map[string]types.Route{ - `route-host-app-service-web`: { - Rule: "Host:web.app.marathon.localhost", - }, - }, - PassHostHeader: true, - EntryPoints: []string{}, - }, - "frontend-app-service-admin": { - Backend: "backend-app-service-admin", - Routes: map[string]types.Route{ - `route-host-app-service-admin`: { - Rule: "Host:admin.app.marathon.localhost", - }, - }, - PassHostHeader: true, - EntryPoints: []string{}, - }, - }, - expectedBackends: map[string]*types.Backend{ - "backend-app-service-web": { - Servers: map[string]types.Server{ - "server-app-taskID-service-web": { - URL: "http://localhost:80", - Weight: label.DefaultWeight, - }, - }, - MaxConn: &types.MaxConn{ - Amount: 1000, - ExtractorFunc: "client.ip", - }, - }, - "backend-app-service-admin": { - Servers: map[string]types.Server{ - "server-app-taskID-service-admin": { - URL: "http://localhost:81", - Weight: label.DefaultWeight, - }, - }, - MaxConn: &types.MaxConn{ - Amount: 1000, - ExtractorFunc: "client.ip", - }, - }, - }, - }, - { - desc: "when all labels are set", - applications: withApplications( - application( - appID("/app"), - appPorts(80, 81), - withTasks(localhostTask(taskPorts(80, 81))), - - // withLabel(label.TraefikBackend, "foobar"), - - withLabel(label.TraefikBackendCircuitBreakerExpression, "NetworkErrorRatio() > 0.5"), - withLabel(label.TraefikBackendHealthCheckPath, "/health"), - withLabel(label.TraefikBackendHealthCheckPort, "880"), - withLabel(label.TraefikBackendHealthCheckInterval, "6"), - withLabel(label.TraefikBackendLoadBalancerMethod, "drr"), - withLabel(label.TraefikBackendLoadBalancerSticky, "true"), - withLabel(label.TraefikBackendLoadBalancerStickiness, "true"), - withLabel(label.TraefikBackendLoadBalancerStickinessCookieName, "chocolate"), - withLabel(label.TraefikBackendMaxConnAmount, "666"), - withLabel(label.TraefikBackendMaxConnExtractorFunc, "client.ip"), - withLabel(label.TraefikBackendBufferingMaxResponseBodyBytes, "10485760"), - withLabel(label.TraefikBackendBufferingMemResponseBodyBytes, "2097152"), - withLabel(label.TraefikBackendBufferingMaxRequestBodyBytes, "10485760"), - withLabel(label.TraefikBackendBufferingMemRequestBodyBytes, "2097152"), - withLabel(label.TraefikBackendBufferingRetryExpression, "IsNetworkError() && Attempts() <= 2"), - - withSegmentLabel(label.TraefikPort, "80", "containous"), - withSegmentLabel(label.TraefikProtocol, "https", "containous"), - withSegmentLabel(label.TraefikWeight, "12", "containous"), - - withSegmentLabel(label.TraefikFrontendPassTLSClientCertPem, "true", "containous"), - withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosNotBefore, "true", "containous"), - withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosNotAfter, "true", "containous"), - withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSans, "true", "containous"), - withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectCommonName, "true", "containous"), - withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectCountry, "true", "containous"), - withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectLocality, "true", "containous"), - withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectOrganization, "true", "containous"), - withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectProvince, "true", "containous"), - withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber, "true", "containous"), - - withSegmentLabel(label.TraefikFrontendAuthBasic, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", "containous"), - withSegmentLabel(label.TraefikFrontendAuthBasicRemoveHeader, "true", "containous"), - withSegmentLabel(label.TraefikFrontendAuthBasicUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", "containous"), - withSegmentLabel(label.TraefikFrontendAuthBasicUsersFile, ".htpasswd", "containous"), - withSegmentLabel(label.TraefikFrontendAuthDigestRemoveHeader, "true", "containous"), - withSegmentLabel(label.TraefikFrontendAuthDigestUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", "containous"), - withSegmentLabel(label.TraefikFrontendAuthDigestUsersFile, ".htpasswd", "containous"), - withSegmentLabel(label.TraefikFrontendAuthForwardAddress, "auth.server", "containous"), - withSegmentLabel(label.TraefikFrontendAuthForwardTrustForwardHeader, "true", "containous"), - withSegmentLabel(label.TraefikFrontendAuthForwardTLSCa, "ca.crt", "containous"), - withSegmentLabel(label.TraefikFrontendAuthForwardTLSCaOptional, "true", "containous"), - withSegmentLabel(label.TraefikFrontendAuthForwardTLSCert, "server.crt", "containous"), - withSegmentLabel(label.TraefikFrontendAuthForwardTLSKey, "server.key", "containous"), - withSegmentLabel(label.TraefikFrontendAuthForwardTLSInsecureSkipVerify, "true", "containous"), - withSegmentLabel(label.TraefikFrontendAuthHeaderField, "X-WebAuth-User", "containous"), - - withSegmentLabel(label.TraefikFrontendEntryPoints, "http,https", "containous"), - withSegmentLabel(label.TraefikFrontendPassHostHeader, "true", "containous"), - withSegmentLabel(label.TraefikFrontendPassTLSCert, "true", "containous"), - withSegmentLabel(label.TraefikFrontendPriority, "666", "containous"), - withSegmentLabel(label.TraefikFrontendRedirectEntryPoint, "https", "containous"), - withSegmentLabel(label.TraefikFrontendRedirectRegex, "nope", "containous"), - withSegmentLabel(label.TraefikFrontendRedirectReplacement, "nope", "containous"), - withSegmentLabel(label.TraefikFrontendRedirectPermanent, "true", "containous"), - withSegmentLabel(label.TraefikFrontendRule, "Host:traefik.io", "containous"), - withSegmentLabel(label.TraefikFrontendWhiteListSourceRange, "10.10.10.10", "containous"), - withSegmentLabel(label.TraefikFrontendWhiteListUseXForwardedFor, "true", "containous"), - - withSegmentLabel(label.TraefikFrontendRequestHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", "containous"), - withSegmentLabel(label.TraefikFrontendResponseHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", "containous"), - withSegmentLabel(label.TraefikFrontendSSLProxyHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", "containous"), - withSegmentLabel(label.TraefikFrontendAllowedHosts, "foo,bar,bor", "containous"), - withSegmentLabel(label.TraefikFrontendHostsProxyHeaders, "foo,bar,bor", "containous"), - withSegmentLabel(label.TraefikFrontendSSLForceHost, "true", "containous"), - withSegmentLabel(label.TraefikFrontendSSLHost, "foo", "containous"), - withSegmentLabel(label.TraefikFrontendCustomFrameOptionsValue, "foo", "containous"), - withSegmentLabel(label.TraefikFrontendContentSecurityPolicy, "foo", "containous"), - withSegmentLabel(label.TraefikFrontendPublicKey, "foo", "containous"), - withSegmentLabel(label.TraefikFrontendReferrerPolicy, "foo", "containous"), - withSegmentLabel(label.TraefikFrontendCustomBrowserXSSValue, "foo", "containous"), - withSegmentLabel(label.TraefikFrontendSTSSeconds, "666", "containous"), - withSegmentLabel(label.TraefikFrontendSSLRedirect, "true", "containous"), - withSegmentLabel(label.TraefikFrontendSSLTemporaryRedirect, "true", "containous"), - withSegmentLabel(label.TraefikFrontendSTSIncludeSubdomains, "true", "containous"), - withSegmentLabel(label.TraefikFrontendSTSPreload, "true", "containous"), - withSegmentLabel(label.TraefikFrontendForceSTSHeader, "true", "containous"), - withSegmentLabel(label.TraefikFrontendFrameDeny, "true", "containous"), - withSegmentLabel(label.TraefikFrontendContentTypeNosniff, "true", "containous"), - withSegmentLabel(label.TraefikFrontendBrowserXSSFilter, "true", "containous"), - withSegmentLabel(label.TraefikFrontendIsDevelopment, "true", "containous"), - - withLabel(label.Prefix+"containous."+label.BaseFrontendErrorPage+"foo."+label.SuffixErrorPageStatus, "404"), - withLabel(label.Prefix+"containous."+label.BaseFrontendErrorPage+"foo."+label.SuffixErrorPageBackend, "foobar"), - withLabel(label.Prefix+"containous."+label.BaseFrontendErrorPage+"foo."+label.SuffixErrorPageQuery, "foo_query"), - withLabel(label.Prefix+"containous."+label.BaseFrontendErrorPage+"bar."+label.SuffixErrorPageStatus, "500,600"), - withLabel(label.Prefix+"containous."+label.BaseFrontendErrorPage+"bar."+label.SuffixErrorPageBackend, "foobar"), - withLabel(label.Prefix+"containous."+label.BaseFrontendErrorPage+"bar."+label.SuffixErrorPageQuery, "bar_query"), - - withSegmentLabel(label.TraefikFrontendRateLimitExtractorFunc, "client.ip", "containous"), - withLabel(label.Prefix+"containous."+label.BaseFrontendRateLimit+"foo."+label.SuffixRateLimitPeriod, "6"), - withLabel(label.Prefix+"containous."+label.BaseFrontendRateLimit+"foo."+label.SuffixRateLimitAverage, "12"), - withLabel(label.Prefix+"containous."+label.BaseFrontendRateLimit+"foo."+label.SuffixRateLimitBurst, "18"), - withLabel(label.Prefix+"containous."+label.BaseFrontendRateLimit+"bar."+label.SuffixRateLimitPeriod, "3"), - withLabel(label.Prefix+"containous."+label.BaseFrontendRateLimit+"bar."+label.SuffixRateLimitAverage, "6"), - withLabel(label.Prefix+"containous."+label.BaseFrontendRateLimit+"bar."+label.SuffixRateLimitBurst, "9"), - )), - expectedFrontends: map[string]*types.Frontend{ - "frontend-app-service-containous": { - EntryPoints: []string{ - "http", - "https", - }, - Backend: "backend-app-service-containous", - Routes: map[string]types.Route{ - "route-host-app-service-containous": { - Rule: "Host:traefik.io", - }, - }, - PassHostHeader: true, - PassTLSCert: true, - Priority: 666, - PassTLSClientCert: &types.TLSClientHeaders{ - PEM: true, - Infos: &types.TLSClientCertificateInfos{ - NotBefore: true, - Sans: true, - NotAfter: true, - Subject: &types.TLSCLientCertificateSubjectInfos{ - CommonName: true, - Country: true, - Locality: true, - Organization: true, - Province: true, - SerialNumber: true, - }, - }, - }, - Auth: &types.Auth{ - HeaderField: "X-WebAuth-User", - Basic: &types.Basic{ - RemoveHeader: true, - Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", - "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, - UsersFile: ".htpasswd", - }, - }, - WhiteList: &types.WhiteList{ - SourceRange: []string{"10.10.10.10"}, - UseXForwardedFor: true, - }, - Headers: &types.Headers{ - CustomRequestHeaders: map[string]string{ - "Access-Control-Allow-Methods": "POST,GET,OPTIONS", - "Content-Type": "application/json; charset=utf-8", - }, - CustomResponseHeaders: map[string]string{ - "Access-Control-Allow-Methods": "POST,GET,OPTIONS", - "Content-Type": "application/json; charset=utf-8", - }, - AllowedHosts: []string{ - "foo", - "bar", - "bor", - }, - HostsProxyHeaders: []string{ - "foo", - "bar", - "bor", - }, - SSLRedirect: true, - SSLTemporaryRedirect: true, - SSLForceHost: true, - SSLHost: "foo", - SSLProxyHeaders: map[string]string{ - "Access-Control-Allow-Methods": "POST,GET,OPTIONS", - "Content-Type": "application/json; charset=utf-8", - }, - STSSeconds: 666, - STSIncludeSubdomains: true, - STSPreload: true, - ForceSTSHeader: true, - FrameDeny: true, - CustomFrameOptionsValue: "foo", - ContentTypeNosniff: true, - BrowserXSSFilter: true, - CustomBrowserXSSValue: "foo", - ContentSecurityPolicy: "foo", - PublicKey: "foo", - ReferrerPolicy: "foo", - IsDevelopment: true, - }, - Errors: map[string]*types.ErrorPage{ - "bar": { - Status: []string{ - "500", - "600", - }, - Backend: "backendfoobar", - Query: "bar_query", - }, - "foo": { - Status: []string{ - "404", - }, - Backend: "backendfoobar", - Query: "foo_query", - }, - }, - RateLimit: &types.RateLimit{ - RateSet: map[string]*types.Rate{ - "bar": { - Period: flaeg.Duration(3 * time.Second), - Average: 6, - Burst: 9, - }, - "foo": { - Period: flaeg.Duration(6 * time.Second), - Average: 12, - Burst: 18, - }, - }, - ExtractorFunc: "client.ip", - }, - Redirect: &types.Redirect{ - EntryPoint: "https", - Permanent: true, - }, - }, - }, - expectedBackends: map[string]*types.Backend{ - "backend-app-service-containous": { - Servers: map[string]types.Server{ - "server-app-taskID-service-containous": { - URL: "https://localhost:80", - Weight: 12, - }, - }, - CircuitBreaker: &types.CircuitBreaker{ - Expression: "NetworkErrorRatio() > 0.5", - }, - LoadBalancer: &types.LoadBalancer{ - Method: "drr", - Sticky: true, - Stickiness: &types.Stickiness{ - CookieName: "chocolate", - }, - }, - MaxConn: &types.MaxConn{ - Amount: 666, - ExtractorFunc: "client.ip", - }, - HealthCheck: &types.HealthCheck{ - Path: "/health", - Port: 880, - Interval: "6", - }, - Buffering: &types.Buffering{ - MaxResponseBodyBytes: 10485760, - MemResponseBodyBytes: 2097152, - MaxRequestBodyBytes: 10485760, - MemRequestBodyBytes: 2097152, - RetryExpression: "IsNetworkError() && Attempts() <= 2", - }, - }, - }, - }, - } - - for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - - p := &Provider{ - Domain: "marathon.localhost", - ExposedByDefault: true, - } - - actualConfig := p.buildConfigurationV2(test.applications) - - assert.NotNil(t, actualConfig) - assert.Equal(t, test.expectedBackends, actualConfig.Backends) - assert.Equal(t, test.expectedFrontends, actualConfig.Frontends) - }) - } -} - func TestApplicationFilterConstraints(t *testing.T) { testCases := []struct { desc string diff --git a/provider/mesos/config_segment_test.go b/provider/mesos/config_segment_test.go new file mode 100644 index 000000000..8214e35d9 --- /dev/null +++ b/provider/mesos/config_segment_test.go @@ -0,0 +1,394 @@ +package mesos + +import ( + "testing" + "time" + + "github.com/containous/flaeg" + "github.com/containous/traefik/provider/label" + "github.com/containous/traefik/types" + "github.com/mesosphere/mesos-dns/records/state" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestBuildConfigurationSegments(t *testing.T) { + p := &Provider{ + Domain: "mesos.localhost", + ExposedByDefault: true, + IPSources: "host", + } + + testCases := []struct { + desc string + tasks []state.Task + expectedFrontends map[string]*types.Frontend + expectedBackends map[string]*types.Backend + }{ + { + desc: "multiple ports with segments", + tasks: []state.Task{ + aTask("app-taskID", + withIP("127.0.0.1"), + withInfo("/app", + withPorts( + withPort("TCP", 80, "web"), + withPort("TCP", 81, "admin"), + ), + ), + withStatus(withHealthy(true), withState("TASK_RUNNING")), + withLabel(label.TraefikBackendMaxConnAmount, "1000"), + withLabel(label.TraefikBackendMaxConnExtractorFunc, "client.ip"), + withSegmentLabel(label.TraefikPort, "80", "web"), + withSegmentLabel(label.TraefikPort, "81", "admin"), + withLabel("traefik..port", "82"), // This should be ignored, as it fails to match the segmentPropertiesRegexp regex. + withSegmentLabel(label.TraefikFrontendRule, "Host:web.app.mesos.localhost", "web"), + withSegmentLabel(label.TraefikFrontendRule, "Host:admin.app.mesos.localhost", "admin"), + ), + }, + expectedFrontends: map[string]*types.Frontend{ + "frontend-app-taskID-service-web": { + Backend: "backend-app-service-web", + Routes: map[string]types.Route{ + `route-host-app-taskID-service-web`: { + Rule: "Host:web.app.mesos.localhost", + }, + }, + PassHostHeader: true, + EntryPoints: []string{}, + }, + "frontend-app-taskID-service-admin": { + Backend: "backend-app-service-admin", + Routes: map[string]types.Route{ + `route-host-app-taskID-service-admin`: { + Rule: "Host:admin.app.mesos.localhost", + }, + }, + PassHostHeader: true, + EntryPoints: []string{}, + }, + }, + expectedBackends: map[string]*types.Backend{ + "backend-app-service-web": { + Servers: map[string]types.Server{ + "server-app-taskID-service-web": { + URL: "http://127.0.0.1:80", + Weight: label.DefaultWeight, + }, + }, + MaxConn: &types.MaxConn{ + Amount: 1000, + ExtractorFunc: "client.ip", + }, + }, + "backend-app-service-admin": { + Servers: map[string]types.Server{ + "server-app-taskID-service-admin": { + URL: "http://127.0.0.1:81", + Weight: label.DefaultWeight, + }, + }, + MaxConn: &types.MaxConn{ + Amount: 1000, + ExtractorFunc: "client.ip", + }, + }, + }, + }, + { + desc: "when all labels are set", + tasks: []state.Task{ + aTask("app-taskID", + withIP("127.0.0.1"), + withInfo("/app", + withPorts( + withPort("TCP", 80, "web"), + withPort("TCP", 81, "admin"), + ), + ), + withStatus(withHealthy(true), withState("TASK_RUNNING")), + + withLabel(label.TraefikBackendCircuitBreakerExpression, "NetworkErrorRatio() > 0.5"), + withLabel(label.TraefikBackendHealthCheckScheme, "http"), + withLabel(label.TraefikBackendHealthCheckPath, "/health"), + withLabel(label.TraefikBackendHealthCheckPort, "880"), + withLabel(label.TraefikBackendHealthCheckInterval, "6"), + withLabel(label.TraefikBackendHealthCheckHostname, "foo.com"), + withLabel(label.TraefikBackendHealthCheckHeaders, "Foo:bar || Bar:foo"), + withLabel(label.TraefikBackendLoadBalancerMethod, "drr"), + withLabel(label.TraefikBackendLoadBalancerSticky, "true"), + withLabel(label.TraefikBackendLoadBalancerStickiness, "true"), + withLabel(label.TraefikBackendLoadBalancerStickinessCookieName, "chocolate"), + withLabel(label.TraefikBackendMaxConnAmount, "666"), + withLabel(label.TraefikBackendMaxConnExtractorFunc, "client.ip"), + withLabel(label.TraefikBackendBufferingMaxResponseBodyBytes, "10485760"), + withLabel(label.TraefikBackendBufferingMemResponseBodyBytes, "2097152"), + withLabel(label.TraefikBackendBufferingMaxRequestBodyBytes, "10485760"), + withLabel(label.TraefikBackendBufferingMemRequestBodyBytes, "2097152"), + withLabel(label.TraefikBackendBufferingRetryExpression, "IsNetworkError() && Attempts() <= 2"), + + withSegmentLabel(label.TraefikPort, "80", "containous"), + withSegmentLabel(label.TraefikPortName, "web", "containous"), + withSegmentLabel(label.TraefikProtocol, "https", "containous"), + withSegmentLabel(label.TraefikWeight, "12", "containous"), + + withSegmentLabel(label.TraefikFrontendPassTLSClientCertPem, "true", "containous"), + withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosNotBefore, "true", "containous"), + withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosNotAfter, "true", "containous"), + withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSans, "true", "containous"), + withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectCommonName, "true", "containous"), + withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectCountry, "true", "containous"), + withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectLocality, "true", "containous"), + withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectOrganization, "true", "containous"), + withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectProvince, "true", "containous"), + withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber, "true", "containous"), + + withSegmentLabel(label.TraefikFrontendAuthBasic, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", "containous"), + withSegmentLabel(label.TraefikFrontendAuthBasicRemoveHeader, "true", "containous"), + withSegmentLabel(label.TraefikFrontendAuthBasicUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", "containous"), + withSegmentLabel(label.TraefikFrontendAuthBasicUsersFile, ".htpasswd", "containous"), + withSegmentLabel(label.TraefikFrontendAuthDigestRemoveHeader, "true", "containous"), + withSegmentLabel(label.TraefikFrontendAuthDigestUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", "containous"), + withSegmentLabel(label.TraefikFrontendAuthDigestUsersFile, ".htpasswd", "containous"), + withSegmentLabel(label.TraefikFrontendAuthForwardAddress, "auth.server", "containous"), + withSegmentLabel(label.TraefikFrontendAuthForwardTrustForwardHeader, "true", "containous"), + withSegmentLabel(label.TraefikFrontendAuthForwardTLSCa, "ca.crt", "containous"), + withSegmentLabel(label.TraefikFrontendAuthForwardTLSCaOptional, "true", "containous"), + withSegmentLabel(label.TraefikFrontendAuthForwardTLSCert, "server.crt", "containous"), + withSegmentLabel(label.TraefikFrontendAuthForwardTLSKey, "server.key", "containous"), + withSegmentLabel(label.TraefikFrontendAuthForwardTLSInsecureSkipVerify, "true", "containous"), + withSegmentLabel(label.TraefikFrontendAuthHeaderField, "X-WebAuth-User", "containous"), + + withSegmentLabel(label.TraefikFrontendEntryPoints, "http,https", "containous"), + withSegmentLabel(label.TraefikFrontendPassHostHeader, "true", "containous"), + withSegmentLabel(label.TraefikFrontendPassTLSCert, "true", "containous"), + withSegmentLabel(label.TraefikFrontendPriority, "666", "containous"), + withSegmentLabel(label.TraefikFrontendRedirectEntryPoint, "https", "containous"), + withSegmentLabel(label.TraefikFrontendRedirectRegex, "nope", "containous"), + withSegmentLabel(label.TraefikFrontendRedirectReplacement, "nope", "containous"), + withSegmentLabel(label.TraefikFrontendRedirectPermanent, "true", "containous"), + withSegmentLabel(label.TraefikFrontendRule, "Host:traefik.io", "containous"), + withSegmentLabel(label.TraefikFrontendWhiteListSourceRange, "10.10.10.10", "containous"), + withSegmentLabel(label.TraefikFrontendWhiteListUseXForwardedFor, "true", "containous"), + + withSegmentLabel(label.TraefikFrontendRequestHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", "containous"), + withSegmentLabel(label.TraefikFrontendResponseHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", "containous"), + withSegmentLabel(label.TraefikFrontendSSLProxyHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", "containous"), + withSegmentLabel(label.TraefikFrontendAllowedHosts, "foo,bar,bor", "containous"), + withSegmentLabel(label.TraefikFrontendHostsProxyHeaders, "foo,bar,bor", "containous"), + withSegmentLabel(label.TraefikFrontendSSLForceHost, "true", "containous"), + withSegmentLabel(label.TraefikFrontendSSLHost, "foo", "containous"), + withSegmentLabel(label.TraefikFrontendCustomFrameOptionsValue, "foo", "containous"), + withSegmentLabel(label.TraefikFrontendContentSecurityPolicy, "foo", "containous"), + withSegmentLabel(label.TraefikFrontendPublicKey, "foo", "containous"), + withSegmentLabel(label.TraefikFrontendReferrerPolicy, "foo", "containous"), + withSegmentLabel(label.TraefikFrontendCustomBrowserXSSValue, "foo", "containous"), + withSegmentLabel(label.TraefikFrontendSTSSeconds, "666", "containous"), + withSegmentLabel(label.TraefikFrontendSSLRedirect, "true", "containous"), + withSegmentLabel(label.TraefikFrontendSSLTemporaryRedirect, "true", "containous"), + withSegmentLabel(label.TraefikFrontendSTSIncludeSubdomains, "true", "containous"), + withSegmentLabel(label.TraefikFrontendSTSPreload, "true", "containous"), + withSegmentLabel(label.TraefikFrontendForceSTSHeader, "true", "containous"), + withSegmentLabel(label.TraefikFrontendFrameDeny, "true", "containous"), + withSegmentLabel(label.TraefikFrontendContentTypeNosniff, "true", "containous"), + withSegmentLabel(label.TraefikFrontendBrowserXSSFilter, "true", "containous"), + withSegmentLabel(label.TraefikFrontendIsDevelopment, "true", "containous"), + + withLabel(label.Prefix+"containous."+label.BaseFrontendErrorPage+"foo."+label.SuffixErrorPageStatus, "404"), + withLabel(label.Prefix+"containous."+label.BaseFrontendErrorPage+"foo."+label.SuffixErrorPageBackend, "foobar"), + withLabel(label.Prefix+"containous."+label.BaseFrontendErrorPage+"foo."+label.SuffixErrorPageQuery, "foo_query"), + withLabel(label.Prefix+"containous."+label.BaseFrontendErrorPage+"bar."+label.SuffixErrorPageStatus, "500,600"), + withLabel(label.Prefix+"containous."+label.BaseFrontendErrorPage+"bar."+label.SuffixErrorPageBackend, "foobar"), + withLabel(label.Prefix+"containous."+label.BaseFrontendErrorPage+"bar."+label.SuffixErrorPageQuery, "bar_query"), + + withSegmentLabel(label.TraefikFrontendRateLimitExtractorFunc, "client.ip", "containous"), + withLabel(label.Prefix+"containous."+label.BaseFrontendRateLimit+"foo."+label.SuffixRateLimitPeriod, "6"), + withLabel(label.Prefix+"containous."+label.BaseFrontendRateLimit+"foo."+label.SuffixRateLimitAverage, "12"), + withLabel(label.Prefix+"containous."+label.BaseFrontendRateLimit+"foo."+label.SuffixRateLimitBurst, "18"), + withLabel(label.Prefix+"containous."+label.BaseFrontendRateLimit+"bar."+label.SuffixRateLimitPeriod, "3"), + withLabel(label.Prefix+"containous."+label.BaseFrontendRateLimit+"bar."+label.SuffixRateLimitAverage, "6"), + withLabel(label.Prefix+"containous."+label.BaseFrontendRateLimit+"bar."+label.SuffixRateLimitBurst, "9"), + ), + }, + expectedFrontends: map[string]*types.Frontend{ + "frontend-app-taskID-service-containous": { + EntryPoints: []string{ + "http", + "https", + }, + Backend: "backend-app-service-containous", + Routes: map[string]types.Route{ + "route-host-app-taskID-service-containous": { + Rule: "Host:traefik.io", + }, + }, + PassHostHeader: true, + PassTLSCert: true, + Priority: 666, + PassTLSClientCert: &types.TLSClientHeaders{ + PEM: true, + Infos: &types.TLSClientCertificateInfos{ + NotBefore: true, + Sans: true, + NotAfter: true, + Subject: &types.TLSCLientCertificateSubjectInfos{ + CommonName: true, + Country: true, + Locality: true, + Organization: true, + Province: true, + SerialNumber: true, + }, + }, + }, + Auth: &types.Auth{ + HeaderField: "X-WebAuth-User", + Basic: &types.Basic{ + RemoveHeader: true, + Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", + "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, + UsersFile: ".htpasswd", + }, + }, + + WhiteList: &types.WhiteList{ + SourceRange: []string{"10.10.10.10"}, + UseXForwardedFor: true, + }, + Headers: &types.Headers{ + CustomRequestHeaders: map[string]string{ + "Access-Control-Allow-Methods": "POST,GET,OPTIONS", + "Content-Type": "application/json; charset=utf-8", + }, + CustomResponseHeaders: map[string]string{ + "Access-Control-Allow-Methods": "POST,GET,OPTIONS", + "Content-Type": "application/json; charset=utf-8", + }, + AllowedHosts: []string{ + "foo", + "bar", + "bor", + }, + HostsProxyHeaders: []string{ + "foo", + "bar", + "bor", + }, + SSLRedirect: true, + SSLTemporaryRedirect: true, + SSLForceHost: true, + SSLHost: "foo", + SSLProxyHeaders: map[string]string{ + "Access-Control-Allow-Methods": "POST,GET,OPTIONS", + "Content-Type": "application/json; charset=utf-8", + }, + STSSeconds: 666, + STSIncludeSubdomains: true, + STSPreload: true, + ForceSTSHeader: true, + FrameDeny: true, + CustomFrameOptionsValue: "foo", + ContentTypeNosniff: true, + BrowserXSSFilter: true, + CustomBrowserXSSValue: "foo", + ContentSecurityPolicy: "foo", + PublicKey: "foo", + ReferrerPolicy: "foo", + IsDevelopment: true, + }, + Errors: map[string]*types.ErrorPage{ + "bar": { + Status: []string{ + "500", + "600", + }, + Backend: "backend-foobar", + Query: "bar_query", + }, + "foo": { + Status: []string{ + "404", + }, + Backend: "backend-foobar", + Query: "foo_query", + }, + }, + RateLimit: &types.RateLimit{ + RateSet: map[string]*types.Rate{ + "bar": { + Period: flaeg.Duration(3 * time.Second), + Average: 6, + Burst: 9, + }, + "foo": { + Period: flaeg.Duration(6 * time.Second), + Average: 12, + Burst: 18, + }, + }, + ExtractorFunc: "client.ip", + }, + Redirect: &types.Redirect{ + EntryPoint: "https", + Permanent: true, + }, + }, + }, + expectedBackends: map[string]*types.Backend{ + "backend-app-service-containous": { + Servers: map[string]types.Server{ + "server-app-taskID-service-containous": { + URL: "https://127.0.0.1:80", + Weight: 12, + }, + }, + CircuitBreaker: &types.CircuitBreaker{ + Expression: "NetworkErrorRatio() > 0.5", + }, + LoadBalancer: &types.LoadBalancer{ + Method: "drr", + Sticky: true, + Stickiness: &types.Stickiness{ + CookieName: "chocolate", + }, + }, + MaxConn: &types.MaxConn{ + Amount: 666, + ExtractorFunc: "client.ip", + }, + HealthCheck: &types.HealthCheck{ + Scheme: "http", + Path: "/health", + Port: 880, + Interval: "6", + Hostname: "foo.com", + Headers: map[string]string{ + "Bar": "foo", + "Foo": "bar", + }, + }, + Buffering: &types.Buffering{ + MaxResponseBodyBytes: 10485760, + MemResponseBodyBytes: 2097152, + MaxRequestBodyBytes: 10485760, + MemRequestBodyBytes: 2097152, + RetryExpression: "IsNetworkError() && Attempts() <= 2", + }, + }, + }, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + actualConfig := p.buildConfigurationV2(test.tasks) + + require.NotNil(t, actualConfig) + assert.Equal(t, test.expectedBackends, actualConfig.Backends) + assert.Equal(t, test.expectedFrontends, actualConfig.Frontends) + }) + } +} diff --git a/provider/mesos/config_test.go b/provider/mesos/config_test.go index 13d195b4e..9961fd118 100644 --- a/provider/mesos/config_test.go +++ b/provider/mesos/config_test.go @@ -262,6 +262,7 @@ func TestBuildConfiguration(t *testing.T) { withLabel(label.TraefikFrontendAuthForwardTLSKey, "server.key"), withLabel(label.TraefikFrontendAuthForwardTLSInsecureSkipVerify, "true"), withLabel(label.TraefikFrontendAuthHeaderField, "X-WebAuth-User"), + withLabel(label.TraefikFrontendAuthForwardAuthResponseHeaders, "X-Auth-User,X-Auth-Token"), ), }, expectedFrontends: map[string]*types.Frontend{ @@ -277,8 +278,7 @@ func TestBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Forward: &types.Forward{ - Address: "auth.server", - TrustForwardHeader: true, + Address: "auth.server", TLS: &types.ClientTLS{ CA: "ca.crt", CAOptional: true, @@ -286,6 +286,8 @@ func TestBuildConfiguration(t *testing.T) { Cert: "server.crt", Key: "server.key", }, + TrustForwardHeader: true, + AuthResponseHeaders: []string{"X-Auth-User", "X-Auth-Token"}, }, }, }, @@ -591,387 +593,6 @@ func TestBuildConfiguration(t *testing.T) { } } -func TestBuildConfigurationSegments(t *testing.T) { - p := &Provider{ - Domain: "mesos.localhost", - ExposedByDefault: true, - IPSources: "host", - } - - testCases := []struct { - desc string - tasks []state.Task - expectedFrontends map[string]*types.Frontend - expectedBackends map[string]*types.Backend - }{ - { - desc: "multiple ports with segments", - tasks: []state.Task{ - aTask("app-taskID", - withIP("127.0.0.1"), - withInfo("/app", - withPorts( - withPort("TCP", 80, "web"), - withPort("TCP", 81, "admin"), - ), - ), - withStatus(withHealthy(true), withState("TASK_RUNNING")), - withLabel(label.TraefikBackendMaxConnAmount, "1000"), - withLabel(label.TraefikBackendMaxConnExtractorFunc, "client.ip"), - withSegmentLabel(label.TraefikPort, "80", "web"), - withSegmentLabel(label.TraefikPort, "81", "admin"), - withLabel("traefik..port", "82"), // This should be ignored, as it fails to match the segmentPropertiesRegexp regex. - withSegmentLabel(label.TraefikFrontendRule, "Host:web.app.mesos.localhost", "web"), - withSegmentLabel(label.TraefikFrontendRule, "Host:admin.app.mesos.localhost", "admin"), - ), - }, - expectedFrontends: map[string]*types.Frontend{ - "frontend-app-taskID-service-web": { - Backend: "backend-app-service-web", - Routes: map[string]types.Route{ - `route-host-app-taskID-service-web`: { - Rule: "Host:web.app.mesos.localhost", - }, - }, - PassHostHeader: true, - EntryPoints: []string{}, - }, - "frontend-app-taskID-service-admin": { - Backend: "backend-app-service-admin", - Routes: map[string]types.Route{ - `route-host-app-taskID-service-admin`: { - Rule: "Host:admin.app.mesos.localhost", - }, - }, - PassHostHeader: true, - EntryPoints: []string{}, - }, - }, - expectedBackends: map[string]*types.Backend{ - "backend-app-service-web": { - Servers: map[string]types.Server{ - "server-app-taskID-service-web": { - URL: "http://127.0.0.1:80", - Weight: label.DefaultWeight, - }, - }, - MaxConn: &types.MaxConn{ - Amount: 1000, - ExtractorFunc: "client.ip", - }, - }, - "backend-app-service-admin": { - Servers: map[string]types.Server{ - "server-app-taskID-service-admin": { - URL: "http://127.0.0.1:81", - Weight: label.DefaultWeight, - }, - }, - MaxConn: &types.MaxConn{ - Amount: 1000, - ExtractorFunc: "client.ip", - }, - }, - }, - }, - { - desc: "when all labels are set", - tasks: []state.Task{ - aTask("app-taskID", - withIP("127.0.0.1"), - withInfo("/app", - withPorts( - withPort("TCP", 80, "web"), - withPort("TCP", 81, "admin"), - ), - ), - withStatus(withHealthy(true), withState("TASK_RUNNING")), - - withLabel(label.TraefikBackendCircuitBreakerExpression, "NetworkErrorRatio() > 0.5"), - withLabel(label.TraefikBackendHealthCheckScheme, "http"), - withLabel(label.TraefikBackendHealthCheckPath, "/health"), - withLabel(label.TraefikBackendHealthCheckPort, "880"), - withLabel(label.TraefikBackendHealthCheckInterval, "6"), - withLabel(label.TraefikBackendHealthCheckHostname, "foo.com"), - withLabel(label.TraefikBackendHealthCheckHeaders, "Foo:bar || Bar:foo"), - withLabel(label.TraefikBackendLoadBalancerMethod, "drr"), - withLabel(label.TraefikBackendLoadBalancerSticky, "true"), - withLabel(label.TraefikBackendLoadBalancerStickiness, "true"), - withLabel(label.TraefikBackendLoadBalancerStickinessCookieName, "chocolate"), - withLabel(label.TraefikBackendMaxConnAmount, "666"), - withLabel(label.TraefikBackendMaxConnExtractorFunc, "client.ip"), - withLabel(label.TraefikBackendBufferingMaxResponseBodyBytes, "10485760"), - withLabel(label.TraefikBackendBufferingMemResponseBodyBytes, "2097152"), - withLabel(label.TraefikBackendBufferingMaxRequestBodyBytes, "10485760"), - withLabel(label.TraefikBackendBufferingMemRequestBodyBytes, "2097152"), - withLabel(label.TraefikBackendBufferingRetryExpression, "IsNetworkError() && Attempts() <= 2"), - - withSegmentLabel(label.TraefikPort, "80", "containous"), - withSegmentLabel(label.TraefikPortName, "web", "containous"), - withSegmentLabel(label.TraefikProtocol, "https", "containous"), - withSegmentLabel(label.TraefikWeight, "12", "containous"), - - withSegmentLabel(label.TraefikFrontendPassTLSClientCertPem, "true", "containous"), - withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosNotBefore, "true", "containous"), - withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosNotAfter, "true", "containous"), - withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSans, "true", "containous"), - withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectCommonName, "true", "containous"), - withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectCountry, "true", "containous"), - withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectLocality, "true", "containous"), - withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectOrganization, "true", "containous"), - withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectProvince, "true", "containous"), - withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber, "true", "containous"), - - withSegmentLabel(label.TraefikFrontendAuthBasic, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", "containous"), - withSegmentLabel(label.TraefikFrontendAuthBasicRemoveHeader, "true", "containous"), - withSegmentLabel(label.TraefikFrontendAuthBasicUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", "containous"), - withSegmentLabel(label.TraefikFrontendAuthBasicUsersFile, ".htpasswd", "containous"), - withSegmentLabel(label.TraefikFrontendAuthDigestRemoveHeader, "true", "containous"), - withSegmentLabel(label.TraefikFrontendAuthDigestUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", "containous"), - withSegmentLabel(label.TraefikFrontendAuthDigestUsersFile, ".htpasswd", "containous"), - withSegmentLabel(label.TraefikFrontendAuthForwardAddress, "auth.server", "containous"), - withSegmentLabel(label.TraefikFrontendAuthForwardTrustForwardHeader, "true", "containous"), - withSegmentLabel(label.TraefikFrontendAuthForwardTLSCa, "ca.crt", "containous"), - withSegmentLabel(label.TraefikFrontendAuthForwardTLSCaOptional, "true", "containous"), - withSegmentLabel(label.TraefikFrontendAuthForwardTLSCert, "server.crt", "containous"), - withSegmentLabel(label.TraefikFrontendAuthForwardTLSKey, "server.key", "containous"), - withSegmentLabel(label.TraefikFrontendAuthForwardTLSInsecureSkipVerify, "true", "containous"), - withSegmentLabel(label.TraefikFrontendAuthHeaderField, "X-WebAuth-User", "containous"), - - withSegmentLabel(label.TraefikFrontendEntryPoints, "http,https", "containous"), - withSegmentLabel(label.TraefikFrontendPassHostHeader, "true", "containous"), - withSegmentLabel(label.TraefikFrontendPassTLSCert, "true", "containous"), - withSegmentLabel(label.TraefikFrontendPriority, "666", "containous"), - withSegmentLabel(label.TraefikFrontendRedirectEntryPoint, "https", "containous"), - withSegmentLabel(label.TraefikFrontendRedirectRegex, "nope", "containous"), - withSegmentLabel(label.TraefikFrontendRedirectReplacement, "nope", "containous"), - withSegmentLabel(label.TraefikFrontendRedirectPermanent, "true", "containous"), - withSegmentLabel(label.TraefikFrontendRule, "Host:traefik.io", "containous"), - withSegmentLabel(label.TraefikFrontendWhiteListSourceRange, "10.10.10.10", "containous"), - withSegmentLabel(label.TraefikFrontendWhiteListUseXForwardedFor, "true", "containous"), - - withSegmentLabel(label.TraefikFrontendRequestHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", "containous"), - withSegmentLabel(label.TraefikFrontendResponseHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", "containous"), - withSegmentLabel(label.TraefikFrontendSSLProxyHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8", "containous"), - withSegmentLabel(label.TraefikFrontendAllowedHosts, "foo,bar,bor", "containous"), - withSegmentLabel(label.TraefikFrontendHostsProxyHeaders, "foo,bar,bor", "containous"), - withSegmentLabel(label.TraefikFrontendSSLForceHost, "true", "containous"), - withSegmentLabel(label.TraefikFrontendSSLHost, "foo", "containous"), - withSegmentLabel(label.TraefikFrontendCustomFrameOptionsValue, "foo", "containous"), - withSegmentLabel(label.TraefikFrontendContentSecurityPolicy, "foo", "containous"), - withSegmentLabel(label.TraefikFrontendPublicKey, "foo", "containous"), - withSegmentLabel(label.TraefikFrontendReferrerPolicy, "foo", "containous"), - withSegmentLabel(label.TraefikFrontendCustomBrowserXSSValue, "foo", "containous"), - withSegmentLabel(label.TraefikFrontendSTSSeconds, "666", "containous"), - withSegmentLabel(label.TraefikFrontendSSLRedirect, "true", "containous"), - withSegmentLabel(label.TraefikFrontendSSLTemporaryRedirect, "true", "containous"), - withSegmentLabel(label.TraefikFrontendSTSIncludeSubdomains, "true", "containous"), - withSegmentLabel(label.TraefikFrontendSTSPreload, "true", "containous"), - withSegmentLabel(label.TraefikFrontendForceSTSHeader, "true", "containous"), - withSegmentLabel(label.TraefikFrontendFrameDeny, "true", "containous"), - withSegmentLabel(label.TraefikFrontendContentTypeNosniff, "true", "containous"), - withSegmentLabel(label.TraefikFrontendBrowserXSSFilter, "true", "containous"), - withSegmentLabel(label.TraefikFrontendIsDevelopment, "true", "containous"), - - withLabel(label.Prefix+"containous."+label.BaseFrontendErrorPage+"foo."+label.SuffixErrorPageStatus, "404"), - withLabel(label.Prefix+"containous."+label.BaseFrontendErrorPage+"foo."+label.SuffixErrorPageBackend, "foobar"), - withLabel(label.Prefix+"containous."+label.BaseFrontendErrorPage+"foo."+label.SuffixErrorPageQuery, "foo_query"), - withLabel(label.Prefix+"containous."+label.BaseFrontendErrorPage+"bar."+label.SuffixErrorPageStatus, "500,600"), - withLabel(label.Prefix+"containous."+label.BaseFrontendErrorPage+"bar."+label.SuffixErrorPageBackend, "foobar"), - withLabel(label.Prefix+"containous."+label.BaseFrontendErrorPage+"bar."+label.SuffixErrorPageQuery, "bar_query"), - - withSegmentLabel(label.TraefikFrontendRateLimitExtractorFunc, "client.ip", "containous"), - withLabel(label.Prefix+"containous."+label.BaseFrontendRateLimit+"foo."+label.SuffixRateLimitPeriod, "6"), - withLabel(label.Prefix+"containous."+label.BaseFrontendRateLimit+"foo."+label.SuffixRateLimitAverage, "12"), - withLabel(label.Prefix+"containous."+label.BaseFrontendRateLimit+"foo."+label.SuffixRateLimitBurst, "18"), - withLabel(label.Prefix+"containous."+label.BaseFrontendRateLimit+"bar."+label.SuffixRateLimitPeriod, "3"), - withLabel(label.Prefix+"containous."+label.BaseFrontendRateLimit+"bar."+label.SuffixRateLimitAverage, "6"), - withLabel(label.Prefix+"containous."+label.BaseFrontendRateLimit+"bar."+label.SuffixRateLimitBurst, "9"), - ), - }, - expectedFrontends: map[string]*types.Frontend{ - "frontend-app-taskID-service-containous": { - EntryPoints: []string{ - "http", - "https", - }, - Backend: "backend-app-service-containous", - Routes: map[string]types.Route{ - "route-host-app-taskID-service-containous": { - Rule: "Host:traefik.io", - }, - }, - PassHostHeader: true, - PassTLSCert: true, - Priority: 666, - PassTLSClientCert: &types.TLSClientHeaders{ - PEM: true, - Infos: &types.TLSClientCertificateInfos{ - NotBefore: true, - Sans: true, - NotAfter: true, - Subject: &types.TLSCLientCertificateSubjectInfos{ - CommonName: true, - Country: true, - Locality: true, - Organization: true, - Province: true, - SerialNumber: true, - }, - }, - }, - Auth: &types.Auth{ - HeaderField: "X-WebAuth-User", - Basic: &types.Basic{ - RemoveHeader: true, - Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", - "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, - UsersFile: ".htpasswd", - }, - }, - - WhiteList: &types.WhiteList{ - SourceRange: []string{"10.10.10.10"}, - UseXForwardedFor: true, - }, - Headers: &types.Headers{ - CustomRequestHeaders: map[string]string{ - "Access-Control-Allow-Methods": "POST,GET,OPTIONS", - "Content-Type": "application/json; charset=utf-8", - }, - CustomResponseHeaders: map[string]string{ - "Access-Control-Allow-Methods": "POST,GET,OPTIONS", - "Content-Type": "application/json; charset=utf-8", - }, - AllowedHosts: []string{ - "foo", - "bar", - "bor", - }, - HostsProxyHeaders: []string{ - "foo", - "bar", - "bor", - }, - SSLRedirect: true, - SSLTemporaryRedirect: true, - SSLForceHost: true, - SSLHost: "foo", - SSLProxyHeaders: map[string]string{ - "Access-Control-Allow-Methods": "POST,GET,OPTIONS", - "Content-Type": "application/json; charset=utf-8", - }, - STSSeconds: 666, - STSIncludeSubdomains: true, - STSPreload: true, - ForceSTSHeader: true, - FrameDeny: true, - CustomFrameOptionsValue: "foo", - ContentTypeNosniff: true, - BrowserXSSFilter: true, - CustomBrowserXSSValue: "foo", - ContentSecurityPolicy: "foo", - PublicKey: "foo", - ReferrerPolicy: "foo", - IsDevelopment: true, - }, - Errors: map[string]*types.ErrorPage{ - "bar": { - Status: []string{ - "500", - "600", - }, - Backend: "backend-foobar", - Query: "bar_query", - }, - "foo": { - Status: []string{ - "404", - }, - Backend: "backend-foobar", - Query: "foo_query", - }, - }, - RateLimit: &types.RateLimit{ - RateSet: map[string]*types.Rate{ - "bar": { - Period: flaeg.Duration(3 * time.Second), - Average: 6, - Burst: 9, - }, - "foo": { - Period: flaeg.Duration(6 * time.Second), - Average: 12, - Burst: 18, - }, - }, - ExtractorFunc: "client.ip", - }, - Redirect: &types.Redirect{ - EntryPoint: "https", - Permanent: true, - }, - }, - }, - expectedBackends: map[string]*types.Backend{ - "backend-app-service-containous": { - Servers: map[string]types.Server{ - "server-app-taskID-service-containous": { - URL: "https://127.0.0.1:80", - Weight: 12, - }, - }, - CircuitBreaker: &types.CircuitBreaker{ - Expression: "NetworkErrorRatio() > 0.5", - }, - LoadBalancer: &types.LoadBalancer{ - Method: "drr", - Sticky: true, - Stickiness: &types.Stickiness{ - CookieName: "chocolate", - }, - }, - MaxConn: &types.MaxConn{ - Amount: 666, - ExtractorFunc: "client.ip", - }, - HealthCheck: &types.HealthCheck{ - Scheme: "http", - Path: "/health", - Port: 880, - Interval: "6", - Hostname: "foo.com", - Headers: map[string]string{ - "Bar": "foo", - "Foo": "bar", - }, - }, - Buffering: &types.Buffering{ - MaxResponseBodyBytes: 10485760, - MemResponseBodyBytes: 2097152, - MaxRequestBodyBytes: 10485760, - MemRequestBodyBytes: 2097152, - RetryExpression: "IsNetworkError() && Attempts() <= 2", - }, - }, - }, - }, - } - - for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - - actualConfig := p.buildConfigurationV2(test.tasks) - - require.NotNil(t, actualConfig) - assert.Equal(t, test.expectedBackends, actualConfig.Backends) - assert.Equal(t, test.expectedFrontends, actualConfig.Frontends) - }) - } -} - func TestTaskFilter(t *testing.T) { testCases := []struct { desc string diff --git a/provider/rancher/config_test.go b/provider/rancher/config_test.go index 1a5f529fd..1bcd2f2c6 100644 --- a/provider/rancher/config_test.go +++ b/provider/rancher/config_test.go @@ -681,6 +681,7 @@ func TestProviderBuildConfiguration(t *testing.T) { label.TraefikFrontendAuthForwardTLSKey: "server.key", label.TraefikFrontendAuthForwardTLSInsecureSkipVerify: "true", label.TraefikFrontendAuthHeaderField: "X-WebAuth-User", + label.TraefikFrontendAuthForwardAuthResponseHeaders: "X-Auth-User,X-Auth-Token", }, Health: "healthy", Containers: []string{"127.0.0.1"}, @@ -694,8 +695,7 @@ func TestProviderBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Forward: &types.Forward{ - Address: "auth.server", - TrustForwardHeader: true, + Address: "auth.server", TLS: &types.ClientTLS{ CA: "ca.crt", CAOptional: true, @@ -703,6 +703,8 @@ func TestProviderBuildConfiguration(t *testing.T) { Cert: "server.crt", Key: "server.key", }, + TrustForwardHeader: true, + AuthResponseHeaders: []string{"X-Auth-User", "X-Auth-Token"}, }, }, Priority: 0, diff --git a/templates/consul_catalog.tmpl b/templates/consul_catalog.tmpl index 12d762efe..2004bc6a0 100644 --- a/templates/consul_catalog.tmpl +++ b/templates/consul_catalog.tmpl @@ -106,6 +106,11 @@ [frontends."frontend-{{ $service.ServiceName }}".auth.forward] address = "{{ $auth.Forward.Address }}" trustForwardHeader = {{ $auth.Forward.TrustForwardHeader }} + {{if $auth.Forward.AuthResponseHeaders }} + authResponseHeaders = [{{range $auth.Forward.AuthResponseHeaders }} + "{{.}}", + {{end}}] + {{end}} {{if $auth.Forward.TLS }} [frontends."frontend-{{ $service.ServiceName }}".auth.forward.tls] diff --git a/templates/docker.tmpl b/templates/docker.tmpl index 83b379b9b..a26345745 100644 --- a/templates/docker.tmpl +++ b/templates/docker.tmpl @@ -107,6 +107,11 @@ [frontends."frontend-{{ $frontendName }}".auth.forward] address = "{{ $auth.Forward.Address }}" trustForwardHeader = {{ $auth.Forward.TrustForwardHeader }} + {{if $auth.Forward.AuthResponseHeaders }} + authResponseHeaders = [{{range $auth.Forward.AuthResponseHeaders }} + "{{.}}", + {{end}}] + {{end}} {{if $auth.Forward.TLS }} [frontends."frontend-{{ $frontendName }}".auth.forward.tls] diff --git a/templates/ecs.tmpl b/templates/ecs.tmpl index 59c040792..b80e3afef 100644 --- a/templates/ecs.tmpl +++ b/templates/ecs.tmpl @@ -108,6 +108,11 @@ [frontends."frontend-{{ $frontendName }}".auth.forward] address = "{{ $auth.Forward.Address }}" trustForwardHeader = {{ $auth.Forward.TrustForwardHeader }} + {{if $auth.Forward.AuthResponseHeaders }} + authResponseHeaders = [{{range $auth.Forward.AuthResponseHeaders }} + "{{.}}", + {{end}}] + {{end}} {{if $auth.Forward.TLS }} [frontends."frontend-{{ $frontendName }}".auth.forward.tls] diff --git a/templates/kv.tmpl b/templates/kv.tmpl index dacd6f358..73842e1d2 100644 --- a/templates/kv.tmpl +++ b/templates/kv.tmpl @@ -106,6 +106,11 @@ [frontends."{{ $frontendName }}".auth.forward] address = "{{ $auth.Forward.Address }}" trustForwardHeader = {{ $auth.Forward.TrustForwardHeader }} + {{if $auth.Forward.AuthResponseHeaders }} + authResponseHeaders = [{{range $auth.Forward.AuthResponseHeaders }} + "{{.}}", + {{end}}] + {{end}} {{if $auth.Forward.TLS }} [frontends."{{ $frontendName }}".auth.forward.tls] diff --git a/templates/marathon.tmpl b/templates/marathon.tmpl index 21ec75a9f..15c5ce285 100644 --- a/templates/marathon.tmpl +++ b/templates/marathon.tmpl @@ -109,6 +109,11 @@ [frontends."{{ $frontendName }}".auth.forward] address = "{{ $auth.Forward.Address }}" trustForwardHeader = {{ $auth.Forward.TrustForwardHeader }} + {{if $auth.Forward.AuthResponseHeaders }} + authResponseHeaders = [{{range $auth.Forward.AuthResponseHeaders }} + "{{.}}", + {{end}}] + {{end}} {{if $auth.Forward.TLS }} [frontends."{{ $frontendName }}".auth.forward.tls] diff --git a/templates/mesos.tmpl b/templates/mesos.tmpl index 8df2c5939..51f30308c 100644 --- a/templates/mesos.tmpl +++ b/templates/mesos.tmpl @@ -109,6 +109,11 @@ [frontends."frontend-{{ $frontendName }}".auth.forward] address = "{{ $auth.Forward.Address }}" trustForwardHeader = {{ $auth.Forward.TrustForwardHeader }} + {{if $auth.Forward.AuthResponseHeaders }} + authResponseHeaders = [{{range $auth.Forward.AuthResponseHeaders }} + "{{.}}", + {{end}}] + {{end}} {{if $auth.Forward.TLS }} [frontends."frontend-{{ $frontendName }}".auth.forward.tls] diff --git a/templates/rancher.tmpl b/templates/rancher.tmpl index ca594a11b..eb4976870 100644 --- a/templates/rancher.tmpl +++ b/templates/rancher.tmpl @@ -107,6 +107,11 @@ [frontends."frontend-{{ $frontendName }}".auth.forward] address = "{{ $auth.Forward.Address }}" trustForwardHeader = {{ $auth.Forward.TrustForwardHeader }} + {{if $auth.Forward.AuthResponseHeaders }} + authResponseHeaders = [{{range $auth.Forward.AuthResponseHeaders }} + "{{.}}", + {{end}}] + {{end}} {{if $auth.Forward.TLS }} [frontends."frontend-{{ $frontendName }}".auth.forward.tls]