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 adba7cead0..bb5975e4ec 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 b6abce863e..5fd14d24df 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 9c66ac1109..2d78a93b6f 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 2647523f05..8cf540daca 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 f21850c258..47899bd9e7 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 abb0eebc9e..0d3e325805 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 214ace6628..ae4d88a6c8 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 35d2bbe5d6..754a3bf303 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 4eb124a54e..9a8da9e693 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 3f356729c2..a9c342305f 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 b1dbbbe491..0089dee156 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 beb4e0b5e8..f77aafb2de 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 e6ad58d193..baf65a943c 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 e1b83e85ad..60b31e7c18 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 f34272bf40..8bbcebaefe 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 51bc7c1be8..2b24194a03 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 0000000000..633325fc9a --- /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 0000000000..f5d2189045 --- /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 0000000000..97c39f7308 --- /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 0000000000..40809f3159 --- /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 c817b2276a..f68e62b6bf 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 0000000000..42ba2007b3 --- /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 0000000000..373e855edc --- /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 0000000000..ac4a5cb411 --- /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 0000000000..583da2bb60 --- /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 94020e40fe..f148a73a09 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 11d7f440c6..f45a655fe4 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 546e34c1e2..0e0713d6b3 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 6e340c74a1..89aa7d9106 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 a8cbdca4ee..187e5aa418 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 1452b5fb34..5a0340c66c 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 c22ea3f459..fcc0ab1ee0 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 bd6efa29aa..cf08747b12 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 0d9ff28a6c..0aa7e2ce61 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 27c1bdb4e0..f021e1eefa 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 ed76807802..bf02ce4b7b 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 e1f604d241..b41e4653e4 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 bafa49c611..59a8c7684f 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 aec443edd7..39289cc3be 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 796acfe15b..5ffa6415c0 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 8b0f61af90..0194ba4c65 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 f6a5d3c9a7..e7471c6497 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 7a379b3a1d..47a5ef9ceb 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 6b5af71d70..13d195b4eb 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 6b913333da..ccd8a43d1d 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 600813e192..a73150ed0a 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 1f82e0759f..53583281a9 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 861f8f010b..7d7efc6de0 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 1a1a1a89e5..4ab3938f92 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 a7ee338aec..12d762efe9 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 c200551c5f..83b379b9b2 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 256e300fb9..59c0407929 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 afeebdabce..f4315cf047 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 01b1b09adb..21ec75a9fd 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 1927e694d9..8df2c59398 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 96500c41bc..ca594a11bd 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 0760c870ae..32a7583dee 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 b92557afbb..a3dd62078a 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 1def53435d..ca935779e0 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 6ecbdf1cd5..57c631dedc 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 b73044b295..9c51c567f1 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 9a8da9e693..96e219ed1f 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 0089dee156..a4f7c5d369 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 baf65a943c..0db0de8c79 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 60b31e7c18..5f4e4730fb 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 8bbcebaefe..a9aff127af 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 412aff25c6..483d6c9c10 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 e6e8aa932b..b9e8bf3ef2 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 74adf62d48..9a876e3f7e 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 3ea8fef243..b592d8bdc2 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 fe40f8e263..7789456aeb 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 5db244e92e..5199c19659 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 53583281a9..4415223b79 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 41ef7ab2cf..773eb8be01 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 fb8514c9ac..8472696d7e 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 674896b437..05cf4eace2 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 1f6a4ac5e0..980f061533 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 358d7c2637..e0b9792e76 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 bb5975e4ec..3ffe72bce8 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 99d8a67d2a..d7a279b5d1 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 08e00133af..851f2296ca 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 5acad5035b..c395dfb343 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 ae4d88a6c8..2ef6a5003b 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 f24ba87665..8a6e9bba02 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 0657cfffb4..cbf1da5fc8 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 a352aad128..578286b951 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 57c631dedc..ed9f339d91 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 15a90ddc2f..96642e3ff8 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 b4595eb689..754e218419 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 85a8beaafd..4f44040f2b 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 a73150ed0a..1a5f529fd5 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 6c92a889ac..44988cb160 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 76e91e5508..0a54d96e48 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 d2bbe40ce1..68b3879254 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 4d0f5c2c27..6e384caf7d 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)