From efc6560d8370a921ee0bc423668b63d2a984d608 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Doumenjou Date: Wed, 29 Aug 2018 11:36:03 +0200 Subject: [PATCH 1/9] Pass the TLS Cert infos in headers --- anonymize/anonymize_config_test.go | 6 +- autogen/gentemplates/gen.go | 164 +++- cmd/bug/bug_test.go | 2 +- cmd/traefik/traefik.go | 2 +- configuration/configuration.go | 2 +- configuration/entrypoints.go | 3 +- configuration/entrypoints_test.go | 4 +- docs/basics.md | 5 +- docs/configuration/backends/consulcatalog.md | 122 +-- docs/configuration/backends/docker.md | 222 ++--- docs/configuration/backends/ecs.md | 222 ++--- docs/configuration/backends/file.md | 18 +- docs/configuration/backends/marathon.md | 226 ++--- docs/configuration/backends/mesos.md | 228 ++--- docs/configuration/backends/rancher.md | 218 ++--- docs/user-guide/examples.md | 1 - .../fixtures/tlsclientheaders/root.pem | 21 + .../fixtures/tlsclientheaders/server.key | 27 + .../fixtures/tlsclientheaders/server.pem | 19 + .../fixtures/tlsclientheaders/simple.toml | 24 + integration/integration_test.go | 1 + .../resources/compose/tlsclientheaders.yml | 6 + integration/tls_client_headers_test.go | 71 ++ middlewares/tlsClientHeaders.go | 251 ++++++ middlewares/tlsClientHeaders_test.go | 799 ++++++++++++++++++ provider/consulcatalog/config.go | 1 + provider/consulcatalog/config_test.go | 103 +-- provider/docker/config.go | 27 +- .../docker/config_container_docker_test.go | 90 ++ .../docker/config_container_swarm_test.go | 66 ++ provider/docker/config_segment_test.go | 92 ++ provider/ecs/config.go | 29 +- provider/ecs/config_segment_test.go | 27 + provider/ecs/config_test.go | 27 + provider/kv/keynames.go | 29 +- provider/kv/kv_config.go | 60 +- provider/kv/kv_config_test.go | 28 + provider/label/names.go | 392 +++++---- provider/label/partial.go | 33 + provider/label/partial_test.go | 175 ++++ provider/marathon/config.go | 1 + provider/marathon/config_test.go | 55 +- provider/mesos/config.go | 1 + provider/mesos/config_test.go | 54 ++ provider/rancher/config.go | 27 +- provider/rancher/config_test.go | 54 ++ server/server.go | 3 +- server/server_loadbalancer.go | 5 +- server/server_middlewares.go | 9 + templates/consul_catalog.tmpl | 24 +- templates/docker.tmpl | 23 + templates/ecs.tmpl | 23 + templates/kv.tmpl | 23 + templates/marathon.tmpl | 25 +- templates/mesos.tmpl | 23 + templates/rancher.tmpl | 23 + tls/tls.go | 36 +- types/types.go | 27 +- 58 files changed, 3352 insertions(+), 927 deletions(-) create mode 100644 integration/fixtures/tlsclientheaders/root.pem create mode 100644 integration/fixtures/tlsclientheaders/server.key create mode 100644 integration/fixtures/tlsclientheaders/server.pem create mode 100644 integration/fixtures/tlsclientheaders/simple.toml create mode 100644 integration/resources/compose/tlsclientheaders.yml create mode 100644 integration/tls_client_headers_test.go create mode 100644 middlewares/tlsClientHeaders.go create mode 100644 middlewares/tlsClientHeaders_test.go diff --git a/anonymize/anonymize_config_test.go b/anonymize/anonymize_config_test.go index adba7cead..bb5975e4e 100644 --- a/anonymize/anonymize_config_test.go +++ b/anonymize/anonymize_config_test.go @@ -54,7 +54,7 @@ func TestDo_globalConfiguration(t *testing.T) { {CertFile: "CertFile 2", KeyFile: "KeyFile 2"}, }, ClientCA: traefiktls.ClientCA{ - Files: []string{"foo ClientCAFiles 1", "foo ClientCAFiles 2", "foo ClientCAFiles 3"}, + Files: traefiktls.FilesOrContents{"foo ClientCAFiles 1", "foo ClientCAFiles 2", "foo ClientCAFiles 3"}, Optional: false, }, }, @@ -99,7 +99,7 @@ func TestDo_globalConfiguration(t *testing.T) { {CertFile: "CertFile 2", KeyFile: "KeyFile 2"}, }, ClientCA: traefiktls.ClientCA{ - Files: []string{"fii ClientCAFiles 1", "fii ClientCAFiles 2", "fii ClientCAFiles 3"}, + Files: traefiktls.FilesOrContents{"fii ClientCAFiles 1", "fii ClientCAFiles 2", "fii ClientCAFiles 3"}, Optional: false, }, }, @@ -181,7 +181,7 @@ func TestDo_globalConfiguration(t *testing.T) { config.MaxIdleConnsPerHost = 666 config.IdleTimeout = flaeg.Duration(666 * time.Second) config.InsecureSkipVerify = true - config.RootCAs = traefiktls.RootCAs{"RootCAs 1", "RootCAs 2", "RootCAs 3"} + config.RootCAs = traefiktls.FilesOrContents{"RootCAs 1", "RootCAs 2", "RootCAs 3"} config.Retry = &configuration.Retry{ Attempts: 666, } diff --git a/autogen/gentemplates/gen.go b/autogen/gentemplates/gen.go index b6abce863..5fd14d24d 100644 --- a/autogen/gentemplates/gen.go +++ b/autogen/gentemplates/gen.go @@ -209,8 +209,30 @@ var _templatesConsul_catalogTmpl = []byte(`[backends] "{{.}}", {{end}}] - {{ $auth := getAuth $service.TraefikLabels }} + {{ $tlsClientCert := getPassTLSClientCert $service.TraefikLabels }} + {{if $tlsClientCert }} + [frontends."frontend-{{ $service.ServiceName }}".passTLSClientCert] + pem = {{ $tlsClientCert.PEM }} + {{ $infos := $tlsClientCert.Infos }} + {{if $infos }} + [frontends."frontend-{{ $service.ServiceName }}".passTLSClientCert.infos] + notAfter = {{ $infos.NotAfter }} + notBefore = {{ $infos.NotBefore }} + sans = {{ $infos.Sans }} + {{ $subject := $infos.Subject }} + {{if $subject }} + [frontends."frontend-{{ $service.ServiceName }}".passTLSClientCert.infos.subject] + country = {{ $subject.Country }} + province = {{ $subject.Province }} + locality = {{ $subject.Locality }} + organization = {{ $subject.Organization }} + commonName = {{ $subject.CommonName }} + serialNumber = {{ $subject.SerialNumber }} + {{end}} + {{end}} + {{end}} + {{ $auth := getAuth $service.TraefikLabels }} {{if $auth }} [frontends."frontend-{{ $service.ServiceName }}".auth] headerField = "{{ $auth.HeaderField }}" @@ -659,6 +681,29 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}} "{{.}}", {{end}}] + {{ $tlsClientCert := getPassTLSClientCert $container.SegmentLabels }} + {{if $tlsClientCert }} + [frontends."frontend-{{ $frontendName }}".passTLSClientCert] + pem = {{ $tlsClientCert.PEM }} + {{ $infos := $tlsClientCert.Infos }} + {{if $infos }} + [frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos] + notAfter = {{ $infos.NotAfter }} + notBefore = {{ $infos.NotBefore }} + sans = {{ $infos.Sans }} + {{ $subject := $infos.Subject }} + {{if $subject }} + [frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos.subject] + country = {{ $subject.Country }} + province = {{ $subject.Province }} + locality = {{ $subject.Locality }} + organization = {{ $subject.Organization }} + commonName = {{ $subject.CommonName }} + serialNumber = {{ $subject.SerialNumber }} + {{end}} + {{end}} + {{end}} + {{ $auth := getAuth $container.SegmentLabels }} {{if $auth }} [frontends."frontend-{{ $frontendName }}".auth] @@ -961,6 +1006,29 @@ var _templatesEcsTmpl = []byte(`[backends] "{{.}}", {{end}}] + {{ $tlsClientCert := getPassTLSClientCert $instance.SegmentLabels }} + {{if $tlsClientCert }} + [frontends."frontend-{{ $frontendName }}".passTLSClientCert] + pem = {{ $tlsClientCert.PEM }} + {{ $infos := $tlsClientCert.Infos }} + {{if $infos }} + [frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos] + notAfter = {{ $infos.NotAfter }} + notBefore = {{ $infos.NotBefore }} + sans = {{ $infos.Sans }} + {{ $subject := $infos.Subject }} + {{if $subject }} + [frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos.subject] + country = {{ $subject.Country }} + province = {{ $subject.Province }} + locality = {{ $subject.Locality }} + organization = {{ $subject.Organization }} + commonName = {{ $subject.CommonName }} + serialNumber = {{ $subject.SerialNumber }} + {{end}} + {{end}} + {{end}} + {{ $auth := getAuth $instance.SegmentLabels }} {{if $auth }} [frontends."frontend-{{ $frontendName }}".auth] @@ -1454,6 +1522,29 @@ var _templatesKvTmpl = []byte(`[backends] "{{.}}", {{end}}] + {{ $tlsClientCert := getPassTLSClientCert $frontend }} + {{if $tlsClientCert }} + [frontends."{{ $frontendName }}".passTLSClientCert] + pem = {{ $tlsClientCert.PEM }} + {{ $infos := $tlsClientCert.Infos }} + {{if $infos }} + [frontends."{{ $frontendName }}".passTLSClientCert.infos] + notAfter = {{ $infos.NotAfter }} + notBefore = {{ $infos.NotBefore }} + sans = {{ $infos.Sans }} + {{ $subject := $infos.Subject }} + {{if $subject }} + [frontends."{{ $frontendName }}".passTLSClientCert.infos.subject] + country = {{ $subject.Country }} + province = {{ $subject.Province }} + locality = {{ $subject.Locality }} + organization = {{ $subject.Organization }} + commonName = {{ $subject.CommonName }} + serialNumber = {{ $subject.SerialNumber }} + {{end}} + {{end}} + {{end}} + {{ $auth := getAuth $frontend }} {{if $auth }} [frontends."{{ $frontendName }}".auth] @@ -1796,7 +1887,30 @@ var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }} "{{.}}", {{end}}] - {{ $auth := getAuth $app.SegmentLabels }} + {{ $tlsClientCert := getPassTLSClientCert $app.SegmentLabels }} + {{if $tlsClientCert }} + [frontends."{{ $frontendName }}".passTLSClientCert] + pem = {{ $tlsClientCert.PEM }} + {{ $infos := $tlsClientCert.Infos }} + {{if $infos }} + [frontends."{{ $frontendName }}".passTLSClientCert.infos] + notAfter = {{ $infos.NotAfter }} + notBefore = {{ $infos.NotBefore }} + sans = {{ $infos.Sans }} + {{ $subject := $infos.Subject }} + {{if $subject }} + [frontends."{{ $frontendName }}".passTLSClientCert.infos.subject] + country = {{ $subject.Country }} + province = {{ $subject.Province }} + locality = {{ $subject.Locality }} + organization = {{ $subject.Organization }} + commonName = {{ $subject.CommonName }} + serialNumber = {{ $subject.SerialNumber }} + {{end}} + {{end}} + {{end}} + + {{ $auth := getAuth $app.SegmentLabels }} {{if $auth }} [frontends."{{ $frontendName }}".auth] headerField = "{{ $auth.HeaderField }}" @@ -2082,6 +2196,29 @@ var _templatesMesosTmpl = []byte(`[backends] "{{.}}", {{end}}] + {{ $tlsClientCert := getPassTLSClientCert $app.TraefikLabels }} + {{if $tlsClientCert }} + [frontends."frontend-{{ $frontendName }}".passTLSClientCert] + pem = {{ $tlsClientCert.PEM }} + {{ $infos := $tlsClientCert.Infos }} + {{if $infos }} + [frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos] + notAfter = {{ $infos.NotAfter }} + notBefore = {{ $infos.NotBefore }} + sans = {{ $infos.Sans }} + {{ $subject := $infos.Subject }} + {{if $subject }} + [frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos.subject] + country = {{ $subject.Country }} + province = {{ $subject.Province }} + locality = {{ $subject.Locality }} + organization = {{ $subject.Organization }} + commonName = {{ $subject.CommonName }} + serialNumber = {{ $subject.SerialNumber }} + {{end}} + {{end}} + {{end}} + {{ $auth := getAuth $app.TraefikLabels }} {{if $auth }} [frontends."frontend-{{ $frontendName }}".auth] @@ -2421,6 +2558,29 @@ var _templatesRancherTmpl = []byte(`{{ $backendServers := .Backends }} "{{.}}", {{end}}] + {{ $tlsClientCert := getPassTLSClientCert $service.SegmentLabels }} + {{if $tlsClientCert }} + [frontends."frontend-{{ $frontendName }}".passTLSClientCert] + pem = {{ $tlsClientCert.PEM }} + {{ $infos := $tlsClientCert.Infos }} + {{if $infos }} + [frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos] + notAfter = {{ $infos.NotAfter }} + notBefore = {{ $infos.NotBefore }} + sans = {{ $infos.Sans }} + {{ $subject := $infos.Subject }} + {{if $subject }} + [frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos.subject] + country = {{ $subject.Country }} + province = {{ $subject.Province }} + locality = {{ $subject.Locality }} + organization = {{ $subject.Organization }} + commonName = {{ $subject.CommonName }} + serialNumber = {{ $subject.SerialNumber }} + {{end}} + {{end}} + {{end}} + {{ $auth := getAuth $service.SegmentLabels }} {{if $auth }} [frontends."frontend-{{ $frontendName }}".auth] diff --git a/cmd/bug/bug_test.go b/cmd/bug/bug_test.go index 9c66ac110..2d78a93b6 100644 --- a/cmd/bug/bug_test.go +++ b/cmd/bug/bug_test.go @@ -34,7 +34,7 @@ func Test_createReport(t *testing.T) { File: &file.Provider{ Directory: "BAR", }, - RootCAs: tls.RootCAs{"fllf"}, + RootCAs: tls.FilesOrContents{"fllf"}, }, } diff --git a/cmd/traefik/traefik.go b/cmd/traefik/traefik.go index 2647523f0..8cf540dac 100644 --- a/cmd/traefik/traefik.go +++ b/cmd/traefik/traefik.go @@ -66,7 +66,7 @@ Complete documentation is available at https://traefik.io`, // add custom parsers f.AddParser(reflect.TypeOf(configuration.EntryPoints{}), &configuration.EntryPoints{}) f.AddParser(reflect.TypeOf(configuration.DefaultEntryPoints{}), &configuration.DefaultEntryPoints{}) - f.AddParser(reflect.TypeOf(traefiktls.RootCAs{}), &traefiktls.RootCAs{}) + f.AddParser(reflect.TypeOf(traefiktls.FilesOrContents{}), &traefiktls.FilesOrContents{}) f.AddParser(reflect.TypeOf(types.Constraints{}), &types.Constraints{}) f.AddParser(reflect.TypeOf(kubernetes.Namespaces{}), &kubernetes.Namespaces{}) f.AddParser(reflect.TypeOf(ecs.Clusters{}), &ecs.Clusters{}) diff --git a/configuration/configuration.go b/configuration/configuration.go index f21850c25..47899bd9e 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -79,7 +79,7 @@ type GlobalConfiguration struct { MaxIdleConnsPerHost int `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used" export:"true"` IdleTimeout flaeg.Duration `description:"(Deprecated) maximum amount of time an idle (keep-alive) connection will remain idle before closing itself." export:"true"` // Deprecated InsecureSkipVerify bool `description:"Disable SSL certificate verification" export:"true"` - RootCAs tls.RootCAs `description:"Add cert file for self-signed certificate"` + RootCAs tls.FilesOrContents `description:"Add cert file for self-signed certificate"` Retry *Retry `description:"Enable retry sending request if network error" export:"true"` HealthCheck *HealthCheckConfig `description:"Health check parameters" export:"true"` RespondingTimeouts *RespondingTimeouts `description:"Timeouts for incoming requests to the Traefik instance" export:"true"` diff --git a/configuration/entrypoints.go b/configuration/entrypoints.go index abb0eebc9..0d3e32580 100644 --- a/configuration/entrypoints.go +++ b/configuration/entrypoints.go @@ -234,7 +234,8 @@ func makeEntryPointTLS(result map[string]string) (*tls.TLS, error) { if configTLS != nil { if len(result["ca"]) > 0 { - files := strings.Split(result["ca"], ",") + files := tls.FilesOrContents{} + files.Set(result["ca"]) optional := toBool(result, "ca_optional") configTLS.ClientCA = tls.ClientCA{ Files: files, diff --git a/configuration/entrypoints_test.go b/configuration/entrypoints_test.go index 214ace662..ae4d88a6c 100644 --- a/configuration/entrypoints_test.go +++ b/configuration/entrypoints_test.go @@ -226,7 +226,7 @@ func TestEntryPoints_Set(t *testing.T) { }, }, ClientCA: tls.ClientCA{ - Files: []string{"car"}, + Files: tls.FilesOrContents{"car"}, Optional: true, }, }, @@ -338,7 +338,7 @@ func TestEntryPoints_Set(t *testing.T) { }, }, ClientCA: tls.ClientCA{ - Files: []string{"car"}, + Files: tls.FilesOrContents{"car"}, Optional: true, }, }, diff --git a/docs/basics.md b/docs/basics.md index 35d2bbe5d..754a3bf30 100644 --- a/docs/basics.md +++ b/docs/basics.md @@ -122,7 +122,7 @@ In order to use regular expressions with Host and Path matchers, you must declar The variable has no special meaning; however, it is required by the [gorilla/mux](https://github.com/gorilla/mux) dependency which embeds the regular expression and defines the syntax. You can optionally enable `passHostHeader` to forward client `Host` header to the backend. -You can also optionally enable `passTLSCert` to forward TLS Client certificates to the backend. +You can also optionally configure the `passTLSClientCert` option to pass the Client certificates to the backend in a specific header. ##### Path Matcher Usage Guidelines @@ -157,7 +157,8 @@ Here is an example of frontends definition: [frontends.frontend2] backend = "backend1" passHostHeader = true - passTLSCert = true + [frontends.frontend2.passTLSClientCert] + pem = true priority = 10 entrypoints = ["https"] # overrides defaultEntryPoints [frontends.frontend2.routes.test_1] diff --git a/docs/configuration/backends/consulcatalog.md b/docs/configuration/backends/consulcatalog.md index 4eb124a54..9a8da9e69 100644 --- a/docs/configuration/backends/consulcatalog.md +++ b/docs/configuration/backends/consulcatalog.md @@ -94,62 +94,72 @@ Additional settings can be defined using Consul Catalog tags. !!! note The default prefix is `traefik`. -| Label | Description | -|-------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `.enable=false` | Disables this container in Træfik. | -| `.protocol=https` | Overrides the default `http` protocol. | -| `.weight=10` | Assigns this weight to the container. | -| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. | -| `.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend. ex: `NetworkErrorRatio() > 0.` | -| `.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. | -| `.backend.healthcheck.interval=1s` | Defines the health check interval. | -| `.backend.healthcheck.port=8080` | Sets a different port for the health check. | -| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. | -| `.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. | -| `.backend.healthcheck.headers=EXPR` | Defines the health check request headers
Format: HEADER:value||HEADER2:value2 | -| `.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm. | -| `.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions. | -| `.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions. | -| `.backend.loadbalancer.sticky=true` | Enables backend sticky sessions. (DEPRECATED) | -| `.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | -| `.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | -| `.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). | -| `.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | -| `.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. | -| `.frontend.auth.basic.usersfile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | -| `.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | -| `.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | -| `.frontend.auth.digest.usersfile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | -| `.frontend.auth.forward.address=https://example.com`| Sets the URL of the authentication server. | -| `.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | -| `.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | -| `.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | -| `.frontend.auth.forward.tls.insecureSkipVerify=true`| If set to true invalid SSL certificates are accepted. | -| `.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | -| `.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | -| `.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. | -| `.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | -| `.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `.frontend.errors..status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. | -| `.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. | -| `.frontend.priority=10` | Overrides default frontend priority. | -| `.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `.frontend.rateLimit.rateSet..period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `.frontend.rateLimit.rateSet..average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `.frontend.rateLimit.rateSet..burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS). | -| `.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.replacement`. | -| `.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.regex`. | -| `.frontend.redirect.permanent=true` | Returns 301 instead of 302. | -| `.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{{.ServiceName}}.{{.Domain}}`. | -| `.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | -| `.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. | +| Label | Description | +|----------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `.enable=false` | Disables this container in Træfik. | +| `.protocol=https` | Overrides the default `http` protocol. | +| `.weight=10` | Assigns this weight to the container. | +| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. | +| `.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend. ex: `NetworkErrorRatio() > 0.` | +| `.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. | +| `.backend.healthcheck.interval=1s` | Defines the health check interval. | +| `.backend.healthcheck.port=8080` | Sets a different port for the health check. | +| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. | +| `.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. | +| `.backend.healthcheck.headers=EXPR` | Defines the health check request headers
Format: HEADER:value||HEADER2:value2 | +| `.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm. | +| `.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions. | +| `.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions. | +| `.backend.loadbalancer.sticky=true` | Enables backend sticky sessions. (DEPRECATED) | +| `.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | +| `.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | +| `.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). | +| `.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | +| `.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. | +| `.frontend.auth.basic.usersfile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | +| `.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | +| `.frontend.auth.digest.usersfile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. | +| `.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | +| `.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | +| `.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | +| `.frontend.auth.forward.tls.insecureSkipVerify=true` | If set to true invalid SSL certificates are accepted. | +| `.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | +| `.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | +| `.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. | +| `.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | +| `.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `.frontend.errors..status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. | +| `.frontend.passTLSClientCert.infos.notAfter=true` | Add the noAfter field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `.frontend.passTLSClientCert.infos.notBefore=true` | Add the noBefore field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `.frontend.passTLSClientCert.infos.sans=true` | Add the sans field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `.frontend.passTLSClientCert.infos.subject.commonName=true` | Add the subject.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `.frontend.passTLSClientCert.infos.subject.country=true` | Add the subject.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `.frontend.passTLSClientCert.infos.subject.locality=true` | Add the subject.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `.frontend.passTLSClientCert.infos.subject.organization=true`| Add the subject.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `.frontend.passTLSClientCert.infos.subject.province=true` | Add the subject.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Add the subject.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `.frontend.passTLSClientCert.pem=true` | Pass the escaped pem in the `X-Forwarded-Ssl-Client-Cert` header. | +| `.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. | +| `.frontend.priority=10` | Overrides default frontend priority. | +| `.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `.frontend.rateLimit.rateSet..period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `.frontend.rateLimit.rateSet..average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `.frontend.rateLimit.rateSet..burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS). | +| `.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.replacement`. | +| `.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.regex`. | +| `.frontend.redirect.permanent=true` | Returns 301 instead of 302. | +| `.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{{.ServiceName}}.{{.Domain}}`. | +| `.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | +| `.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. | ### Multiple frontends for a single service diff --git a/docs/configuration/backends/docker.md b/docs/configuration/backends/docker.md index 3f356729c..a9c342305 100644 --- a/docs/configuration/backends/docker.md +++ b/docs/configuration/backends/docker.md @@ -207,67 +207,77 @@ services: Labels can be used on containers to override default behavior. -| Label | Description | -|------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `traefik.docker.network` | Overrides the default docker network to use for connections to the container. [1] | -| `traefik.domain` | Sets the default base domain for the frontend rules. For more information, check the [Container Labels section's of the user guide "Let's Encrypt & Docker"](/user-guide/docker-and-lets-encrypt/#container-labels) | -| `traefik.enable=false` | Disables this container in Træfik. | -| `traefik.port=80` | Registers this port. Useful when the container exposes multiples ports. | -| `traefik.protocol=https` | Overrides the default `http` protocol | -| `traefik.weight=10` | Assigns this weight to the container | -| `traefik.backend=foo` | Gives the name `foo` to the generated backend for this container. | -| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend | -| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. | -| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. | -| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. | -| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. | -| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. | -| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers
Format: HEADER:value||HEADER2:value2 | -| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm | -| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions | -| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions | -| `traefik.backend.loadbalancer.sticky=true` | Enables backend sticky sessions (DEPRECATED) | -| `traefik.backend.loadbalancer.swarm=true` | Uses Swarm's inbuilt load balancer (only relevant under Swarm Mode). | -| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | -| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | -| `traefik.frontend.auth.basic=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` [2] (DEPRECATED). | -| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | -| `traefik.frontend.auth.basic.users=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` [2]. | -| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets the basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | -| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | -| `traefik.frontend.auth.digest.users=EXPR` | Sets the digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | -| `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets the digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | -| `traefik.frontend.auth.forward.address=https://example.com`| Sets the URL of the authentication server. | -| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | -| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true`| If set to true invalid SSL certificates are accepted. | -| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | -| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header user to pass the authenticated user to the application. | -| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | -| `traefik.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.errors..status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. | -| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. | -| `traefik.frontend.priority=10` | Overrides default frontend priority | -| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) | -| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.replacement`. | -| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.regex`. | -| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. | -| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{containerName}.{domain}` or `Host:{service}.{project_name}.{domain}` if you are using `docker-compose`. | -| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access.
If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | -| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. | +| Label | Description | +|---------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `traefik.docker.network` | Overrides the default docker network to use for connections to the container. [1] | +| `traefik.domain` | Sets the default base domain for the frontend rules. For more information, check the [Container Labels section's of the user guide "Let's Encrypt & Docker"](/user-guide/docker-and-lets-encrypt/#container-labels) | +| `traefik.enable=false` | Disables this container in Træfik. | +| `traefik.port=80` | Registers this port. Useful when the container exposes multiples ports. | +| `traefik.protocol=https` | Overrides the default `http` protocol | +| `traefik.weight=10` | Assigns this weight to the container | +| `traefik.backend=foo` | Gives the name `foo` to the generated backend for this container. | +| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend | +| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. | +| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. | +| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. | +| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. | +| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. | +| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers
Format: HEADER:value||HEADER2:value2 | +| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm | +| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions | +| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions | +| `traefik.backend.loadbalancer.sticky=true` | Enables backend sticky sessions (DEPRECATED) | +| `traefik.backend.loadbalancer.swarm=true` | Uses Swarm's inbuilt load balancer (only relevant under Swarm Mode). | +| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | +| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | +| `traefik.frontend.auth.basic=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` [2] (DEPRECATED). | +| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | +| `traefik.frontend.auth.basic.users=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` [2]. | +| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets the basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | +| `traefik.frontend.auth.digest.users=EXPR` | Sets the digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | +| `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets the digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. | +| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | +| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true` | If set to true invalid SSL certificates are accepted. | +| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | +| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header user to pass the authenticated user to the application. | +| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | +| `traefik.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.errors..status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. | +| `traefik.frontend.passTLSClientCert.infos.notAfter=true` | Add the noAfter field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.notBefore=true` | Add the noBefore field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.sans=true` | Add the sans field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.commonName=true` | Add the subject.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.country=true` | Add the subject.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.locality=true` | Add the subject.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.organization=true`| Add the subject.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.province=true` | Add the subject.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Add the subject.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.pem=true` | Pass the escaped pem in the `X-Forwarded-Ssl-Client-Cert` header. | +| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend (DEPRECATED). | +| `traefik.frontend.priority=10` | Overrides default frontend priority | +| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) | +| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.replacement`. | +| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.regex`. | +| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. | +| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{containerName}.{domain}` or `Host:{service}.{project_name}.{domain}` if you are using `docker-compose`. | +| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access.
If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | +| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. | [1] `traefik.docker.network`: If a container is linked to several networks, be sure to set the proper network name (you can check with `docker inspect `) otherwise it will randomly pick one (depending on how docker is returning them). @@ -320,46 +330,56 @@ You can define as many segments as ports exposed in a container. Segment labels override the default behavior. -| Label | Description | -|---------------------------------------------------------------------------|---------------------------------------------------------------| -| `traefik..backend=BACKEND` | Same as `traefik.backend` | -| `traefik..domain=DOMAIN` | Same as `traefik.domain` | -| `traefik..port=PORT` | Same as `traefik.port` | -| `traefik..protocol=http` | Same as `traefik.protocol` | -| `traefik..weight=10` | Same as `traefik.weight` | -| `traefik..frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` | -| `traefik..frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` | -| `traefik..frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` | -| `traefik..frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` | -| `traefik..frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` | -| `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | -| `traefik..frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` | -| `traefik..frontend.auth.forward.address=https://example.com`| Same as `traefik.frontend.auth.forward.address` | -| `traefik..frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` | -| `traefik..frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` | -| `traefik..frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` | -| `traefik..frontend.auth.forward.tls.insecureSkipVerify=true`| Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify`| -| `traefik..frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` | -| `traefik..frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` | -| `traefik..frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` | -| `traefik..frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` | -| `traefik..frontend.errors..backend=NAME` | Same as `traefik.frontend.errors..backend` | -| `traefik..frontend.errors..query=PATH` | Same as `traefik.frontend.errors..query` | -| `traefik..frontend.errors..status=RANGE` | Same as `traefik.frontend.errors..status` | -| `traefik..frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` | -| `traefik..frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` | -| `traefik..frontend.priority=10` | Same as `traefik.frontend.priority` | -| `traefik..frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` | -| `traefik..frontend.rateLimit.rateSet..period=6` | Same as `traefik.frontend.rateLimit.rateSet..period` | -| `traefik..frontend.rateLimit.rateSet..average=6` | Same as `traefik.frontend.rateLimit.rateSet..average` | -| `traefik..frontend.rateLimit.rateSet..burst=6` | Same as `traefik.frontend.rateLimit.rateSet..burst` | -| `traefik..frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` | -| `traefik..frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` | -| `traefik..frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` | -| `traefik..frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` | -| `traefik..frontend.rule=EXP` | Same as `traefik.frontend.rule` | -| `traefik..frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` | -| `traefik..frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` | +| Label | Description | +|------------------------------------------------------------------------------------|------------------------------------------------------------------------| +| `traefik..backend=BACKEND` | Same as `traefik.backend` | +| `traefik..domain=DOMAIN` | Same as `traefik.domain` | +| `traefik..port=PORT` | Same as `traefik.port` | +| `traefik..protocol=http` | Same as `traefik.protocol` | +| `traefik..weight=10` | Same as `traefik.weight` | +| `traefik..frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` | +| `traefik..frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` | +| `traefik..frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` | +| `traefik..frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` | +| `traefik..frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` | +| `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | +| `traefik..frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` | +| `traefik..frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` | +| `traefik..frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` | +| `traefik..frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` | +| `traefik..frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` | +| `traefik..frontend.auth.forward.tls.insecureSkipVerify=true` | Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` | +| `traefik..frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` | +| `traefik..frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` | +| `traefik..frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` | +| `traefik..frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` | +| `traefik..frontend.errors..backend=NAME` | Same as `traefik.frontend.errors..backend` | +| `traefik..frontend.errors..query=PATH` | Same as `traefik.frontend.errors..query` | +| `traefik..frontend.errors..status=RANGE` | Same as `traefik.frontend.errors..status` | +| `traefik..frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` | +| `traefik..frontend.passTLSClientCert.infos.notAfter=true` | Same as `traefik.frontend.passTLSClientCert.infos.notAfter` | +| `traefik..frontend.passTLSClientCert.infos.notBefore=true` | Same as `traefik.frontend.passTLSClientCert.infos.notBefore` | +| `traefik..frontend.passTLSClientCert.infos.sans=true` | Same as `traefik.frontend.passTLSClientCert.infos.sans` | +| `traefik..frontend.passTLSClientCert.infos.subject.commonName=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.commonName` | +| `traefik..frontend.passTLSClientCert.infos.subject.country=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.country` | +| `traefik..frontend.passTLSClientCert.infos.subject.locality=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.locality` | +| `traefik..frontend.passTLSClientCert.infos.subject.organization=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.organization`| +| `traefik..frontend.passTLSClientCert.infos.subject.province=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.province` | +| `traefik..frontend.passTLSClientCert.infos.subject.serialNumber=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.serialNumber`| +| `traefik..frontend.passTLSClientCert.pem=true` | Same as `traefik.frontend.passTLSClientCert.infos.pem` | +| `traefik..frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` | +| `traefik..frontend.priority=10` | Same as `traefik.frontend.priority` | +| `traefik..frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` | +| `traefik..frontend.rateLimit.rateSet..period=6` | Same as `traefik.frontend.rateLimit.rateSet..period` | +| `traefik..frontend.rateLimit.rateSet..average=6` | Same as `traefik.frontend.rateLimit.rateSet..average` | +| `traefik..frontend.rateLimit.rateSet..burst=6` | Same as `traefik.frontend.rateLimit.rateSet..burst` | +| `traefik..frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` | +| `traefik..frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` | +| `traefik..frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` | +| `traefik..frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` | +| `traefik..frontend.rule=EXP` | Same as `traefik.frontend.rule` | +| `traefik..frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` | +| `traefik..frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` | #### Custom Headers diff --git a/docs/configuration/backends/ecs.md b/docs/configuration/backends/ecs.md index b1dbbbe49..0089dee15 100644 --- a/docs/configuration/backends/ecs.md +++ b/docs/configuration/backends/ecs.md @@ -136,66 +136,76 @@ Træfik needs the following policy to read ECS information: Labels can be used on task containers to override default behaviour: -| Label | Description | -|------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `traefik.domain` | Sets the default domain for frontend rules. | -| `traefik.enable=false` | Disables this container in Træfik. | -| `traefik.port=80` | Overrides the default `port` value. Overrides `NetworkBindings` from Docker Container | -| `traefik.protocol=https` | Overrides the default `http` protocol | -| `traefik.weight=10` | Assigns this weight to the container | -| `traefik.backend=foo` | Gives the name `foo` to the generated backend for this container. | -| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend | -| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. | -| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. (Default: 30s) | -| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. | -| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. | -| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. | -| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers
Format: HEADER:value||HEADER2:value2 | -| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm | -| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions | -| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie manually name for sticky sessions | -| `traefik.backend.loadbalancer.sticky=true` | Enables backend sticky sessions (DEPRECATED) | -| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | -| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | -| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). | -| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | -| `traefik.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. | -| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | -| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | -| `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | -| `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | -| `traefik.frontend.auth.forward.address=https://example.com`| Sets the URL of the authentication server. | -| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | -| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true`| If set to true invalid SSL certificates are accepted. | -| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | -| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. | -| `traefik.frontend.auth.removeHeader=true` | If set to true, removes the Authorization header. | -| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | -| `traefik.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.errors..status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. | -| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. | -| `traefik.frontend.priority=10` | Overrides default frontend priority | -| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) | -| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.replacement`. | -| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.regex`. | -| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. | -| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{instance_name}.{domain}`. | -| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | -| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. | +| Label | Description | +|---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `traefik.domain` | Sets the default domain for frontend rules. | +| `traefik.enable=false` | Disables this container in Træfik. | +| `traefik.port=80` | Overrides the default `port` value. Overrides `NetworkBindings` from Docker Container | +| `traefik.protocol=https` | Overrides the default `http` protocol | +| `traefik.weight=10` | Assigns this weight to the container | +| `traefik.backend=foo` | Gives the name `foo` to the generated backend for this container. | +| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend | +| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. | +| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. (Default: 30s) | +| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. | +| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. | +| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. | +| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers
Format: HEADER:value||HEADER2:value2 | +| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm | +| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions | +| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie manually name for sticky sessions | +| `traefik.backend.loadbalancer.sticky=true` | Enables backend sticky sessions (DEPRECATED) | +| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | +| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | +| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). | +| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | +| `traefik.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. | +| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | +| `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | +| `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. | +| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | +| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true` | If set to true invalid SSL certificates are accepted. | +| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | +| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. | +| `traefik.frontend.auth.removeHeader=true` | If set to true, removes the Authorization header. | +| `traefik.frontend.passTLSClientCert.infos.notAfter=true` | Add the noAfter field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.notBefore=true` | Add the noBefore field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.sans=true` | Add the sans field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.commonName=true` | Add the subject.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.country=true` | Add the subject.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.locality=true` | Add the subject.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.organization=true`| Add the subject.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.province=true` | Add the subject.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Add the subject.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.pem=true` | Pass the escaped pem in the `X-Forwarded-Ssl-Client-Cert` header. | +| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | +| `traefik.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.errors..status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. | +| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. | +| `traefik.frontend.priority=10` | Overrides default frontend priority | +| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) | +| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.replacement`. | +| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.regex`. | +| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. | +| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{instance_name}.{domain}`. | +| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | +| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. | ### Custom Headers @@ -237,47 +247,57 @@ You can define as many segments as ports exposed in an application. Segment labels override the default behavior. -| Label | Description | -|---------------------------------------------------------------------------|----------------------------------------------------------------| -| `traefik..backend=BACKEND` | Same as `traefik.backend` | -| `traefik..domain=DOMAIN` | Same as `traefik.domain` | -| `traefik..port=PORT` | Same as `traefik.port` | -| `traefik..protocol=http` | Same as `traefik.protocol` | -| `traefik..weight=10` | Same as `traefik.weight` | -| `traefik..frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` | -| `traefik..frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` | -| `traefik..frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` | -| `traefik..frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` | -| `traefik..frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` | -| `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | -| `traefik..frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` | -| `traefik..frontend.auth.forward.address=https://example.com`| Same as `traefik.frontend.auth.forward.address` | -| `traefik..frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` | -| `traefik..frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` | -| `traefik..frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` | -| `traefik..frontend.auth.forward.tls.insecureSkipVerify=true`| Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` | -| `traefik..frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` | -| `traefik..frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` | -| `traefik..frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` | -| `traefik..frontend.auth.removeHeader=true` | Same as `traefik.frontend.auth.removeHeader` | -| `traefik..frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` | -| `traefik..frontend.errors..backend=NAME` | Same as `traefik.frontend.errors..backend` | -| `traefik..frontend.errors..query=PATH` | Same as `traefik.frontend.errors..query` | -| `traefik..frontend.errors..status=RANGE` | Same as `traefik.frontend.errors..status` | -| `traefik..frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` | -| `traefik..frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` | -| `traefik..frontend.priority=10` | Same as `traefik.frontend.priority` | -| `traefik..frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` | -| `traefik..frontend.rateLimit.rateSet..period=6` | Same as `traefik.frontend.rateLimit.rateSet..period` | -| `traefik..frontend.rateLimit.rateSet..average=6` | Same as `traefik.frontend.rateLimit.rateSet..average` | -| `traefik..frontend.rateLimit.rateSet..burst=6` | Same as `traefik.frontend.rateLimit.rateSet..burst` | -| `traefik..frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` | -| `traefik..frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` | -| `traefik..frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` | -| `traefik..frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` | -| `traefik..frontend.rule=EXP` | Same as `traefik.frontend.rule` | -| `traefik..frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` | -| `traefik..frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` | +| Label | Description | +|-------------------------------------------------------------------------------------|-------------------------------------------------------------------------| +| `traefik..backend=BACKEND` | Same as `traefik.backend` | +| `traefik..domain=DOMAIN` | Same as `traefik.domain` | +| `traefik..port=PORT` | Same as `traefik.port` | +| `traefik..protocol=http` | Same as `traefik.protocol` | +| `traefik..weight=10` | Same as `traefik.weight` | +| `traefik..frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` | +| `traefik..frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` | +| `traefik..frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` | +| `traefik..frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` | +| `traefik..frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` | +| `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | +| `traefik..frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` | +| `traefik..frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` | +| `traefik..frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` | +| `traefik..frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` | +| `traefik..frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` | +| `traefik..frontend.auth.forward.tls.insecureSkipVerify=true` | Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` | +| `traefik..frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` | +| `traefik..frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` | +| `traefik..frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` | +| `traefik..frontend.auth.removeHeader=true` | Same as `traefik.frontend.auth.removeHeader` | +| `traefik..frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` | +| `traefik..frontend.errors..backend=NAME` | Same as `traefik.frontend.errors..backend` | +| `traefik..frontend.errors..query=PATH` | Same as `traefik.frontend.errors..query` | +| `traefik..frontend.errors..status=RANGE` | Same as `traefik.frontend.errors..status` | +| `traefik..frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` | +| `traefik..frontend.passTLSClientCert.infos.notAfter=true` | Same as `traefik.frontend.passTLSClientCert.infos.notAfter` | +| `traefik..frontend.passTLSClientCert.infos.notBefore=true` | Same as `traefik.frontend.passTLSClientCert.infos.notBefore` | +| `traefik..frontend.passTLSClientCert.infos.sans=true` | Same as `traefik.frontend.passTLSClientCert.infos.sans` | +| `traefik..frontend.passTLSClientCert.infos.subject.commonName=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.commonName` | +| `traefik..frontend.passTLSClientCert.infos.subject.country=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.country` | +| `traefik..frontend.passTLSClientCert.infos.subject.locality=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.locality` | +| `traefik..frontend.passTLSClientCert.infos.subject.organization=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.organization` | +| `traefik..frontend.passTLSClientCert.infos.subject.province=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.province` | +| `traefik..frontend.passTLSClientCert.infos.subject.serialNumber=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.serialNumber` | +| `traefik..frontend.passTLSClientCert.pem=true` | Same as `traefik.frontend.passTLSClientCert.infos.pem` | +| `traefik..frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` | +| `traefik..frontend.priority=10` | Same as `traefik.frontend.priority` | +| `traefik..frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` | +| `traefik..frontend.rateLimit.rateSet..period=6` | Same as `traefik.frontend.rateLimit.rateSet..period` | +| `traefik..frontend.rateLimit.rateSet..average=6` | Same as `traefik.frontend.rateLimit.rateSet..average` | +| `traefik..frontend.rateLimit.rateSet..burst=6` | Same as `traefik.frontend.rateLimit.rateSet..burst` | +| `traefik..frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` | +| `traefik..frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` | +| `traefik..frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` | +| `traefik..frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` | +| `traefik..frontend.rule=EXP` | Same as `traefik.frontend.rule` | +| `traefik..frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` | +| `traefik..frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` | #### Custom Headers diff --git a/docs/configuration/backends/file.md b/docs/configuration/backends/file.md index beb4e0b5e..f77aafb2d 100644 --- a/docs/configuration/backends/file.md +++ b/docs/configuration/backends/file.md @@ -53,7 +53,6 @@ Træfik can be configured with a file. entryPoints = ["http", "https"] backend = "backend1" passHostHeader = true - passTLSCert = true priority = 42 # Use frontends.frontend1.auth.basic below instead @@ -61,7 +60,22 @@ Træfik can be configured with a file. "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", ] - + [frontends.frontend1.passTLSClientCert] + # Pass the escaped pem in a `X-Forwarded-Ssl-Client-Cert` header + pem = true + # Pass the escaped client cert infos selected below in a `X-Forwarded-Ssl-Client-Cert-Infos` header + # The unescaped header is like `Subject="C=%s,ST=%s,L=%s,O=%s,CN=%s",NB=%d,NA=%d,SAN=%s` + # It there is more than one certificates, their are separated by a `;` + [frontends.frontend-server.passTLSClientCert.infos] + notBefore = true + notAfter = true + [frontends.frontend-server.passTLSClientCert.infos.subject] + country = true + province = true + locality = true + organization = true + commonName = true + serialNumber = true [frontends.frontend1.auth] headerField = "X-WebAuth-User" [frontends.frontend1.auth.basic] diff --git a/docs/configuration/backends/marathon.md b/docs/configuration/backends/marathon.md index e6ad58d19..baf65a943 100644 --- a/docs/configuration/backends/marathon.md +++ b/docs/configuration/backends/marathon.md @@ -193,67 +193,77 @@ They may be specified on one of two levels: Application or service. The following labels can be defined on Marathon applications. They adjust the behavior for the entire application. -| Label | Description | -|------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `traefik.domain` | Sets the default domain used for the frontend rules. | -| `traefik.enable=false` | Disables this container in Træfik. | -| `traefik.port=80` | Registers this port. Useful when the container exposes multiples ports. | -| `traefik.portIndex=1` | Registers port by index in the application's ports array. Useful when the application exposes multiple ports. | -| `traefik.protocol=https` | Overrides the default `http` protocol. | -| `traefik.weight=10` | Assigns this weight to the container. | -| `traefik.backend=foo` | Gives the name `foo` to the generated backend for this container. | -| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend | -| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. | -| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. (Default: 30s) | -| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. | -| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. | -| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. | -| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers
Format: HEADER:value||HEADER2:value2 | -| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm | -| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions | -| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions | -| `traefik.backend.loadbalancer.sticky=true` | Enables backend sticky sessions (DEPRECATED) | -| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | -| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | -| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). | -| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | -| `traefik.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. | -| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | -| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | -| `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | -| `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | -| `traefik.frontend.auth.forward.address=https://example.com`| Sets the URL of the authentication server. | -| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | -| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true`| If set to true invalid SSL certificates are accepted. | -| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | -| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. | -| `traefik.frontend.auth.removeHeader=true` | If set to true, removes the Authorization header. | -| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | -| `traefik.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.errors..status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. | -| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. | -| `traefik.frontend.priority=10` | Overrides default frontend priority | -| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) | -| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.replacement`. | -| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.regex`. | -| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. | -| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{sub_domain}.{domain}`. | -| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | -| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. | +| Label | Description | +|---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `traefik.domain` | Sets the default domain used for the frontend rules. | +| `traefik.enable=false` | Disables this container in Træfik. | +| `traefik.port=80` | Registers this port. Useful when the container exposes multiples ports. | +| `traefik.portIndex=1` | Registers port by index in the application's ports array. Useful when the application exposes multiple ports. | +| `traefik.protocol=https` | Overrides the default `http` protocol. | +| `traefik.weight=10` | Assigns this weight to the container. | +| `traefik.backend=foo` | Gives the name `foo` to the generated backend for this container. | +| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend | +| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. | +| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. (Default: 30s) | +| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. | +| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. | +| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. | +| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers
Format: HEADER:value||HEADER2:value2 | +| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm | +| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions | +| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions | +| `traefik.backend.loadbalancer.sticky=true` | Enables backend sticky sessions (DEPRECATED) | +| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | +| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | +| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). | +| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | +| `traefik.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. | +| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | +| `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | +| `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. | +| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | +| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true` | If set to true invalid SSL certificates are accepted. | +| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | +| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. | +| `traefik.frontend.auth.removeHeader=true` | If set to true, removes the Authorization header. | +| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | +| `traefik.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.errors..status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. | +| `traefik.frontend.passTLSClientCert.infos.notAfter=true` | Add the noAfter field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.notBefore=true` | Add the noBefore field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.sans=true` | Add the sans field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.commonName=true` | Add the subject.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.country=true` | Add the subject.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.locality=true` | Add the subject.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.organization=true`| Add the subject.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.province=true` | Add the subject.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Add the subject.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.pem=true` | Pass the escaped pem in the `X-Forwarded-Ssl-Client-Cert` header. | +| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. | +| `traefik.frontend.priority=10` | Overrides default frontend priority | +| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) | +| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.replacement`. | +| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.regex`. | +| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. | +| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{sub_domain}.{domain}`. | +| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | +| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. | #### Custom Headers @@ -296,48 +306,58 @@ You can define as many segments as ports exposed in an application. Segment labels override the default behavior. -| Label | Description | -|---------------------------------------------------------------------------|----------------------------------------------------------------| -| `traefik..backend=BACKEND` | Same as `traefik.backend` | -| `traefik..domain=DOMAIN` | Same as `traefik.domain` | -| `traefik..portIndex=1` | Same as `traefik.portIndex` | -| `traefik..port=PORT` | Same as `traefik.port` | -| `traefik..protocol=http` | Same as `traefik.protocol` | -| `traefik..weight=10` | Same as `traefik.weight` | -| `traefik..frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` | -| `traefik..frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` | -| `traefik..frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` | -| `traefik..frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` | -| `traefik..frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` | -| `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | -| `traefik..frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` | -| `traefik..frontend.auth.forward.address=https://example.com`| Same as `traefik.frontend.auth.forward.address` | -| `traefik..frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` | -| `traefik..frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` | -| `traefik..frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` | -| `traefik..frontend.auth.forward.tls.insecureSkipVerify=true`| Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` | -| `traefik..frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` | -| `traefik..frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` | -| `traefik..frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` | -| `traefik..frontend.auth.removeHeader=true` | Same as `traefik.frontend.auth.removeHeader` | -| `traefik..frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` | -| `traefik..frontend.errors..backend=NAME` | Same as `traefik.frontend.errors..backend` | -| `traefik..frontend.errors..query=PATH` | Same as `traefik.frontend.errors..query` | -| `traefik..frontend.errors..status=RANGE` | Same as `traefik.frontend.errors..status` | -| `traefik..frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` | -| `traefik..frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` | -| `traefik..frontend.priority=10` | Same as `traefik.frontend.priority` | -| `traefik..frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` | -| `traefik..frontend.rateLimit.rateSet..period=6` | Same as `traefik.frontend.rateLimit.rateSet..period` | -| `traefik..frontend.rateLimit.rateSet..average=6` | Same as `traefik.frontend.rateLimit.rateSet..average` | -| `traefik..frontend.rateLimit.rateSet..burst=6` | Same as `traefik.frontend.rateLimit.rateSet..burst` | -| `traefik..frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` | -| `traefik..frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` | -| `traefik..frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` | -| `traefik..frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` | -| `traefik..frontend.rule=EXP` | Same as `traefik.frontend.rule` | -| `traefik..frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` | -| `traefik..frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` | +| Label | Description | +|------------------------------------------------------------------------------------|------------------------------------------------------------------------| +| `traefik..backend=BACKEND` | Same as `traefik.backend` | +| `traefik..domain=DOMAIN` | Same as `traefik.domain` | +| `traefik..portIndex=1` | Same as `traefik.portIndex` | +| `traefik..port=PORT` | Same as `traefik.port` | +| `traefik..protocol=http` | Same as `traefik.protocol` | +| `traefik..weight=10` | Same as `traefik.weight` | +| `traefik..frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` | +| `traefik..frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` | +| `traefik..frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` | +| `traefik..frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` | +| `traefik..frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` | +| `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | +| `traefik..frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` | +| `traefik..frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` | +| `traefik..frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` | +| `traefik..frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` | +| `traefik..frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` | +| `traefik..frontend.auth.forward.tls.insecureSkipVerify=true` | Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` | +| `traefik..frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` | +| `traefik..frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` | +| `traefik..frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` | +| `traefik..frontend.auth.removeHeader=true` | Same as `traefik.frontend.auth.removeHeader` | +| `traefik..frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` | +| `traefik..frontend.errors..backend=NAME` | Same as `traefik.frontend.errors..backend` | +| `traefik..frontend.errors..query=PATH` | Same as `traefik.frontend.errors..query` | +| `traefik..frontend.errors..status=RANGE` | Same as `traefik.frontend.errors..status` | +| `traefik..frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` | +| `traefik..frontend.passTLSClientCert.infos.notAfter=true` | Same as `traefik.frontend.passTLSClientCert.infos.notAfter` | +| `traefik..frontend.passTLSClientCert.infos.notBefore=true` | Same as `traefik.frontend.passTLSClientCert.infos.notBefore` | +| `traefik..frontend.passTLSClientCert.infos.sans=true` | Same as `traefik.frontend.passTLSClientCert.infos.sans` | +| `traefik..frontend.passTLSClientCert.infos.subject.commonName=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.commonName` | +| `traefik..frontend.passTLSClientCert.infos.subject.country=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.country` | +| `traefik..frontend.passTLSClientCert.infos.subject.locality=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.locality` | +| `traefik..frontend.passTLSClientCert.infos.subject.organization=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.organization`| +| `traefik..frontend.passTLSClientCert.infos.subject.province=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.province` | +| `traefik..frontend.passTLSClientCert.infos.subject.serialNumber=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.serialNumber`| +| `traefik..frontend.passTLSClientCert.pem=true` | Same as `traefik.frontend.passTLSClientCert.infos.pem` | +| `traefik..frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` | +| `traefik..frontend.priority=10` | Same as `traefik.frontend.priority` | +| `traefik..frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` | +| `traefik..frontend.rateLimit.rateSet..period=6` | Same as `traefik.frontend.rateLimit.rateSet..period` | +| `traefik..frontend.rateLimit.rateSet..average=6` | Same as `traefik.frontend.rateLimit.rateSet..average` | +| `traefik..frontend.rateLimit.rateSet..burst=6` | Same as `traefik.frontend.rateLimit.rateSet..burst` | +| `traefik..frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` | +| `traefik..frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` | +| `traefik..frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` | +| `traefik..frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` | +| `traefik..frontend.rule=EXP` | Same as `traefik.frontend.rule` | +| `traefik..frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` | +| `traefik..frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` | #### Custom Headers diff --git a/docs/configuration/backends/mesos.md b/docs/configuration/backends/mesos.md index e1b83e85a..60b31e7c1 100644 --- a/docs/configuration/backends/mesos.md +++ b/docs/configuration/backends/mesos.md @@ -106,67 +106,77 @@ domain = "mesos.localhost" The following labels can be defined on Mesos tasks. They adjust the behavior for the entire application. -| Label | Description | -|------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `traefik.domain` | Sets the default domain for the frontend rules. | -| `traefik.enable=false` | Disables this container in Træfik. | -| `traefik.port=80` | Registers this port. Useful when the application exposes multiple ports. | -| `traefik.portName=web` | Registers port by name in the application's ports array. Useful when the application exposes multiple ports. | -| `traefik.portIndex=1` | Registers port by index in the application's ports array. Useful when the application exposes multiple ports. | -| `traefik.protocol=https` | Overrides the default `http` protocol | -| `traefik.weight=10` | Assigns this weight to the container | -| `traefik.backend=foo` | Gives the name `foo` to the generated backend for this container. | -| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend | -| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. | -| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. (Default: 30s) | -| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. | -| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. | -| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. | -| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers
Format: HEADER:value||HEADER2:value2 | -| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm | -| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions | -| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie manually name for sticky sessions | -| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | -| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | -| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). | -| `traefik.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. | -| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | -| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | -| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | -| `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | -| `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | -| `traefik.frontend.auth.forward.address=https://example.com`| Sets the URL of the authentication server. | -| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | -| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true`| If set to true invalid SSL certificates are accepted. | -| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | -| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. | -| `traefik.frontend.auth.removeHeader=true` | If set to true, removes the Authorization header. | -| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | -| `traefik.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.errors..status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. | -| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. | -| `traefik.frontend.priority=10` | Overrides default frontend priority | -| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) | -| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.replacement`. | -| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.regex`. | -| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. | -| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{discovery_name}.{domain}`. | -| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | -| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. | +| Label | Description | +|---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `traefik.domain` | Sets the default domain for the frontend rules. | +| `traefik.enable=false` | Disables this container in Træfik. | +| `traefik.port=80` | Registers this port. Useful when the application exposes multiple ports. | +| `traefik.portName=web` | Registers port by name in the application's ports array. Useful when the application exposes multiple ports. | +| `traefik.portIndex=1` | Registers port by index in the application's ports array. Useful when the application exposes multiple ports. | +| `traefik.protocol=https` | Overrides the default `http` protocol | +| `traefik.weight=10` | Assigns this weight to the container | +| `traefik.backend=foo` | Gives the name `foo` to the generated backend for this container. | +| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend | +| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. | +| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. (Default: 30s) | +| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. | +| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. | +| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. | +| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers
Format: HEADER:value||HEADER2:value2 | +| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm | +| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions | +| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie manually name for sticky sessions | +| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | +| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | +| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). | +| `traefik.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. | +| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | +| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | +| `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | +| `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. | +| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | +| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true` | If set to true invalid SSL certificates are accepted. | +| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | +| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. | +| `traefik.frontend.auth.removeHeader=true` | If set to true, removes the Authorization header. | +| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | +| `traefik.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.errors..status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. | +| `traefik.frontend.passTLSClientCert.infos.notAfter=true` | Add the noAfter field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.notBefore=true` | Add the noBefore field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.sans=true` | Add the sans field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.commonName=true` | Add the subject.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.country=true` | Add the subject.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.locality=true` | Add the subject.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.organization=true`| Add the subject.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.province=true` | Add the subject.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Add the subject.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.pem=true` | Pass the escaped pem in the `X-Forwarded-Ssl-Client-Cert` header. | +| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. | +| `traefik.frontend.priority=10` | Overrides default frontend priority | +| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) | +| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.replacement`. | +| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.regex`. | +| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. | +| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{discovery_name}.{domain}`. | +| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | +| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. | ### Custom Headers @@ -210,49 +220,59 @@ Additionally, if a segment name matches a named port, that port will be used unl Segment labels override the default behavior. -| Label | Description | -|---------------------------------------------------------------------------|----------------------------------------------------------------| -| `traefik..backend=BACKEND` | Same as `traefik.backend` | -| `traefik..domain=DOMAIN` | Same as `traefik.domain` | -| `traefik..portIndex=1` | Same as `traefik.portIndex` | -| `traefik..portName=web` | Same as `traefik.portName` | -| `traefik..port=PORT` | Same as `traefik.port` | -| `traefik..protocol=http` | Same as `traefik.protocol` | -| `traefik..weight=10` | Same as `traefik.weight` | -| `traefik..frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` | -| `traefik..frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` | -| `traefik..frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` | -| `traefik..frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` | -| `traefik..frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` | -| `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | -| `traefik..frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` | -| `traefik..frontend.auth.forward.address=https://example.com`| Same as `traefik.frontend.auth.forward.address` | -| `traefik..frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` | -| `traefik..frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` | -| `traefik..frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` | -| `traefik..frontend.auth.forward.tls.insecureSkipVerify=true`| Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` | -| `traefik..frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` | -| `traefik..frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` | -| `traefik..frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` | -| `traefik..frontend.auth.removeHeader=true` | Same as `traefik.frontend.auth.removeHeader` | -| `traefik..frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` | -| `traefik..frontend.errors..backend=NAME` | Same as `traefik.frontend.errors..backend` | -| `traefik..frontend.errors..query=PATH` | Same as `traefik.frontend.errors..query` | -| `traefik..frontend.errors..status=RANGE` | Same as `traefik.frontend.errors..status` | -| `traefik..frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` | -| `traefik..frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` | -| `traefik..frontend.priority=10` | Same as `traefik.frontend.priority` | -| `traefik..frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` | -| `traefik..frontend.rateLimit.rateSet..period=6` | Same as `traefik.frontend.rateLimit.rateSet..period` | -| `traefik..frontend.rateLimit.rateSet..average=6` | Same as `traefik.frontend.rateLimit.rateSet..average` | -| `traefik..frontend.rateLimit.rateSet..burst=6` | Same as `traefik.frontend.rateLimit.rateSet..burst` | -| `traefik..frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` | -| `traefik..frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` | -| `traefik..frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` | -| `traefik..frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` | -| `traefik..frontend.rule=EXP` | Same as `traefik.frontend.rule` | -| `traefik..frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` | -| `traefik..frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` | +| Label | Description | +|------------------------------------------------------------------------------------|------------------------------------------------------------------------| +| `traefik..backend=BACKEND` | Same as `traefik.backend` | +| `traefik..domain=DOMAIN` | Same as `traefik.domain` | +| `traefik..portIndex=1` | Same as `traefik.portIndex` | +| `traefik..portName=web` | Same as `traefik.portName` | +| `traefik..port=PORT` | Same as `traefik.port` | +| `traefik..protocol=http` | Same as `traefik.protocol` | +| `traefik..weight=10` | Same as `traefik.weight` | +| `traefik..frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` | +| `traefik..frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` | +| `traefik..frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` | +| `traefik..frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` | +| `traefik..frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` | +| `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | +| `traefik..frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` | +| `traefik..frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` | +| `traefik..frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` | +| `traefik..frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` | +| `traefik..frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` | +| `traefik..frontend.auth.forward.tls.insecureSkipVerify=true` | Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` | +| `traefik..frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` | +| `traefik..frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` | +| `traefik..frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` | +| `traefik..frontend.auth.removeHeader=true` | Same as `traefik.frontend.auth.removeHeader` | +| `traefik..frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` | +| `traefik..frontend.errors..backend=NAME` | Same as `traefik.frontend.errors..backend` | +| `traefik..frontend.errors..query=PATH` | Same as `traefik.frontend.errors..query` | +| `traefik..frontend.errors..status=RANGE` | Same as `traefik.frontend.errors..status` | +| `traefik..frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` | +| `traefik..frontend.passTLSClientCert.infos.notAfter=true` | Same as `traefik.frontend.passTLSClientCert.infos.notAfter` | +| `traefik..frontend.passTLSClientCert.infos.notBefore=true` | Same as `traefik.frontend.passTLSClientCert.infos.notBefore` | +| `traefik..frontend.passTLSClientCert.infos.sans=true` | Same as `traefik.frontend.passTLSClientCert.infos.sans` | +| `traefik..frontend.passTLSClientCert.infos.subject.commonName=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.commonName` | +| `traefik..frontend.passTLSClientCert.infos.subject.country=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.country` | +| `traefik..frontend.passTLSClientCert.infos.subject.locality=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.locality` | +| `traefik..frontend.passTLSClientCert.infos.subject.organization=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.organization`| +| `traefik..frontend.passTLSClientCert.infos.subject.province=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.province` | +| `traefik..frontend.passTLSClientCert.infos.subject.serialNumber=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.serialNumber`| +| `traefik..frontend.passTLSClientCert.pem=true` | Same as `traefik.frontend.passTLSClientCert.infos.pem` | +| `traefik..frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` | +| `traefik..frontend.priority=10` | Same as `traefik.frontend.priority` | +| `traefik..frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` | +| `traefik..frontend.rateLimit.rateSet..period=6` | Same as `traefik.frontend.rateLimit.rateSet..period` | +| `traefik..frontend.rateLimit.rateSet..average=6` | Same as `traefik.frontend.rateLimit.rateSet..average` | +| `traefik..frontend.rateLimit.rateSet..burst=6` | Same as `traefik.frontend.rateLimit.rateSet..burst` | +| `traefik..frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` | +| `traefik..frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` | +| `traefik..frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` | +| `traefik..frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` | +| `traefik..frontend.rule=EXP` | Same as `traefik.frontend.rule` | +| `traefik..frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` | +| `traefik..frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` | #### Custom Headers diff --git a/docs/configuration/backends/rancher.md b/docs/configuration/backends/rancher.md index f34272bf4..8bbcebaef 100644 --- a/docs/configuration/backends/rancher.md +++ b/docs/configuration/backends/rancher.md @@ -138,65 +138,75 @@ secretKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" Labels can be used on task containers to override default behavior: -| Label | Description | -|------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `traefik.domain` | Sets the default domain for the frontend rules. | -| `traefik.enable=false` | Disables this container in Træfik. | -| `traefik.port=80` | Registers this port. Useful when the container exposes multiple ports. | -| `traefik.protocol=https` | Overrides the default `http` protocol. | -| `traefik.weight=10` | Assigns this weight to the container. | -| `traefik.backend=foo` | Gives the name `foo` to the generated backend for this container. | -| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. | -| `traefik.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend | -| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. | -| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. | -| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. | -| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. | -| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. | -| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers
Format: HEADER:value||HEADER2:value2 | -| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm | -| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions | -| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions | -| `traefik.backend.loadbalancer.sticky=true` | Enables backend sticky sessions (DEPRECATED) | -| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | -| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | -| `traefik.frontend.auth.basic=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). | -| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | -| `traefik.frontend.auth.basic.users=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` . | -| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets the basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | -| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | -| `traefik.frontend.auth.digest.users=EXPR` | Sets the digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | -| `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets the digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | -| `traefik.frontend.auth.forward.address=https://example.com`| Sets the URL of the authentication server. | -| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | -| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true`| If set to true invalid SSL certificates are accepted. | -| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | -| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | -| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. | -| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | -| `traefik.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.errors..status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | -| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. | -| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. | -| `traefik.frontend.priority=10` | Overrides default frontend priority | -| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.rateLimit.rateSet..burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | -| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) | -| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.replacement`. | -| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.regex`. | -| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. | -| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{containerName}.{domain}` or `Host:{service}.{project_name}.{domain}` if you are using `docker-compose`. | -| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access.
If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | -| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. | +| Label | Description | +|---------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `traefik.domain` | Sets the default domain for the frontend rules. | +| `traefik.enable=false` | Disables this container in Træfik. | +| `traefik.port=80` | Registers this port. Useful when the container exposes multiple ports. | +| `traefik.protocol=https` | Overrides the default `http` protocol. | +| `traefik.weight=10` | Assigns this weight to the container. | +| `traefik.backend=foo` | Gives the name `foo` to the generated backend for this container. | +| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. | +| `traefik.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend | +| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. | +| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. | +| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. | +| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. | +| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. | +| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers
Format: HEADER:value||HEADER2:value2 | +| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm | +| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions | +| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions | +| `traefik.backend.loadbalancer.sticky=true` | Enables backend sticky sessions (DEPRECATED) | +| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | +| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | +| `traefik.frontend.auth.basic=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). | +| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | +| `traefik.frontend.auth.basic.users=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` . | +| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets the basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | +| `traefik.frontend.auth.digest.users=EXPR` | Sets the digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | +| `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets the digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. | +| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). | +| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true` | If set to true invalid SSL certificates are accepted. | +| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | +| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | +| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. | +| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | +| `traefik.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.errors..status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | +| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. | +| `traefik.frontend.passTLSClientCert.infos.notAfter=true` | Add the noAfter field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.notBefore=true` | Add the noBefore field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.sans=true` | Add the sans field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.commonName=true` | Add the subject.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.country=true` | Add the subject.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.locality=true` | Add the subject.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.organization=true`| Add the subject.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.province=true` | Add the subject.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Add the subject.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. | +| `traefik.frontend.passTLSClientCert.pem=true` | Pass the escaped pem in the `X-Forwarded-Ssl-Client-Cert` header. | +| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. | +| `traefik.frontend.priority=10` | Overrides default frontend priority | +| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.rateLimit.rateSet..burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. | +| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) | +| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.replacement`. | +| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.
Must be set with `traefik.frontend.redirect.regex`. | +| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. | +| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{containerName}.{domain}` or `Host:{service}.{project_name}.{domain}` if you are using `docker-compose`. | +| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.
An unset or empty list allows all Source-IPs to access.
If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. | +| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. | #### Custom Headers @@ -238,46 +248,56 @@ You can define as many segments as ports exposed in a container. Segment labels override the default behavior. -| Label | Description | -|---------------------------------------------------------------------------|---------------------------------------------------------------| -| `traefik..backend=BACKEND` | Same as `traefik.backend` | -| `traefik..domain=DOMAIN` | Same as `traefik.domain` | -| `traefik..port=PORT` | Same as `traefik.port` | -| `traefik..protocol=http` | Same as `traefik.protocol` | -| `traefik..weight=10` | Same as `traefik.weight` | -| `traefik..frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` | -| `traefik..frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` | -| `traefik..frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` | -| `traefik..frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` | -| `traefik..frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` | -| `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | -| `traefik..frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` | -| `traefik..frontend.auth.forward.address=https://example.com`| Same as `traefik.frontend.auth.forward.address` | -| `traefik..frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` | -| `traefik..frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` | -| `traefik..frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` | -| `traefik..frontend.auth.forward.tls.insecureSkipVerify=true`| Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify`| -| `traefik..frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` | -| `traefik..frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` | -| `traefik..frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` | -| `traefik..frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` | -| `traefik..frontend.errors..backend=NAME` | Same as `traefik.frontend.errors..backend` | -| `traefik..frontend.errors..query=PATH` | Same as `traefik.frontend.errors..query` | -| `traefik..frontend.errors..status=RANGE` | Same as `traefik.frontend.errors..status` | -| `traefik..frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` | -| `traefik..frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` | -| `traefik..frontend.priority=10` | Same as `traefik.frontend.priority` | -| `traefik..frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` | -| `traefik..frontend.rateLimit.rateSet..period=6` | Same as `traefik.frontend.rateLimit.rateSet..period` | -| `traefik..frontend.rateLimit.rateSet..average=6` | Same as `traefik.frontend.rateLimit.rateSet..average` | -| `traefik..frontend.rateLimit.rateSet..burst=6` | Same as `traefik.frontend.rateLimit.rateSet..burst` | -| `traefik..frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` | -| `traefik..frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` | -| `traefik..frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` | -| `traefik..frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` | -| `traefik..frontend.rule=EXP` | Same as `traefik.frontend.rule` | -| `traefik..frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` | -| `traefik..frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` | +| Label | Description | +|------------------------------------------------------------------------------------|------------------------------------------------------------------------| +| `traefik..backend=BACKEND` | Same as `traefik.backend` | +| `traefik..domain=DOMAIN` | Same as `traefik.domain` | +| `traefik..port=PORT` | Same as `traefik.port` | +| `traefik..protocol=http` | Same as `traefik.protocol` | +| `traefik..weight=10` | Same as `traefik.weight` | +| `traefik..frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` | +| `traefik..frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` | +| `traefik..frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` | +| `traefik..frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` | +| `traefik..frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` | +| `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | +| `traefik..frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` | +| `traefik..frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` | +| `traefik..frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` | +| `traefik..frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` | +| `traefik..frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` | +| `traefik..frontend.auth.forward.tls.insecureSkipVerify=true` | Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` | +| `traefik..frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` | +| `traefik..frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` | +| `traefik..frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` | +| `traefik..frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` | +| `traefik..frontend.errors..backend=NAME` | Same as `traefik.frontend.errors..backend` | +| `traefik..frontend.errors..query=PATH` | Same as `traefik.frontend.errors..query` | +| `traefik..frontend.errors..status=RANGE` | Same as `traefik.frontend.errors..status` | +| `traefik..frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` | +| `traefik..frontend.passTLSClientCert.infos.notAfter=true` | Same as `traefik.frontend.passTLSClientCert.infos.notAfter` | +| `traefik..frontend.passTLSClientCert.infos.notBefore=true` | Same as `traefik.frontend.passTLSClientCert.infos.notBefore` | +| `traefik..frontend.passTLSClientCert.infos.sans=true` | Same as `traefik.frontend.passTLSClientCert.infos.sans` | +| `traefik..frontend.passTLSClientCert.infos.subject.commonName=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.commonName` | +| `traefik..frontend.passTLSClientCert.infos.subject.country=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.country` | +| `traefik..frontend.passTLSClientCert.infos.subject.locality=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.locality` | +| `traefik..frontend.passTLSClientCert.infos.subject.organization=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.organization`| +| `traefik..frontend.passTLSClientCert.infos.subject.province=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.province` | +| `traefik..frontend.passTLSClientCert.infos.subject.serialNumber=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.serialNumber`| +| `traefik..frontend.passTLSClientCert.pem=true` | Same as `traefik.frontend.passTLSClientCert.infos.pem` | +| `traefik..frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` | +| `traefik..frontend.priority=10` | Same as `traefik.frontend.priority` | +| `traefik..frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` | +| `traefik..frontend.rateLimit.rateSet..period=6` | Same as `traefik.frontend.rateLimit.rateSet..period` | +| `traefik..frontend.rateLimit.rateSet..average=6` | Same as `traefik.frontend.rateLimit.rateSet..average` | +| `traefik..frontend.rateLimit.rateSet..burst=6` | Same as `traefik.frontend.rateLimit.rateSet..burst` | +| `traefik..frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` | +| `traefik..frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` | +| `traefik..frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` | +| `traefik..frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` | +| `traefik..frontend.rule=EXP` | Same as `traefik.frontend.rule` | +| `traefik..frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` | +| `traefik..frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` | #### Custom Headers diff --git a/docs/user-guide/examples.md b/docs/user-guide/examples.md index 51bc7c1be..2b24194a0 100644 --- a/docs/user-guide/examples.md +++ b/docs/user-guide/examples.md @@ -311,7 +311,6 @@ The `consul` provider contains the configuration. [frontends.frontend2] backend = "backend1" passHostHeader = true - passTLSCert = true entrypoints = ["https"] # overrides defaultEntryPoints [frontends.frontend2.routes.test_1] rule = "Host:{subdomain:[a-z]+}.localhost" diff --git a/integration/fixtures/tlsclientheaders/root.pem b/integration/fixtures/tlsclientheaders/root.pem new file mode 100644 index 000000000..633325fc9 --- /dev/null +++ b/integration/fixtures/tlsclientheaders/root.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDhDCCAmygAwIBAgIJAK4Ed0WF/YNQMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNV +BAYTAkZSMQ8wDQYDVQQIDAZGUkFOQ0UxETAPBgNVBAcMCFRPVUxPVVNFMRMwEQYD +VQQKDApjb250YWlub3VzMQ8wDQYDVQQDDAZzZXJ2ZXIwHhcNMTgwMzIxMTMzOTM4 +WhcNMjEwMTA4MTMzOTM4WjBXMQswCQYDVQQGEwJGUjEPMA0GA1UECAwGRlJBTkNF +MREwDwYDVQQHDAhUT1VMT1VTRTETMBEGA1UECgwKY29udGFpbm91czEPMA0GA1UE +AwwGc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2DfZMdW1 +QKmdOTPULt6WUMVFU3PUcovq4cVtvNAzAshduC/7nHZx60uFzVKLYnOfZ+5VYfOS +zfVXPvltmBSWga1Yj6CuzfDZwY1nkcoL+22yBD6x4w2nB7aFaPNgj6M4ALVEZRKX +lMow+a0c0mOr1kLHm99MT/oabcdI+wbAp8VnLz9DF6SD7iDjIOb4RjvmcyetBzwu +1rQYti0bFHOnLCxiz0asXly0zspFajWkbGkvBdvEoP2qOHMeTV604PaBwpIMX/ly +ymGgYUctHeC16ptDRDDj7Spmu7ec2NzjgNW+MOth6EkFlhYgg1OEIXP+IFJ5LbS8 +1t/Y+fDUoc6+IQIDAQABo1MwUTAdBgNVHQ4EFgQUYeZvrzWyLI3TjmTIJYpSTjTb +/XUwHwYDVR0jBBgwFoAUYeZvrzWyLI3TjmTIJYpSTjTb/XUwDwYDVR0TAQH/BAUw +AwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAYQL8d/WQxu7rE58GC7le53FNzujMNZ+h +1kdS35LrTXPv5b6QTi5oUGi5LCesP4HnCpGdMFodyydhY8rhIDZWEFgkJZOLZhdt +sAyRONdI/Ms/NGQO2oJD+TlV92e4k3ey4WJyXIFHXE2Apb77VlsiHp8pI/iF/R5t +h4o4OADG7k6Fjf/wx7A18ru2eoH+PcwA8i6sQaQ1qEwxC0b3rh2TwaCpFQVcmMv5 +5jKPRBN0UC0PyHwqFZsSg1folhMAIBAjUsHgA6WleN9zMCyLAIn0LSai1CpFby6o +d6xu6pp8pwot8YTL0yS5T0X9aNhK2/uDoP50ei6eWI3uuPa8NJxbyA== +-----END CERTIFICATE----- diff --git a/integration/fixtures/tlsclientheaders/server.key b/integration/fixtures/tlsclientheaders/server.key new file mode 100644 index 000000000..f5d218904 --- /dev/null +++ b/integration/fixtures/tlsclientheaders/server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAo0eupztBxEchz/9BbegBzKX35YUt0S2Xzp/mFM+hXQylWDHB +z3wED7R89v3sY6ePTk/tAT5l6uKjmQ/zRlQFf7QtVWKUtYq8rjuFn9/EeC+233mx +kVP7QAcuT6T8PzoUgysW6Tx3zz18VDRMnPhx1fjA1jAq3+IU03BpbFz7CkYCxkG/ +1wWHmsB16LH2bMxJrzapph2nSDnUkoATugSJec+DxTtX1hdjAaJK/JsIwioA/Lyy +6YgE2oX7uRZBou0bA3y0TDnFoIVAVqISYWfszTGDlwL+SD/P9GYa19GZk4imdp8j +LD/+J9eLuHG75rkROvE4xbSPbGIGkZOEYTmjHwIDAQABAoIBAGok81kroHlkdIqu +uW4lYOYVDq5agYp2RTXBpOTqhU/kJKjMz91+FXXQM1ytfbra9sJGGyCv27lyVD/w +qomRnXGDQ+U6DMpnwnjRoPBpm2M2QX/NsK11FuRsxqJn8sN3klYi8OX2tTw4EFb9 +GMECkZ4z88hJz9VzN26sqRwU5e2qw45Fhk+Jl6RBsiBfMGNGsmI5n1yIgvQd2PoM +wVxHI+bb3rWL7zE7wy2rb2c+J0P2gy7fZlFN2ZLkC5RjTqdzD2P4erp4gcpgffuO +0Epu7ZzuJ0UKCBXJOkhjlM79opLK6IBpF1YgxVCoMPbQVYAHP9hSwuz6hgc0ocwa ++6PqzSECgYEA1kTSFN8tHq2VMFgwPyguppSmeJJdIcnMYdicJNkv9YXeIt4mAk9c +Qm5eMLoqRJL94fdRDGb7QIqcfSrQODHy5dmqrTZd+TeSc4VRC1gZ7RPg5ja8b0dR +DoktPizIYzWrNEaEjhWojqYXT5DOOmNgDbOYrlR6Qdrd6VOmQkIgHz0CgYEAwxSf +NMe6LasWg9PYgLeVBcNc9oOjGvczOmNULngte8LpiJm4yzI0gMer20VdCtXYsyR1 +Zs/rItzSQuvr+3v5qW2NfJ/TaJkZ+bcc/fGJ2LcnM2Kfjfih8DSy5/MBzNM4cqw2 +arHVvQlAvfOSB8WoFzdXOS41Z+BumLsZE3/mMYsCgYBGNTKpCB+ep730o1DbwOzY +RGjvpPXDNn4zqWgwYsHmL0EEJ8pIg3x1f/h4+ucSpR9vRTxXVf8JvOFd2gN0BlnS +mqnkK6ZLHLxuAcb2cp28IwFULac8xx92JdifQMlASLuaW2jfrZUXeLC2r3oDg8Bb +fPeQV7nfjjmcVH5rw4MG+QKBgQCi4RH4oJZLUSEQWo3XEvDjCfYRgWFqv2FPa+W6 +ku7u+ZPBURAg4D9EEvLjtmt0A47WLCe1+v3JcvQ/mfnDVQTkOKs8lbmPCN3OSNx1 +DvnYLzwUxFCR2jljdKy3y4cCPI1R+YXJ2ceq+RHMR5Ty1k59a+BwxqsimxncfcL3 +K//H9wKBgQChT3kvF9Igcdna8g+JneGD6RHXJX1o80QrO+eWma4NozEOmXqA7R7r ++GwAyqy9GFM7pwUhHmhJAxILMBxR84EY7kCBvi1VlZ3JbT7w0gjjOqPHklvbsPj9 +BruA5xPMq1gzCOgejQIRoODtpH1S6Fi/YMTO6eq75qw6minHWi4dPw== +-----END RSA PRIVATE KEY----- diff --git a/integration/fixtures/tlsclientheaders/server.pem b/integration/fixtures/tlsclientheaders/server.pem new file mode 100644 index 000000000..97c39f730 --- /dev/null +++ b/integration/fixtures/tlsclientheaders/server.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDKjCCAhICCQDKAJTeuq3LHjANBgkqhkiG9w0BAQsFADBXMQswCQYDVQQGEwJG +UjEPMA0GA1UECAwGRlJBTkNFMREwDwYDVQQHDAhUT1VMT1VTRTETMBEGA1UECgwK +Y29udGFpbm91czEPMA0GA1UEAwwGc2VydmVyMB4XDTE4MDMyMTEzNDM0MVoXDTIx +MDEwODEzNDM0MVowVzELMAkGA1UEBhMCRlIxDzANBgNVBAgMBkZSQU5DRTERMA8G +A1UEBwwIVE9VTE9VU0UxEzARBgNVBAoMCmNvbnRhaW5vdXMxDzANBgNVBAMMBnNl +cnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKNHrqc7QcRHIc// +QW3oAcyl9+WFLdEtl86f5hTPoV0MpVgxwc98BA+0fPb97GOnj05P7QE+Zerio5kP +80ZUBX+0LVVilLWKvK47hZ/fxHgvtt95sZFT+0AHLk+k/D86FIMrFuk8d889fFQ0 +TJz4cdX4wNYwKt/iFNNwaWxc+wpGAsZBv9cFh5rAdeix9mzMSa82qaYdp0g51JKA +E7oEiXnPg8U7V9YXYwGiSvybCMIqAPy8sumIBNqF+7kWQaLtGwN8tEw5xaCFQFai +EmFn7M0xg5cC/kg/z/RmGtfRmZOIpnafIyw//ifXi7hxu+a5ETrxOMW0j2xiBpGT +hGE5ox8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAPYDdGyNWp7R9j2oxZEbQS4lb ++2Ol1r6PFo/zmpB6GK3CSNo65a0DtW/ITeQi97MMgGS1D3wnaFPrwxtp0mEn7HjU +uDcufHBqqBsjYC3NEtt+yyxNeYddLD/GdFXw4d6wNRdRaFCq5N1CPQzF4VTdoSLD +xsOq/WAHHc2cyZyOprAqm2UXyWXxn4yWZqzDsZ41/v2f3uMNxeqyIEtNZVzTKQBu +wWw+jlQKGu0T8Ex1f0jaKI1OPtN5dzaIfO8acHcuNdmnE+hVsoqe17Dckxsj1ORf +8ZcZ4qvULVouGINQBP4fcl5jv6TOm1U+ZSk01FcHPmiDEMB6Utyy4ZLHPbmKYg== +-----END CERTIFICATE----- diff --git a/integration/fixtures/tlsclientheaders/simple.toml b/integration/fixtures/tlsclientheaders/simple.toml new file mode 100644 index 000000000..40809f315 --- /dev/null +++ b/integration/fixtures/tlsclientheaders/simple.toml @@ -0,0 +1,24 @@ +logLevel = "DEBUG" +defaultEntryPoints = ["https"] +debug = true +rootCAs = [ """{{ .RootCertContent }}""" ] + +[entryPoints] + [entryPoints.https] + address = ":8443" + + [entryPoints.https.tls] + + [entryPoints.https.tls.ClientCA] + files = [ """{{ .RootCertContent }}""" ] + optional = false + + [[entryPoints.https.tls.certificates]] + certFile = """{{ .ServerCertContent }}""" + keyFile = """{{ .ServerKeyContent }}""" + +[api] + +[docker] +endpoint = "unix:///var/run/docker.sock" +watch = true diff --git a/integration/integration_test.go b/integration/integration_test.go index c817b2276..f68e62b6b 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -60,6 +60,7 @@ func init() { check.Suite(&RateLimitSuite{}) check.Suite(&RetrySuite{}) check.Suite(&SimpleSuite{}) + check.Suite(&TLSClientHeadersSuite{}) check.Suite(&TimeoutSuite{}) check.Suite(&TracingSuite{}) check.Suite(&WebsocketSuite{}) diff --git a/integration/resources/compose/tlsclientheaders.yml b/integration/resources/compose/tlsclientheaders.yml new file mode 100644 index 000000000..42ba2007b --- /dev/null +++ b/integration/resources/compose/tlsclientheaders.yml @@ -0,0 +1,6 @@ +whoami: + image: containous/whoami + labels: + - traefik.frontend.passTLSClientCert.pem=true + - traefik.frontend.rule=PathPrefix:/ + diff --git a/integration/tls_client_headers_test.go b/integration/tls_client_headers_test.go new file mode 100644 index 000000000..373e855ed --- /dev/null +++ b/integration/tls_client_headers_test.go @@ -0,0 +1,71 @@ +package integration + +import ( + "crypto/tls" + "io/ioutil" + "net/http" + "os" + "time" + + "github.com/containous/traefik/integration/try" + "github.com/go-check/check" + checker "github.com/vdemeester/shakers" +) + +const ( + rootCertPath = "./fixtures/tlsclientheaders/root.pem" + certPemPath = "./fixtures/tlsclientheaders/server.pem" + certKeyPath = "./fixtures/tlsclientheaders/server.key" +) + +type TLSClientHeadersSuite struct{ BaseSuite } + +func (s *TLSClientHeadersSuite) SetUpSuite(c *check.C) { + s.createComposeProject(c, "tlsclientheaders") + s.composeProject.Start(c) +} + +func (s *TLSClientHeadersSuite) TestTLSClientHeaders(c *check.C) { + rootCertContent, err := ioutil.ReadFile(rootCertPath) + c.Assert(err, check.IsNil) + serverCertContent, err := ioutil.ReadFile(certPemPath) + c.Assert(err, check.IsNil) + ServerKeyContent, err := ioutil.ReadFile(certKeyPath) + c.Assert(err, check.IsNil) + + file := s.adaptFile(c, "fixtures/tlsclientheaders/simple.toml", struct { + RootCertContent string + ServerCertContent string + ServerKeyContent string + }{ + RootCertContent: string(rootCertContent), + ServerCertContent: string(serverCertContent), + ServerKeyContent: string(ServerKeyContent), + }) + defer os.Remove(file) + + cmd, display := s.traefikCmd(withConfigFile(file)) + defer display(c) + err = cmd.Start() + c.Assert(err, checker.IsNil) + defer cmd.Process.Kill() + + err = try.GetRequest("http://127.0.0.1:8080/api/providers", 2*time.Second, try.BodyContains("PathPrefix:/")) + c.Assert(err, checker.IsNil) + + request, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:8443", nil) + c.Assert(err, checker.IsNil) + + certificate, err := tls.LoadX509KeyPair(certPemPath, certKeyPath) + c.Assert(err, checker.IsNil) + + tr := &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + Certificates: []tls.Certificate{certificate}, + }, + } + + err = try.RequestWithTransport(request, 2*time.Second, tr, try.BodyContains("Forwarded-Tls-Client-Cert: MIIDKjCCAhICCQDKAJTeuq3LHjANBgkqhkiG9w0BAQsFADBXMQswCQYDVQQGEwJGUjEPMA0GA1UECAwGRlJBTkNFMREwDwYDVQQHDAhUT1VMT1VTRTETMBEGA1UECgwKY29udGFpbm91czEPMA0GA1UEAwwGc2VydmVyMB4XDTE4MDMyMTEzNDM0MVoXDTIxMDEwODEzNDM0MVowVzELMAkGA1UEBhMCRlIxDzANBgNVBAgMBkZSQU5DRTERMA8GA1UEBwwIVE9VTE9VU0UxEzARBgNVBAoMCmNvbnRhaW5vdXMxDzANBgNVBAMMBnNlcnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKNHrqc7QcRHIc%2F%2FQW3oAcyl9%2BWFLdEtl86f5hTPoV0MpVgxwc98BA%2B0fPb97GOnj05P7QE%2BZerio5kP80ZUBX%2B0LVVilLWKvK47hZ%2FfxHgvtt95sZFT%2B0AHLk%2Bk%2FD86FIMrFuk8d889fFQ0TJz4cdX4wNYwKt%2FiFNNwaWxc%2BwpGAsZBv9cFh5rAdeix9mzMSa82qaYdp0g51JKAE7oEiXnPg8U7V9YXYwGiSvybCMIqAPy8sumIBNqF%2B7kWQaLtGwN8tEw5xaCFQFaiEmFn7M0xg5cC%2Fkg%2Fz%2FRmGtfRmZOIpnafIyw%2F%2FifXi7hxu%2Ba5ETrxOMW0j2xiBpGThGE5ox8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAPYDdGyNWp7R9j2oxZEbQS4lb%2B2Ol1r6PFo%2FzmpB6GK3CSNo65a0DtW%2FITeQi97MMgGS1D3wnaFPrwxtp0mEn7HjUuDcufHBqqBsjYC3NEtt%2ByyxNeYddLD%2FGdFXw4d6wNRdRaFCq5N1CPQzF4VTdoSLDxsOq%2FWAHHc2cyZyOprAqm2UXyWXxn4yWZqzDsZ41%2Fv2f3uMNxeqyIEtNZVzTKQBuwWw%2BjlQKGu0T8Ex1f0jaKI1OPtN5dzaIfO8acHcuNdmnE%2BhVsoqe17Dckxsj1ORf8ZcZ4qvULVouGINQBP4fcl5jv6TOm1U%2BZSk01FcHPmiDEMB6Utyy4ZLHPbmKYg%3D%3D")) + c.Assert(err, checker.IsNil) +} diff --git a/middlewares/tlsClientHeaders.go b/middlewares/tlsClientHeaders.go new file mode 100644 index 000000000..ac4a5cb41 --- /dev/null +++ b/middlewares/tlsClientHeaders.go @@ -0,0 +1,251 @@ +package middlewares + +import ( + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "net/http" + "net/url" + "strings" + + "github.com/containous/traefik/log" + "github.com/containous/traefik/types" +) + +const xForwardedTLSClientCert = "X-Forwarded-Tls-Client-Cert" +const xForwardedTLSClientCertInfos = "X-Forwarded-Tls-Client-Cert-Infos" + +// TLSClientCertificateInfos is a struct for specifying the configuration for the tlsClientHeaders middleware. +type TLSClientCertificateInfos struct { + NotAfter bool + NotBefore bool + Subject *TLSCLientCertificateSubjectInfos + Sans bool +} + +// TLSCLientCertificateSubjectInfos contains the configuration for the certificate subject infos. +type TLSCLientCertificateSubjectInfos struct { + Country bool + Province bool + Locality bool + Organization bool + CommonName bool + SerialNumber bool +} + +// TLSClientHeaders is a middleware that helps setup a few tls infos features. +type TLSClientHeaders struct { + PEM bool // pass the sanitized pem to the backend in a specific header + Infos *TLSClientCertificateInfos // pass selected informations from the client certificate +} + +func newTLSCLientCertificateSubjectInfos(infos *types.TLSCLientCertificateSubjectInfos) *TLSCLientCertificateSubjectInfos { + if infos == nil { + return nil + } + + return &TLSCLientCertificateSubjectInfos{ + SerialNumber: infos.SerialNumber, + CommonName: infos.CommonName, + Country: infos.Country, + Locality: infos.Locality, + Organization: infos.Organization, + Province: infos.Province, + } +} + +func newTLSClientInfos(infos *types.TLSClientCertificateInfos) *TLSClientCertificateInfos { + if infos == nil { + return nil + } + + return &TLSClientCertificateInfos{ + NotBefore: infos.NotBefore, + NotAfter: infos.NotAfter, + Sans: infos.Sans, + Subject: newTLSCLientCertificateSubjectInfos(infos.Subject), + } +} + +// NewTLSClientHeaders constructs a new TLSClientHeaders instance from supplied frontend header struct. +func NewTLSClientHeaders(frontend *types.Frontend) *TLSClientHeaders { + if frontend == nil { + return nil + } + + var pem bool + var infos *TLSClientCertificateInfos + + if frontend.PassTLSClientCert != nil { + conf := frontend.PassTLSClientCert + pem = conf.PEM + infos = newTLSClientInfos(conf.Infos) + } + + return &TLSClientHeaders{ + PEM: pem, + Infos: infos, + } +} + +func (s *TLSClientHeaders) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + s.ModifyRequestHeaders(r) + // If there is a next, call it. + if next != nil { + next(w, r) + } +} + +// sanitize As we pass the raw certificates, remove the useless data and make it http request compliant +func sanitize(cert []byte) string { + s := string(cert) + r := strings.NewReplacer("-----BEGIN CERTIFICATE-----", "", + "-----END CERTIFICATE-----", "", + "\n", "") + cleaned := r.Replace(s) + + return url.QueryEscape(cleaned) +} + +// extractCertificate extract the certificate from the request +func extractCertificate(cert *x509.Certificate) string { + b := pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw} + certPEM := pem.EncodeToMemory(&b) + if certPEM == nil { + log.Error("Cannot extract the certificate content") + return "" + } + return sanitize(certPEM) +} + +// getXForwardedTLSClientCert Build a string with the client certificates +func getXForwardedTLSClientCert(certs []*x509.Certificate) string { + var headerValues []string + + for _, peerCert := range certs { + headerValues = append(headerValues, extractCertificate(peerCert)) + } + + return strings.Join(headerValues, ",") +} + +// getSANs get the Subject Alternate Name values +func getSANs(cert *x509.Certificate) []string { + var sans []string + if cert == nil { + return sans + } + + sans = append(cert.DNSNames, cert.EmailAddresses...) + + var ips []string + for _, ip := range cert.IPAddresses { + ips = append(ips, ip.String()) + } + sans = append(sans, ips...) + + var uris []string + for _, uri := range cert.URIs { + uris = append(uris, uri.String()) + } + + return append(sans, uris...) +} + +// getSubjectInfos extract the requested informations from the certificate subject +func (s *TLSClientHeaders) getSubjectInfos(cs *pkix.Name) string { + var subject string + + if s.Infos != nil && s.Infos.Subject != nil { + options := s.Infos.Subject + + var content []string + + if options.Country && len(cs.Country) > 0 { + content = append(content, fmt.Sprintf("C=%s", cs.Country[0])) + } + + if options.Province && len(cs.Province) > 0 { + content = append(content, fmt.Sprintf("ST=%s", cs.Province[0])) + } + + if options.Locality && len(cs.Locality) > 0 { + content = append(content, fmt.Sprintf("L=%s", cs.Locality[0])) + } + + if options.Organization && len(cs.Organization) > 0 { + content = append(content, fmt.Sprintf("O=%s", cs.Organization[0])) + } + + if options.CommonName && len(cs.CommonName) > 0 { + content = append(content, fmt.Sprintf("CN=%s", cs.CommonName)) + } + + if len(content) > 0 { + subject = `Subject="` + strings.Join(content, ",") + `"` + } + } + + return subject +} + +// getXForwardedTLSClientCertInfos Build a string with the wanted client certificates informations +// like Subject="C=%s,ST=%s,L=%s,O=%s,CN=%s",NB=%d,NA=%d,SAN=%s; +func (s *TLSClientHeaders) getXForwardedTLSClientCertInfos(certs []*x509.Certificate) string { + var headerValues []string + + for _, peerCert := range certs { + var values []string + var sans string + var nb string + var na string + + subject := s.getSubjectInfos(&peerCert.Subject) + if len(subject) > 0 { + values = append(values, subject) + } + + ci := s.Infos + if ci != nil { + if ci.NotBefore { + nb = fmt.Sprintf("NB=%d", uint64(peerCert.NotBefore.Unix())) + values = append(values, nb) + } + if ci.NotAfter { + na = fmt.Sprintf("NA=%d", uint64(peerCert.NotAfter.Unix())) + values = append(values, na) + } + + if ci.Sans { + sans = fmt.Sprintf("SAN=%s", strings.Join(getSANs(peerCert), ",")) + values = append(values, sans) + } + } + + value := strings.Join(values, ",") + headerValues = append(headerValues, value) + } + + return strings.Join(headerValues, ";") +} + +// ModifyRequestHeaders set the wanted headers with the certificates informations +func (s *TLSClientHeaders) ModifyRequestHeaders(r *http.Request) { + if s.PEM { + if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 { + r.Header.Set(xForwardedTLSClientCert, getXForwardedTLSClientCert(r.TLS.PeerCertificates)) + } else { + log.Warn("Try to extract certificate on a request without TLS") + } + } + + if s.Infos != nil { + if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 { + headerContent := s.getXForwardedTLSClientCertInfos(r.TLS.PeerCertificates) + r.Header.Set(xForwardedTLSClientCertInfos, url.QueryEscape(headerContent)) + } else { + log.Warn("Try to extract certificate on a request without TLS") + } + } +} diff --git a/middlewares/tlsClientHeaders_test.go b/middlewares/tlsClientHeaders_test.go new file mode 100644 index 000000000..583da2bb6 --- /dev/null +++ b/middlewares/tlsClientHeaders_test.go @@ -0,0 +1,799 @@ +package middlewares + +import ( + "crypto/tls" + "crypto/x509" + "encoding/pem" + "net" + "net/http" + "net/http/httptest" + "net/url" + "regexp" + "strings" + "testing" + + "github.com/containous/traefik/testhelpers" + "github.com/containous/traefik/types" + "github.com/stretchr/testify/require" +) + +const ( + rootCrt = `-----BEGIN CERTIFICATE----- +MIIDhjCCAm6gAwIBAgIJAIKZlW9a3VrYMA0GCSqGSIb3DQEBCwUAMFgxCzAJBgNV +BAYTAkZSMRMwEQYDVQQIDApTb21lLVN0YXRlMREwDwYDVQQHDAhUb3Vsb3VzZTEh +MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMB4XDTE4MDcxNzIwMzQz +OFoXDTE4MDgxNjIwMzQzOFowWDELMAkGA1UEBhMCRlIxEzARBgNVBAgMClNvbWUt +U3RhdGUxETAPBgNVBAcMCFRvdWxvdXNlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn +aXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1P8GJ +H9LkIxIIqK9MyUpushnjmjwccpSMB3OecISKYLy62QDIcAw6NzGcSe8hMwciMJr+ +CdCjJlohybnaRI9hrJ3GPnI++UT/MMthf2IIcjmJxmD4k9L1fgs1V6zSTlo0+o0x +0gkAGlWvRkgA+3nt555ee84XQZuneKKeRRIlSA1ygycewFobZ/pGYijIEko+gYkV +sF3LnRGxNl673w+EQsvI7+z29T1nzjmM/xE7WlvnsrVd1/N61jAohLota0YTufwd +ioJZNryzuPejHBCiQRGMbJ7uEEZLiSCN6QiZEfqhS3AulykjgFXQQHn4zoVljSBR +UyLV0prIn5Scbks/AgMBAAGjUzBRMB0GA1UdDgQWBBTroRRnSgtkV+8dumtcftb/ +lwIkATAfBgNVHSMEGDAWgBTroRRnSgtkV+8dumtcftb/lwIkATAPBgNVHRMBAf8E +BTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAJ67U5cLa0ZFa/7zQQT4ldkY6YOEgR +0LNoTu51hc+ozaXSvF8YIBzkEpEnbGS3x4xodrwEBZjK2LFhNu/33gkCAuhmedgk +KwZrQM6lqRFGHGVOlkVz+QrJ2EsKYaO4SCUIwVjijXRLA7A30G5C/CIh66PsMgBY +6QHXVPEWm/v1d1Q/DfFfFzSOa1n1rIUw03qVJsxqSwfwYcegOF8YvS/eH4HUr2gF +cEujh6CCnylf35ExHa45atr3+xxbOVdNjobISkYADtbhAAn4KjLS4v8W6445vxxj +G5EIZLjOHyWg1sGaHaaAPkVpZQg8EKm21c4hrEEMfel60AMSSzad/a/V +-----END CERTIFICATE-----` + + minimalCert = `-----BEGIN CERTIFICATE----- +MIIDGTCCAgECCQCqLd75YLi2kDANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJG +UjETMBEGA1UECAwKU29tZS1TdGF0ZTERMA8GA1UEBwwIVG91bG91c2UxITAfBgNV +BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0xODA3MTgwODI4MTZaFw0x +ODA4MTcwODI4MTZaMEUxCzAJBgNVBAYTAkZSMRMwEQYDVQQIDApTb21lLVN0YXRl +MSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQC/+frDMMTLQyXG34F68BPhQq0kzK4LIq9Y0/gl +FjySZNn1C0QDWA1ubVCAcA6yY204I9cxcQDPNrhC7JlS5QA8Y5rhIBrqQlzZizAi +Rj3NTrRjtGUtOScnHuJaWjLy03DWD+aMwb7q718xt5SEABmmUvLwQK+EjW2MeDwj +y8/UEIpvrRDmdhGaqv7IFpIDkcIF7FowJ/hwDvx3PMc+z/JWK0ovzpvgbx69AVbw +ZxCimeha65rOqVi+lEetD26le+WnOdYsdJ2IkmpPNTXGdfb15xuAc+gFXfMCh7Iw +3Ynl6dZtZM/Ok2kiA7/OsmVnRKkWrtBfGYkI9HcNGb3zrk6nAgMBAAEwDQYJKoZI +hvcNAQELBQADggEBAC/R+Yvhh1VUhcbK49olWsk/JKqfS3VIDQYZg1Eo+JCPbwgS +I1BSYVfMcGzuJTX6ua3m/AHzGF3Tap4GhF4tX12jeIx4R4utnjj7/YKkTvuEM2f4 +xT56YqI7zalGScIB0iMeyNz1QcimRl+M/49au8ow9hNX8C2tcA2cwd/9OIj/6T8q +SBRHc6ojvbqZSJCO0jziGDT1L3D+EDgTjED4nd77v/NRdP+egb0q3P0s4dnQ/5AV +aQlQADUn61j3ScbGJ4NSeZFFvsl38jeRi/MEzp0bGgNBcPj6JHi7qbbauZcZfQ05 +jECvgAY7Nfd9mZ1KtyNaW31is+kag7NsvjxU/kM= +-----END CERTIFICATE-----` + + completeCert = `Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=FR, ST=Some-State, L=Toulouse, O=Internet Widgits Pty Ltd + Validity + Not Before: Jul 18 08:00:16 2018 GMT + Not After : Jul 18 08:00:16 2019 GMT + Subject: C=FR, ST=SomeState, L=Toulouse, O=Cheese, CN=*.cheese.org + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:a6:1f:96:7c:c1:cc:b8:1c:b5:91:5d:b8:bf:70: + bc:f7:b8:04:4f:2a:42:de:ea:c5:c3:19:0b:03:04: + ec:ef:a1:24:25:de:ad:05:e7:26:ea:89:6c:59:60: + 10:18:0c:73:f1:bf:d3:cc:7b:ed:6b:9c:ea:1d:88: + e2:ee:14:81:d7:07:ee:87:95:3d:36:df:9c:38:b7: + 7b:1e:2b:51:9c:4a:1f:d0:cc:5b:af:5d:6c:5c:35: + 49:32:e4:01:5b:f9:8c:71:cf:62:48:5a:ea:b7:31: + 58:e2:c6:d0:5b:1c:50:b5:5c:6d:5a:6f:da:41:5e: + d5:4c:6e:1a:21:f3:40:f9:9e:52:76:50:25:3e:03: + 9b:87:19:48:5b:47:87:d3:67:c6:25:69:77:29:8e: + 56:97:45:d9:6f:64:a8:4e:ad:35:75:2e:fc:6a:2e: + 47:87:76:fc:4e:3e:44:e9:16:b2:c7:f0:23:98:13: + a2:df:15:23:cb:0c:3d:fd:48:5e:c7:2c:86:70:63: + 8b:c6:c8:89:17:52:d5:a7:8e:cb:4e:11:9d:69:8e: + 8e:59:cc:7e:a3:bd:a1:11:88:d7:cf:7b:8c:19:46: + 9c:1b:7a:c9:39:81:4c:58:08:1f:c7:ce:b0:0e:79: + 64:d3:11:72:65:e6:dd:bd:00:7f:22:30:46:9b:66: + 9c:b9 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + X509v3 Subject Alternative Name: + DNS:*.cheese.org, DNS:*.cheese.net, DNS:cheese.in, IP Address:10.0.1.0, IP Address:10.0.1.2, email:test@cheese.org, email:test@cheese.net + X509v3 Subject Key Identifier: + AB:6B:89:25:11:FC:5E:7B:D4:B0:F7:D4:B6:D9:EB:D0:30:93:E5:58 + Signature Algorithm: sha1WithRSAEncryption + ad:87:84:a0:88:a3:4c:d9:0a:c0:14:e4:2d:9a:1d:bb:57:b7: + 12:ef:3a:fb:8b:b2:ce:32:b8:04:e6:59:c8:4f:14:6a:b5:12: + 46:e9:c9:0a:11:64:ea:a1:86:20:96:0e:a7:40:e3:aa:e5:98: + 91:36:89:77:b6:b9:73:7e:1a:58:19:ae:d1:14:83:1e:c1:5f: + a5:a0:32:bb:52:68:b4:8d:a3:1d:b3:08:d7:45:6e:3b:87:64: + 7e:ef:46:e6:6f:d5:79:d7:1d:57:68:67:d8:18:39:61:5b:8b: + 1a:7f:88:da:0a:51:9b:3d:6c:5d:b1:cf:b7:e9:1e:06:65:8e: + 96:d3:61:96:f8:a2:61:f9:40:5e:fa:bc:76:b9:64:0e:6f:90: + 37:de:ac:6d:7f:36:84:35:19:88:8c:26:af:3e:c3:6a:1a:03: + ed:d7:90:89:ed:18:4c:9e:94:1f:d8:ae:6c:61:36:17:72:f9: + bb:de:0a:56:9a:79:b4:7d:4a:9d:cb:4a:7d:71:9f:38:e7:8d: + f0:87:24:21:0a:24:1f:82:9a:6b:67:ce:7d:af:cb:91:6b:8a: + de:e6:d8:6f:a1:37:b9:2d:d0:cb:e8:4e:f4:43:af:ad:90:13: + 7d:61:7a:ce:86:48:fc:00:8c:37:fb:e0:31:6b:e2:18:ad:fd: + 1e:df:08:db +-----BEGIN CERTIFICATE----- +MIIDvTCCAqWgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJGUjET +MBEGA1UECAwKU29tZS1TdGF0ZTERMA8GA1UEBwwIVG91bG91c2UxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0xODA3MTgwODAwMTZaFw0xOTA3 +MTgwODAwMTZaMFwxCzAJBgNVBAYTAkZSMRIwEAYDVQQIDAlTb21lU3RhdGUxETAP +BgNVBAcMCFRvdWxvdXNlMQ8wDQYDVQQKDAZDaGVlc2UxFTATBgNVBAMMDCouY2hl +ZXNlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKYflnzBzLgc +tZFduL9wvPe4BE8qQt7qxcMZCwME7O+hJCXerQXnJuqJbFlgEBgMc/G/08x77Wuc +6h2I4u4UgdcH7oeVPTbfnDi3ex4rUZxKH9DMW69dbFw1STLkAVv5jHHPYkha6rcx +WOLG0FscULVcbVpv2kFe1UxuGiHzQPmeUnZQJT4Dm4cZSFtHh9NnxiVpdymOVpdF +2W9kqE6tNXUu/GouR4d2/E4+ROkWssfwI5gTot8VI8sMPf1IXscshnBji8bIiRdS +1aeOy04RnWmOjlnMfqO9oRGI1897jBlGnBt6yTmBTFgIH8fOsA55ZNMRcmXm3b0A +fyIwRptmnLkCAwEAAaOBjTCBijAJBgNVHRMEAjAAMF4GA1UdEQRXMFWCDCouY2hl +ZXNlLm9yZ4IMKi5jaGVlc2UubmV0ggljaGVlc2UuaW6HBAoAAQCHBAoAAQKBD3Rl +c3RAY2hlZXNlLm9yZ4EPdGVzdEBjaGVlc2UubmV0MB0GA1UdDgQWBBSra4klEfxe +e9Sw99S22evQMJPlWDANBgkqhkiG9w0BAQUFAAOCAQEArYeEoIijTNkKwBTkLZod +u1e3Eu86+4uyzjK4BOZZyE8UarUSRunJChFk6qGGIJYOp0DjquWYkTaJd7a5c34a +WBmu0RSDHsFfpaAyu1JotI2jHbMI10VuO4dkfu9G5m/VedcdV2hn2Bg5YVuLGn+I +2gpRmz1sXbHPt+keBmWOltNhlviiYflAXvq8drlkDm+QN96sbX82hDUZiIwmrz7D +ahoD7deQie0YTJ6UH9iubGE2F3L5u94KVpp5tH1KnctKfXGfOOeN8IckIQokH4Ka +a2fOfa/LkWuK3ubYb6E3uS3Qy+hO9EOvrZATfWF6zoZI/ACMN/vgMWviGK39Ht8I +2w== +-----END CERTIFICATE----- +` +) + +func getCleanCertContents(certContents []string) string { + var re = regexp.MustCompile("-----BEGIN CERTIFICATE-----(?s)(.*)") + + var cleanedCertContent []string + for _, certContent := range certContents { + cert := re.FindString(string(certContent)) + cleanedCertContent = append(cleanedCertContent, sanitize([]byte(cert))) + } + + return strings.Join(cleanedCertContent, ",") +} + +func getCertificate(certContent string) *x509.Certificate { + roots := x509.NewCertPool() + ok := roots.AppendCertsFromPEM([]byte(rootCrt)) + if !ok { + panic("failed to parse root certificate") + } + + block, _ := pem.Decode([]byte(certContent)) + if block == nil { + panic("failed to parse certificate PEM") + } + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + panic("failed to parse certificate: " + err.Error()) + } + + return cert +} + +func buildTLSWith(certContents []string) *tls.ConnectionState { + var peerCertificates []*x509.Certificate + + for _, certContent := range certContents { + peerCertificates = append(peerCertificates, getCertificate(certContent)) + } + + return &tls.ConnectionState{PeerCertificates: peerCertificates} +} + +var myPassTLSClientCustomHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("bar")) +}) + +func getExpectedSanitized(s string) string { + return url.QueryEscape(strings.Replace(s, "\n", "", -1)) +} + +func TestSanitize(t *testing.T) { + testCases := []struct { + desc string + toSanitize []byte + expected string + }{ + { + desc: "Empty", + }, + { + desc: "With a minimal cert", + toSanitize: []byte(minimalCert), + expected: getExpectedSanitized(`MIIDGTCCAgECCQCqLd75YLi2kDANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJG +UjETMBEGA1UECAwKU29tZS1TdGF0ZTERMA8GA1UEBwwIVG91bG91c2UxITAfBgNV +BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0xODA3MTgwODI4MTZaFw0x +ODA4MTcwODI4MTZaMEUxCzAJBgNVBAYTAkZSMRMwEQYDVQQIDApTb21lLVN0YXRl +MSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQC/+frDMMTLQyXG34F68BPhQq0kzK4LIq9Y0/gl +FjySZNn1C0QDWA1ubVCAcA6yY204I9cxcQDPNrhC7JlS5QA8Y5rhIBrqQlzZizAi +Rj3NTrRjtGUtOScnHuJaWjLy03DWD+aMwb7q718xt5SEABmmUvLwQK+EjW2MeDwj +y8/UEIpvrRDmdhGaqv7IFpIDkcIF7FowJ/hwDvx3PMc+z/JWK0ovzpvgbx69AVbw +ZxCimeha65rOqVi+lEetD26le+WnOdYsdJ2IkmpPNTXGdfb15xuAc+gFXfMCh7Iw +3Ynl6dZtZM/Ok2kiA7/OsmVnRKkWrtBfGYkI9HcNGb3zrk6nAgMBAAEwDQYJKoZI +hvcNAQELBQADggEBAC/R+Yvhh1VUhcbK49olWsk/JKqfS3VIDQYZg1Eo+JCPbwgS +I1BSYVfMcGzuJTX6ua3m/AHzGF3Tap4GhF4tX12jeIx4R4utnjj7/YKkTvuEM2f4 +xT56YqI7zalGScIB0iMeyNz1QcimRl+M/49au8ow9hNX8C2tcA2cwd/9OIj/6T8q +SBRHc6ojvbqZSJCO0jziGDT1L3D+EDgTjED4nd77v/NRdP+egb0q3P0s4dnQ/5AV +aQlQADUn61j3ScbGJ4NSeZFFvsl38jeRi/MEzp0bGgNBcPj6JHi7qbbauZcZfQ05 +jECvgAY7Nfd9mZ1KtyNaW31is+kag7NsvjxU/kM=`), + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + require.Equal(t, test.expected, sanitize(test.toSanitize), "The sanitized certificates should be equal") + }) + } + +} + +func TestTlsClientheadersWithPEM(t *testing.T) { + testCases := []struct { + desc string + certContents []string // set the request TLS attribute if defined + tlsClientCertHeaders *types.TLSClientHeaders + expectedHeader string + }{ + { + desc: "No TLS, no option", + }, + { + desc: "TLS, no option", + certContents: []string{minimalCert}, + }, + { + desc: "No TLS, with pem option true", + tlsClientCertHeaders: &types.TLSClientHeaders{PEM: true}, + }, + { + desc: "TLS with simple certificate, with pem option true", + certContents: []string{minimalCert}, + tlsClientCertHeaders: &types.TLSClientHeaders{PEM: true}, + expectedHeader: getCleanCertContents([]string{minimalCert}), + }, + { + desc: "TLS with complete certificate, with pem option true", + certContents: []string{completeCert}, + tlsClientCertHeaders: &types.TLSClientHeaders{PEM: true}, + expectedHeader: getCleanCertContents([]string{completeCert}), + }, + { + desc: "TLS with two certificate, with pem option true", + certContents: []string{minimalCert, completeCert}, + tlsClientCertHeaders: &types.TLSClientHeaders{PEM: true}, + expectedHeader: getCleanCertContents([]string{minimalCert, completeCert}), + }, + } + + for _, test := range testCases { + tlsClientHeaders := NewTLSClientHeaders(&types.Frontend{PassTLSClientCert: test.tlsClientCertHeaders}) + + res := httptest.NewRecorder() + req := testhelpers.MustNewRequest(http.MethodGet, "http://example.com/foo", nil) + + if test.certContents != nil && len(test.certContents) > 0 { + req.TLS = buildTLSWith(test.certContents) + } + + tlsClientHeaders.ServeHTTP(res, req, myPassTLSClientCustomHandler) + + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + require.Equal(t, http.StatusOK, res.Code, "Http Status should be OK") + require.Equal(t, "bar", res.Body.String(), "Should be the expected body") + + if test.expectedHeader != "" { + require.Equal(t, getCleanCertContents(test.certContents), req.Header.Get(xForwardedTLSClientCert), "The request header should contain the cleaned certificate") + } else { + require.Empty(t, req.Header.Get(xForwardedTLSClientCert)) + } + require.Empty(t, res.Header().Get(xForwardedTLSClientCert), "The response header should be always empty") + }) + } + +} + +func TestGetSans(t *testing.T) { + urlFoo, err := url.Parse("my.foo.com") + require.NoError(t, err) + urlBar, err := url.Parse("my.bar.com") + require.NoError(t, err) + + testCases := []struct { + desc string + cert *x509.Certificate // set the request TLS attribute if defined + expected []string + }{ + { + desc: "With nil", + }, + { + desc: "Certificate without Sans", + cert: &x509.Certificate{}, + }, + { + desc: "Certificate with all Sans", + cert: &x509.Certificate{ + DNSNames: []string{"foo", "bar"}, + EmailAddresses: []string{"test@test.com", "test2@test.com"}, + IPAddresses: []net.IP{net.IPv4(10, 0, 0, 1), net.IPv4(10, 0, 0, 2)}, + URIs: []*url.URL{urlFoo, urlBar}, + }, + expected: []string{"foo", "bar", "test@test.com", "test2@test.com", "10.0.0.1", "10.0.0.2", urlFoo.String(), urlBar.String()}, + }, + } + + for _, test := range testCases { + sans := getSANs(test.cert) + test := test + + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + if len(test.expected) > 0 { + for i, expected := range test.expected { + require.Equal(t, expected, sans[i]) + } + } else { + require.Empty(t, sans) + } + }) + } + +} + +func TestTlsClientheadersWithCertInfos(t *testing.T) { + minimalCertAllInfos := `Subject="C=FR,ST=Some-State,O=Internet Widgits Pty Ltd",NB=1531902496,NA=1534494496,SAN=` + completeCertAllInfos := `Subject="C=FR,ST=SomeState,L=Toulouse,O=Cheese,CN=*.cheese.org",NB=1531900816,NA=1563436816,SAN=*.cheese.org,*.cheese.net,cheese.in,test@cheese.org,test@cheese.net,10.0.1.0,10.0.1.2` + + testCases := []struct { + desc string + certContents []string // set the request TLS attribute if defined + tlsClientCertHeaders *types.TLSClientHeaders + expectedHeader string + }{ + { + desc: "No TLS, no option", + }, + { + desc: "TLS, no option", + certContents: []string{minimalCert}, + }, + { + desc: "No TLS, with pem option true", + tlsClientCertHeaders: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + Subject: &types.TLSCLientCertificateSubjectInfos{ + CommonName: true, + Organization: true, + Locality: true, + Province: true, + Country: true, + SerialNumber: true, + }, + }, + }, + }, + { + desc: "No TLS, with pem option true with no flag", + tlsClientCertHeaders: &types.TLSClientHeaders{ + PEM: false, + Infos: &types.TLSClientCertificateInfos{ + Subject: &types.TLSCLientCertificateSubjectInfos{}, + }, + }, + }, + { + desc: "TLS with simple certificate, with all infos", + certContents: []string{minimalCert}, + tlsClientCertHeaders: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + NotAfter: true, + NotBefore: true, + Subject: &types.TLSCLientCertificateSubjectInfos{ + CommonName: true, + Organization: true, + Locality: true, + Province: true, + Country: true, + SerialNumber: true, + }, + Sans: true, + }, + }, + expectedHeader: url.QueryEscape(minimalCertAllInfos), + }, + { + desc: "TLS with simple certificate, with some infos", + certContents: []string{minimalCert}, + tlsClientCertHeaders: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + NotAfter: true, + Subject: &types.TLSCLientCertificateSubjectInfos{ + Organization: true, + }, + Sans: true, + }, + }, + expectedHeader: url.QueryEscape(`Subject="O=Internet Widgits Pty Ltd",NA=1534494496,SAN=`), + }, + { + desc: "TLS with complete certificate, with all infos", + certContents: []string{completeCert}, + tlsClientCertHeaders: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + NotAfter: true, + NotBefore: true, + Subject: &types.TLSCLientCertificateSubjectInfos{ + CommonName: true, + Organization: true, + Locality: true, + Province: true, + Country: true, + SerialNumber: true, + }, + Sans: true, + }, + }, + expectedHeader: url.QueryEscape(completeCertAllInfos), + }, + { + desc: "TLS with 2 certificates, with all infos", + certContents: []string{minimalCert, completeCert}, + tlsClientCertHeaders: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + NotAfter: true, + NotBefore: true, + Subject: &types.TLSCLientCertificateSubjectInfos{ + CommonName: true, + Organization: true, + Locality: true, + Province: true, + Country: true, + SerialNumber: true, + }, + Sans: true, + }, + }, + expectedHeader: url.QueryEscape(strings.Join([]string{minimalCertAllInfos, completeCertAllInfos}, ";")), + }, + } + + for _, test := range testCases { + tlsClientHeaders := NewTLSClientHeaders(&types.Frontend{PassTLSClientCert: test.tlsClientCertHeaders}) + + res := httptest.NewRecorder() + req := testhelpers.MustNewRequest(http.MethodGet, "http://example.com/foo", nil) + + if test.certContents != nil && len(test.certContents) > 0 { + req.TLS = buildTLSWith(test.certContents) + } + + tlsClientHeaders.ServeHTTP(res, req, myPassTLSClientCustomHandler) + + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + require.Equal(t, http.StatusOK, res.Code, "Http Status should be OK") + require.Equal(t, "bar", res.Body.String(), "Should be the expected body") + + if test.expectedHeader != "" { + require.Equal(t, test.expectedHeader, req.Header.Get(xForwardedTLSClientCertInfos), "The request header should contain the cleaned certificate") + } else { + require.Empty(t, req.Header.Get(xForwardedTLSClientCertInfos)) + } + require.Empty(t, res.Header().Get(xForwardedTLSClientCertInfos), "The response header should be always empty") + }) + } + +} + +func TestNewTLSClientHeadersFromStruct(t *testing.T) { + testCases := []struct { + desc string + frontend *types.Frontend + expected *TLSClientHeaders + }{ + { + desc: "Without frontend", + }, + { + desc: "frontend without the option", + frontend: &types.Frontend{}, + expected: &TLSClientHeaders{}, + }, + { + desc: "frontend with the pem set false", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + PEM: false, + }, + }, + expected: &TLSClientHeaders{PEM: false}, + }, + { + desc: "frontend with the pem set true", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + PEM: true, + }, + }, + expected: &TLSClientHeaders{PEM: true}, + }, + { + desc: "frontend with the Infos with no flag", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + NotAfter: false, + NotBefore: false, + Sans: false, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{}, + }, + }, + { + desc: "frontend with the Infos basic", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + NotAfter: true, + NotBefore: true, + Sans: true, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{ + NotBefore: true, + NotAfter: true, + Sans: true, + }, + }, + }, + { + desc: "frontend with the Infos NotAfter", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + NotAfter: true, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{ + NotAfter: true, + }, + }, + }, + { + desc: "frontend with the Infos NotBefore", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + NotBefore: true, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{ + NotBefore: true, + }, + }, + }, + { + desc: "frontend with the Infos Sans", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + Sans: true, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{ + Sans: true, + }, + }, + }, + { + desc: "frontend with the Infos Subject Organization", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + Subject: &types.TLSCLientCertificateSubjectInfos{ + Organization: true, + }, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{ + Subject: &TLSCLientCertificateSubjectInfos{ + Organization: true, + }, + }, + }, + }, + { + desc: "frontend with the Infos Subject Country", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + Subject: &types.TLSCLientCertificateSubjectInfos{ + Country: true, + }, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{ + Subject: &TLSCLientCertificateSubjectInfos{ + Country: true, + }, + }, + }, + }, + { + desc: "frontend with the Infos Subject SerialNumber", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + Subject: &types.TLSCLientCertificateSubjectInfos{ + SerialNumber: true, + }, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{ + Subject: &TLSCLientCertificateSubjectInfos{ + SerialNumber: true, + }, + }, + }, + }, + { + desc: "frontend with the Infos Subject Province", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + Subject: &types.TLSCLientCertificateSubjectInfos{ + Province: true, + }, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{ + Subject: &TLSCLientCertificateSubjectInfos{ + Province: true, + }, + }, + }, + }, + { + desc: "frontend with the Infos Subject Locality", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + Subject: &types.TLSCLientCertificateSubjectInfos{ + Locality: true, + }, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{ + Subject: &TLSCLientCertificateSubjectInfos{ + Locality: true, + }, + }, + }, + }, + { + desc: "frontend with the Infos Subject CommonName", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + Subject: &types.TLSCLientCertificateSubjectInfos{ + CommonName: true, + }, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{ + Subject: &TLSCLientCertificateSubjectInfos{ + CommonName: true, + }, + }, + }, + }, + { + desc: "frontend with the Infos NotBefore", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + Sans: true, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{ + Sans: true, + }, + }, + }, + { + desc: "frontend with the Infos all", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + NotAfter: true, + NotBefore: true, + Subject: &types.TLSCLientCertificateSubjectInfos{ + CommonName: true, + Country: true, + Locality: true, + Organization: true, + Province: true, + SerialNumber: true, + }, + Sans: true, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{ + NotBefore: true, + NotAfter: true, + Sans: true, + Subject: &TLSCLientCertificateSubjectInfos{ + Province: true, + Organization: true, + Locality: true, + Country: true, + CommonName: true, + SerialNumber: true, + }, + }}, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + require.Equal(t, test.expected, NewTLSClientHeaders(test.frontend)) + }) + } + +} diff --git a/provider/consulcatalog/config.go b/provider/consulcatalog/config.go index 94020e40f..f148a73a0 100644 --- a/provider/consulcatalog/config.go +++ b/provider/consulcatalog/config.go @@ -44,6 +44,7 @@ func (p *Provider) buildConfigurationV2(catalog []catalogUpdate) *types.Configur "getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority), "getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader), "getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert), + "getPassTLSClientCert": label.GetTLSClientCert, "getWhiteList": label.GetWhiteList, "getRedirect": label.GetRedirect, "getErrorPages": label.GetErrorPages, diff --git a/provider/consulcatalog/config_test.go b/provider/consulcatalog/config_test.go index 11d7f440c..f45a655fe 100644 --- a/provider/consulcatalog/config_test.go +++ b/provider/consulcatalog/config_test.go @@ -319,82 +319,6 @@ 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.TraefikFrontendAuthForwardTrustForwardHeader + "=true", - label.TraefikFrontendAuthForwardTLSCa + "=ca.crt", - label.TraefikFrontendAuthForwardTLSCaOptional + "=true", - label.TraefikFrontendAuthForwardTLSCert + "=server.crt", - label.TraefikFrontendAuthForwardTLSKey + "=server.key", - label.TraefikFrontendAuthForwardTLSInsecureSkipVerify + "=true", - label.TraefikFrontendAuthHeaderField + "=X-WebAuth-User", - }, - }, - 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{ - HeaderField: "X-WebAuth-User", - Forward: &types.Forward{ - Address: "auth.server", - TrustForwardHeader: true, - TLS: &types.ClientTLS{ - CA: "ca.crt", - CAOptional: true, - InsecureSkipVerify: true, - Cert: "server.crt", - Key: "server.key", - }, - }, - }, - 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{ @@ -423,6 +347,17 @@ func TestProviderBuildConfiguration(t *testing.T) { label.TraefikBackendBufferingMemRequestBodyBytes + "=2097152", label.TraefikBackendBufferingRetryExpression + "=IsNetworkError() && Attempts() <= 2", + label.TraefikFrontendPassTLSClientCertPem + "=true", + label.TraefikFrontendPassTLSClientCertInfosNotBefore + "=true", + label.TraefikFrontendPassTLSClientCertInfosNotAfter + "=true", + label.TraefikFrontendPassTLSClientCertInfosSans + "=true", + label.TraefikFrontendPassTLSClientCertInfosSubjectCommonName + "=true", + label.TraefikFrontendPassTLSClientCertInfosSubjectCountry + "=true", + label.TraefikFrontendPassTLSClientCertInfosSubjectLocality + "=true", + label.TraefikFrontendPassTLSClientCertInfosSubjectOrganization + "=true", + label.TraefikFrontendPassTLSClientCertInfosSubjectProvince + "=true", + label.TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber + "=true", + label.TraefikFrontendAuthBasic + "=test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", label.TraefikFrontendAuthBasicRemoveHeader + "=true", label.TraefikFrontendAuthBasicUsers + "=test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", @@ -539,6 +474,22 @@ func TestProviderBuildConfiguration(t *testing.T) { 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{ diff --git a/provider/docker/config.go b/provider/docker/config.go index 546e34c1e..0e0713d6b 100644 --- a/provider/docker/config.go +++ b/provider/docker/config.go @@ -42,19 +42,20 @@ func (p *Provider) buildConfigurationV2(containersInspected []dockerData) *types "getLoadBalancer": label.GetLoadBalancer, // Frontend functions - "getBackendName": getBackendName, - "getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority), - "getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader), - "getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert), - "getEntryPoints": label.GetFuncSliceString(label.TraefikFrontendEntryPoints), - "getBasicAuth": label.GetFuncSliceString(label.TraefikFrontendAuthBasic), // Deprecated - "getAuth": label.GetAuth, - "getFrontendRule": p.getFrontendRule, - "getRedirect": label.GetRedirect, - "getErrorPages": label.GetErrorPages, - "getRateLimit": label.GetRateLimit, - "getHeaders": label.GetHeaders, - "getWhiteList": label.GetWhiteList, + "getBackendName": getBackendName, + "getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority), + "getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader), + "getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert), + "getPassTLSClientCert": label.GetTLSClientCert, + "getEntryPoints": label.GetFuncSliceString(label.TraefikFrontendEntryPoints), + "getBasicAuth": label.GetFuncSliceString(label.TraefikFrontendAuthBasic), // Deprecated + "getAuth": label.GetAuth, + "getFrontendRule": p.getFrontendRule, + "getRedirect": label.GetRedirect, + "getErrorPages": label.GetErrorPages, + "getRateLimit": label.GetRateLimit, + "getHeaders": label.GetHeaders, + "getWhiteList": label.GetWhiteList, } // filter containers diff --git a/provider/docker/config_container_docker_test.go b/provider/docker/config_container_docker_test.go index 6e340c74a..89aa7d910 100644 --- a/provider/docker/config_container_docker_test.go +++ b/provider/docker/config_container_docker_test.go @@ -111,6 +111,69 @@ func TestDockerBuildConfiguration(t *testing.T) { }, }, }, + { + desc: "when pass tls client certificate", + containers: []docker.ContainerJSON{ + containerJSON( + name("test"), + labels(map[string]string{ + label.TraefikFrontendPassTLSClientCertPem: "true", + label.TraefikFrontendPassTLSClientCertInfosNotBefore: "true", + label.TraefikFrontendPassTLSClientCertInfosNotAfter: "true", + label.TraefikFrontendPassTLSClientCertInfosSans: "true", + label.TraefikFrontendPassTLSClientCertInfosSubjectCommonName: "true", + label.TraefikFrontendPassTLSClientCertInfosSubjectCountry: "true", + label.TraefikFrontendPassTLSClientCertInfosSubjectLocality: "true", + label.TraefikFrontendPassTLSClientCertInfosSubjectOrganization: "true", + label.TraefikFrontendPassTLSClientCertInfosSubjectProvince: "true", + label.TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber: "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{}, + 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, + }, + }, + }, + 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", containers: []docker.ContainerJSON{ @@ -388,6 +451,17 @@ func TestDockerBuildConfiguration(t *testing.T) { label.TraefikBackendBufferingMemRequestBodyBytes: "2097152", label.TraefikBackendBufferingRetryExpression: "IsNetworkError() && Attempts() <= 2", + label.TraefikFrontendPassTLSClientCertPem: "true", + label.TraefikFrontendPassTLSClientCertInfosNotBefore: "true", + label.TraefikFrontendPassTLSClientCertInfosNotAfter: "true", + label.TraefikFrontendPassTLSClientCertInfosSans: "true", + label.TraefikFrontendPassTLSClientCertInfosSubjectCommonName: "true", + label.TraefikFrontendPassTLSClientCertInfosSubjectCountry: "true", + label.TraefikFrontendPassTLSClientCertInfosSubjectLocality: "true", + label.TraefikFrontendPassTLSClientCertInfosSubjectOrganization: "true", + label.TraefikFrontendPassTLSClientCertInfosSubjectProvince: "true", + label.TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber: "true", + label.TraefikFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", label.TraefikFrontendAuthBasicRemoveHeader: "true", label.TraefikFrontendAuthBasicUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", @@ -475,6 +549,22 @@ func TestDockerBuildConfiguration(t *testing.T) { 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{ diff --git a/provider/docker/config_container_swarm_test.go b/provider/docker/config_container_swarm_test.go index a8cbdca4e..187e5aa41 100644 --- a/provider/docker/config_container_swarm_test.go +++ b/provider/docker/config_container_swarm_test.go @@ -93,6 +93,72 @@ func TestSwarmBuildConfiguration(t *testing.T) { }, }, }, + { + desc: "when pass tls client cert configuration", + services: []swarm.Service{ + swarmService( + serviceName("test"), + serviceLabels(map[string]string{ + label.TraefikPort: "80", + label.TraefikFrontendPassTLSClientCertPem: "true", + label.TraefikFrontendPassTLSClientCertInfosNotBefore: "true", + label.TraefikFrontendPassTLSClientCertInfosNotAfter: "true", + label.TraefikFrontendPassTLSClientCertInfosSans: "true", + label.TraefikFrontendPassTLSClientCertInfosSubjectCommonName: "true", + label.TraefikFrontendPassTLSClientCertInfosSubjectCountry: "true", + label.TraefikFrontendPassTLSClientCertInfosSubjectLocality: "true", + label.TraefikFrontendPassTLSClientCertInfosSubjectOrganization: "true", + label.TraefikFrontendPassTLSClientCertInfosSubjectProvince: "true", + label.TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber: "true", + }), + withEndpointSpec(modeVIP), + withEndpoint(virtualIP("1", "127.0.0.1/24")), + ), + }, + expectedFrontends: map[string]*types.Frontend{ + "frontend-Host-test-docker-localhost-0": { + Backend: "backend-test", + PassHostHeader: true, + EntryPoints: []string{}, + 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, + }, + }, + }, + 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, + }, + }, + }, + }, + networks: map[string]*docker.NetworkResource{ + "1": { + Name: "foo", + }, + }, + }, { desc: "when frontend basic auth configuration", services: []swarm.Service{ diff --git a/provider/docker/config_segment_test.go b/provider/docker/config_segment_test.go index 1452b5fb3..5a0340c66 100644 --- a/provider/docker/config_segment_test.go +++ b/provider/docker/config_segment_test.go @@ -65,6 +65,71 @@ func TestSegmentBuildConfiguration(t *testing.T) { }, }, }, + { + desc: "pass tls client cert", + containers: []docker.ContainerJSON{ + containerJSON( + name("foo"), + labels(map[string]string{ + "traefik.sauternes.port": "2503", + "traefik.sauternes.frontend.entryPoints": "http,https", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertPem: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosNotAfter: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosNotBefore: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSans: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectCommonName: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectCountry: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectLocality: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectOrganization: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectProvince: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectSerialNumber: "true", + }), + ports(nat.PortMap{ + "80/tcp": {}, + }), + withNetwork("bridge", ipv4("127.0.0.1")), + ), + }, + expectedFrontends: map[string]*types.Frontend{ + "frontend-sauternes-foo-sauternes": { + Backend: "backend-foo-sauternes", + PassHostHeader: true, + EntryPoints: []string{"http", "https"}, + Routes: map[string]types.Route{ + "route-frontend-sauternes-foo-sauternes": { + Rule: "Host:foo.docker.localhost", + }, + }, + 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, + }, + }, + }, + }, + }, + expectedBackends: map[string]*types.Backend{ + "backend-foo-sauternes": { + Servers: map[string]types.Server{ + "server-foo-863563a2e23c95502862016417ee95ea": { + URL: "http://127.0.0.1:2503", + Weight: label.DefaultWeight, + }, + }, + CircuitBreaker: nil, + }, + }, + }, { desc: "auth basic", containers: []docker.ContainerJSON{ @@ -286,6 +351,17 @@ func TestSegmentBuildConfiguration(t *testing.T) { label.Prefix + "sauternes." + label.SuffixProtocol: "https", label.Prefix + "sauternes." + label.SuffixWeight: "12", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertPem: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosNotAfter: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosNotBefore: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSans: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectCommonName: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectCountry: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectLocality: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectOrganization: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectProvince: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectSerialNumber: "true", + label.Prefix + "sauternes." + label.SuffixFrontendAuthBasicRemoveHeader: "true", label.Prefix + "sauternes." + label.SuffixFrontendAuthBasicUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", label.Prefix + "sauternes." + label.SuffixFrontendAuthBasicUsersFile: ".htpasswd", @@ -367,6 +443,22 @@ func TestSegmentBuildConfiguration(t *testing.T) { 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{ diff --git a/provider/ecs/config.go b/provider/ecs/config.go index c22ea3f45..fcc0ab1ee 100644 --- a/provider/ecs/config.go +++ b/provider/ecs/config.go @@ -31,20 +31,21 @@ func (p *Provider) buildConfigurationV2(instances []ecsInstance) (*types.Configu "getServers": getServers, // Frontend functions - "filterFrontends": filterFrontends, - "getFrontendRule": p.getFrontendRule, - "getFrontendName": p.getFrontendName, - "getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader), - "getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert), - "getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority), - "getBasicAuth": label.GetFuncSliceString(label.TraefikFrontendAuthBasic), // Deprecated - "getAuth": label.GetAuth, - "getEntryPoints": label.GetFuncSliceString(label.TraefikFrontendEntryPoints), - "getRedirect": label.GetRedirect, - "getErrorPages": label.GetErrorPages, - "getRateLimit": label.GetRateLimit, - "getHeaders": label.GetHeaders, - "getWhiteList": label.GetWhiteList, + "filterFrontends": filterFrontends, + "getFrontendRule": p.getFrontendRule, + "getFrontendName": p.getFrontendName, + "getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader), + "getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert), + "getPassTLSClientCert": label.GetTLSClientCert, + "getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority), + "getBasicAuth": label.GetFuncSliceString(label.TraefikFrontendAuthBasic), // Deprecated + "getAuth": label.GetAuth, + "getEntryPoints": label.GetFuncSliceString(label.TraefikFrontendEntryPoints), + "getRedirect": label.GetRedirect, + "getErrorPages": label.GetErrorPages, + "getRateLimit": label.GetRateLimit, + "getHeaders": label.GetHeaders, + "getWhiteList": label.GetWhiteList, } services := make(map[string][]ecsInstance) diff --git a/provider/ecs/config_segment_test.go b/provider/ecs/config_segment_test.go index bd6efa29a..cf08747b1 100644 --- a/provider/ecs/config_segment_test.go +++ b/provider/ecs/config_segment_test.go @@ -326,6 +326,17 @@ func TestSegmentBuildConfiguration(t *testing.T) { label.Prefix + "sauternes." + label.SuffixFrontendAuthForwardTLSInsecureSkipVerify: "true", label.Prefix + "sauternes." + label.SuffixFrontendAuthHeaderField: "X-WebAuth-User", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertPem: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosNotBefore: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosNotAfter: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSans: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectCommonName: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectCountry: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectLocality: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectOrganization: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectProvince: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectSerialNumber: "true", + label.Prefix + "sauternes." + label.SuffixFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", label.Prefix + "sauternes." + label.SuffixFrontendEntryPoints: "http,https", label.Prefix + "sauternes." + label.SuffixFrontendPassHostHeader: "true", @@ -396,6 +407,22 @@ func TestSegmentBuildConfiguration(t *testing.T) { 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{ diff --git a/provider/ecs/config_test.go b/provider/ecs/config_test.go index 0d9ff28a6..0aa7e2ce6 100644 --- a/provider/ecs/config_test.go +++ b/provider/ecs/config_test.go @@ -357,6 +357,17 @@ func TestBuildConfiguration(t *testing.T) { label.TraefikBackendBufferingMemRequestBodyBytes: aws.String("2097152"), label.TraefikBackendBufferingRetryExpression: aws.String("IsNetworkError() && Attempts() <= 2"), + label.TraefikFrontendPassTLSClientCertPem: aws.String("true"), + label.TraefikFrontendPassTLSClientCertInfosNotBefore: aws.String("true"), + label.TraefikFrontendPassTLSClientCertInfosNotAfter: aws.String("true"), + label.TraefikFrontendPassTLSClientCertInfosSans: aws.String("true"), + label.TraefikFrontendPassTLSClientCertInfosSubjectCommonName: aws.String("true"), + label.TraefikFrontendPassTLSClientCertInfosSubjectCountry: aws.String("true"), + label.TraefikFrontendPassTLSClientCertInfosSubjectLocality: aws.String("true"), + label.TraefikFrontendPassTLSClientCertInfosSubjectOrganization: aws.String("true"), + label.TraefikFrontendPassTLSClientCertInfosSubjectProvince: aws.String("true"), + label.TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber: aws.String("true"), + label.TraefikFrontendAuthBasic: aws.String("test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), label.TraefikFrontendAuthBasicRemoveHeader: aws.String("true"), label.TraefikFrontendAuthBasicUsers: aws.String("test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), @@ -490,6 +501,22 @@ func TestBuildConfiguration(t *testing.T) { 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{ diff --git a/provider/kv/keynames.go b/provider/kv/keynames.go index 27c1bdb4e..f021e1eef 100644 --- a/provider/kv/keynames.go +++ b/provider/kv/keynames.go @@ -25,14 +25,27 @@ const ( pathBackendBufferingMemRequestBodyBytes = pathBackendBuffering + "memrequestbodybytes" pathBackendBufferingRetryExpression = pathBackendBuffering + "retryexpression" - pathFrontends = "/frontends/" - pathFrontendBackend = "/backend" - pathFrontendPriority = "/priority" - pathFrontendPassHostHeaderDeprecated = "/passHostHeader" // Deprecated - pathFrontendPassHostHeader = "/passhostheader" - pathFrontendPassTLSCert = "/passtlscert" - pathFrontendWhiteListSourceRange = "/whitelist/sourcerange" - pathFrontendWhiteListUseXForwardedFor = "/whitelist/usexforwardedfor" + pathFrontends = "/frontends/" + pathFrontendBackend = "/backend" + pathFrontendPriority = "/priority" + pathFrontendPassHostHeaderDeprecated = "/passHostHeader" // Deprecated + pathFrontendPassHostHeader = "/passhostheader" + pathFrontendPassTLSClientCert = "/passTLSClientCert" + pathFrontendPassTLSClientCertPem = pathFrontendPassTLSClientCert + "/pem" + pathFrontendPassTLSClientCertInfos = pathFrontendPassTLSClientCert + "/infos" + pathFrontendPassTLSClientCertInfosNotAfter = pathFrontendPassTLSClientCertInfos + "/notAfter" + pathFrontendPassTLSClientCertInfosNotBefore = pathFrontendPassTLSClientCertInfos + "/notBefore" + pathFrontendPassTLSClientCertInfosSans = pathFrontendPassTLSClientCertInfos + "/sans" + pathFrontendPassTLSClientCertInfosSubject = pathFrontendPassTLSClientCertInfos + "/subject" + pathFrontendPassTLSClientCertInfosSubjectCommonName = pathFrontendPassTLSClientCertInfosSubject + "/commonName" + pathFrontendPassTLSClientCertInfosSubjectCountry = pathFrontendPassTLSClientCertInfosSubject + "/country" + pathFrontendPassTLSClientCertInfosSubjectLocality = pathFrontendPassTLSClientCertInfosSubject + "/locality" + pathFrontendPassTLSClientCertInfosSubjectOrganization = pathFrontendPassTLSClientCertInfosSubject + "/organization" + pathFrontendPassTLSClientCertInfosSubjectProvince = pathFrontendPassTLSClientCertInfosSubject + "/province" + pathFrontendPassTLSClientCertInfosSubjectSerialNumber = pathFrontendPassTLSClientCertInfosSubject + "/serialNumber" + pathFrontendPassTLSCert = "/passtlscert" + pathFrontendWhiteListSourceRange = "/whitelist/sourcerange" + pathFrontendWhiteListUseXForwardedFor = "/whitelist/usexforwardedfor" pathFrontendBasicAuth = "/basicauth" // Deprecated pathFrontendAuth = "/auth/" diff --git a/provider/kv/kv_config.go b/provider/kv/kv_config.go index ed7680780..bf02ce4b7 100644 --- a/provider/kv/kv_config.go +++ b/provider/kv/kv_config.go @@ -41,19 +41,20 @@ func (p *Provider) buildConfiguration() *types.Configuration { "getTLSSection": p.getTLSSection, // Frontend functions - "getBackendName": p.getFuncString(pathFrontendBackend, ""), - "getPriority": p.getFuncInt(pathFrontendPriority, label.DefaultFrontendPriority), - "getPassHostHeader": p.getPassHostHeader(), - "getPassTLSCert": p.getFuncBool(pathFrontendPassTLSCert, label.DefaultPassTLSCert), - "getEntryPoints": p.getFuncList(pathFrontendEntryPoints), - "getBasicAuth": p.getFuncList(pathFrontendBasicAuth), // Deprecated - "getAuth": p.getAuth, - "getRoutes": p.getRoutes, - "getRedirect": p.getRedirect, - "getErrorPages": p.getErrorPages, - "getRateLimit": p.getRateLimit, - "getHeaders": p.getHeaders, - "getWhiteList": p.getWhiteList, + "getBackendName": p.getFuncString(pathFrontendBackend, ""), + "getPriority": p.getFuncInt(pathFrontendPriority, label.DefaultFrontendPriority), + "getPassHostHeader": p.getPassHostHeader(), + "getPassTLSCert": p.getFuncBool(pathFrontendPassTLSCert, label.DefaultPassTLSCert), + "getPassTLSClientCert": p.getTLSClientCert, + "getEntryPoints": p.getFuncList(pathFrontendEntryPoints), + "getBasicAuth": p.getFuncList(pathFrontendBasicAuth), // Deprecated + "getAuth": p.getAuth, + "getRoutes": p.getRoutes, + "getRedirect": p.getRedirect, + "getErrorPages": p.getErrorPages, + "getRateLimit": p.getRateLimit, + "getHeaders": p.getHeaders, + "getWhiteList": p.getWhiteList, // Backend functions "getServers": p.getServers, @@ -369,6 +370,39 @@ func (p *Provider) getTLSSection(prefix string) []*tls.Configuration { return tlsSection } +// getTLSClientCert create TLS client header configuration from labels +func (p *Provider) getTLSClientCert(rootPath string) *types.TLSClientHeaders { + if !p.hasPrefix(rootPath, pathFrontendPassTLSClientCert) { + return nil + } + + tlsClientHeaders := &types.TLSClientHeaders{ + PEM: p.getBool(false, rootPath, pathFrontendPassTLSClientCertPem), + } + + if p.hasPrefix(rootPath, pathFrontendPassTLSClientCertInfos) { + infos := &types.TLSClientCertificateInfos{ + NotAfter: p.getBool(false, rootPath, pathFrontendPassTLSClientCertInfosNotAfter), + NotBefore: p.getBool(false, rootPath, pathFrontendPassTLSClientCertInfosNotBefore), + Sans: p.getBool(false, rootPath, pathFrontendPassTLSClientCertInfosSans), + } + + if p.hasPrefix(rootPath, pathFrontendPassTLSClientCertInfosSubject) { + subject := &types.TLSCLientCertificateSubjectInfos{ + CommonName: p.getBool(false, rootPath, pathFrontendPassTLSClientCertInfosSubjectCommonName), + Country: p.getBool(false, rootPath, pathFrontendPassTLSClientCertInfosSubjectCountry), + Locality: p.getBool(false, rootPath, pathFrontendPassTLSClientCertInfosSubjectLocality), + Organization: p.getBool(false, rootPath, pathFrontendPassTLSClientCertInfosSubjectOrganization), + Province: p.getBool(false, rootPath, pathFrontendPassTLSClientCertInfosSubjectProvince), + SerialNumber: p.getBool(false, rootPath, pathFrontendPassTLSClientCertInfosSubjectSerialNumber), + } + infos.Subject = subject + } + tlsClientHeaders.Infos = infos + } + return tlsClientHeaders +} + // hasDeprecatedBasicAuth check if the frontend basic auth use the deprecated configuration func (p *Provider) hasDeprecatedBasicAuth(rootPath string) bool { return len(p.getList(rootPath, pathFrontendBasicAuth)) > 0 diff --git a/provider/kv/kv_config_test.go b/provider/kv/kv_config_test.go index e1f604d24..b41e4653e 100644 --- a/provider/kv/kv_config_test.go +++ b/provider/kv/kv_config_test.go @@ -277,6 +277,18 @@ func TestProviderBuildConfiguration(t *testing.T) { withPair(pathFrontendBackend, "backend1"), withPair(pathFrontendPriority, "6"), withPair(pathFrontendPassHostHeader, "false"), + + withPair(pathFrontendPassTLSClientCertPem, "true"), + withPair(pathFrontendPassTLSClientCertInfosNotBefore, "true"), + withPair(pathFrontendPassTLSClientCertInfosNotAfter, "true"), + withPair(pathFrontendPassTLSClientCertInfosSans, "true"), + withPair(pathFrontendPassTLSClientCertInfosSubjectCommonName, "true"), + withPair(pathFrontendPassTLSClientCertInfosSubjectCountry, "true"), + withPair(pathFrontendPassTLSClientCertInfosSubjectLocality, "true"), + withPair(pathFrontendPassTLSClientCertInfosSubjectOrganization, "true"), + withPair(pathFrontendPassTLSClientCertInfosSubjectProvince, "true"), + withPair(pathFrontendPassTLSClientCertInfosSubjectSerialNumber, "true"), + withPair(pathFrontendPassTLSCert, "true"), withList(pathFrontendEntryPoints, "http", "https"), withList(pathFrontendWhiteListSourceRange, "1.1.1.1/24", "1234:abcd::42/32"), @@ -401,6 +413,22 @@ func TestProviderBuildConfiguration(t *testing.T) { SourceRange: []string{"1.1.1.1/24", "1234:abcd::42/32"}, UseXForwardedFor: true, }, + 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{ diff --git a/provider/label/names.go b/provider/label/names.go index bafa49c61..59a8c7684 100644 --- a/provider/label/names.go +++ b/provider/label/names.go @@ -2,187 +2,213 @@ package label // Traefik labels const ( - Prefix = "traefik." - SuffixBackend = "backend" - SuffixDomain = "domain" - SuffixEnable = "enable" - SuffixPort = "port" - SuffixPortName = "portName" - SuffixPortIndex = "portIndex" - SuffixProtocol = "protocol" - SuffixTags = "tags" - SuffixWeight = "weight" - SuffixBackendID = "backend.id" - SuffixBackendCircuitBreaker = "backend.circuitbreaker" - SuffixBackendCircuitBreakerExpression = "backend.circuitbreaker.expression" - SuffixBackendHealthCheckScheme = "backend.healthcheck.scheme" - SuffixBackendHealthCheckPath = "backend.healthcheck.path" - SuffixBackendHealthCheckPort = "backend.healthcheck.port" - SuffixBackendHealthCheckInterval = "backend.healthcheck.interval" - SuffixBackendHealthCheckHostname = "backend.healthcheck.hostname" - SuffixBackendHealthCheckHeaders = "backend.healthcheck.headers" - SuffixBackendLoadBalancer = "backend.loadbalancer" - SuffixBackendLoadBalancerMethod = SuffixBackendLoadBalancer + ".method" - SuffixBackendLoadBalancerSticky = SuffixBackendLoadBalancer + ".sticky" - SuffixBackendLoadBalancerStickiness = SuffixBackendLoadBalancer + ".stickiness" - SuffixBackendLoadBalancerStickinessCookieName = SuffixBackendLoadBalancer + ".stickiness.cookieName" - SuffixBackendMaxConnAmount = "backend.maxconn.amount" - SuffixBackendMaxConnExtractorFunc = "backend.maxconn.extractorfunc" - SuffixBackendBuffering = "backend.buffering" - SuffixBackendBufferingMaxRequestBodyBytes = SuffixBackendBuffering + ".maxRequestBodyBytes" - SuffixBackendBufferingMemRequestBodyBytes = SuffixBackendBuffering + ".memRequestBodyBytes" - SuffixBackendBufferingMaxResponseBodyBytes = SuffixBackendBuffering + ".maxResponseBodyBytes" - SuffixBackendBufferingMemResponseBodyBytes = SuffixBackendBuffering + ".memResponseBodyBytes" - SuffixBackendBufferingRetryExpression = SuffixBackendBuffering + ".retryExpression" - SuffixFrontend = "frontend" - SuffixFrontendAuth = SuffixFrontend + ".auth" - SuffixFrontendAuthBasic = SuffixFrontendAuth + ".basic" - SuffixFrontendAuthBasicRemoveHeader = SuffixFrontendAuthBasic + ".removeHeader" - SuffixFrontendAuthBasicUsers = SuffixFrontendAuthBasic + ".users" - SuffixFrontendAuthBasicUsersFile = SuffixFrontendAuthBasic + ".usersFile" - SuffixFrontendAuthDigest = SuffixFrontendAuth + ".digest" - SuffixFrontendAuthDigestRemoveHeader = SuffixFrontendAuthDigest + ".removeHeader" - SuffixFrontendAuthDigestUsers = SuffixFrontendAuthDigest + ".users" - SuffixFrontendAuthDigestUsersFile = SuffixFrontendAuthDigest + ".usersFile" - SuffixFrontendAuthForward = SuffixFrontendAuth + ".forward" - SuffixFrontendAuthForwardAddress = SuffixFrontendAuthForward + ".address" - SuffixFrontendAuthForwardTLS = SuffixFrontendAuthForward + ".tls" - SuffixFrontendAuthForwardTLSCa = SuffixFrontendAuthForwardTLS + ".ca" - SuffixFrontendAuthForwardTLSCaOptional = SuffixFrontendAuthForwardTLS + ".caOptional" - SuffixFrontendAuthForwardTLSCert = SuffixFrontendAuthForwardTLS + ".cert" - SuffixFrontendAuthForwardTLSInsecureSkipVerify = SuffixFrontendAuthForwardTLS + ".insecureSkipVerify" - SuffixFrontendAuthForwardTLSKey = SuffixFrontendAuthForwardTLS + ".key" - SuffixFrontendAuthForwardTrustForwardHeader = SuffixFrontendAuthForward + ".trustForwardHeader" - SuffixFrontendAuthHeaderField = SuffixFrontendAuth + ".headerField" - SuffixFrontendEntryPoints = "frontend.entryPoints" - SuffixFrontendHeaders = "frontend.headers." - SuffixFrontendRequestHeaders = SuffixFrontendHeaders + "customRequestHeaders" - SuffixFrontendResponseHeaders = SuffixFrontendHeaders + "customResponseHeaders" - SuffixFrontendHeadersAllowedHosts = SuffixFrontendHeaders + "allowedHosts" - SuffixFrontendHeadersHostsProxyHeaders = SuffixFrontendHeaders + "hostsProxyHeaders" - SuffixFrontendHeadersSSLForceHost = SuffixFrontendHeaders + "SSLForceHost" - SuffixFrontendHeadersSSLRedirect = SuffixFrontendHeaders + "SSLRedirect" - SuffixFrontendHeadersSSLTemporaryRedirect = SuffixFrontendHeaders + "SSLTemporaryRedirect" - SuffixFrontendHeadersSSLHost = SuffixFrontendHeaders + "SSLHost" - SuffixFrontendHeadersSSLProxyHeaders = SuffixFrontendHeaders + "SSLProxyHeaders" - SuffixFrontendHeadersSTSSeconds = SuffixFrontendHeaders + "STSSeconds" - SuffixFrontendHeadersSTSIncludeSubdomains = SuffixFrontendHeaders + "STSIncludeSubdomains" - SuffixFrontendHeadersSTSPreload = SuffixFrontendHeaders + "STSPreload" - SuffixFrontendHeadersForceSTSHeader = SuffixFrontendHeaders + "forceSTSHeader" - SuffixFrontendHeadersFrameDeny = SuffixFrontendHeaders + "frameDeny" - SuffixFrontendHeadersCustomFrameOptionsValue = SuffixFrontendHeaders + "customFrameOptionsValue" - SuffixFrontendHeadersContentTypeNosniff = SuffixFrontendHeaders + "contentTypeNosniff" - SuffixFrontendHeadersBrowserXSSFilter = SuffixFrontendHeaders + "browserXSSFilter" - SuffixFrontendHeadersCustomBrowserXSSValue = SuffixFrontendHeaders + "customBrowserXSSValue" - SuffixFrontendHeadersContentSecurityPolicy = SuffixFrontendHeaders + "contentSecurityPolicy" - SuffixFrontendHeadersPublicKey = SuffixFrontendHeaders + "publicKey" - SuffixFrontendHeadersReferrerPolicy = SuffixFrontendHeaders + "referrerPolicy" - SuffixFrontendHeadersIsDevelopment = SuffixFrontendHeaders + "isDevelopment" - SuffixFrontendPassHostHeader = "frontend.passHostHeader" - SuffixFrontendPassTLSCert = "frontend.passTLSCert" - SuffixFrontendPriority = "frontend.priority" - SuffixFrontendRateLimitExtractorFunc = "frontend.rateLimit.extractorFunc" - SuffixFrontendRedirectEntryPoint = "frontend.redirect.entryPoint" - SuffixFrontendRedirectRegex = "frontend.redirect.regex" - SuffixFrontendRedirectReplacement = "frontend.redirect.replacement" - SuffixFrontendRedirectPermanent = "frontend.redirect.permanent" - SuffixFrontendRule = "frontend.rule" - SuffixFrontendWhitelistSourceRange = "frontend.whitelistSourceRange" // Deprecated - SuffixFrontendWhiteList = "frontend.whiteList." - SuffixFrontendWhiteListSourceRange = SuffixFrontendWhiteList + "sourceRange" - SuffixFrontendWhiteListUseXForwardedFor = SuffixFrontendWhiteList + "useXForwardedFor" - TraefikDomain = Prefix + SuffixDomain - TraefikEnable = Prefix + SuffixEnable - TraefikPort = Prefix + SuffixPort - TraefikPortName = Prefix + SuffixPortName - TraefikPortIndex = Prefix + SuffixPortIndex - TraefikProtocol = Prefix + SuffixProtocol - TraefikTags = Prefix + SuffixTags - TraefikWeight = Prefix + SuffixWeight - TraefikBackend = Prefix + SuffixBackend - TraefikBackendID = Prefix + SuffixBackendID - TraefikBackendCircuitBreaker = Prefix + SuffixBackendCircuitBreaker - TraefikBackendCircuitBreakerExpression = Prefix + SuffixBackendCircuitBreakerExpression - TraefikBackendHealthCheckScheme = Prefix + SuffixBackendHealthCheckScheme - TraefikBackendHealthCheckPath = Prefix + SuffixBackendHealthCheckPath - TraefikBackendHealthCheckPort = Prefix + SuffixBackendHealthCheckPort - TraefikBackendHealthCheckInterval = Prefix + SuffixBackendHealthCheckInterval - TraefikBackendHealthCheckHostname = Prefix + SuffixBackendHealthCheckHostname - TraefikBackendHealthCheckHeaders = Prefix + SuffixBackendHealthCheckHeaders - TraefikBackendLoadBalancer = Prefix + SuffixBackendLoadBalancer - TraefikBackendLoadBalancerMethod = Prefix + SuffixBackendLoadBalancerMethod - TraefikBackendLoadBalancerSticky = Prefix + SuffixBackendLoadBalancerSticky - TraefikBackendLoadBalancerStickiness = Prefix + SuffixBackendLoadBalancerStickiness - TraefikBackendLoadBalancerStickinessCookieName = Prefix + SuffixBackendLoadBalancerStickinessCookieName - TraefikBackendMaxConnAmount = Prefix + SuffixBackendMaxConnAmount - TraefikBackendMaxConnExtractorFunc = Prefix + SuffixBackendMaxConnExtractorFunc - TraefikBackendBuffering = Prefix + SuffixBackendBuffering - TraefikBackendBufferingMaxRequestBodyBytes = Prefix + SuffixBackendBufferingMaxRequestBodyBytes - TraefikBackendBufferingMemRequestBodyBytes = Prefix + SuffixBackendBufferingMemRequestBodyBytes - TraefikBackendBufferingMaxResponseBodyBytes = Prefix + SuffixBackendBufferingMaxResponseBodyBytes - TraefikBackendBufferingMemResponseBodyBytes = Prefix + SuffixBackendBufferingMemResponseBodyBytes - TraefikBackendBufferingRetryExpression = Prefix + SuffixBackendBufferingRetryExpression - TraefikFrontend = Prefix + SuffixFrontend - TraefikFrontendAuth = Prefix + SuffixFrontendAuth - TraefikFrontendAuthBasic = Prefix + SuffixFrontendAuthBasic - TraefikFrontendAuthBasicRemoveHeader = Prefix + SuffixFrontendAuthBasicRemoveHeader - TraefikFrontendAuthBasicUsers = Prefix + SuffixFrontendAuthBasicUsers - TraefikFrontendAuthBasicUsersFile = Prefix + SuffixFrontendAuthBasicUsersFile - TraefikFrontendAuthDigest = Prefix + SuffixFrontendAuthDigest - TraefikFrontendAuthDigestRemoveHeader = Prefix + SuffixFrontendAuthDigestRemoveHeader - TraefikFrontendAuthDigestUsers = Prefix + SuffixFrontendAuthDigestUsers - TraefikFrontendAuthDigestUsersFile = Prefix + SuffixFrontendAuthDigestUsersFile - TraefikFrontendAuthForward = Prefix + SuffixFrontendAuthForward - TraefikFrontendAuthForwardAddress = Prefix + SuffixFrontendAuthForwardAddress - TraefikFrontendAuthForwardTLS = Prefix + SuffixFrontendAuthForwardTLS - TraefikFrontendAuthForwardTLSCa = Prefix + SuffixFrontendAuthForwardTLSCa - TraefikFrontendAuthForwardTLSCaOptional = Prefix + SuffixFrontendAuthForwardTLSCaOptional - TraefikFrontendAuthForwardTLSCert = Prefix + SuffixFrontendAuthForwardTLSCert - TraefikFrontendAuthForwardTLSInsecureSkipVerify = Prefix + SuffixFrontendAuthForwardTLSInsecureSkipVerify - TraefikFrontendAuthForwardTLSKey = Prefix + SuffixFrontendAuthForwardTLSKey - TraefikFrontendAuthForwardTrustForwardHeader = Prefix + SuffixFrontendAuthForwardTrustForwardHeader - TraefikFrontendAuthHeaderField = Prefix + SuffixFrontendAuthHeaderField - TraefikFrontendEntryPoints = Prefix + SuffixFrontendEntryPoints - TraefikFrontendPassHostHeader = Prefix + SuffixFrontendPassHostHeader - TraefikFrontendPassTLSCert = Prefix + SuffixFrontendPassTLSCert - TraefikFrontendPriority = Prefix + SuffixFrontendPriority - TraefikFrontendRateLimitExtractorFunc = Prefix + SuffixFrontendRateLimitExtractorFunc - TraefikFrontendRedirectEntryPoint = Prefix + SuffixFrontendRedirectEntryPoint - TraefikFrontendRedirectRegex = Prefix + SuffixFrontendRedirectRegex - TraefikFrontendRedirectReplacement = Prefix + SuffixFrontendRedirectReplacement - TraefikFrontendRedirectPermanent = Prefix + SuffixFrontendRedirectPermanent - TraefikFrontendRule = Prefix + SuffixFrontendRule - TraefikFrontendWhitelistSourceRange = Prefix + SuffixFrontendWhitelistSourceRange // Deprecated - TraefikFrontendWhiteListSourceRange = Prefix + SuffixFrontendWhiteListSourceRange - TraefikFrontendWhiteListUseXForwardedFor = Prefix + SuffixFrontendWhiteListUseXForwardedFor - TraefikFrontendRequestHeaders = Prefix + SuffixFrontendRequestHeaders - TraefikFrontendResponseHeaders = Prefix + SuffixFrontendResponseHeaders - TraefikFrontendAllowedHosts = Prefix + SuffixFrontendHeadersAllowedHosts - TraefikFrontendHostsProxyHeaders = Prefix + SuffixFrontendHeadersHostsProxyHeaders - TraefikFrontendSSLForceHost = Prefix + SuffixFrontendHeadersSSLForceHost - TraefikFrontendSSLRedirect = Prefix + SuffixFrontendHeadersSSLRedirect - TraefikFrontendSSLTemporaryRedirect = Prefix + SuffixFrontendHeadersSSLTemporaryRedirect - TraefikFrontendSSLHost = Prefix + SuffixFrontendHeadersSSLHost - TraefikFrontendSSLProxyHeaders = Prefix + SuffixFrontendHeadersSSLProxyHeaders - TraefikFrontendSTSSeconds = Prefix + SuffixFrontendHeadersSTSSeconds - TraefikFrontendSTSIncludeSubdomains = Prefix + SuffixFrontendHeadersSTSIncludeSubdomains - TraefikFrontendSTSPreload = Prefix + SuffixFrontendHeadersSTSPreload - TraefikFrontendForceSTSHeader = Prefix + SuffixFrontendHeadersForceSTSHeader - TraefikFrontendFrameDeny = Prefix + SuffixFrontendHeadersFrameDeny - TraefikFrontendCustomFrameOptionsValue = Prefix + SuffixFrontendHeadersCustomFrameOptionsValue - TraefikFrontendContentTypeNosniff = Prefix + SuffixFrontendHeadersContentTypeNosniff - TraefikFrontendBrowserXSSFilter = Prefix + SuffixFrontendHeadersBrowserXSSFilter - TraefikFrontendCustomBrowserXSSValue = Prefix + SuffixFrontendHeadersCustomBrowserXSSValue - TraefikFrontendContentSecurityPolicy = Prefix + SuffixFrontendHeadersContentSecurityPolicy - TraefikFrontendPublicKey = Prefix + SuffixFrontendHeadersPublicKey - TraefikFrontendReferrerPolicy = Prefix + SuffixFrontendHeadersReferrerPolicy - TraefikFrontendIsDevelopment = Prefix + SuffixFrontendHeadersIsDevelopment - BaseFrontendErrorPage = "frontend.errors." - SuffixErrorPageBackend = "backend" - SuffixErrorPageQuery = "query" - SuffixErrorPageStatus = "status" - BaseFrontendRateLimit = "frontend.rateLimit.rateSet." - SuffixRateLimitPeriod = "period" - SuffixRateLimitAverage = "average" - SuffixRateLimitBurst = "burst" + Prefix = "traefik." + SuffixBackend = "backend" + SuffixDomain = "domain" + SuffixEnable = "enable" + SuffixPort = "port" + SuffixPortName = "portName" + SuffixPortIndex = "portIndex" + SuffixProtocol = "protocol" + SuffixTags = "tags" + SuffixWeight = "weight" + SuffixBackendID = "backend.id" + SuffixBackendCircuitBreaker = "backend.circuitbreaker" + SuffixBackendCircuitBreakerExpression = "backend.circuitbreaker.expression" + SuffixBackendHealthCheckScheme = "backend.healthcheck.scheme" + SuffixBackendHealthCheckPath = "backend.healthcheck.path" + SuffixBackendHealthCheckPort = "backend.healthcheck.port" + SuffixBackendHealthCheckInterval = "backend.healthcheck.interval" + SuffixBackendHealthCheckHostname = "backend.healthcheck.hostname" + SuffixBackendHealthCheckHeaders = "backend.healthcheck.headers" + SuffixBackendLoadBalancer = "backend.loadbalancer" + SuffixBackendLoadBalancerMethod = SuffixBackendLoadBalancer + ".method" + SuffixBackendLoadBalancerSticky = SuffixBackendLoadBalancer + ".sticky" + SuffixBackendLoadBalancerStickiness = SuffixBackendLoadBalancer + ".stickiness" + SuffixBackendLoadBalancerStickinessCookieName = SuffixBackendLoadBalancer + ".stickiness.cookieName" + SuffixBackendMaxConnAmount = "backend.maxconn.amount" + SuffixBackendMaxConnExtractorFunc = "backend.maxconn.extractorfunc" + SuffixBackendBuffering = "backend.buffering" + SuffixBackendBufferingMaxRequestBodyBytes = SuffixBackendBuffering + ".maxRequestBodyBytes" + SuffixBackendBufferingMemRequestBodyBytes = SuffixBackendBuffering + ".memRequestBodyBytes" + SuffixBackendBufferingMaxResponseBodyBytes = SuffixBackendBuffering + ".maxResponseBodyBytes" + SuffixBackendBufferingMemResponseBodyBytes = SuffixBackendBuffering + ".memResponseBodyBytes" + SuffixBackendBufferingRetryExpression = SuffixBackendBuffering + ".retryExpression" + SuffixFrontend = "frontend" + SuffixFrontendAuth = SuffixFrontend + ".auth" + SuffixFrontendAuthBasic = SuffixFrontendAuth + ".basic" + SuffixFrontendAuthBasicRemoveHeader = SuffixFrontendAuthBasic + ".removeHeader" + SuffixFrontendAuthBasicUsers = SuffixFrontendAuthBasic + ".users" + SuffixFrontendAuthBasicUsersFile = SuffixFrontendAuthBasic + ".usersFile" + SuffixFrontendAuthDigest = SuffixFrontendAuth + ".digest" + SuffixFrontendAuthDigestRemoveHeader = SuffixFrontendAuthDigest + ".removeHeader" + SuffixFrontendAuthDigestUsers = SuffixFrontendAuthDigest + ".users" + SuffixFrontendAuthDigestUsersFile = SuffixFrontendAuthDigest + ".usersFile" + SuffixFrontendAuthForward = SuffixFrontendAuth + ".forward" + SuffixFrontendAuthForwardAddress = SuffixFrontendAuthForward + ".address" + SuffixFrontendAuthForwardTLS = SuffixFrontendAuthForward + ".tls" + SuffixFrontendAuthForwardTLSCa = SuffixFrontendAuthForwardTLS + ".ca" + SuffixFrontendAuthForwardTLSCaOptional = SuffixFrontendAuthForwardTLS + ".caOptional" + SuffixFrontendAuthForwardTLSCert = SuffixFrontendAuthForwardTLS + ".cert" + SuffixFrontendAuthForwardTLSInsecureSkipVerify = SuffixFrontendAuthForwardTLS + ".insecureSkipVerify" + SuffixFrontendAuthForwardTLSKey = SuffixFrontendAuthForwardTLS + ".key" + SuffixFrontendAuthForwardTrustForwardHeader = SuffixFrontendAuthForward + ".trustForwardHeader" + SuffixFrontendAuthHeaderField = SuffixFrontendAuth + ".headerField" + SuffixFrontendEntryPoints = "frontend.entryPoints" + SuffixFrontendHeaders = "frontend.headers." + SuffixFrontendRequestHeaders = SuffixFrontendHeaders + "customRequestHeaders" + SuffixFrontendResponseHeaders = SuffixFrontendHeaders + "customResponseHeaders" + SuffixFrontendHeadersAllowedHosts = SuffixFrontendHeaders + "allowedHosts" + SuffixFrontendHeadersHostsProxyHeaders = SuffixFrontendHeaders + "hostsProxyHeaders" + SuffixFrontendHeadersSSLForceHost = SuffixFrontendHeaders + "SSLForceHost" + SuffixFrontendHeadersSSLRedirect = SuffixFrontendHeaders + "SSLRedirect" + SuffixFrontendHeadersSSLTemporaryRedirect = SuffixFrontendHeaders + "SSLTemporaryRedirect" + SuffixFrontendHeadersSSLHost = SuffixFrontendHeaders + "SSLHost" + SuffixFrontendHeadersSSLProxyHeaders = SuffixFrontendHeaders + "SSLProxyHeaders" + SuffixFrontendHeadersSTSSeconds = SuffixFrontendHeaders + "STSSeconds" + SuffixFrontendHeadersSTSIncludeSubdomains = SuffixFrontendHeaders + "STSIncludeSubdomains" + SuffixFrontendHeadersSTSPreload = SuffixFrontendHeaders + "STSPreload" + SuffixFrontendHeadersForceSTSHeader = SuffixFrontendHeaders + "forceSTSHeader" + SuffixFrontendHeadersFrameDeny = SuffixFrontendHeaders + "frameDeny" + SuffixFrontendHeadersCustomFrameOptionsValue = SuffixFrontendHeaders + "customFrameOptionsValue" + SuffixFrontendHeadersContentTypeNosniff = SuffixFrontendHeaders + "contentTypeNosniff" + SuffixFrontendHeadersBrowserXSSFilter = SuffixFrontendHeaders + "browserXSSFilter" + SuffixFrontendHeadersCustomBrowserXSSValue = SuffixFrontendHeaders + "customBrowserXSSValue" + SuffixFrontendHeadersContentSecurityPolicy = SuffixFrontendHeaders + "contentSecurityPolicy" + SuffixFrontendHeadersPublicKey = SuffixFrontendHeaders + "publicKey" + SuffixFrontendHeadersReferrerPolicy = SuffixFrontendHeaders + "referrerPolicy" + SuffixFrontendHeadersIsDevelopment = SuffixFrontendHeaders + "isDevelopment" + SuffixFrontendPassHostHeader = "frontend.passHostHeader" + SuffixFrontendPassTLSClientCert = "frontend.passTLSClientCert" + SuffixFrontendPassTLSClientCertPem = SuffixFrontendPassTLSClientCert + ".pem" + SuffixFrontendPassTLSClientCertInfos = SuffixFrontendPassTLSClientCert + ".infos" + SuffixFrontendPassTLSClientCertInfosNotAfter = SuffixFrontendPassTLSClientCertInfos + ".notAfter" + SuffixFrontendPassTLSClientCertInfosNotBefore = SuffixFrontendPassTLSClientCertInfos + ".notBefore" + SuffixFrontendPassTLSClientCertInfosSans = SuffixFrontendPassTLSClientCertInfos + ".sans" + SuffixFrontendPassTLSClientCertInfosSubject = SuffixFrontendPassTLSClientCertInfos + ".subject" + SuffixFrontendPassTLSClientCertInfosSubjectCommonName = SuffixFrontendPassTLSClientCertInfosSubject + ".commonName" + SuffixFrontendPassTLSClientCertInfosSubjectCountry = SuffixFrontendPassTLSClientCertInfosSubject + ".country" + SuffixFrontendPassTLSClientCertInfosSubjectLocality = SuffixFrontendPassTLSClientCertInfosSubject + ".locality" + SuffixFrontendPassTLSClientCertInfosSubjectOrganization = SuffixFrontendPassTLSClientCertInfosSubject + ".organization" + SuffixFrontendPassTLSClientCertInfosSubjectProvince = SuffixFrontendPassTLSClientCertInfosSubject + ".province" + SuffixFrontendPassTLSClientCertInfosSubjectSerialNumber = SuffixFrontendPassTLSClientCertInfosSubject + ".serialNumber" + SuffixFrontendPassTLSCert = "frontend.passTLSCert" // Deprecated + SuffixFrontendPriority = "frontend.priority" + SuffixFrontendRateLimitExtractorFunc = "frontend.rateLimit.extractorFunc" + SuffixFrontendRedirectEntryPoint = "frontend.redirect.entryPoint" + SuffixFrontendRedirectRegex = "frontend.redirect.regex" + SuffixFrontendRedirectReplacement = "frontend.redirect.replacement" + SuffixFrontendRedirectPermanent = "frontend.redirect.permanent" + SuffixFrontendRule = "frontend.rule" + SuffixFrontendWhitelistSourceRange = "frontend.whitelistSourceRange" // Deprecated + SuffixFrontendWhiteList = "frontend.whiteList." + SuffixFrontendWhiteListSourceRange = SuffixFrontendWhiteList + "sourceRange" + SuffixFrontendWhiteListUseXForwardedFor = SuffixFrontendWhiteList + "useXForwardedFor" + TraefikDomain = Prefix + SuffixDomain + TraefikEnable = Prefix + SuffixEnable + TraefikPort = Prefix + SuffixPort + TraefikPortName = Prefix + SuffixPortName + TraefikPortIndex = Prefix + SuffixPortIndex + TraefikProtocol = Prefix + SuffixProtocol + TraefikTags = Prefix + SuffixTags + TraefikWeight = Prefix + SuffixWeight + TraefikBackend = Prefix + SuffixBackend + TraefikBackendID = Prefix + SuffixBackendID + TraefikBackendCircuitBreaker = Prefix + SuffixBackendCircuitBreaker + TraefikBackendCircuitBreakerExpression = Prefix + SuffixBackendCircuitBreakerExpression + TraefikBackendHealthCheckScheme = Prefix + SuffixBackendHealthCheckScheme + TraefikBackendHealthCheckPath = Prefix + SuffixBackendHealthCheckPath + TraefikBackendHealthCheckPort = Prefix + SuffixBackendHealthCheckPort + TraefikBackendHealthCheckInterval = Prefix + SuffixBackendHealthCheckInterval + TraefikBackendHealthCheckHostname = Prefix + SuffixBackendHealthCheckHostname + TraefikBackendHealthCheckHeaders = Prefix + SuffixBackendHealthCheckHeaders + TraefikBackendLoadBalancer = Prefix + SuffixBackendLoadBalancer + TraefikBackendLoadBalancerMethod = Prefix + SuffixBackendLoadBalancerMethod + TraefikBackendLoadBalancerSticky = Prefix + SuffixBackendLoadBalancerSticky + TraefikBackendLoadBalancerStickiness = Prefix + SuffixBackendLoadBalancerStickiness + TraefikBackendLoadBalancerStickinessCookieName = Prefix + SuffixBackendLoadBalancerStickinessCookieName + TraefikBackendMaxConnAmount = Prefix + SuffixBackendMaxConnAmount + TraefikBackendMaxConnExtractorFunc = Prefix + SuffixBackendMaxConnExtractorFunc + TraefikBackendBuffering = Prefix + SuffixBackendBuffering + TraefikBackendBufferingMaxRequestBodyBytes = Prefix + SuffixBackendBufferingMaxRequestBodyBytes + TraefikBackendBufferingMemRequestBodyBytes = Prefix + SuffixBackendBufferingMemRequestBodyBytes + TraefikBackendBufferingMaxResponseBodyBytes = Prefix + SuffixBackendBufferingMaxResponseBodyBytes + TraefikBackendBufferingMemResponseBodyBytes = Prefix + SuffixBackendBufferingMemResponseBodyBytes + TraefikBackendBufferingRetryExpression = Prefix + SuffixBackendBufferingRetryExpression + TraefikFrontend = Prefix + SuffixFrontend + TraefikFrontendAuth = Prefix + SuffixFrontendAuth + TraefikFrontendAuthBasic = Prefix + SuffixFrontendAuthBasic + TraefikFrontendAuthBasicRemoveHeader = Prefix + SuffixFrontendAuthBasicRemoveHeader + TraefikFrontendAuthBasicUsers = Prefix + SuffixFrontendAuthBasicUsers + TraefikFrontendAuthBasicUsersFile = Prefix + SuffixFrontendAuthBasicUsersFile + TraefikFrontendAuthDigest = Prefix + SuffixFrontendAuthDigest + TraefikFrontendAuthDigestRemoveHeader = Prefix + SuffixFrontendAuthDigestRemoveHeader + TraefikFrontendAuthDigestUsers = Prefix + SuffixFrontendAuthDigestUsers + TraefikFrontendAuthDigestUsersFile = Prefix + SuffixFrontendAuthDigestUsersFile + TraefikFrontendAuthForward = Prefix + SuffixFrontendAuthForward + TraefikFrontendAuthForwardAddress = Prefix + SuffixFrontendAuthForwardAddress + TraefikFrontendAuthForwardTLS = Prefix + SuffixFrontendAuthForwardTLS + TraefikFrontendAuthForwardTLSCa = Prefix + SuffixFrontendAuthForwardTLSCa + TraefikFrontendAuthForwardTLSCaOptional = Prefix + SuffixFrontendAuthForwardTLSCaOptional + TraefikFrontendAuthForwardTLSCert = Prefix + SuffixFrontendAuthForwardTLSCert + TraefikFrontendAuthForwardTLSInsecureSkipVerify = Prefix + SuffixFrontendAuthForwardTLSInsecureSkipVerify + TraefikFrontendAuthForwardTLSKey = Prefix + SuffixFrontendAuthForwardTLSKey + TraefikFrontendAuthForwardTrustForwardHeader = Prefix + SuffixFrontendAuthForwardTrustForwardHeader + TraefikFrontendAuthHeaderField = Prefix + SuffixFrontendAuthHeaderField + TraefikFrontendEntryPoints = Prefix + SuffixFrontendEntryPoints + TraefikFrontendPassHostHeader = Prefix + SuffixFrontendPassHostHeader + TraefikFrontendPassTLSClientCert = Prefix + SuffixFrontendPassTLSClientCert + TraefikFrontendPassTLSClientCertPem = Prefix + SuffixFrontendPassTLSClientCertPem + TraefikFrontendPassTLSClientCertInfos = Prefix + SuffixFrontendPassTLSClientCertInfos + TraefikFrontendPassTLSClientCertInfosNotAfter = Prefix + SuffixFrontendPassTLSClientCertInfosNotAfter + TraefikFrontendPassTLSClientCertInfosNotBefore = Prefix + SuffixFrontendPassTLSClientCertInfosNotBefore + TraefikFrontendPassTLSClientCertInfosSans = Prefix + SuffixFrontendPassTLSClientCertInfosSans + TraefikFrontendPassTLSClientCertInfosSubject = Prefix + SuffixFrontendPassTLSClientCertInfosSubject + TraefikFrontendPassTLSClientCertInfosSubjectCommonName = Prefix + SuffixFrontendPassTLSClientCertInfosSubjectCommonName + TraefikFrontendPassTLSClientCertInfosSubjectCountry = Prefix + SuffixFrontendPassTLSClientCertInfosSubjectCountry + TraefikFrontendPassTLSClientCertInfosSubjectLocality = Prefix + SuffixFrontendPassTLSClientCertInfosSubjectLocality + TraefikFrontendPassTLSClientCertInfosSubjectOrganization = Prefix + SuffixFrontendPassTLSClientCertInfosSubjectOrganization + TraefikFrontendPassTLSClientCertInfosSubjectProvince = Prefix + SuffixFrontendPassTLSClientCertInfosSubjectProvince + TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber = Prefix + SuffixFrontendPassTLSClientCertInfosSubjectSerialNumber + TraefikFrontendPassTLSCert = Prefix + SuffixFrontendPassTLSCert // Deprecated + TraefikFrontendPriority = Prefix + SuffixFrontendPriority + TraefikFrontendRateLimitExtractorFunc = Prefix + SuffixFrontendRateLimitExtractorFunc + TraefikFrontendRedirectEntryPoint = Prefix + SuffixFrontendRedirectEntryPoint + TraefikFrontendRedirectRegex = Prefix + SuffixFrontendRedirectRegex + TraefikFrontendRedirectReplacement = Prefix + SuffixFrontendRedirectReplacement + TraefikFrontendRedirectPermanent = Prefix + SuffixFrontendRedirectPermanent + TraefikFrontendRule = Prefix + SuffixFrontendRule + TraefikFrontendWhitelistSourceRange = Prefix + SuffixFrontendWhitelistSourceRange // Deprecated + TraefikFrontendWhiteListSourceRange = Prefix + SuffixFrontendWhiteListSourceRange + TraefikFrontendWhiteListUseXForwardedFor = Prefix + SuffixFrontendWhiteListUseXForwardedFor + TraefikFrontendRequestHeaders = Prefix + SuffixFrontendRequestHeaders + TraefikFrontendResponseHeaders = Prefix + SuffixFrontendResponseHeaders + TraefikFrontendAllowedHosts = Prefix + SuffixFrontendHeadersAllowedHosts + TraefikFrontendHostsProxyHeaders = Prefix + SuffixFrontendHeadersHostsProxyHeaders + TraefikFrontendSSLForceHost = Prefix + SuffixFrontendHeadersSSLForceHost + TraefikFrontendSSLRedirect = Prefix + SuffixFrontendHeadersSSLRedirect + TraefikFrontendSSLTemporaryRedirect = Prefix + SuffixFrontendHeadersSSLTemporaryRedirect + TraefikFrontendSSLHost = Prefix + SuffixFrontendHeadersSSLHost + TraefikFrontendSSLProxyHeaders = Prefix + SuffixFrontendHeadersSSLProxyHeaders + TraefikFrontendSTSSeconds = Prefix + SuffixFrontendHeadersSTSSeconds + TraefikFrontendSTSIncludeSubdomains = Prefix + SuffixFrontendHeadersSTSIncludeSubdomains + TraefikFrontendSTSPreload = Prefix + SuffixFrontendHeadersSTSPreload + TraefikFrontendForceSTSHeader = Prefix + SuffixFrontendHeadersForceSTSHeader + TraefikFrontendFrameDeny = Prefix + SuffixFrontendHeadersFrameDeny + TraefikFrontendCustomFrameOptionsValue = Prefix + SuffixFrontendHeadersCustomFrameOptionsValue + TraefikFrontendContentTypeNosniff = Prefix + SuffixFrontendHeadersContentTypeNosniff + TraefikFrontendBrowserXSSFilter = Prefix + SuffixFrontendHeadersBrowserXSSFilter + TraefikFrontendCustomBrowserXSSValue = Prefix + SuffixFrontendHeadersCustomBrowserXSSValue + TraefikFrontendContentSecurityPolicy = Prefix + SuffixFrontendHeadersContentSecurityPolicy + TraefikFrontendPublicKey = Prefix + SuffixFrontendHeadersPublicKey + TraefikFrontendReferrerPolicy = Prefix + SuffixFrontendHeadersReferrerPolicy + TraefikFrontendIsDevelopment = Prefix + SuffixFrontendHeadersIsDevelopment + BaseFrontendErrorPage = "frontend.errors." + SuffixErrorPageBackend = "backend" + SuffixErrorPageQuery = "query" + SuffixErrorPageStatus = "status" + BaseFrontendRateLimit = "frontend.rateLimit.rateSet." + SuffixRateLimitPeriod = "period" + SuffixRateLimitAverage = "average" + SuffixRateLimitBurst = "burst" ) diff --git a/provider/label/partial.go b/provider/label/partial.go index aec443edd..39289cc3b 100644 --- a/provider/label/partial.go +++ b/provider/label/partial.go @@ -60,6 +60,39 @@ func GetRedirect(labels map[string]string) *types.Redirect { return nil } +// GetTLSClientCert create TLS client header configuration from labels +func GetTLSClientCert(labels map[string]string) *types.TLSClientHeaders { + if !HasPrefix(labels, TraefikFrontendPassTLSClientCert) { + return nil + } + + tlsClientHeaders := &types.TLSClientHeaders{ + PEM: GetBoolValue(labels, TraefikFrontendPassTLSClientCertPem, false), + } + + if HasPrefix(labels, TraefikFrontendPassTLSClientCertInfos) { + infos := &types.TLSClientCertificateInfos{ + NotAfter: GetBoolValue(labels, TraefikFrontendPassTLSClientCertInfosNotAfter, false), + NotBefore: GetBoolValue(labels, TraefikFrontendPassTLSClientCertInfosNotBefore, false), + Sans: GetBoolValue(labels, TraefikFrontendPassTLSClientCertInfosSans, false), + } + + if HasPrefix(labels, TraefikFrontendPassTLSClientCertInfosSubject) { + subject := &types.TLSCLientCertificateSubjectInfos{ + CommonName: GetBoolValue(labels, TraefikFrontendPassTLSClientCertInfosSubjectCommonName, false), + Country: GetBoolValue(labels, TraefikFrontendPassTLSClientCertInfosSubjectCountry, false), + Locality: GetBoolValue(labels, TraefikFrontendPassTLSClientCertInfosSubjectLocality, false), + Organization: GetBoolValue(labels, TraefikFrontendPassTLSClientCertInfosSubjectOrganization, false), + Province: GetBoolValue(labels, TraefikFrontendPassTLSClientCertInfosSubjectProvince, false), + SerialNumber: GetBoolValue(labels, TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber, false), + } + infos.Subject = subject + } + tlsClientHeaders.Infos = infos + } + return tlsClientHeaders +} + // GetAuth Create auth from labels func GetAuth(labels map[string]string) *types.Auth { if !HasPrefix(labels, TraefikFrontendAuth) { diff --git a/provider/label/partial_test.go b/provider/label/partial_test.go index 796acfe15..5ffa6415c 100644 --- a/provider/label/partial_test.go +++ b/provider/label/partial_test.go @@ -798,3 +798,178 @@ func TestGetAuth(t *testing.T) { }) } } +func TestGetPassTLSClientCert(t *testing.T) { + testCases := []struct { + desc string + labels map[string]string + expected *types.TLSClientHeaders + }{ + { + desc: "should return nil when no tags", + labels: map[string]string{}, + expected: nil, + }, + { + desc: "should return tlsClientHeaders with true pem flag", + labels: map[string]string{ + TraefikFrontendPassTLSClientCertPem: "true", + }, + expected: &types.TLSClientHeaders{ + PEM: true, + }, + }, + { + desc: "should return tlsClientHeaders with infos and NotAfter true", + labels: map[string]string{ + TraefikFrontendPassTLSClientCertInfosNotAfter: "true", + }, + expected: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + NotAfter: true, + }, + }, + }, + { + desc: "should return tlsClientHeaders with infos and NotBefore true", + labels: map[string]string{ + TraefikFrontendPassTLSClientCertInfosNotBefore: "true", + }, + expected: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + NotBefore: true, + }, + }, + }, + { + desc: "should return tlsClientHeaders with infos and sans true", + labels: map[string]string{ + TraefikFrontendPassTLSClientCertInfosSans: "true", + }, + expected: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + Sans: true, + }, + }, + }, + { + desc: "should return tlsClientHeaders with infos and subject with commonName true", + labels: map[string]string{ + TraefikFrontendPassTLSClientCertInfosSubjectCommonName: "true", + }, + expected: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + Subject: &types.TLSCLientCertificateSubjectInfos{ + CommonName: true, + }, + }, + }, + }, + { + desc: "should return tlsClientHeaders with infos and subject with country true", + labels: map[string]string{ + TraefikFrontendPassTLSClientCertInfosSubjectCountry: "true", + }, + expected: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + Subject: &types.TLSCLientCertificateSubjectInfos{ + Country: true, + }, + }, + }, + }, + { + desc: "should return tlsClientHeaders with infos and subject with locality true", + labels: map[string]string{ + TraefikFrontendPassTLSClientCertInfosSubjectLocality: "true", + }, + expected: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + Subject: &types.TLSCLientCertificateSubjectInfos{ + Locality: true, + }, + }, + }, + }, + { + desc: "should return tlsClientHeaders with infos and subject with organization true", + labels: map[string]string{ + TraefikFrontendPassTLSClientCertInfosSubjectOrganization: "true", + }, + expected: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + Subject: &types.TLSCLientCertificateSubjectInfos{ + Organization: true, + }, + }, + }, + }, + { + desc: "should return tlsClientHeaders with infos and subject with province true", + labels: map[string]string{ + TraefikFrontendPassTLSClientCertInfosSubjectProvince: "true", + }, + expected: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + Subject: &types.TLSCLientCertificateSubjectInfos{ + Province: true, + }, + }, + }, + }, + { + desc: "should return tlsClientHeaders with infos and subject with serialNumber true", + labels: map[string]string{ + TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber: "true", + }, + expected: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + Subject: &types.TLSCLientCertificateSubjectInfos{ + SerialNumber: true, + }, + }, + }, + }, + { + desc: "should return tlsClientHeaders with all infos", + labels: map[string]string{ + TraefikFrontendPassTLSClientCertPem: "true", + TraefikFrontendPassTLSClientCertInfosNotAfter: "true", + TraefikFrontendPassTLSClientCertInfosNotBefore: "true", + TraefikFrontendPassTLSClientCertInfosSans: "true", + TraefikFrontendPassTLSClientCertInfosSubjectCommonName: "true", + TraefikFrontendPassTLSClientCertInfosSubjectCountry: "true", + TraefikFrontendPassTLSClientCertInfosSubjectLocality: "true", + TraefikFrontendPassTLSClientCertInfosSubjectOrganization: "true", + TraefikFrontendPassTLSClientCertInfosSubjectProvince: "true", + TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber: "true", + }, + expected: &types.TLSClientHeaders{ + PEM: true, + Infos: &types.TLSClientCertificateInfos{ + Sans: true, + NotBefore: true, + NotAfter: true, + Subject: &types.TLSCLientCertificateSubjectInfos{ + Province: true, + Organization: true, + Locality: true, + Country: true, + CommonName: true, + SerialNumber: true, + }, + }, + }, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + result := GetTLSClientCert(test.labels) + + assert.Equal(t, test.expected, result) + }) + } +} diff --git a/provider/marathon/config.go b/provider/marathon/config.go index 8b0f61af9..0194ba4c6 100644 --- a/provider/marathon/config.go +++ b/provider/marathon/config.go @@ -46,6 +46,7 @@ func (p *Provider) buildConfigurationV2(applications *marathon.Applications) *ty "getFrontendName": p.getFrontendName, "getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader), "getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert), + "getPassTLSClientCert": label.GetTLSClientCert, "getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority), "getEntryPoints": label.GetFuncSliceString(label.TraefikFrontendEntryPoints), "getBasicAuth": label.GetFuncSliceString(label.TraefikFrontendAuthBasic), // Deprecated diff --git a/provider/marathon/config_test.go b/provider/marathon/config_test.go index f6a5d3c9a..e7471c649 100644 --- a/provider/marathon/config_test.go +++ b/provider/marathon/config_test.go @@ -374,6 +374,17 @@ func TestBuildConfiguration(t *testing.T) { withLabel(label.TraefikBackendBufferingMemRequestBodyBytes, "2097152"), withLabel(label.TraefikBackendBufferingRetryExpression, "IsNetworkError() && Attempts() <= 2"), + withLabel(label.TraefikFrontendPassTLSClientCertPem, "true"), + withLabel(label.TraefikFrontendPassTLSClientCertInfosNotBefore, "true"), + withLabel(label.TraefikFrontendPassTLSClientCertInfosNotAfter, "true"), + withLabel(label.TraefikFrontendPassTLSClientCertInfosSans, "true"), + withLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectCommonName, "true"), + withLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectCountry, "true"), + withLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectLocality, "true"), + withLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectOrganization, "true"), + withLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectProvince, "true"), + withLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber, "true"), + withLabel(label.TraefikFrontendAuthBasic, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), withLabel(label.TraefikFrontendAuthBasicRemoveHeader, "true"), withLabel(label.TraefikFrontendAuthBasicUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), @@ -401,7 +412,6 @@ func TestBuildConfiguration(t *testing.T) { withLabel(label.TraefikFrontendRule, "Host:traefik.io"), withLabel(label.TraefikFrontendWhiteListSourceRange, "10.10.10.10"), withLabel(label.TraefikFrontendWhiteListUseXForwardedFor, "true"), - withLabel(label.TraefikFrontendRequestHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8"), withLabel(label.TraefikFrontendResponseHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8"), withLabel(label.TraefikFrontendSSLProxyHeaders, "Access-Control-Allow-Methods:POST,GET,OPTIONS || Content-type: application/json; charset=utf-8"), @@ -455,6 +465,22 @@ func TestBuildConfiguration(t *testing.T) { 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{ @@ -766,6 +792,17 @@ func TestBuildConfigurationSegments(t *testing.T) { 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"), @@ -847,6 +884,22 @@ func TestBuildConfigurationSegments(t *testing.T) { 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{ diff --git a/provider/mesos/config.go b/provider/mesos/config.go index 7a379b3a1..47a5ef9ce 100644 --- a/provider/mesos/config.go +++ b/provider/mesos/config.go @@ -48,6 +48,7 @@ func (p *Provider) buildConfigurationV2(tasks []state.Task) *types.Configuration "getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority), "getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader), "getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert), + "getPassTLSClientCert": label.GetTLSClientCert, "getFrontendRule": p.getFrontendRule, "getRedirect": label.GetRedirect, "getErrorPages": label.GetErrorPages, diff --git a/provider/mesos/config_test.go b/provider/mesos/config_test.go index 6b5af71d7..13d195b4e 100644 --- a/provider/mesos/config_test.go +++ b/provider/mesos/config_test.go @@ -330,6 +330,17 @@ func TestBuildConfiguration(t *testing.T) { withLabel(label.TraefikBackendBufferingMemRequestBodyBytes, "2097152"), withLabel(label.TraefikBackendBufferingRetryExpression, "IsNetworkError() && Attempts() <= 2"), + withLabel(label.TraefikFrontendPassTLSClientCertPem, "true"), + withLabel(label.TraefikFrontendPassTLSClientCertInfosNotBefore, "true"), + withLabel(label.TraefikFrontendPassTLSClientCertInfosNotAfter, "true"), + withLabel(label.TraefikFrontendPassTLSClientCertInfosSans, "true"), + withLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectCommonName, "true"), + withLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectCountry, "true"), + withLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectLocality, "true"), + withLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectOrganization, "true"), + withLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectProvince, "true"), + withLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber, "true"), + withLabel(label.TraefikFrontendAuthBasic, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), withLabel(label.TraefikFrontendAuthBasicRemoveHeader, "true"), withLabel(label.TraefikFrontendAuthBasicUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), @@ -417,6 +428,22 @@ func TestBuildConfiguration(t *testing.T) { 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{ @@ -684,6 +711,17 @@ func TestBuildConfigurationSegments(t *testing.T) { 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"), @@ -766,6 +804,22 @@ func TestBuildConfigurationSegments(t *testing.T) { 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{ diff --git a/provider/rancher/config.go b/provider/rancher/config.go index 6b913333d..ccd8a43d1 100644 --- a/provider/rancher/config.go +++ b/provider/rancher/config.go @@ -28,19 +28,20 @@ func (p *Provider) buildConfigurationV2(services []rancherData) *types.Configura "getServers": getServers, // Frontend functions - "getBackendName": getBackendName, - "getFrontendRule": p.getFrontendRule, - "getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority), - "getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader), - "getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert), - "getEntryPoints": label.GetFuncSliceString(label.TraefikFrontendEntryPoints), - "getBasicAuth": label.GetFuncSliceString(label.TraefikFrontendAuthBasic), // Deprecated - "getAuth": label.GetAuth, - "getErrorPages": label.GetErrorPages, - "getRateLimit": label.GetRateLimit, - "getRedirect": label.GetRedirect, - "getHeaders": label.GetHeaders, - "getWhiteList": label.GetWhiteList, + "getBackendName": getBackendName, + "getFrontendRule": p.getFrontendRule, + "getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority), + "getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader), + "getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert), + "getPassTLSClientCert": label.GetTLSClientCert, + "getEntryPoints": label.GetFuncSliceString(label.TraefikFrontendEntryPoints), + "getBasicAuth": label.GetFuncSliceString(label.TraefikFrontendAuthBasic), // Deprecated + "getAuth": label.GetAuth, + "getErrorPages": label.GetErrorPages, + "getRateLimit": label.GetRateLimit, + "getRedirect": label.GetRedirect, + "getHeaders": label.GetHeaders, + "getWhiteList": label.GetWhiteList, } // filter services diff --git a/provider/rancher/config_test.go b/provider/rancher/config_test.go index 600813e19..a73150ed0 100644 --- a/provider/rancher/config_test.go +++ b/provider/rancher/config_test.go @@ -59,6 +59,17 @@ func TestProviderBuildConfiguration(t *testing.T) { label.TraefikBackendBufferingMemRequestBodyBytes: "2097152", label.TraefikBackendBufferingRetryExpression: "IsNetworkError() && Attempts() <= 2", + label.TraefikFrontendPassTLSClientCertPem: "true", + label.TraefikFrontendPassTLSClientCertInfosNotBefore: "true", + label.TraefikFrontendPassTLSClientCertInfosNotAfter: "true", + label.TraefikFrontendPassTLSClientCertInfosSans: "true", + label.TraefikFrontendPassTLSClientCertInfosSubjectCommonName: "true", + label.TraefikFrontendPassTLSClientCertInfosSubjectCountry: "true", + label.TraefikFrontendPassTLSClientCertInfosSubjectLocality: "true", + label.TraefikFrontendPassTLSClientCertInfosSubjectOrganization: "true", + label.TraefikFrontendPassTLSClientCertInfosSubjectProvince: "true", + label.TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber: "true", + label.TraefikFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", label.TraefikFrontendAuthBasicRemoveHeader: "true", label.TraefikFrontendAuthBasicUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", @@ -144,6 +155,22 @@ func TestProviderBuildConfiguration(t *testing.T) { 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{ @@ -291,6 +318,17 @@ func TestProviderBuildConfiguration(t *testing.T) { label.Prefix + "sauternes." + label.SuffixProtocol: "https", label.Prefix + "sauternes." + label.SuffixWeight: "12", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertPem: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosNotAfter: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosNotBefore: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSans: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectCommonName: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectCountry: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectLocality: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectOrganization: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectProvince: "true", + label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectSerialNumber: "true", + label.Prefix + "sauternes." + label.SuffixFrontendRule: "Host:traefik.wtf", label.Prefix + "sauternes." + label.SuffixFrontendAuthBasicRemoveHeader: "true", label.Prefix + "sauternes." + label.SuffixFrontendAuthBasicUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", @@ -372,6 +410,22 @@ func TestProviderBuildConfiguration(t *testing.T) { 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{ diff --git a/server/server.go b/server/server.go index 1f82e0759..53583281a 100644 --- a/server/server.go +++ b/server/server.go @@ -6,7 +6,6 @@ import ( "crypto/x509" "encoding/json" "fmt" - "io/ioutil" stdlog "log" "net" "net/http" @@ -438,7 +437,7 @@ func (s *Server) createTLSConfig(entryPointName string, tlsOption *traefiktls.TL if len(tlsOption.ClientCA.Files) > 0 { pool := x509.NewCertPool() for _, caFile := range tlsOption.ClientCA.Files { - data, err := ioutil.ReadFile(caFile) + data, err := caFile.Read() if err != nil { return nil, err } diff --git a/server/server_loadbalancer.go b/server/server_loadbalancer.go index 861f8f010..7d7efc6de 100644 --- a/server/server_loadbalancer.go +++ b/server/server_loadbalancer.go @@ -5,7 +5,6 @@ import ( "crypto/x509" "errors" "fmt" - "io/ioutil" "net" "net/http" "net/url" @@ -280,7 +279,7 @@ func createHTTPTransport(globalConfiguration configuration.GlobalConfiguration) return transport, nil } -func createRootCACertPool(rootCAs traefiktls.RootCAs) *x509.CertPool { +func createRootCACertPool(rootCAs traefiktls.FilesOrContents) *x509.CertPool { roots := x509.NewCertPool() for _, cert := range rootCAs { @@ -314,7 +313,7 @@ func createClientTLSConfig(entryPointName string, tlsOption *traefiktls.TLS) (*t if len(tlsOption.ClientCA.Files) > 0 { pool := x509.NewCertPool() for _, caFile := range tlsOption.ClientCA.Files { - data, err := ioutil.ReadFile(caFile) + data, err := caFile.Read() if err != nil { return nil, err } diff --git a/server/server_middlewares.go b/server/server_middlewares.go index 1a1a1a89e..4ab3938f9 100644 --- a/server/server_middlewares.go +++ b/server/server_middlewares.go @@ -119,6 +119,15 @@ func (s *Server) buildMiddlewares(frontendName string, frontend *types.Frontend, middle = append(middle, handler) } + // TLSClientHeaders + tlsClientHeadersMiddleware := middlewares.NewTLSClientHeaders(frontend) + if tlsClientHeadersMiddleware != nil { + log.Debugf("Adding TLSClientHeaders middleware for frontend %s", frontendName) + + handler := s.tracingMiddleware.NewNegroniHandlerWrapper("TLSClientHeaders", tlsClientHeadersMiddleware, false) + middle = append(middle, handler) + } + return middle, buildModifyResponse(secureMiddleware, headerMiddleware), postConfig, nil } diff --git a/templates/consul_catalog.tmpl b/templates/consul_catalog.tmpl index a7ee338ae..12d762efe 100644 --- a/templates/consul_catalog.tmpl +++ b/templates/consul_catalog.tmpl @@ -74,8 +74,30 @@ "{{.}}", {{end}}] - {{ $auth := getAuth $service.TraefikLabels }} + {{ $tlsClientCert := getPassTLSClientCert $service.TraefikLabels }} + {{if $tlsClientCert }} + [frontends."frontend-{{ $service.ServiceName }}".passTLSClientCert] + pem = {{ $tlsClientCert.PEM }} + {{ $infos := $tlsClientCert.Infos }} + {{if $infos }} + [frontends."frontend-{{ $service.ServiceName }}".passTLSClientCert.infos] + notAfter = {{ $infos.NotAfter }} + notBefore = {{ $infos.NotBefore }} + sans = {{ $infos.Sans }} + {{ $subject := $infos.Subject }} + {{if $subject }} + [frontends."frontend-{{ $service.ServiceName }}".passTLSClientCert.infos.subject] + country = {{ $subject.Country }} + province = {{ $subject.Province }} + locality = {{ $subject.Locality }} + organization = {{ $subject.Organization }} + commonName = {{ $subject.CommonName }} + serialNumber = {{ $subject.SerialNumber }} + {{end}} + {{end}} + {{end}} + {{ $auth := getAuth $service.TraefikLabels }} {{if $auth }} [frontends."frontend-{{ $service.ServiceName }}".auth] headerField = "{{ $auth.HeaderField }}" diff --git a/templates/docker.tmpl b/templates/docker.tmpl index c200551c5..83b379b9b 100644 --- a/templates/docker.tmpl +++ b/templates/docker.tmpl @@ -75,6 +75,29 @@ "{{.}}", {{end}}] + {{ $tlsClientCert := getPassTLSClientCert $container.SegmentLabels }} + {{if $tlsClientCert }} + [frontends."frontend-{{ $frontendName }}".passTLSClientCert] + pem = {{ $tlsClientCert.PEM }} + {{ $infos := $tlsClientCert.Infos }} + {{if $infos }} + [frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos] + notAfter = {{ $infos.NotAfter }} + notBefore = {{ $infos.NotBefore }} + sans = {{ $infos.Sans }} + {{ $subject := $infos.Subject }} + {{if $subject }} + [frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos.subject] + country = {{ $subject.Country }} + province = {{ $subject.Province }} + locality = {{ $subject.Locality }} + organization = {{ $subject.Organization }} + commonName = {{ $subject.CommonName }} + serialNumber = {{ $subject.SerialNumber }} + {{end}} + {{end}} + {{end}} + {{ $auth := getAuth $container.SegmentLabels }} {{if $auth }} [frontends."frontend-{{ $frontendName }}".auth] diff --git a/templates/ecs.tmpl b/templates/ecs.tmpl index 256e300fb..59c040792 100644 --- a/templates/ecs.tmpl +++ b/templates/ecs.tmpl @@ -76,6 +76,29 @@ "{{.}}", {{end}}] + {{ $tlsClientCert := getPassTLSClientCert $instance.SegmentLabels }} + {{if $tlsClientCert }} + [frontends."frontend-{{ $frontendName }}".passTLSClientCert] + pem = {{ $tlsClientCert.PEM }} + {{ $infos := $tlsClientCert.Infos }} + {{if $infos }} + [frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos] + notAfter = {{ $infos.NotAfter }} + notBefore = {{ $infos.NotBefore }} + sans = {{ $infos.Sans }} + {{ $subject := $infos.Subject }} + {{if $subject }} + [frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos.subject] + country = {{ $subject.Country }} + province = {{ $subject.Province }} + locality = {{ $subject.Locality }} + organization = {{ $subject.Organization }} + commonName = {{ $subject.CommonName }} + serialNumber = {{ $subject.SerialNumber }} + {{end}} + {{end}} + {{end}} + {{ $auth := getAuth $instance.SegmentLabels }} {{if $auth }} [frontends."frontend-{{ $frontendName }}".auth] diff --git a/templates/kv.tmpl b/templates/kv.tmpl index afeebdabc..f4315cf04 100644 --- a/templates/kv.tmpl +++ b/templates/kv.tmpl @@ -74,6 +74,29 @@ "{{.}}", {{end}}] + {{ $tlsClientCert := getPassTLSClientCert $frontend }} + {{if $tlsClientCert }} + [frontends."{{ $frontendName }}".passTLSClientCert] + pem = {{ $tlsClientCert.PEM }} + {{ $infos := $tlsClientCert.Infos }} + {{if $infos }} + [frontends."{{ $frontendName }}".passTLSClientCert.infos] + notAfter = {{ $infos.NotAfter }} + notBefore = {{ $infos.NotBefore }} + sans = {{ $infos.Sans }} + {{ $subject := $infos.Subject }} + {{if $subject }} + [frontends."{{ $frontendName }}".passTLSClientCert.infos.subject] + country = {{ $subject.Country }} + province = {{ $subject.Province }} + locality = {{ $subject.Locality }} + organization = {{ $subject.Organization }} + commonName = {{ $subject.CommonName }} + serialNumber = {{ $subject.SerialNumber }} + {{end}} + {{end}} + {{end}} + {{ $auth := getAuth $frontend }} {{if $auth }} [frontends."{{ $frontendName }}".auth] diff --git a/templates/marathon.tmpl b/templates/marathon.tmpl index 01b1b09ad..21ec75a9f 100644 --- a/templates/marathon.tmpl +++ b/templates/marathon.tmpl @@ -77,7 +77,30 @@ "{{.}}", {{end}}] - {{ $auth := getAuth $app.SegmentLabels }} + {{ $tlsClientCert := getPassTLSClientCert $app.SegmentLabels }} + {{if $tlsClientCert }} + [frontends."{{ $frontendName }}".passTLSClientCert] + pem = {{ $tlsClientCert.PEM }} + {{ $infos := $tlsClientCert.Infos }} + {{if $infos }} + [frontends."{{ $frontendName }}".passTLSClientCert.infos] + notAfter = {{ $infos.NotAfter }} + notBefore = {{ $infos.NotBefore }} + sans = {{ $infos.Sans }} + {{ $subject := $infos.Subject }} + {{if $subject }} + [frontends."{{ $frontendName }}".passTLSClientCert.infos.subject] + country = {{ $subject.Country }} + province = {{ $subject.Province }} + locality = {{ $subject.Locality }} + organization = {{ $subject.Organization }} + commonName = {{ $subject.CommonName }} + serialNumber = {{ $subject.SerialNumber }} + {{end}} + {{end}} + {{end}} + + {{ $auth := getAuth $app.SegmentLabels }} {{if $auth }} [frontends."{{ $frontendName }}".auth] headerField = "{{ $auth.HeaderField }}" diff --git a/templates/mesos.tmpl b/templates/mesos.tmpl index 1927e694d..8df2c5939 100644 --- a/templates/mesos.tmpl +++ b/templates/mesos.tmpl @@ -77,6 +77,29 @@ "{{.}}", {{end}}] + {{ $tlsClientCert := getPassTLSClientCert $app.TraefikLabels }} + {{if $tlsClientCert }} + [frontends."frontend-{{ $frontendName }}".passTLSClientCert] + pem = {{ $tlsClientCert.PEM }} + {{ $infos := $tlsClientCert.Infos }} + {{if $infos }} + [frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos] + notAfter = {{ $infos.NotAfter }} + notBefore = {{ $infos.NotBefore }} + sans = {{ $infos.Sans }} + {{ $subject := $infos.Subject }} + {{if $subject }} + [frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos.subject] + country = {{ $subject.Country }} + province = {{ $subject.Province }} + locality = {{ $subject.Locality }} + organization = {{ $subject.Organization }} + commonName = {{ $subject.CommonName }} + serialNumber = {{ $subject.SerialNumber }} + {{end}} + {{end}} + {{end}} + {{ $auth := getAuth $app.TraefikLabels }} {{if $auth }} [frontends."frontend-{{ $frontendName }}".auth] diff --git a/templates/rancher.tmpl b/templates/rancher.tmpl index 96500c41b..ca594a11b 100644 --- a/templates/rancher.tmpl +++ b/templates/rancher.tmpl @@ -75,6 +75,29 @@ "{{.}}", {{end}}] + {{ $tlsClientCert := getPassTLSClientCert $service.SegmentLabels }} + {{if $tlsClientCert }} + [frontends."frontend-{{ $frontendName }}".passTLSClientCert] + pem = {{ $tlsClientCert.PEM }} + {{ $infos := $tlsClientCert.Infos }} + {{if $infos }} + [frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos] + notAfter = {{ $infos.NotAfter }} + notBefore = {{ $infos.NotBefore }} + sans = {{ $infos.Sans }} + {{ $subject := $infos.Subject }} + {{if $subject }} + [frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos.subject] + country = {{ $subject.Country }} + province = {{ $subject.Province }} + locality = {{ $subject.Locality }} + organization = {{ $subject.Organization }} + commonName = {{ $subject.CommonName }} + serialNumber = {{ $subject.SerialNumber }} + {{end}} + {{end}} + {{end}} + {{ $auth := getAuth $service.SegmentLabels }} {{if $auth }} [frontends."frontend-{{ $frontendName }}".auth] diff --git a/tls/tls.go b/tls/tls.go index 0760c870a..32a7583de 100644 --- a/tls/tls.go +++ b/tls/tls.go @@ -16,7 +16,7 @@ const ( // ClientCA defines traefik CA files for a entryPoint // and it indicates if they are mandatory or have just to be analyzed if provided type ClientCA struct { - Files []string + Files FilesOrContents Optional bool } @@ -25,14 +25,14 @@ type TLS struct { MinVersion string `export:"true"` CipherSuites []string Certificates Certificates - ClientCAFiles []string // Deprecated + ClientCAFiles FilesOrContents // Deprecated ClientCA ClientCA DefaultCertificate *Certificate SniStrict bool `export:"true"` } -// RootCAs hold the CA we want to have in root -type RootCAs []FileOrContent +// FilesOrContents hold the CA we want to have in root +type FilesOrContents []FileOrContent // Configuration allows mapping a TLS certificate to a list of entrypoints type Configuration struct { @@ -42,7 +42,7 @@ type Configuration struct { // String is the method to format the flag's value, part of the flag.Value interface. // The String method's output will be used in diagnostics. -func (r *RootCAs) String() string { +func (r *FilesOrContents) String() string { sliceOfString := make([]string, len([]FileOrContent(*r))) for key, value := range *r { sliceOfString[key] = value.String() @@ -53,30 +53,30 @@ func (r *RootCAs) String() string { // Set is the method to set the flag value, part of the flag.Value interface. // Set's argument is a string to be parsed to set the flag. // It's a comma-separated list, so we split it. -func (r *RootCAs) Set(value string) error { - rootCAs := strings.Split(value, ",") - if len(rootCAs) == 0 { - return fmt.Errorf("bad RootCAs format: %s", value) +func (r *FilesOrContents) Set(value string) error { + filesOrContents := strings.Split(value, ",") + if len(filesOrContents) == 0 { + return fmt.Errorf("bad FilesOrContents format: %s", value) } - for _, rootCA := range rootCAs { - *r = append(*r, FileOrContent(rootCA)) + for _, fileOrContent := range filesOrContents { + *r = append(*r, FileOrContent(fileOrContent)) } return nil } -// Get return the RootCAs list -func (r *RootCAs) Get() interface{} { +// Get return the FilesOrContents list +func (r *FilesOrContents) Get() interface{} { return *r } -// SetValue sets the RootCAs with val -func (r *RootCAs) SetValue(val interface{}) { - *r = val.(RootCAs) +// SetValue sets the FilesOrContents with val +func (r *FilesOrContents) SetValue(val interface{}) { + *r = val.(FilesOrContents) } // Type is type of the struct -func (r *RootCAs) Type() string { - return "rootcas" +func (r *FilesOrContents) Type() string { + return "filesorcontents" } // SortTLSPerEntryPoints converts TLS configuration sorted by Certificates into TLS configuration sorted by EntryPoints diff --git a/types/types.go b/types/types.go index b92557afb..a3dd62078 100644 --- a/types/types.go +++ b/types/types.go @@ -182,7 +182,8 @@ type Frontend struct { Backend string `json:"backend,omitempty"` Routes map[string]Route `json:"routes,omitempty" hash:"ignore"` PassHostHeader bool `json:"passHostHeader,omitempty"` - PassTLSCert bool `json:"passTLSCert,omitempty"` + PassTLSCert bool `json:"passTLSCert,omitempty"` // Deprecated use PassTLSClientCert instead + PassTLSClientCert *TLSClientHeaders `json:"passTLSClientCert,omitempty"` Priority int `json:"priority"` BasicAuth []string `json:"basicAuth"` // Deprecated WhitelistSourceRange []string `json:"whitelistSourceRange,omitempty"` // Deprecated @@ -611,3 +612,27 @@ func (h HTTPCodeRanges) Contains(statusCode int) bool { } return false } + +// TLSClientHeaders holds the TLS client cert headers configuration. +type TLSClientHeaders struct { + PEM bool `description:"Enable header with escaped client pem" json:"pem"` + Infos *TLSClientCertificateInfos `description:"Enable header with configured client cert infos" json:"infos,omitempty"` +} + +// TLSClientCertificateInfos holds the client TLS certificate infos configuration +type TLSClientCertificateInfos struct { + NotAfter bool `description:"Add NotAfter info in header" json:"notAfter"` + NotBefore bool `description:"Add NotBefore info in header" json:"notBefore"` + Subject *TLSCLientCertificateSubjectInfos `description:"Add Subject info in header" json:"subject,omitempty"` + Sans bool `description:"Add Sans info in header" json:"sans"` +} + +// TLSCLientCertificateSubjectInfos holds the client TLS certificate subject infos configuration +type TLSCLientCertificateSubjectInfos struct { + Country bool `description:"Add Country info in header" json:"country"` + Province bool `description:"Add Province info in header" json:"province"` + Locality bool `description:"Add Locality info in header" json:"locality"` + Organization bool `description:"Add Organization info in header" json:"organization"` + CommonName bool `description:"Add CommonName info in header" json:"commonName"` + SerialNumber bool `description:"Add SerialNumber info in header" json:"serialNumber"` +} From f400292be74a439f8fcb45cbbcdbcead433938cd Mon Sep 17 00:00:00 2001 From: SALLEYRON Julien Date: Wed, 29 Aug 2018 11:58:03 +0200 Subject: [PATCH 2/9] Enable retry on websocket --- integration/retry_test.go | 27 +++++++++ middlewares/retry.go | 14 +++-- middlewares/retry_test.go | 118 +++++++++++++++++++++++++++++--------- 3 files changed, 125 insertions(+), 34 deletions(-) diff --git a/integration/retry_test.go b/integration/retry_test.go index 1def53435..ca935779e 100644 --- a/integration/retry_test.go +++ b/integration/retry_test.go @@ -7,6 +7,7 @@ import ( "github.com/containous/traefik/integration/try" "github.com/go-check/check" + "github.com/gorilla/websocket" checker "github.com/vdemeester/shakers" ) @@ -38,3 +39,29 @@ func (s *RetrySuite) TestRetry(c *check.C) { c.Assert(err, checker.IsNil) c.Assert(response.StatusCode, checker.Equals, http.StatusOK) } + +func (s *RetrySuite) TestRetryWebsocket(c *check.C) { + whoamiEndpoint := s.composeProject.Container(c, "whoami").NetworkSettings.IPAddress + file := s.adaptFile(c, "fixtures/retry/simple.toml", struct { + WhoamiEndpoint string + }{whoamiEndpoint}) + defer os.Remove(file) + + cmd, display := s.traefikCmd(withConfigFile(file)) + defer display(c) + err := cmd.Start() + c.Assert(err, checker.IsNil) + defer cmd.Process.Kill() + + err = try.GetRequest("http://127.0.0.1:8080/api/providers", 60*time.Second, try.BodyContains("PathPrefix:/")) + c.Assert(err, checker.IsNil) + + // This simulates a DialTimeout when connecting to the backend server. + _, response, err := websocket.DefaultDialer.Dial("ws://127.0.0.1:8000/echo", nil) + c.Assert(err, checker.IsNil) + c.Assert(response.StatusCode, checker.Equals, http.StatusSwitchingProtocols) + + _, response, err = websocket.DefaultDialer.Dial("ws://127.0.0.1:8000/echo", nil) + c.Assert(err, checker.IsNil) + c.Assert(response.StatusCode, checker.Equals, http.StatusSwitchingProtocols) +} diff --git a/middlewares/retry.go b/middlewares/retry.go index 6ecbdf1cd..57c631ded 100644 --- a/middlewares/retry.go +++ b/middlewares/retry.go @@ -2,6 +2,7 @@ package middlewares import ( "bufio" + "fmt" "io/ioutil" "net" "net/http" @@ -41,11 +42,8 @@ func (retry *Retry) ServeHTTP(rw http.ResponseWriter, r *http.Request) { attempts := 1 for { attemptsExhausted := attempts >= retry.attempts - // Websocket requests can't be retried at this point in time. - // This is due to the fact that gorilla/websocket doesn't use the request - // context and so we don't get httptrace information. - // Websocket clients should however retry on their own anyway. - shouldRetry := !attemptsExhausted && !isWebsocketRequest(r) + + shouldRetry := !attemptsExhausted retryResponseWriter := newRetryResponseWriter(rw, shouldRetry) // Disable retries when the backend already received request data @@ -150,7 +148,11 @@ func (rr *retryResponseWriterWithoutCloseNotify) WriteHeader(code int) { } func (rr *retryResponseWriterWithoutCloseNotify) Hijack() (net.Conn, *bufio.ReadWriter, error) { - return rr.responseWriter.(http.Hijacker).Hijack() + hijacker, ok := rr.responseWriter.(http.Hijacker) + if !ok { + return nil, nil, fmt.Errorf("%T is not a http.Hijacker", rr.responseWriter) + } + return hijacker.Hijack() } func (rr *retryResponseWriterWithoutCloseNotify) Flush() { diff --git a/middlewares/retry_test.go b/middlewares/retry_test.go index b73044b29..9c51c567f 100644 --- a/middlewares/retry_test.go +++ b/middlewares/retry_test.go @@ -3,21 +3,24 @@ package middlewares import ( "net/http" "net/http/httptest" + "strings" "testing" "github.com/containous/traefik/testhelpers" + "github.com/gorilla/websocket" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/vulcand/oxy/forward" "github.com/vulcand/oxy/roundrobin" ) func TestRetry(t *testing.T) { testCases := []struct { - desc string - maxRequestAttempts int - wantRetryAttempts int - wantResponseStatus int - amountFaultyEndpoints int - isWebsocketHandshakeRequest bool + desc string + maxRequestAttempts int + wantRetryAttempts int + wantResponseStatus int + amountFaultyEndpoints int }{ { desc: "no retry on success", @@ -54,14 +57,6 @@ func TestRetry(t *testing.T) { wantResponseStatus: http.StatusInternalServerError, amountFaultyEndpoints: 3, }, - { - desc: "websocket request should not be retried", - maxRequestAttempts: 3, - wantRetryAttempts: 0, - wantResponseStatus: http.StatusBadGateway, - amountFaultyEndpoints: 1, - isWebsocketHandshakeRequest: true, - }, } backendServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { @@ -74,10 +69,10 @@ func TestRetry(t *testing.T) { t.Fatalf("Error creating forwarder: %s", err) } - for _, tc := range testCases { - tc := tc + for _, test := range testCases { + test := test - t.Run(tc.desc, func(t *testing.T) { + t.Run(test.desc, func(t *testing.T) { t.Parallel() loadBalancer, err := roundrobin.New(forwarder) @@ -86,7 +81,7 @@ func TestRetry(t *testing.T) { } basePort := 33444 - for i := 0; i < tc.amountFaultyEndpoints; i++ { + for i := 0; i < test.amountFaultyEndpoints; i++ { // 192.0.2.0 is a non-routable IP for testing purposes. // See: https://stackoverflow.com/questions/528538/non-routable-ip-address/18436928#18436928 // We only use the port specification here because the URL is used as identifier @@ -98,24 +93,91 @@ func TestRetry(t *testing.T) { loadBalancer.UpsertServer(testhelpers.MustParseURL(backendServer.URL)) retryListener := &countingRetryListener{} - retry := NewRetry(tc.maxRequestAttempts, loadBalancer, retryListener) + retry := NewRetry(test.maxRequestAttempts, loadBalancer, retryListener) recorder := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, "http://localhost:3000/ok", nil) - if tc.isWebsocketHandshakeRequest { - req.Header.Add("Connection", "Upgrade") - req.Header.Add("Upgrade", "websocket") - } - retry.ServeHTTP(recorder, req) - if tc.wantResponseStatus != recorder.Code { - t.Errorf("got status code %d, want %d", recorder.Code, tc.wantResponseStatus) + assert.Equal(t, test.wantResponseStatus, recorder.Code) + assert.Equal(t, test.wantRetryAttempts, retryListener.timesCalled) + }) + } +} + +func TestRetryWebsocket(t *testing.T) { + testCases := []struct { + desc string + maxRequestAttempts int + expectedRetryAttempts int + expectedResponseStatus int + expectedError bool + amountFaultyEndpoints int + }{ + { + desc: "Switching ok after 2 retries", + maxRequestAttempts: 3, + expectedRetryAttempts: 2, + amountFaultyEndpoints: 2, + expectedResponseStatus: http.StatusSwitchingProtocols, + }, + { + desc: "Switching failed", + maxRequestAttempts: 2, + expectedRetryAttempts: 1, + amountFaultyEndpoints: 2, + expectedResponseStatus: http.StatusBadGateway, + expectedError: true, + }, + } + + forwarder, err := forward.New() + if err != nil { + t.Fatalf("Error creating forwarder: %s", err) + } + + backendServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + upgrader := websocket.Upgrader{} + upgrader.Upgrade(rw, req, nil) + })) + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + loadBalancer, err := roundrobin.New(forwarder) + if err != nil { + t.Fatalf("Error creating load balancer: %s", err) } - if tc.wantRetryAttempts != retryListener.timesCalled { - t.Errorf("retry listener called %d time(s), want %d time(s)", retryListener.timesCalled, tc.wantRetryAttempts) + + basePort := 33444 + for i := 0; i < test.amountFaultyEndpoints; i++ { + // 192.0.2.0 is a non-routable IP for testing purposes. + // See: https://stackoverflow.com/questions/528538/non-routable-ip-address/18436928#18436928 + // We only use the port specification here because the URL is used as identifier + // in the load balancer and using the exact same URL would not add a new server. + loadBalancer.UpsertServer(testhelpers.MustParseURL("http://192.0.2.0:" + string(basePort+i))) } + + // add the functioning server to the end of the load balancer list + loadBalancer.UpsertServer(testhelpers.MustParseURL(backendServer.URL)) + + retryListener := &countingRetryListener{} + retry := NewRetry(test.maxRequestAttempts, loadBalancer, retryListener) + + retryServer := httptest.NewServer(retry) + + url := strings.Replace(retryServer.URL, "http", "ws", 1) + _, response, err := websocket.DefaultDialer.Dial(url, nil) + + if !test.expectedError { + require.NoError(t, err) + } + + assert.Equal(t, test.expectedResponseStatus, response.StatusCode) + assert.Equal(t, test.expectedRetryAttempts, retryListener.timesCalled) }) } } From 0f0ba099c9fd2a88b6eae85717080fb5d76868ba Mon Sep 17 00:00:00 2001 From: Damien Duportal Date: Thu, 30 Aug 2018 02:24:03 +0200 Subject: [PATCH 3/9] Make the "base domain" on all providers --- docs/configuration/backends/consulcatalog.md | 4 ++-- docs/configuration/backends/ecs.md | 4 ++-- docs/configuration/backends/marathon.md | 4 ++-- docs/configuration/backends/mesos.md | 4 ++-- docs/configuration/backends/rancher.md | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/configuration/backends/consulcatalog.md b/docs/configuration/backends/consulcatalog.md index 9a8da9e69..96e219ed1 100644 --- a/docs/configuration/backends/consulcatalog.md +++ b/docs/configuration/backends/consulcatalog.md @@ -31,7 +31,7 @@ exposedByDefault = false # stale = false -# Default domain used. +# Default base domain used for the frontend rules. # # Optional # @@ -210,7 +210,7 @@ If you need to support multiple frontends for a service, for example when having | `.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. | | `.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. | - + ### Examples If you want that Træfik uses Consul tags correctly you need to defined them like that: diff --git a/docs/configuration/backends/ecs.md b/docs/configuration/backends/ecs.md index 0089dee15..a4f7c5d36 100644 --- a/docs/configuration/backends/ecs.md +++ b/docs/configuration/backends/ecs.md @@ -32,7 +32,7 @@ clusters = ["default"] # watch = true -# Default domain used. +# Default base domain used for the frontend rules. # Can be overridden by setting the "traefik.domain" label. # # Optional @@ -138,7 +138,7 @@ Labels can be used on task containers to override default behaviour: | Label | Description | |---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `traefik.domain` | Sets the default domain for frontend rules. | +| `traefik.domain` | Sets the default base domain for frontend rules. | | `traefik.enable=false` | Disables this container in Træfik. | | `traefik.port=80` | Overrides the default `port` value. Overrides `NetworkBindings` from Docker Container | | `traefik.protocol=https` | Overrides the default `http` protocol | diff --git a/docs/configuration/backends/marathon.md b/docs/configuration/backends/marathon.md index baf65a943..0db0de8c7 100644 --- a/docs/configuration/backends/marathon.md +++ b/docs/configuration/backends/marathon.md @@ -31,7 +31,7 @@ endpoint = "http://127.0.0.1:8080" # watch = true -# Default domain used. +# Default base domain used for the frontend rules. # Can be overridden by setting the "traefik.domain" label on an application. # # Required @@ -195,7 +195,7 @@ The following labels can be defined on Marathon applications. They adjust the be | Label | Description | |---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `traefik.domain` | Sets the default domain used for the frontend rules. | +| `traefik.domain` | Sets the default base domain used for the frontend rules. | | `traefik.enable=false` | Disables this container in Træfik. | | `traefik.port=80` | Registers this port. Useful when the container exposes multiples ports. | | `traefik.portIndex=1` | Registers port by index in the application's ports array. Useful when the application exposes multiple ports. | diff --git a/docs/configuration/backends/mesos.md b/docs/configuration/backends/mesos.md index 60b31e7c1..5f4e4730f 100644 --- a/docs/configuration/backends/mesos.md +++ b/docs/configuration/backends/mesos.md @@ -27,7 +27,7 @@ endpoint = "http://127.0.0.1:8080" # watch = true -# Default domain used. +# Default base domain used for the frontend rules. # Can be overridden by setting the "traefik.domain" label on an application. # # Required @@ -108,7 +108,7 @@ The following labels can be defined on Mesos tasks. They adjust the behavior for | Label | Description | |---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `traefik.domain` | Sets the default domain for the frontend rules. | +| `traefik.domain` | Sets the default base domain for the frontend rules. | | `traefik.enable=false` | Disables this container in Træfik. | | `traefik.port=80` | Registers this port. Useful when the application exposes multiple ports. | | `traefik.portName=web` | Registers port by name in the application's ports array. Useful when the application exposes multiple ports. | diff --git a/docs/configuration/backends/rancher.md b/docs/configuration/backends/rancher.md index 8bbcebaef..a9aff127a 100644 --- a/docs/configuration/backends/rancher.md +++ b/docs/configuration/backends/rancher.md @@ -12,7 +12,7 @@ Træfik can be configured to use Rancher as a provider. # Enable Rancher Provider. [rancher] -# Default domain used. +# Default base domain used for the frontend rules. # Can be overridden by setting the "traefik.domain" label on an service. # # Required @@ -140,7 +140,7 @@ Labels can be used on task containers to override default behavior: | Label | Description | |---------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `traefik.domain` | Sets the default domain for the frontend rules. | +| `traefik.domain` | Sets the default base domain for the frontend rules. | | `traefik.enable=false` | Disables this container in Træfik. | | `traefik.port=80` | Registers this port. Useful when the container exposes multiple ports. | | `traefik.protocol=https` | Overrides the default `http` protocol. | From e4bb506ace1ea5ef24df5d405722d692f9eb94d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Cro=C3=ABs?= Date: Mon, 3 Sep 2018 10:12:03 +0200 Subject: [PATCH 4/9] StoreConfig always initializes the account if it is missing --- cmd/storeconfig/storeconfig.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/cmd/storeconfig/storeconfig.go b/cmd/storeconfig/storeconfig.go index 412aff25c..483d6c9c1 100644 --- a/cmd/storeconfig/storeconfig.go +++ b/cmd/storeconfig/storeconfig.go @@ -85,8 +85,13 @@ func Run(kv *staert.KvSource, traefikConfiguration *cmd.TraefikConfiguration) fu } } + accountInitialized, err := keyExists(kv, traefikConfiguration.GlobalConfiguration.ACME.Storage) + if err != nil { + return err + } + // Check to see if ACME account object is already in kv store - if traefikConfiguration.GlobalConfiguration.ACME.OverrideCertificates { + if traefikConfiguration.GlobalConfiguration.ACME.OverrideCertificates || !accountInitialized { // Store the ACME Account into the KV Store // Certificates in KV Store will be overridden @@ -114,6 +119,15 @@ func Run(kv *staert.KvSource, traefikConfiguration *cmd.TraefikConfiguration) fu } } +func keyExists(source *staert.KvSource, key string) (bool, error) { + list, err := source.List(key, nil) + if err != nil { + return false, err + } + + return len(list) > 0, nil +} + // migrateACMEData allows migrating data from acme.json file to KV store in function of the file format func migrateACMEData(fileName string) (*acme.Account, error) { From b98f5ed8b1ed72b2b2af24814ac6922e1f531d81 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Tue, 4 Sep 2018 15:54:06 +0200 Subject: [PATCH 5/9] Remove a dead link. --- docs/configuration/api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration/api.md b/docs/configuration/api.md index e6e8aa932..b9e8bf3ef 100644 --- a/docs/configuration/api.md +++ b/docs/configuration/api.md @@ -33,7 +33,7 @@ debug = true ``` -For more customization, see [entry points](/configuration/entrypoints/) documentation and [examples](/user-guide/examples/#ping-health-check). +For more customization, see [entry points](/configuration/entrypoints/) documentation. ## Web UI From 913d8737ccb53b0352917de3f32a92861dab6377 Mon Sep 17 00:00:00 2001 From: NicoMen Date: Tue, 4 Sep 2018 17:14:04 +0200 Subject: [PATCH 6/9] =?UTF-8?q?Allow=20Tr=C3=A6fik=20to=20know=20if=20a=20?= =?UTF-8?q?Host=20rule=20is=20malformed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- provider/acme/provider.go | 2 +- rules/rules.go | 18 +++++++++++++++-- rules/rules_test.go | 41 ++++++++++++++++++++++++++++----------- server/server.go | 10 ++++++++-- 4 files changed, 55 insertions(+), 16 deletions(-) diff --git a/provider/acme/provider.go b/provider/acme/provider.go index 74adf62d4..9a876e3f7 100644 --- a/provider/acme/provider.go +++ b/provider/acme/provider.go @@ -181,7 +181,7 @@ func (p *Provider) watchNewDomains() { } if len(domains) == 0 { - log.Debugf("No domain parsed in rule %q", route.Rule) + log.Debugf("No domain parsed in rule %q in provider ACME", route.Rule) continue } diff --git a/rules/rules.go b/rules/rules.go index 3ea8fef24..b592d8bdc 100644 --- a/rules/rules.go +++ b/rules/rules.go @@ -9,7 +9,6 @@ import ( "sort" "strings" - "github.com/BurntSushi/ty/fun" "github.com/containous/mux" "github.com/containous/traefik/types" ) @@ -270,9 +269,11 @@ func (r *Rules) Parse(expression string) (*mux.Route, error) { // ParseDomains parses rules expressions and returns domains func (r *Rules) ParseDomains(expression string) ([]string, error) { var domains []string + isHostRule := false err := r.parseRules(expression, func(functionName string, function interface{}, arguments []string) error { if functionName == "Host" { + isHostRule = true domains = append(domains, arguments...) } return nil @@ -281,5 +282,18 @@ func (r *Rules) ParseDomains(expression string) ([]string, error) { return nil, fmt.Errorf("error parsing domains: %v", err) } - return fun.Map(types.CanonicalDomain, domains).([]string), nil + var cleanDomains []string + for _, domain := range domains { + canonicalDomain := types.CanonicalDomain(domain) + if len(canonicalDomain) > 0 { + cleanDomains = append(cleanDomains, canonicalDomain) + } + } + + // Return an error if an Host rule is detected but no domain are parsed + if isHostRule && len(cleanDomains) == 0 { + return nil, fmt.Errorf("unable to parse correctly the domains in the Host rule from %q", expression) + } + + return cleanDomains, nil } diff --git a/rules/rules_test.go b/rules/rules_test.go index fe40f8e26..7789456ae 100644 --- a/rules/rules_test.go +++ b/rules/rules_test.go @@ -54,24 +54,38 @@ func TestParseDomains(t *testing.T) { rules := &Rules{} tests := []struct { - expression string - domain []string + description string + expression string + domain []string + errorExpected bool }{ { - expression: "Host:foo.bar,test.bar", - domain: []string{"foo.bar", "test.bar"}, + description: "Many host rules", + expression: "Host:foo.bar,test.bar", + domain: []string{"foo.bar", "test.bar"}, + errorExpected: false, }, { - expression: "Path:/test", - domain: []string{}, + description: "No host rule", + expression: "Path:/test", + errorExpected: false, }, { - expression: "Host:foo.bar;Path:/test", - domain: []string{"foo.bar"}, + description: "Host rule and another rule", + expression: "Host:foo.bar;Path:/test", + domain: []string{"foo.bar"}, + errorExpected: false, }, { - expression: "Host: Foo.Bar ;Path:/test", - domain: []string{"foo.bar"}, + description: "Host rule to trim and another rule", + expression: "Host: Foo.Bar ;Path:/test", + domain: []string{"foo.bar"}, + errorExpected: false, + }, + { + description: "Host rule with no domain", + expression: "Host: ;Path:/test", + errorExpected: true, }, } @@ -81,7 +95,12 @@ func TestParseDomains(t *testing.T) { t.Parallel() domains, err := rules.ParseDomains(test.expression) - require.NoError(t, err, "%s: Error while parsing domain.", test.expression) + + if test.errorExpected { + require.Errorf(t, err, "unable to parse correctly the domains in the Host rule from %q", test.expression) + } else { + require.NoError(t, err, "%s: Error while parsing domain.", test.expression) + } assert.EqualValues(t, test.domain, domains, "%s: Error parsing domains from expression.", test.expression) }) diff --git a/server/server.go b/server/server.go index 5db244e92..5199c1965 100644 --- a/server/server.go +++ b/server/server.go @@ -676,9 +676,15 @@ func (s *Server) postLoadConfiguration() { domains, err := rules.ParseDomains(route.Rule) if err != nil { log.Errorf("Error parsing domains: %v", err) - } else { - s.globalConfiguration.ACME.LoadCertificateForDomains(domains) + continue } + + if len(domains) == 0 { + log.Debugf("No domain parsed in rule %q", route.Rule) + continue + } + + s.globalConfiguration.ACME.LoadCertificateForDomains(domains) } } } From 74ace58ae1c42f9a2cd775fd900fec7064667b74 Mon Sep 17 00:00:00 2001 From: NicoMen Date: Thu, 6 Sep 2018 14:24:03 +0200 Subject: [PATCH 7/9] Avoid goroutine leak in server --- server/server.go | 4 +++- server/server_configuration.go | 6 ++++-- server/server_signals.go | 26 +++++++++++++++----------- server/server_signals_windows.go | 2 +- 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/server/server.go b/server/server.go index 53583281a..4415223b7 100644 --- a/server/server.go +++ b/server/server.go @@ -248,7 +248,9 @@ func (s *Server) Start() { s.listenConfigurations(stop) }) s.startProvider() - go s.listenSignals() + s.routinesPool.Go(func(stop chan bool) { + s.listenSignals(stop) + }) } // StartWithContext starts the server and Stop/Close it when context is Done diff --git a/server/server_configuration.go b/server/server_configuration.go index 41ef7ab2c..773eb8be0 100644 --- a/server/server_configuration.go +++ b/server/server_configuration.go @@ -447,8 +447,10 @@ func (s *Server) throttleProviderConfigReload(throttle time.Duration, publish ch case <-stop: return case nextConfig := <-ring.Out(): - publish <- nextConfig.(types.ConfigMessage) - time.Sleep(throttle) + if config, ok := nextConfig.(types.ConfigMessage); ok { + publish <- config + time.Sleep(throttle) + } } } }) diff --git a/server/server_signals.go b/server/server_signals.go index fb8514c9a..8472696d7 100644 --- a/server/server_signals.go +++ b/server/server_signals.go @@ -13,21 +13,25 @@ func (s *Server) configureSignals() { signal.Notify(s.signals, syscall.SIGUSR1) } -func (s *Server) listenSignals() { +func (s *Server) listenSignals(stop chan bool) { for { - sig := <-s.signals - switch sig { - case syscall.SIGUSR1: - log.Infof("Closing and re-opening log files for rotation: %+v", sig) + select { + case <-stop: + return + case sig := <-s.signals: + switch sig { + case syscall.SIGUSR1: + log.Infof("Closing and re-opening log files for rotation: %+v", sig) - if s.accessLoggerMiddleware != nil { - if err := s.accessLoggerMiddleware.Rotate(); err != nil { - log.Errorf("Error rotating access log: %v", err) + if s.accessLoggerMiddleware != nil { + if err := s.accessLoggerMiddleware.Rotate(); err != nil { + log.Errorf("Error rotating access log: %v", err) + } } - } - if err := log.RotateFile(); err != nil { - log.Errorf("Error rotating traefik log: %v", err) + if err := log.RotateFile(); err != nil { + log.Errorf("Error rotating traefik log: %v", err) + } } } } diff --git a/server/server_signals_windows.go b/server/server_signals_windows.go index 674896b43..05cf4eace 100644 --- a/server/server_signals_windows.go +++ b/server/server_signals_windows.go @@ -4,4 +4,4 @@ package server func (s *Server) configureSignals() {} -func (s *Server) listenSignals() {} +func (s *Server) listenSignals(stop chan bool) {} From 48e7a87741a41c6506b884b94434fe4f9345148b Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Fri, 7 Sep 2018 09:40:03 +0200 Subject: [PATCH 8/9] Update go version --- CONTRIBUTING.md | 2 +- Gopkg.lock | 2 +- anonymize/anonymize_config_test.go | 28 +++++----- build.Dockerfile | 2 +- cmd/bug/bug.go | 2 +- cmd/healthcheck/healthcheck.go | 2 +- configuration/entrypoints_test.go | 26 ++++----- healthcheck/healthcheck.go | 2 +- middlewares/accesslog/logger_test.go | 56 +++++++++---------- middlewares/ip_whitelister.go | 2 +- middlewares/retry.go | 2 +- provider/ecs/ecs.go | 2 +- provider/kubernetes/kubernetes_test.go | 24 ++++---- provider/mesos/mesos.go | 2 +- provider/rancher/config_test.go | 2 +- provider/rancher/deprecated_config_test.go | 2 +- provider/rancher/metadata.go | 4 +- .../github.com/vulcand/oxy/buffer/buffer.go | 11 +++- 18 files changed, 90 insertions(+), 83 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1f6a4ac5e..980f06153 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,7 +13,7 @@ You need to run the `binary` target. This will create binaries for Linux platfor $ make binary docker build -t "traefik-dev:no-more-godep-ever" -f build.Dockerfile . Sending build context to Docker daemon 295.3 MB -Step 0 : FROM golang:1.10-alpine +Step 0 : FROM golang:1.11-alpine ---> 8c6473912976 Step 1 : RUN go get github.com/golang/dep/cmd/dep [...] diff --git a/Gopkg.lock b/Gopkg.lock index 358d7c263..e0b9792e7 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -1272,7 +1272,7 @@ "roundrobin", "utils" ] - revision = "f6bbeac6d5c4c06f88ba07ed42983ff36a5b407e" + revision = "77148e9694210e5f5610328f1cd7cf65583014c2" [[projects]] name = "github.com/vulcand/predicate" diff --git a/anonymize/anonymize_config_test.go b/anonymize/anonymize_config_test.go index bb5975e4e..3ffe72bce 100644 --- a/anonymize/anonymize_config_test.go +++ b/anonymize/anonymize_config_test.go @@ -213,7 +213,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, Endpoint: "docker Endpoint", @@ -244,7 +244,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, Directory: "file Directory", @@ -309,7 +309,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, Endpoint: "", @@ -349,7 +349,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, Endpoint: "ConsulCatalog Endpoint", @@ -374,7 +374,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, Endpoint: "k8s Endpoint", @@ -400,7 +400,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, Endpoint: "mesos Endpoint", @@ -429,7 +429,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, Endpoint: "eureka Endpoint", @@ -452,7 +452,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, Domain: "ecs Domain", @@ -481,7 +481,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, APIConfiguration: rancher.APIConfiguration{ @@ -519,7 +519,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, AccessKeyID: "dynamodb AccessKeyID", @@ -546,7 +546,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, Endpoint: "etcd Endpoint", @@ -578,7 +578,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, Endpoint: "zk Endpoint", @@ -610,7 +610,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, Endpoint: "boltdb Endpoint", @@ -642,7 +642,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, Endpoint: "consul Endpoint", diff --git a/build.Dockerfile b/build.Dockerfile index 99d8a67d2..d7a279b5d 100644 --- a/build.Dockerfile +++ b/build.Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.10-alpine +FROM golang:1.11-alpine RUN apk --update upgrade \ && apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar \ diff --git a/cmd/bug/bug.go b/cmd/bug/bug.go index 08e00133a..851f2296c 100644 --- a/cmd/bug/bug.go +++ b/cmd/bug/bug.go @@ -94,7 +94,7 @@ func NewCmd(traefikConfiguration *cmd.TraefikConfiguration, traefikPointersConfi Description: `Report an issue on Traefik bugtracker`, Config: traefikConfiguration, DefaultPointersConfig: traefikPointersConfiguration, - Run: runCmd(traefikConfiguration), + Run: runCmd(traefikConfiguration), Metadata: map[string]string{ "parseAllSources": "true", }, diff --git a/cmd/healthcheck/healthcheck.go b/cmd/healthcheck/healthcheck.go index 5acad5035..c395dfb34 100644 --- a/cmd/healthcheck/healthcheck.go +++ b/cmd/healthcheck/healthcheck.go @@ -20,7 +20,7 @@ func NewCmd(traefikConfiguration *cmd.TraefikConfiguration, traefikPointersConfi Description: `Calls traefik /ping to check health (web provider must be enabled)`, Config: traefikConfiguration, DefaultPointersConfig: traefikPointersConfiguration, - Run: runCmd(traefikConfiguration), + Run: runCmd(traefikConfiguration), Metadata: map[string]string{ "parseAllSources": "true", }, diff --git a/configuration/entrypoints_test.go b/configuration/entrypoints_test.go index ae4d88a6c..2ef6a5003 100644 --- a/configuration/entrypoints_test.go +++ b/configuration/entrypoints_test.go @@ -67,19 +67,19 @@ func Test_parseEntryPointsConfiguration(t *testing.T) { "ca_optional": "true", "compress": "true", "forwardedheaders_trustedips": "10.0.0.3/24,20.0.0.3/24", - "name": "foo", - "proxyprotocol_trustedips": "192.168.0.1", - "redirect_entrypoint": "https", - "redirect_permanent": "true", - "redirect_regex": "http://localhost/(.*)", - "redirect_replacement": "http://mydomain/$1", - "tls": "goo,gii", - "tls_acme": "TLS", - "tls_ciphersuites": "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", - "tls_minversion": "VersionTLS11", - "whitelistsourcerange": "10.42.0.0/16,152.89.1.33/32,afed:be44::/16", - "whitelist_sourcerange": "10.42.0.0/16,152.89.1.33/32,afed:be44::/16", - "whitelist_usexforwardedfor": "true", + "name": "foo", + "proxyprotocol_trustedips": "192.168.0.1", + "redirect_entrypoint": "https", + "redirect_permanent": "true", + "redirect_regex": "http://localhost/(.*)", + "redirect_replacement": "http://mydomain/$1", + "tls": "goo,gii", + "tls_acme": "TLS", + "tls_ciphersuites": "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", + "tls_minversion": "VersionTLS11", + "whitelistsourcerange": "10.42.0.0/16,152.89.1.33/32,afed:be44::/16", + "whitelist_sourcerange": "10.42.0.0/16,152.89.1.33/32,afed:be44::/16", + "whitelist_usexforwardedfor": "true", }, }, { diff --git a/healthcheck/healthcheck.go b/healthcheck/healthcheck.go index f24ba8766..8a6e9bba0 100644 --- a/healthcheck/healthcheck.go +++ b/healthcheck/healthcheck.go @@ -118,7 +118,7 @@ func (hc *HealthCheck) execute(ctx context.Context, backend *BackendConfig) { for { select { case <-ctx.Done(): - log.Debug("Stopping current health check goroutines of backend: %s", backend.name) + log.Debugf("Stopping current health check goroutines of backend: %s", backend.name) return case <-ticker.C: log.Debugf("Refreshing health check for backend: %s", backend.name) diff --git a/middlewares/accesslog/logger_test.go b/middlewares/accesslog/logger_test.go index 0657cfffb..cbf1da5fc 100644 --- a/middlewares/accesslog/logger_test.go +++ b/middlewares/accesslog/logger_test.go @@ -192,28 +192,28 @@ func TestLoggerJSON(t *testing.T) { Format: JSONFormat, }, expected: map[string]func(t *testing.T, value interface{}){ - RequestHost: assertString(testHostname), - RequestAddr: assertString(testHostname), - RequestMethod: assertString(testMethod), - RequestPath: assertString(testPath), - RequestProtocol: assertString(testProto), - RequestPort: assertString("-"), - RequestLine: assertString(fmt.Sprintf("%s %s %s", testMethod, testPath, testProto)), - DownstreamStatus: assertFloat64(float64(testStatus)), - DownstreamStatusLine: assertString(fmt.Sprintf("%d ", testStatus)), - DownstreamContentSize: assertFloat64(float64(len(testContent))), - OriginContentSize: assertFloat64(float64(len(testContent))), - OriginStatus: assertFloat64(float64(testStatus)), - RequestRefererHeader: assertString(testReferer), - RequestUserAgentHeader: assertString(testUserAgent), - FrontendName: assertString(testFrontendName), - BackendURL: assertString(testBackendName), - ClientUsername: assertString(testUsername), - ClientHost: assertString(testHostname), - ClientPort: assertString(fmt.Sprintf("%d", testPort)), - ClientAddr: assertString(fmt.Sprintf("%s:%d", testHostname, testPort)), - "level": assertString("info"), - "msg": assertString(""), + RequestHost: assertString(testHostname), + RequestAddr: assertString(testHostname), + RequestMethod: assertString(testMethod), + RequestPath: assertString(testPath), + RequestProtocol: assertString(testProto), + RequestPort: assertString("-"), + RequestLine: assertString(fmt.Sprintf("%s %s %s", testMethod, testPath, testProto)), + DownstreamStatus: assertFloat64(float64(testStatus)), + DownstreamStatusLine: assertString(fmt.Sprintf("%d ", testStatus)), + DownstreamContentSize: assertFloat64(float64(len(testContent))), + OriginContentSize: assertFloat64(float64(len(testContent))), + OriginStatus: assertFloat64(float64(testStatus)), + RequestRefererHeader: assertString(testReferer), + RequestUserAgentHeader: assertString(testUserAgent), + FrontendName: assertString(testFrontendName), + BackendURL: assertString(testBackendName), + ClientUsername: assertString(testUsername), + ClientHost: assertString(testHostname), + ClientPort: assertString(fmt.Sprintf("%d", testPort)), + ClientAddr: assertString(fmt.Sprintf("%s:%d", testHostname, testPort)), + "level": assertString("info"), + "msg": assertString(""), "downstream_Content-Type": assertString("text/plain; charset=utf-8"), RequestCount: assertFloat64NotZero(), Duration: assertFloat64NotZero(), @@ -234,9 +234,9 @@ func TestLoggerJSON(t *testing.T) { }, }, expected: map[string]func(t *testing.T, value interface{}){ - "level": assertString("info"), - "msg": assertString(""), - "time": assertNotEqual(""), + "level": assertString("info"), + "msg": assertString(""), + "time": assertNotEqual(""), "downstream_Content-Type": assertString("text/plain; charset=utf-8"), RequestRefererHeader: assertString(testReferer), RequestUserAgentHeader: assertString(testUserAgent), @@ -273,9 +273,9 @@ func TestLoggerJSON(t *testing.T) { }, }, expected: map[string]func(t *testing.T, value interface{}){ - "level": assertString("info"), - "msg": assertString(""), - "time": assertNotEqual(""), + "level": assertString("info"), + "msg": assertString(""), + "time": assertNotEqual(""), "downstream_Content-Type": assertString("REDACTED"), RequestRefererHeader: assertString("REDACTED"), RequestUserAgentHeader: assertString("REDACTED"), diff --git a/middlewares/ip_whitelister.go b/middlewares/ip_whitelister.go index a352aad12..578286b95 100644 --- a/middlewares/ip_whitelister.go +++ b/middlewares/ip_whitelister.go @@ -45,7 +45,7 @@ func (wl *IPWhiteLister) handle(w http.ResponseWriter, r *http.Request, next htt return } - tracing.SetErrorAndDebugLog(r, "request %+v matched white list %s - passing", r, wl.whiteLister) + tracing.SetErrorAndDebugLog(r, "request %+v matched white list %v - passing", r, wl.whiteLister) next.ServeHTTP(w, r) } diff --git a/middlewares/retry.go b/middlewares/retry.go index 57c631ded..ed9f339d9 100644 --- a/middlewares/retry.go +++ b/middlewares/retry.go @@ -126,7 +126,7 @@ func (rr *retryResponseWriterWithoutCloseNotify) Header() http.Header { func (rr *retryResponseWriterWithoutCloseNotify) Write(buf []byte) (int, error) { if rr.ShouldRetry() { - return 0, nil + return len(buf), nil } return rr.responseWriter.Write(buf) } diff --git a/provider/ecs/ecs.go b/provider/ecs/ecs.go index 15a90ddc2..96642e3ff 100644 --- a/provider/ecs/ecs.go +++ b/provider/ecs/ecs.go @@ -386,7 +386,7 @@ func (p *Provider) lookupEc2Instances(ctx context.Context, client *awsClient, cl }) if err != nil { - log.Errorf("Unable to describe instances [%s]: %v", err) + log.Errorf("Unable to describe instances: %v", err) return nil, err } } diff --git a/provider/kubernetes/kubernetes_test.go b/provider/kubernetes/kubernetes_test.go index b4595eb68..754e21841 100644 --- a/provider/kubernetes/kubernetes_test.go +++ b/provider/kubernetes/kubernetes_test.go @@ -517,37 +517,37 @@ func TestModifierType(t *testing.T) { expectedModifierRule string }{ { - desc: "Request modifier annotation missing", + desc: "Request modifier annotation missing", requestModifierAnnotation: "", expectedModifierRule: "", }, { - desc: "AddPrefix modifier annotation", + desc: "AddPrefix modifier annotation", requestModifierAnnotation: " AddPrefix: /foo", expectedModifierRule: "AddPrefix:/foo", }, { - desc: "ReplacePath modifier annotation", + desc: "ReplacePath modifier annotation", requestModifierAnnotation: " ReplacePath: /foo", expectedModifierRule: "ReplacePath:/foo", }, { - desc: "ReplacePathRegex modifier annotation", + desc: "ReplacePathRegex modifier annotation", requestModifierAnnotation: " ReplacePathRegex: /foo /bar", expectedModifierRule: "ReplacePathRegex:/foo /bar", }, { - desc: "AddPrefix modifier annotation", + desc: "AddPrefix modifier annotation", requestModifierAnnotation: "AddPrefix:/foo", expectedModifierRule: "AddPrefix:/foo", }, { - desc: "ReplacePath modifier annotation", + desc: "ReplacePath modifier annotation", requestModifierAnnotation: "ReplacePath:/foo", expectedModifierRule: "ReplacePath:/foo", }, { - desc: "ReplacePathRegex modifier annotation", + desc: "ReplacePathRegex modifier annotation", requestModifierAnnotation: "ReplacePathRegex:/foo /bar", expectedModifierRule: "ReplacePathRegex:/foo /bar", }, @@ -609,23 +609,23 @@ func TestModifierFails(t *testing.T) { requestModifierAnnotation string }{ { - desc: "Request modifier missing part of annotation", + desc: "Request modifier missing part of annotation", requestModifierAnnotation: "AddPrefix: ", }, { - desc: "Request modifier full of spaces annotation", + desc: "Request modifier full of spaces annotation", requestModifierAnnotation: " ", }, { - desc: "Request modifier missing both parts of annotation", + desc: "Request modifier missing both parts of annotation", requestModifierAnnotation: " : ", }, { - desc: "Request modifier using unknown rule", + desc: "Request modifier using unknown rule", requestModifierAnnotation: "Foo: /bar", }, { - desc: "Missing Rule", + desc: "Missing Rule", requestModifierAnnotation: " : /bar", }, } diff --git a/provider/mesos/mesos.go b/provider/mesos/mesos.go index 85a8beaaf..4f44040f2 100644 --- a/provider/mesos/mesos.go +++ b/provider/mesos/mesos.go @@ -128,7 +128,7 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s func detectMasters(zk string, masters []string) <-chan []string { changed := make(chan []string, 1) if zk != "" { - log.Debugf("Starting master detector for ZK ", zk) + log.Debugf("Starting master detector for ZK %s", zk) if md, err := detector.New(zk); err != nil { log.Errorf("Failed to create master detector: %v", err) } else if err := md.Detect(detect.NewMasters(masters, changed)); err != nil { diff --git a/provider/rancher/config_test.go b/provider/rancher/config_test.go index a73150ed0..1a5f529fd 100644 --- a/provider/rancher/config_test.go +++ b/provider/rancher/config_test.go @@ -743,7 +743,7 @@ func TestProviderBuildConfiguration(t *testing.T) { func TestProviderServiceFilter(t *testing.T) { provider := &Provider{ - Domain: "rancher.localhost", + Domain: "rancher.localhost", EnableServiceHealthFilter: true, } diff --git a/provider/rancher/deprecated_config_test.go b/provider/rancher/deprecated_config_test.go index 6c92a889a..44988cb16 100644 --- a/provider/rancher/deprecated_config_test.go +++ b/provider/rancher/deprecated_config_test.go @@ -175,7 +175,7 @@ func TestProviderBuildConfigurationV1(t *testing.T) { func TestProviderServiceFilterV1(t *testing.T) { provider := &Provider{ - Domain: "rancher.localhost", + Domain: "rancher.localhost", EnableServiceHealthFilter: true, } diff --git a/provider/rancher/metadata.go b/provider/rancher/metadata.go index 76e91e550..0a54d96e4 100644 --- a/provider/rancher/metadata.go +++ b/provider/rancher/metadata.go @@ -29,7 +29,7 @@ func (p *Provider) metadataProvide(configurationChan chan<- types.ConfigMessage, operation := func() error { client, err := rancher.NewClientAndWait(metadataServiceURL) if err != nil { - log.Errorln("Failed to create Rancher metadata service client: %s", err) + log.Errorf("Failed to create Rancher metadata service client: %v", err) return err } @@ -38,7 +38,7 @@ func (p *Provider) metadataProvide(configurationChan chan<- types.ConfigMessage, stacks, err := client.GetStacks() if err != nil { - log.Errorf("Failed to query Rancher metadata service: %s", err) + log.Errorf("Failed to query Rancher metadata service: %v", err) return } diff --git a/vendor/github.com/vulcand/oxy/buffer/buffer.go b/vendor/github.com/vulcand/oxy/buffer/buffer.go index d2bbe40ce..68b387925 100644 --- a/vendor/github.com/vulcand/oxy/buffer/buffer.go +++ b/vendor/github.com/vulcand/oxy/buffer/buffer.go @@ -383,7 +383,14 @@ func (b *bufferWriter) Header() http.Header { } func (b *bufferWriter) Write(buf []byte) (int, error) { - return b.buffer.Write(buf) + length, err := b.buffer.Write(buf) + if err != nil { + // Since go1.11 (https://github.com/golang/go/commit/8f38f28222abccc505b9a1992deecfe3e2cb85de) + // if the writer returns an error, the reverse proxy panics + b.log.Error(err) + length = len(buf) + } + return length, nil } // WriteHeader sets rw.Code. @@ -410,7 +417,7 @@ func (b *bufferWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { return conn, rw, err } b.log.Warningf("Upstream ResponseWriter of type %v does not implement http.Hijacker. Returning dummy channel.", reflect.TypeOf(b.responseWriter)) - return nil, nil, fmt.Errorf("The response writer that was wrapped in this proxy, does not implement http.Hijacker. It is of type: %v", reflect.TypeOf(b.responseWriter)) + return nil, nil, fmt.Errorf("the response writer wrapped in this proxy does not implement http.Hijacker. Its type is: %v”", reflect.TypeOf(b.responseWriter)) } // SizeErrHandler Size error handler From c68ebaa2ca0f76863e65b994ef87a9f09d4cecad Mon Sep 17 00:00:00 2001 From: SALLEYRON Julien Date: Fri, 7 Sep 2018 11:34:03 +0200 Subject: [PATCH 9/9] Prepare release v1.7.0-rc4 --- CHANGELOG.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d0f5c2c2..6e384caf7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,44 @@ # Change Log +## [v1.7.0-rc4](https://github.com/containous/traefik/tree/v1.7.0-rc4) (2018-09-07) +[All Commits](https://github.com/containous/traefik/compare/v1.7.0-rc3...v1.7.0-rc4) + +**Enhancements:** +- **[acme]** Use official Pebble Image. ([#3708](https://github.com/containous/traefik/pull/3708) by [ldez](https://github.com/ldez)) +- **[consulcatalog]** Multiple frontends for consulcatalog ([#3796](https://github.com/containous/traefik/pull/3796) by [hsmade](https://github.com/hsmade)) +- **[ecs]** Add segment support for ECS ([#3817](https://github.com/containous/traefik/pull/3817) by [mmatur](https://github.com/mmatur)) +- **[k8s]** Remove unnecessary loop ([#3799](https://github.com/containous/traefik/pull/3799) by [ZloyDyadka](https://github.com/ZloyDyadka)) +- **[middleware,consulcatalog,docker,ecs,kv,marathon,mesos,rancher]** Pass the TLS Cert infos in headers ([#3826](https://github.com/containous/traefik/pull/3826) by [jbdoumenjou](https://github.com/jbdoumenjou)) + +**Bug fixes:** +- **[acme,cluster]** StoreConfig always initializes the account if it is missing ([#3844](https://github.com/containous/traefik/pull/3844) by [geraldcroes](https://github.com/geraldcroes)) +- **[acme]** Set a keyType to ACME if the account is stored with no KeyType ([#3733](https://github.com/containous/traefik/pull/3733) by [nmengin](https://github.com/nmengin)) +- **[authentication,consulcatalog,docker,ecs,k8s,kv,marathon,mesos,rancher]** Auth Forward with certificates in templates. ([#3804](https://github.com/containous/traefik/pull/3804) by [ldez](https://github.com/ldez)) +- **[k8s]** Prevent unparsable strings from being rendered in the Kubernetes template ([#3753](https://github.com/containous/traefik/pull/3753) by [dtomcej](https://github.com/dtomcej)) +- **[k8s]** Don't merge kubernetes ingresses when priority is set ([#3743](https://github.com/containous/traefik/pull/3743) by [dtomcej](https://github.com/dtomcej)) +- **[kv]** Include missing key in error message for KV store ([#3779](https://github.com/containous/traefik/pull/3779) by [camelpunch](https://github.com/camelpunch)) +- **[metrics]** Avoid a panic during Prometheus registering ([#3717](https://github.com/containous/traefik/pull/3717) by [nmengin](https://github.com/nmengin)) +- **[middleware,websocket]** Enable retry on websocket ([#3825](https://github.com/containous/traefik/pull/3825) by [Juliens](https://github.com/Juliens)) +- **[middleware]** Extend https redirection tests, and fix incorrect behavior ([#3742](https://github.com/containous/traefik/pull/3742) by [dtomcej](https://github.com/dtomcej)) +- **[oxy]** Handle Te header when http2 ([#3824](https://github.com/containous/traefik/pull/3824) by [Juliens](https://github.com/Juliens)) +- **[server]** Avoid goroutine leak in server ([#3851](https://github.com/containous/traefik/pull/3851) by [nmengin](https://github.com/nmengin)) + +**Documentation:** +- **[acme]** Fix documentation for route53 acme provider ([#3811](https://github.com/containous/traefik/pull/3811) by [A-Shleifman](https://github.com/A-Shleifman)) +- **[acme]** Update ACME documentation about TLS-ALPN challenge ([#3756](https://github.com/containous/traefik/pull/3756) by [ldez](https://github.com/ldez)) +- **[docker]** Change syntax in quick start guide ([#3726](https://github.com/containous/traefik/pull/3726) by [trotro](https://github.com/trotro)) +- **[docker]** Improve the wording in the documentation for Docker and fix title for Docker User Guide ([#3797](https://github.com/containous/traefik/pull/3797) by [dduportal](https://github.com/dduportal)) +- **[docker]** Typo in docker-and-lets-encrypt.md ([#3724](https://github.com/containous/traefik/pull/3724) by [A-Shleifman](https://github.com/A-Shleifman)) +- **[k8s]** Update kubernetes docs to reflect https options ([#3807](https://github.com/containous/traefik/pull/3807) by [dtomcej](https://github.com/dtomcej)) +- **[k8s]** Update kubernetes.md ([#3719](https://github.com/containous/traefik/pull/3719) by [kmaris](https://github.com/kmaris)) +- **[k8s]** Improve Connection Limit Kubernetes Documentation ([#3711](https://github.com/containous/traefik/pull/3711) by [dtomcej](https://github.com/dtomcej)) +- **[provider]** Typo in auth labels. ([#3730](https://github.com/containous/traefik/pull/3730) by [ldez](https://github.com/ldez)) +- **[tracing]** Simple documentation grammar update in tracing ([#3720](https://github.com/containous/traefik/pull/3720) by [loadstar81](https://github.com/loadstar81)) +- Make the "base domain" on all providers ([#3835](https://github.com/containous/traefik/pull/3835) by [dduportal](https://github.com/dduportal)) + +**Misc:** +- Merge v1.6.6 into v1.7 ([#3802](https://github.com/containous/traefik/pull/3802) by [ldez](https://github.com/ldez)) + ## [v1.6.6](https://github.com/containous/traefik/tree/v1.6.6) (2018-08-20) [All Commits](https://github.com/containous/traefik/compare/v1.6.5...v1.6.6)