diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d0f5c2c2..6e384caf7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,44 @@ # Change Log +## [v1.7.0-rc4](https://github.com/containous/traefik/tree/v1.7.0-rc4) (2018-09-07) +[All Commits](https://github.com/containous/traefik/compare/v1.7.0-rc3...v1.7.0-rc4) + +**Enhancements:** +- **[acme]** Use official Pebble Image. ([#3708](https://github.com/containous/traefik/pull/3708) by [ldez](https://github.com/ldez)) +- **[consulcatalog]** Multiple frontends for consulcatalog ([#3796](https://github.com/containous/traefik/pull/3796) by [hsmade](https://github.com/hsmade)) +- **[ecs]** Add segment support for ECS ([#3817](https://github.com/containous/traefik/pull/3817) by [mmatur](https://github.com/mmatur)) +- **[k8s]** Remove unnecessary loop ([#3799](https://github.com/containous/traefik/pull/3799) by [ZloyDyadka](https://github.com/ZloyDyadka)) +- **[middleware,consulcatalog,docker,ecs,kv,marathon,mesos,rancher]** Pass the TLS Cert infos in headers ([#3826](https://github.com/containous/traefik/pull/3826) by [jbdoumenjou](https://github.com/jbdoumenjou)) + +**Bug fixes:** +- **[acme,cluster]** StoreConfig always initializes the account if it is missing ([#3844](https://github.com/containous/traefik/pull/3844) by [geraldcroes](https://github.com/geraldcroes)) +- **[acme]** Set a keyType to ACME if the account is stored with no KeyType ([#3733](https://github.com/containous/traefik/pull/3733) by [nmengin](https://github.com/nmengin)) +- **[authentication,consulcatalog,docker,ecs,k8s,kv,marathon,mesos,rancher]** Auth Forward with certificates in templates. ([#3804](https://github.com/containous/traefik/pull/3804) by [ldez](https://github.com/ldez)) +- **[k8s]** Prevent unparsable strings from being rendered in the Kubernetes template ([#3753](https://github.com/containous/traefik/pull/3753) by [dtomcej](https://github.com/dtomcej)) +- **[k8s]** Don't merge kubernetes ingresses when priority is set ([#3743](https://github.com/containous/traefik/pull/3743) by [dtomcej](https://github.com/dtomcej)) +- **[kv]** Include missing key in error message for KV store ([#3779](https://github.com/containous/traefik/pull/3779) by [camelpunch](https://github.com/camelpunch)) +- **[metrics]** Avoid a panic during Prometheus registering ([#3717](https://github.com/containous/traefik/pull/3717) by [nmengin](https://github.com/nmengin)) +- **[middleware,websocket]** Enable retry on websocket ([#3825](https://github.com/containous/traefik/pull/3825) by [Juliens](https://github.com/Juliens)) +- **[middleware]** Extend https redirection tests, and fix incorrect behavior ([#3742](https://github.com/containous/traefik/pull/3742) by [dtomcej](https://github.com/dtomcej)) +- **[oxy]** Handle Te header when http2 ([#3824](https://github.com/containous/traefik/pull/3824) by [Juliens](https://github.com/Juliens)) +- **[server]** Avoid goroutine leak in server ([#3851](https://github.com/containous/traefik/pull/3851) by [nmengin](https://github.com/nmengin)) + +**Documentation:** +- **[acme]** Fix documentation for route53 acme provider ([#3811](https://github.com/containous/traefik/pull/3811) by [A-Shleifman](https://github.com/A-Shleifman)) +- **[acme]** Update ACME documentation about TLS-ALPN challenge ([#3756](https://github.com/containous/traefik/pull/3756) by [ldez](https://github.com/ldez)) +- **[docker]** Change syntax in quick start guide ([#3726](https://github.com/containous/traefik/pull/3726) by [trotro](https://github.com/trotro)) +- **[docker]** Improve the wording in the documentation for Docker and fix title for Docker User Guide ([#3797](https://github.com/containous/traefik/pull/3797) by [dduportal](https://github.com/dduportal)) +- **[docker]** Typo in docker-and-lets-encrypt.md ([#3724](https://github.com/containous/traefik/pull/3724) by [A-Shleifman](https://github.com/A-Shleifman)) +- **[k8s]** Update kubernetes docs to reflect https options ([#3807](https://github.com/containous/traefik/pull/3807) by [dtomcej](https://github.com/dtomcej)) +- **[k8s]** Update kubernetes.md ([#3719](https://github.com/containous/traefik/pull/3719) by [kmaris](https://github.com/kmaris)) +- **[k8s]** Improve Connection Limit Kubernetes Documentation ([#3711](https://github.com/containous/traefik/pull/3711) by [dtomcej](https://github.com/dtomcej)) +- **[provider]** Typo in auth labels. ([#3730](https://github.com/containous/traefik/pull/3730) by [ldez](https://github.com/ldez)) +- **[tracing]** Simple documentation grammar update in tracing ([#3720](https://github.com/containous/traefik/pull/3720) by [loadstar81](https://github.com/loadstar81)) +- Make the "base domain" on all providers ([#3835](https://github.com/containous/traefik/pull/3835) by [dduportal](https://github.com/dduportal)) + +**Misc:** +- Merge v1.6.6 into v1.7 ([#3802](https://github.com/containous/traefik/pull/3802) by [ldez](https://github.com/ldez)) + ## [v1.6.6](https://github.com/containous/traefik/tree/v1.6.6) (2018-08-20) [All Commits](https://github.com/containous/traefik/compare/v1.6.5...v1.6.6) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1f6a4ac5e..980f06153 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,7 +13,7 @@ You need to run the `binary` target. This will create binaries for Linux platfor $ make binary docker build -t "traefik-dev:no-more-godep-ever" -f build.Dockerfile . Sending build context to Docker daemon 295.3 MB -Step 0 : FROM golang:1.10-alpine +Step 0 : FROM golang:1.11-alpine ---> 8c6473912976 Step 1 : RUN go get github.com/golang/dep/cmd/dep [...] diff --git a/Gopkg.lock b/Gopkg.lock index 3a6374681..f16434d5c 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -1263,7 +1263,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 f0bc25fc6..ad8e56045 100644 --- a/anonymize/anonymize_config_test.go +++ b/anonymize/anonymize_config_test.go @@ -51,7 +51,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, }, }, @@ -100,7 +100,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, }, }, @@ -185,7 +185,7 @@ func TestDo_globalConfiguration(t *testing.T) { config.ProvidersThrottleDuration = parse.Duration(666 * time.Second) config.MaxIdleConnsPerHost = 666 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, } @@ -217,7 +217,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, Endpoint: "docker Endpoint", @@ -248,7 +248,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, Directory: "file Directory", @@ -269,7 +269,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, Endpoint: "", @@ -309,7 +309,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, Endpoint: "ConsulCatalog Endpoint", @@ -334,7 +334,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, Endpoint: "k8s Endpoint", @@ -360,7 +360,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, Endpoint: "mesos Endpoint", @@ -389,7 +389,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, Endpoint: "eureka Endpoint", @@ -411,7 +411,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, Domain: "ecs Domain", @@ -439,7 +439,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, APIConfiguration: rancher.APIConfiguration{ @@ -477,7 +477,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, AccessKeyID: "dynamodb AccessKeyID", @@ -504,7 +504,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, Endpoint: "etcd Endpoint", @@ -536,7 +536,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, Endpoint: "zk Endpoint", @@ -568,7 +568,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, Endpoint: "boltdb Endpoint", @@ -600,7 +600,7 @@ func TestDo_globalConfiguration(t *testing.T) { MustMatch: true, }, }, - Trace: true, + Trace: true, DebugLogGeneratedTemplate: true, }, Endpoint: "consul Endpoint", diff --git a/autogen/gentemplates/gen.go b/autogen/gentemplates/gen.go index 392951292..e4b2a7087 100644 --- a/autogen/gentemplates/gen.go +++ b/autogen/gentemplates/gen.go @@ -129,8 +129,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 }}" @@ -375,6 +397,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] @@ -622,6 +667,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] @@ -1125,6 +1193,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] @@ -1387,7 +1478,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 }}" @@ -1634,6 +1748,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] @@ -1903,6 +2040,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/build.Dockerfile b/build.Dockerfile index 99d8a67d2..d7a279b5d 100644 --- a/build.Dockerfile +++ b/build.Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.10-alpine +FROM golang:1.11-alpine RUN apk --update upgrade \ && apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar \ diff --git a/cmd/bug/bug.go b/cmd/bug/bug.go index 08e00133a..851f2296c 100644 --- a/cmd/bug/bug.go +++ b/cmd/bug/bug.go @@ -94,7 +94,7 @@ func NewCmd(traefikConfiguration *cmd.TraefikConfiguration, traefikPointersConfi Description: `Report an issue on Traefik bugtracker`, Config: traefikConfiguration, DefaultPointersConfig: traefikPointersConfiguration, - Run: runCmd(traefikConfiguration), + Run: runCmd(traefikConfiguration), Metadata: map[string]string{ "parseAllSources": "true", }, diff --git a/cmd/bug/bug_test.go b/cmd/bug/bug_test.go index 9c66ac110..2d78a93b6 100644 --- a/cmd/bug/bug_test.go +++ b/cmd/bug/bug_test.go @@ -34,7 +34,7 @@ func Test_createReport(t *testing.T) { File: &file.Provider{ Directory: "BAR", }, - RootCAs: tls.RootCAs{"fllf"}, + RootCAs: tls.FilesOrContents{"fllf"}, }, } diff --git a/cmd/healthcheck/healthcheck.go b/cmd/healthcheck/healthcheck.go index 6a9d585c7..1e31ec679 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/cmd/storeconfig/storeconfig.go b/cmd/storeconfig/storeconfig.go index 412aff25c..483d6c9c1 100644 --- a/cmd/storeconfig/storeconfig.go +++ b/cmd/storeconfig/storeconfig.go @@ -85,8 +85,13 @@ func Run(kv *staert.KvSource, traefikConfiguration *cmd.TraefikConfiguration) fu } } + accountInitialized, err := keyExists(kv, traefikConfiguration.GlobalConfiguration.ACME.Storage) + if err != nil { + return err + } + // Check to see if ACME account object is already in kv store - if traefikConfiguration.GlobalConfiguration.ACME.OverrideCertificates { + if traefikConfiguration.GlobalConfiguration.ACME.OverrideCertificates || !accountInitialized { // Store the ACME Account into the KV Store // Certificates in KV Store will be overridden @@ -114,6 +119,15 @@ func Run(kv *staert.KvSource, traefikConfiguration *cmd.TraefikConfiguration) fu } } +func keyExists(source *staert.KvSource, key string) (bool, error) { + list, err := source.List(key, nil) + if err != nil { + return false, err + } + + return len(list) > 0, nil +} + // migrateACMEData allows migrating data from acme.json file to KV store in function of the file format func migrateACMEData(fileName string) (*acme.Account, error) { diff --git a/cmd/traefik/traefik.go b/cmd/traefik/traefik.go index 070d02759..e9efb920e 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 85453aae9..5dc736e07 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -75,7 +75,7 @@ type GlobalConfiguration struct { ProvidersThrottleDuration parse.Duration `description:"Backends throttle duration: minimum duration between 2 events from providers before applying a new configuration. It avoids unnecessary reloads if multiples events are sent in a short amount of time." export:"true"` MaxIdleConnsPerHost int `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used" export:"true"` 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 01f4558a5..3062ad237 100644 --- a/configuration/entrypoints.go +++ b/configuration/entrypoints.go @@ -248,7 +248,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 3557dc87c..d3b5a4c84 100644 --- a/configuration/entrypoints_test.go +++ b/configuration/entrypoints_test.go @@ -69,21 +69,21 @@ 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", - "whitelist_sourcerange": "10.42.0.0/16,152.89.1.33/32,afed:be44::/16", - "whitelist_ipstrategy_depth": "3", - "whitelist_ipstrategy_excludedips": "10.0.0.3/24,20.0.0.3/24", - "clientipstrategy_depth": "3", - "clientipstrategy_excludedips": "10.0.0.3/24,20.0.0.3/24", + "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", + "whitelist_sourcerange": "10.42.0.0/16,152.89.1.33/32,afed:be44::/16", + "whitelist_ipstrategy_depth": "3", + "whitelist_ipstrategy_excludedips": "10.0.0.3/24,20.0.0.3/24", + "clientipstrategy_depth": "3", + "clientipstrategy_excludedips": "10.0.0.3/24,20.0.0.3/24", }, }, { @@ -232,7 +232,7 @@ func TestEntryPoints_Set(t *testing.T) { }, }, ClientCA: tls.ClientCA{ - Files: []string{"car"}, + Files: tls.FilesOrContents{"car"}, Optional: true, }, }, @@ -352,7 +352,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 87fde9fb3..06bdc7105 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 91c8a9123..e1659ee0e 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 # @@ -94,63 +94,73 @@ 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.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.ipStrategy=true` | Uses the default IPStrategy.
Can be used when there is an existing `clientIPStrategy` but you want the remote address for whitelisting. | -| `.frontend.whiteList.ipStrategy.depth=5` | See [whitelist](/configuration/entrypoints/#white-listing) | -| `.frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | See [whitelist](/configuration/entrypoints/#white-listing) | +| 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.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.ipStrategy=true` | Uses the default IPStrategy.
Can be used when there is an existing `clientIPStrategy` but you want the remote address for whitelisting. | +| `.frontend.whiteList.ipStrategy.depth=5` | See [whitelist](/configuration/entrypoints/#white-listing) | +| `.frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | See [whitelist](/configuration/entrypoints/#white-listing) | ### Multiple frontends for a single service @@ -201,7 +211,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/docker.md b/docs/configuration/backends/docker.md index 37c61c610..1a1278cf6 100644 --- a/docs/configuration/backends/docker.md +++ b/docs/configuration/backends/docker.md @@ -207,68 +207,78 @@ 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.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.ipStrategy=true` | Uses the default IPStrategy.
Can be used when there is an existing `clientIPStrategy` but you want the remote address for whitelisting. | -| `traefik.frontend.whiteList.ipStrategy.depth=5` | See [whitelist](/configuration/entrypoints/#white-listing) | -| `traefik.frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | See [whitelist](/configuration/entrypoints/#white-listing) | +| 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.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.ipStrategy=true` | Uses the default IPStrategy.
Can be used when there is an existing `clientIPStrategy` but you want the remote address for whitelisting. | +| `traefik.frontend.whiteList.ipStrategy.depth=5` | See [whitelist](/configuration/entrypoints/#white-listing) | +| `traefik.frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | See [whitelist](/configuration/entrypoints/#white-listing) | [1] `traefik.docker.network`: 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,48 +330,58 @@ 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.ipStrategy=true` | Same as `traefik.frontend.whiteList.ipStrategy` | -| `traefik..frontend.whiteList.ipStrategy.depth=5` | Same as `traefik.frontend.whiteList.ipStrategy.depth` | -| `traefik..frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | Same as `traefik.frontend.whiteList.ipStrategy.excludedIPs` | +| 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.ipStrategy=true` | Same as `traefik.frontend.whiteList.ipStrategy` | +| `traefik..frontend.whiteList.ipStrategy.depth=5` | Same as `traefik.frontend.whiteList.ipStrategy.depth` | +| `traefik..frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | Same as `traefik.frontend.whiteList.ipStrategy.excludedIPs` | #### Custom Headers diff --git a/docs/configuration/backends/ecs.md b/docs/configuration/backends/ecs.md index c2bd67b4c..c1631d6e0 100644 --- a/docs/configuration/backends/ecs.md +++ b/docs/configuration/backends/ecs.md @@ -26,7 +26,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 @@ -130,67 +130,77 @@ Træfik needs the following policy to read ECS information: Labels can be used on task containers to override default behavior: -| 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.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.ipStrategy=true` | Uses the default IPStrategy.
Can be used when there is an existing `clientIPStrategy` but you want the remote address for whitelisting. | -| `traefik.frontend.whiteList.ipStrategy.depth=5` | See [whitelist](/configuration/entrypoints/#white-listing) | -| `traefik.frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | See [whitelist](/configuration/entrypoints/#white-listing) | +| 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.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.ipStrategy=true` | Uses the default IPStrategy.
Can be used when there is an existing `clientIPStrategy` but you want the remote address for whitelisting. | +| `traefik.frontend.whiteList.ipStrategy.depth=5` | See [whitelist](/configuration/entrypoints/#white-listing) | +| `traefik.frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | See [whitelist](/configuration/entrypoints/#white-listing) | ### Custom Headers @@ -232,50 +242,60 @@ 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` | -| `traefik..frontend.whiteList.ipStrategy=true` | Same as `traefik.frontend.whiteList.ipStrategy` | -| `traefik..frontend.whiteList.ipStrategy.depth=5` | Same as `traefik.frontend.whiteList.ipStrategy.depth` | -| `traefik..frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | Same as `traefik.frontend.whiteList.ipStrategy.excludedIPs` | +| 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` | +| `traefik..frontend.whiteList.ipStrategy=true` | Same as `traefik.frontend.whiteList.ipStrategy` | +| `traefik..frontend.whiteList.ipStrategy.depth=5` | Same as `traefik.frontend.whiteList.ipStrategy.depth` | +| `traefik..frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | Same as `traefik.frontend.whiteList.ipStrategy.excludedIPs` | #### Custom Headers diff --git a/docs/configuration/backends/file.md b/docs/configuration/backends/file.md index b4f0c284a..30f1078d7 100644 --- a/docs/configuration/backends/file.md +++ b/docs/configuration/backends/file.md @@ -53,9 +53,24 @@ Træfik can be configured with a file. entryPoints = ["http", "https"] backend = "backend1" passHostHeader = true - passTLSCert = true priority = 42 + [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 8dcf7c4ba..f66868610 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 @@ -193,68 +193,78 @@ 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.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.ipStrategy=true` | Uses the default IPStrategy.
Can be used when there is an existing `clientIPStrategy` but you want the remote address for whitelisting. | -| `traefik.frontend.whiteList.ipStrategy.depth=5` | See [whitelist](/configuration/entrypoints/#white-listing) | -| `traefik.frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | See [whitelist](/configuration/entrypoints/#white-listing) | +| Label | Description | +|---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `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. | +| `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.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.ipStrategy=true` | Uses the default IPStrategy.
Can be used when there is an existing `clientIPStrategy` but you want the remote address for whitelisting. | +| `traefik.frontend.whiteList.ipStrategy.depth=5` | See [whitelist](/configuration/entrypoints/#white-listing) | +| `traefik.frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | See [whitelist](/configuration/entrypoints/#white-listing) | #### Custom Headers @@ -326,6 +336,16 @@ Segment labels override the default behavior. | `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` | diff --git a/docs/configuration/backends/mesos.md b/docs/configuration/backends/mesos.md index 3f4febf15..2e62487ad 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 @@ -154,6 +154,16 @@ The following labels can be defined on Mesos tasks. They adjust the behavior for | `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. | @@ -242,6 +252,16 @@ Segment labels override the default behavior. | `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` | diff --git a/docs/configuration/backends/rancher.md b/docs/configuration/backends/rancher.md index 9a86083e8..c98b0e1e5 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 @@ -138,66 +138,76 @@ 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.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.ipStrategy=true` | Uses the default IPStrategy.
Can be used when there is an existing `clientIPStrategy` but you want the remote address for whitelisting. | -| `traefik.frontend.whiteList.ipStrategy.depth=5` | See [whitelist](/configuration/entrypoints/#white-listing) | -| `traefik.frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | See [whitelist](/configuration/entrypoints/#white-listing) | +| 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.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.ipStrategy=true` | Uses the default IPStrategy.
Can be used when there is an existing `clientIPStrategy` but you want the remote address for whitelisting. | +| `traefik.frontend.whiteList.ipStrategy.depth=5` | See [whitelist](/configuration/entrypoints/#white-listing) | +| `traefik.frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | See [whitelist](/configuration/entrypoints/#white-listing) | #### Custom Headers @@ -239,48 +249,58 @@ 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.ipStrategy=true` | Same as `traefik.frontend.whiteList.ipStrategy` | -| `traefik..frontend.whiteList.ipStrategy.depth=5` | Same as `traefik.frontend.whiteList.ipStrategy.depth` | -| `traefik..frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | Same as `traefik.frontend.whiteList.ipStrategy.excludedIPs` | +| 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.ipStrategy=true` | Same as `traefik.frontend.whiteList.ipStrategy` | +| `traefik..frontend.whiteList.ipStrategy.depth=5` | Same as `traefik.frontend.whiteList.ipStrategy.depth` | +| `traefik..frontend.whiteList.ipStrategy.excludedIPs=127.0.0.1` | Same as `traefik.frontend.whiteList.ipStrategy.excludedIPs` | #### Custom Headers diff --git a/docs/user-guide/examples.md b/docs/user-guide/examples.md index 51bc7c1be..2b24194a0 100644 --- a/docs/user-guide/examples.md +++ b/docs/user-guide/examples.md @@ -311,7 +311,6 @@ The `consul` provider contains the configuration. [frontends.frontend2] backend = "backend1" passHostHeader = true - passTLSCert = true entrypoints = ["https"] # overrides defaultEntryPoints [frontends.frontend2.routes.test_1] rule = "Host:{subdomain:[a-z]+}.localhost" diff --git a/healthcheck/healthcheck.go b/healthcheck/healthcheck.go index ba14bdcf5..f948b0867 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/integration/fixtures/tlsclientheaders/root.pem b/integration/fixtures/tlsclientheaders/root.pem new file mode 100644 index 000000000..633325fc9 --- /dev/null +++ b/integration/fixtures/tlsclientheaders/root.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDhDCCAmygAwIBAgIJAK4Ed0WF/YNQMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNV +BAYTAkZSMQ8wDQYDVQQIDAZGUkFOQ0UxETAPBgNVBAcMCFRPVUxPVVNFMRMwEQYD +VQQKDApjb250YWlub3VzMQ8wDQYDVQQDDAZzZXJ2ZXIwHhcNMTgwMzIxMTMzOTM4 +WhcNMjEwMTA4MTMzOTM4WjBXMQswCQYDVQQGEwJGUjEPMA0GA1UECAwGRlJBTkNF +MREwDwYDVQQHDAhUT1VMT1VTRTETMBEGA1UECgwKY29udGFpbm91czEPMA0GA1UE +AwwGc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2DfZMdW1 +QKmdOTPULt6WUMVFU3PUcovq4cVtvNAzAshduC/7nHZx60uFzVKLYnOfZ+5VYfOS +zfVXPvltmBSWga1Yj6CuzfDZwY1nkcoL+22yBD6x4w2nB7aFaPNgj6M4ALVEZRKX +lMow+a0c0mOr1kLHm99MT/oabcdI+wbAp8VnLz9DF6SD7iDjIOb4RjvmcyetBzwu +1rQYti0bFHOnLCxiz0asXly0zspFajWkbGkvBdvEoP2qOHMeTV604PaBwpIMX/ly +ymGgYUctHeC16ptDRDDj7Spmu7ec2NzjgNW+MOth6EkFlhYgg1OEIXP+IFJ5LbS8 +1t/Y+fDUoc6+IQIDAQABo1MwUTAdBgNVHQ4EFgQUYeZvrzWyLI3TjmTIJYpSTjTb +/XUwHwYDVR0jBBgwFoAUYeZvrzWyLI3TjmTIJYpSTjTb/XUwDwYDVR0TAQH/BAUw +AwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAYQL8d/WQxu7rE58GC7le53FNzujMNZ+h +1kdS35LrTXPv5b6QTi5oUGi5LCesP4HnCpGdMFodyydhY8rhIDZWEFgkJZOLZhdt +sAyRONdI/Ms/NGQO2oJD+TlV92e4k3ey4WJyXIFHXE2Apb77VlsiHp8pI/iF/R5t +h4o4OADG7k6Fjf/wx7A18ru2eoH+PcwA8i6sQaQ1qEwxC0b3rh2TwaCpFQVcmMv5 +5jKPRBN0UC0PyHwqFZsSg1folhMAIBAjUsHgA6WleN9zMCyLAIn0LSai1CpFby6o +d6xu6pp8pwot8YTL0yS5T0X9aNhK2/uDoP50ei6eWI3uuPa8NJxbyA== +-----END CERTIFICATE----- diff --git a/integration/fixtures/tlsclientheaders/server.key b/integration/fixtures/tlsclientheaders/server.key new file mode 100644 index 000000000..f5d218904 --- /dev/null +++ b/integration/fixtures/tlsclientheaders/server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAo0eupztBxEchz/9BbegBzKX35YUt0S2Xzp/mFM+hXQylWDHB +z3wED7R89v3sY6ePTk/tAT5l6uKjmQ/zRlQFf7QtVWKUtYq8rjuFn9/EeC+233mx +kVP7QAcuT6T8PzoUgysW6Tx3zz18VDRMnPhx1fjA1jAq3+IU03BpbFz7CkYCxkG/ +1wWHmsB16LH2bMxJrzapph2nSDnUkoATugSJec+DxTtX1hdjAaJK/JsIwioA/Lyy +6YgE2oX7uRZBou0bA3y0TDnFoIVAVqISYWfszTGDlwL+SD/P9GYa19GZk4imdp8j +LD/+J9eLuHG75rkROvE4xbSPbGIGkZOEYTmjHwIDAQABAoIBAGok81kroHlkdIqu +uW4lYOYVDq5agYp2RTXBpOTqhU/kJKjMz91+FXXQM1ytfbra9sJGGyCv27lyVD/w +qomRnXGDQ+U6DMpnwnjRoPBpm2M2QX/NsK11FuRsxqJn8sN3klYi8OX2tTw4EFb9 +GMECkZ4z88hJz9VzN26sqRwU5e2qw45Fhk+Jl6RBsiBfMGNGsmI5n1yIgvQd2PoM +wVxHI+bb3rWL7zE7wy2rb2c+J0P2gy7fZlFN2ZLkC5RjTqdzD2P4erp4gcpgffuO +0Epu7ZzuJ0UKCBXJOkhjlM79opLK6IBpF1YgxVCoMPbQVYAHP9hSwuz6hgc0ocwa ++6PqzSECgYEA1kTSFN8tHq2VMFgwPyguppSmeJJdIcnMYdicJNkv9YXeIt4mAk9c +Qm5eMLoqRJL94fdRDGb7QIqcfSrQODHy5dmqrTZd+TeSc4VRC1gZ7RPg5ja8b0dR +DoktPizIYzWrNEaEjhWojqYXT5DOOmNgDbOYrlR6Qdrd6VOmQkIgHz0CgYEAwxSf +NMe6LasWg9PYgLeVBcNc9oOjGvczOmNULngte8LpiJm4yzI0gMer20VdCtXYsyR1 +Zs/rItzSQuvr+3v5qW2NfJ/TaJkZ+bcc/fGJ2LcnM2Kfjfih8DSy5/MBzNM4cqw2 +arHVvQlAvfOSB8WoFzdXOS41Z+BumLsZE3/mMYsCgYBGNTKpCB+ep730o1DbwOzY +RGjvpPXDNn4zqWgwYsHmL0EEJ8pIg3x1f/h4+ucSpR9vRTxXVf8JvOFd2gN0BlnS +mqnkK6ZLHLxuAcb2cp28IwFULac8xx92JdifQMlASLuaW2jfrZUXeLC2r3oDg8Bb +fPeQV7nfjjmcVH5rw4MG+QKBgQCi4RH4oJZLUSEQWo3XEvDjCfYRgWFqv2FPa+W6 +ku7u+ZPBURAg4D9EEvLjtmt0A47WLCe1+v3JcvQ/mfnDVQTkOKs8lbmPCN3OSNx1 +DvnYLzwUxFCR2jljdKy3y4cCPI1R+YXJ2ceq+RHMR5Ty1k59a+BwxqsimxncfcL3 +K//H9wKBgQChT3kvF9Igcdna8g+JneGD6RHXJX1o80QrO+eWma4NozEOmXqA7R7r ++GwAyqy9GFM7pwUhHmhJAxILMBxR84EY7kCBvi1VlZ3JbT7w0gjjOqPHklvbsPj9 +BruA5xPMq1gzCOgejQIRoODtpH1S6Fi/YMTO6eq75qw6minHWi4dPw== +-----END RSA PRIVATE KEY----- diff --git a/integration/fixtures/tlsclientheaders/server.pem b/integration/fixtures/tlsclientheaders/server.pem new file mode 100644 index 000000000..97c39f730 --- /dev/null +++ b/integration/fixtures/tlsclientheaders/server.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDKjCCAhICCQDKAJTeuq3LHjANBgkqhkiG9w0BAQsFADBXMQswCQYDVQQGEwJG +UjEPMA0GA1UECAwGRlJBTkNFMREwDwYDVQQHDAhUT1VMT1VTRTETMBEGA1UECgwK +Y29udGFpbm91czEPMA0GA1UEAwwGc2VydmVyMB4XDTE4MDMyMTEzNDM0MVoXDTIx +MDEwODEzNDM0MVowVzELMAkGA1UEBhMCRlIxDzANBgNVBAgMBkZSQU5DRTERMA8G +A1UEBwwIVE9VTE9VU0UxEzARBgNVBAoMCmNvbnRhaW5vdXMxDzANBgNVBAMMBnNl +cnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKNHrqc7QcRHIc// +QW3oAcyl9+WFLdEtl86f5hTPoV0MpVgxwc98BA+0fPb97GOnj05P7QE+Zerio5kP +80ZUBX+0LVVilLWKvK47hZ/fxHgvtt95sZFT+0AHLk+k/D86FIMrFuk8d889fFQ0 +TJz4cdX4wNYwKt/iFNNwaWxc+wpGAsZBv9cFh5rAdeix9mzMSa82qaYdp0g51JKA +E7oEiXnPg8U7V9YXYwGiSvybCMIqAPy8sumIBNqF+7kWQaLtGwN8tEw5xaCFQFai +EmFn7M0xg5cC/kg/z/RmGtfRmZOIpnafIyw//ifXi7hxu+a5ETrxOMW0j2xiBpGT +hGE5ox8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAPYDdGyNWp7R9j2oxZEbQS4lb ++2Ol1r6PFo/zmpB6GK3CSNo65a0DtW/ITeQi97MMgGS1D3wnaFPrwxtp0mEn7HjU +uDcufHBqqBsjYC3NEtt+yyxNeYddLD/GdFXw4d6wNRdRaFCq5N1CPQzF4VTdoSLD +xsOq/WAHHc2cyZyOprAqm2UXyWXxn4yWZqzDsZ41/v2f3uMNxeqyIEtNZVzTKQBu +wWw+jlQKGu0T8Ex1f0jaKI1OPtN5dzaIfO8acHcuNdmnE+hVsoqe17Dckxsj1ORf +8ZcZ4qvULVouGINQBP4fcl5jv6TOm1U+ZSk01FcHPmiDEMB6Utyy4ZLHPbmKYg== +-----END CERTIFICATE----- diff --git a/integration/fixtures/tlsclientheaders/simple.toml b/integration/fixtures/tlsclientheaders/simple.toml new file mode 100644 index 000000000..40809f315 --- /dev/null +++ b/integration/fixtures/tlsclientheaders/simple.toml @@ -0,0 +1,24 @@ +logLevel = "DEBUG" +defaultEntryPoints = ["https"] +debug = true +rootCAs = [ """{{ .RootCertContent }}""" ] + +[entryPoints] + [entryPoints.https] + address = ":8443" + + [entryPoints.https.tls] + + [entryPoints.https.tls.ClientCA] + files = [ """{{ .RootCertContent }}""" ] + optional = false + + [[entryPoints.https.tls.certificates]] + certFile = """{{ .ServerCertContent }}""" + keyFile = """{{ .ServerKeyContent }}""" + +[api] + +[docker] +endpoint = "unix:///var/run/docker.sock" +watch = true diff --git a/integration/integration_test.go b/integration/integration_test.go index 44e280da5..880301d73 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -59,6 +59,7 @@ func init() { check.Suite(&RateLimitSuite{}) check.Suite(&RetrySuite{}) check.Suite(&SimpleSuite{}) + check.Suite(&TLSClientHeadersSuite{}) check.Suite(&TimeoutSuite{}) check.Suite(&TracingSuite{}) check.Suite(&WebsocketSuite{}) diff --git a/integration/resources/compose/tlsclientheaders.yml b/integration/resources/compose/tlsclientheaders.yml new file mode 100644 index 000000000..42ba2007b --- /dev/null +++ b/integration/resources/compose/tlsclientheaders.yml @@ -0,0 +1,6 @@ +whoami: + image: containous/whoami + labels: + - traefik.frontend.passTLSClientCert.pem=true + - traefik.frontend.rule=PathPrefix:/ + diff --git a/integration/retry_test.go b/integration/retry_test.go index 1def53435..ca935779e 100644 --- a/integration/retry_test.go +++ b/integration/retry_test.go @@ -7,6 +7,7 @@ import ( "github.com/containous/traefik/integration/try" "github.com/go-check/check" + "github.com/gorilla/websocket" checker "github.com/vdemeester/shakers" ) @@ -38,3 +39,29 @@ func (s *RetrySuite) TestRetry(c *check.C) { c.Assert(err, checker.IsNil) c.Assert(response.StatusCode, checker.Equals, http.StatusOK) } + +func (s *RetrySuite) TestRetryWebsocket(c *check.C) { + whoamiEndpoint := s.composeProject.Container(c, "whoami").NetworkSettings.IPAddress + file := s.adaptFile(c, "fixtures/retry/simple.toml", struct { + WhoamiEndpoint string + }{whoamiEndpoint}) + defer os.Remove(file) + + cmd, display := s.traefikCmd(withConfigFile(file)) + defer display(c) + err := cmd.Start() + c.Assert(err, checker.IsNil) + defer cmd.Process.Kill() + + err = try.GetRequest("http://127.0.0.1:8080/api/providers", 60*time.Second, try.BodyContains("PathPrefix:/")) + c.Assert(err, checker.IsNil) + + // This simulates a DialTimeout when connecting to the backend server. + _, response, err := websocket.DefaultDialer.Dial("ws://127.0.0.1:8000/echo", nil) + c.Assert(err, checker.IsNil) + c.Assert(response.StatusCode, checker.Equals, http.StatusSwitchingProtocols) + + _, response, err = websocket.DefaultDialer.Dial("ws://127.0.0.1:8000/echo", nil) + c.Assert(err, checker.IsNil) + c.Assert(response.StatusCode, checker.Equals, http.StatusSwitchingProtocols) +} diff --git a/integration/tls_client_headers_test.go b/integration/tls_client_headers_test.go new file mode 100644 index 000000000..373e855ed --- /dev/null +++ b/integration/tls_client_headers_test.go @@ -0,0 +1,71 @@ +package integration + +import ( + "crypto/tls" + "io/ioutil" + "net/http" + "os" + "time" + + "github.com/containous/traefik/integration/try" + "github.com/go-check/check" + checker "github.com/vdemeester/shakers" +) + +const ( + rootCertPath = "./fixtures/tlsclientheaders/root.pem" + certPemPath = "./fixtures/tlsclientheaders/server.pem" + certKeyPath = "./fixtures/tlsclientheaders/server.key" +) + +type TLSClientHeadersSuite struct{ BaseSuite } + +func (s *TLSClientHeadersSuite) SetUpSuite(c *check.C) { + s.createComposeProject(c, "tlsclientheaders") + s.composeProject.Start(c) +} + +func (s *TLSClientHeadersSuite) TestTLSClientHeaders(c *check.C) { + rootCertContent, err := ioutil.ReadFile(rootCertPath) + c.Assert(err, check.IsNil) + serverCertContent, err := ioutil.ReadFile(certPemPath) + c.Assert(err, check.IsNil) + ServerKeyContent, err := ioutil.ReadFile(certKeyPath) + c.Assert(err, check.IsNil) + + file := s.adaptFile(c, "fixtures/tlsclientheaders/simple.toml", struct { + RootCertContent string + ServerCertContent string + ServerKeyContent string + }{ + RootCertContent: string(rootCertContent), + ServerCertContent: string(serverCertContent), + ServerKeyContent: string(ServerKeyContent), + }) + defer os.Remove(file) + + cmd, display := s.traefikCmd(withConfigFile(file)) + defer display(c) + err = cmd.Start() + c.Assert(err, checker.IsNil) + defer cmd.Process.Kill() + + err = try.GetRequest("http://127.0.0.1:8080/api/providers", 2*time.Second, try.BodyContains("PathPrefix:/")) + c.Assert(err, checker.IsNil) + + request, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:8443", nil) + c.Assert(err, checker.IsNil) + + certificate, err := tls.LoadX509KeyPair(certPemPath, certKeyPath) + c.Assert(err, checker.IsNil) + + tr := &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + Certificates: []tls.Certificate{certificate}, + }, + } + + err = try.RequestWithTransport(request, 2*time.Second, tr, try.BodyContains("Forwarded-Tls-Client-Cert: MIIDKjCCAhICCQDKAJTeuq3LHjANBgkqhkiG9w0BAQsFADBXMQswCQYDVQQGEwJGUjEPMA0GA1UECAwGRlJBTkNFMREwDwYDVQQHDAhUT1VMT1VTRTETMBEGA1UECgwKY29udGFpbm91czEPMA0GA1UEAwwGc2VydmVyMB4XDTE4MDMyMTEzNDM0MVoXDTIxMDEwODEzNDM0MVowVzELMAkGA1UEBhMCRlIxDzANBgNVBAgMBkZSQU5DRTERMA8GA1UEBwwIVE9VTE9VU0UxEzARBgNVBAoMCmNvbnRhaW5vdXMxDzANBgNVBAMMBnNlcnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKNHrqc7QcRHIc%2F%2FQW3oAcyl9%2BWFLdEtl86f5hTPoV0MpVgxwc98BA%2B0fPb97GOnj05P7QE%2BZerio5kP80ZUBX%2B0LVVilLWKvK47hZ%2FfxHgvtt95sZFT%2B0AHLk%2Bk%2FD86FIMrFuk8d889fFQ0TJz4cdX4wNYwKt%2FiFNNwaWxc%2BwpGAsZBv9cFh5rAdeix9mzMSa82qaYdp0g51JKAE7oEiXnPg8U7V9YXYwGiSvybCMIqAPy8sumIBNqF%2B7kWQaLtGwN8tEw5xaCFQFaiEmFn7M0xg5cC%2Fkg%2Fz%2FRmGtfRmZOIpnafIyw%2F%2FifXi7hxu%2Ba5ETrxOMW0j2xiBpGThGE5ox8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAPYDdGyNWp7R9j2oxZEbQS4lb%2B2Ol1r6PFo%2FzmpB6GK3CSNo65a0DtW%2FITeQi97MMgGS1D3wnaFPrwxtp0mEn7HjUuDcufHBqqBsjYC3NEtt%2ByyxNeYddLD%2FGdFXw4d6wNRdRaFCq5N1CPQzF4VTdoSLDxsOq%2FWAHHc2cyZyOprAqm2UXyWXxn4yWZqzDsZ41%2Fv2f3uMNxeqyIEtNZVzTKQBuwWw%2BjlQKGu0T8Ex1f0jaKI1OPtN5dzaIfO8acHcuNdmnE%2BhVsoqe17Dckxsj1ORf8ZcZ4qvULVouGINQBP4fcl5jv6TOm1U%2BZSk01FcHPmiDEMB6Utyy4ZLHPbmKYg%3D%3D")) + c.Assert(err, checker.IsNil) +} diff --git a/middlewares/accesslog/logger_test.go b/middlewares/accesslog/logger_test.go index c2ade2df3..f78defdd8 100644 --- a/middlewares/accesslog/logger_test.go +++ b/middlewares/accesslog/logger_test.go @@ -193,26 +193,26 @@ 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("-"), - DownstreamStatus: assertFloat64(float64(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("-"), + DownstreamStatus: assertFloat64(float64(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(), @@ -233,9 +233,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), @@ -272,9 +272,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 cc66f4e92..a25921014 100644 --- a/middlewares/ip_whitelister.go +++ b/middlewares/ip_whitelister.go @@ -48,7 +48,7 @@ func (wl *IPWhiteLister) handle(w http.ResponseWriter, r *http.Request, next htt return } log.Debugf("Accept %s: %+v", wl.strategy.GetIP(r), r) - tracing.SetErrorAndDebugLog(r, "request %+v matched white list %s - passing", r, wl.whiteLister) + tracing.SetErrorAndDebugLog(r, "request %+v matched white list %v - passing", r, wl.whiteLister) next.ServeHTTP(w, r) } diff --git a/middlewares/retry.go b/middlewares/retry.go index 6ecbdf1cd..ed9f339d9 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 @@ -128,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) } @@ -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 9ca32a68e..792f3cdf7 100644 --- a/middlewares/retry_test.go +++ b/middlewares/retry_test.go @@ -3,22 +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", @@ -55,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) { @@ -75,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) @@ -87,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 @@ -101,24 +95,91 @@ func TestRetry(t *testing.T) { assert.NoError(t, err) 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) }) } } diff --git a/middlewares/tlsClientHeaders.go b/middlewares/tlsClientHeaders.go new file mode 100644 index 000000000..ac4a5cb41 --- /dev/null +++ b/middlewares/tlsClientHeaders.go @@ -0,0 +1,251 @@ +package middlewares + +import ( + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "fmt" + "net/http" + "net/url" + "strings" + + "github.com/containous/traefik/log" + "github.com/containous/traefik/types" +) + +const xForwardedTLSClientCert = "X-Forwarded-Tls-Client-Cert" +const xForwardedTLSClientCertInfos = "X-Forwarded-Tls-Client-Cert-Infos" + +// TLSClientCertificateInfos is a struct for specifying the configuration for the tlsClientHeaders middleware. +type TLSClientCertificateInfos struct { + NotAfter bool + NotBefore bool + Subject *TLSCLientCertificateSubjectInfos + Sans bool +} + +// TLSCLientCertificateSubjectInfos contains the configuration for the certificate subject infos. +type TLSCLientCertificateSubjectInfos struct { + Country bool + Province bool + Locality bool + Organization bool + CommonName bool + SerialNumber bool +} + +// TLSClientHeaders is a middleware that helps setup a few tls infos features. +type TLSClientHeaders struct { + PEM bool // pass the sanitized pem to the backend in a specific header + Infos *TLSClientCertificateInfos // pass selected informations from the client certificate +} + +func newTLSCLientCertificateSubjectInfos(infos *types.TLSCLientCertificateSubjectInfos) *TLSCLientCertificateSubjectInfos { + if infos == nil { + return nil + } + + return &TLSCLientCertificateSubjectInfos{ + SerialNumber: infos.SerialNumber, + CommonName: infos.CommonName, + Country: infos.Country, + Locality: infos.Locality, + Organization: infos.Organization, + Province: infos.Province, + } +} + +func newTLSClientInfos(infos *types.TLSClientCertificateInfos) *TLSClientCertificateInfos { + if infos == nil { + return nil + } + + return &TLSClientCertificateInfos{ + NotBefore: infos.NotBefore, + NotAfter: infos.NotAfter, + Sans: infos.Sans, + Subject: newTLSCLientCertificateSubjectInfos(infos.Subject), + } +} + +// NewTLSClientHeaders constructs a new TLSClientHeaders instance from supplied frontend header struct. +func NewTLSClientHeaders(frontend *types.Frontend) *TLSClientHeaders { + if frontend == nil { + return nil + } + + var pem bool + var infos *TLSClientCertificateInfos + + if frontend.PassTLSClientCert != nil { + conf := frontend.PassTLSClientCert + pem = conf.PEM + infos = newTLSClientInfos(conf.Infos) + } + + return &TLSClientHeaders{ + PEM: pem, + Infos: infos, + } +} + +func (s *TLSClientHeaders) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + s.ModifyRequestHeaders(r) + // If there is a next, call it. + if next != nil { + next(w, r) + } +} + +// sanitize As we pass the raw certificates, remove the useless data and make it http request compliant +func sanitize(cert []byte) string { + s := string(cert) + r := strings.NewReplacer("-----BEGIN CERTIFICATE-----", "", + "-----END CERTIFICATE-----", "", + "\n", "") + cleaned := r.Replace(s) + + return url.QueryEscape(cleaned) +} + +// extractCertificate extract the certificate from the request +func extractCertificate(cert *x509.Certificate) string { + b := pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw} + certPEM := pem.EncodeToMemory(&b) + if certPEM == nil { + log.Error("Cannot extract the certificate content") + return "" + } + return sanitize(certPEM) +} + +// getXForwardedTLSClientCert Build a string with the client certificates +func getXForwardedTLSClientCert(certs []*x509.Certificate) string { + var headerValues []string + + for _, peerCert := range certs { + headerValues = append(headerValues, extractCertificate(peerCert)) + } + + return strings.Join(headerValues, ",") +} + +// getSANs get the Subject Alternate Name values +func getSANs(cert *x509.Certificate) []string { + var sans []string + if cert == nil { + return sans + } + + sans = append(cert.DNSNames, cert.EmailAddresses...) + + var ips []string + for _, ip := range cert.IPAddresses { + ips = append(ips, ip.String()) + } + sans = append(sans, ips...) + + var uris []string + for _, uri := range cert.URIs { + uris = append(uris, uri.String()) + } + + return append(sans, uris...) +} + +// getSubjectInfos extract the requested informations from the certificate subject +func (s *TLSClientHeaders) getSubjectInfos(cs *pkix.Name) string { + var subject string + + if s.Infos != nil && s.Infos.Subject != nil { + options := s.Infos.Subject + + var content []string + + if options.Country && len(cs.Country) > 0 { + content = append(content, fmt.Sprintf("C=%s", cs.Country[0])) + } + + if options.Province && len(cs.Province) > 0 { + content = append(content, fmt.Sprintf("ST=%s", cs.Province[0])) + } + + if options.Locality && len(cs.Locality) > 0 { + content = append(content, fmt.Sprintf("L=%s", cs.Locality[0])) + } + + if options.Organization && len(cs.Organization) > 0 { + content = append(content, fmt.Sprintf("O=%s", cs.Organization[0])) + } + + if options.CommonName && len(cs.CommonName) > 0 { + content = append(content, fmt.Sprintf("CN=%s", cs.CommonName)) + } + + if len(content) > 0 { + subject = `Subject="` + strings.Join(content, ",") + `"` + } + } + + return subject +} + +// getXForwardedTLSClientCertInfos Build a string with the wanted client certificates informations +// like Subject="C=%s,ST=%s,L=%s,O=%s,CN=%s",NB=%d,NA=%d,SAN=%s; +func (s *TLSClientHeaders) getXForwardedTLSClientCertInfos(certs []*x509.Certificate) string { + var headerValues []string + + for _, peerCert := range certs { + var values []string + var sans string + var nb string + var na string + + subject := s.getSubjectInfos(&peerCert.Subject) + if len(subject) > 0 { + values = append(values, subject) + } + + ci := s.Infos + if ci != nil { + if ci.NotBefore { + nb = fmt.Sprintf("NB=%d", uint64(peerCert.NotBefore.Unix())) + values = append(values, nb) + } + if ci.NotAfter { + na = fmt.Sprintf("NA=%d", uint64(peerCert.NotAfter.Unix())) + values = append(values, na) + } + + if ci.Sans { + sans = fmt.Sprintf("SAN=%s", strings.Join(getSANs(peerCert), ",")) + values = append(values, sans) + } + } + + value := strings.Join(values, ",") + headerValues = append(headerValues, value) + } + + return strings.Join(headerValues, ";") +} + +// ModifyRequestHeaders set the wanted headers with the certificates informations +func (s *TLSClientHeaders) ModifyRequestHeaders(r *http.Request) { + if s.PEM { + if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 { + r.Header.Set(xForwardedTLSClientCert, getXForwardedTLSClientCert(r.TLS.PeerCertificates)) + } else { + log.Warn("Try to extract certificate on a request without TLS") + } + } + + if s.Infos != nil { + if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 { + headerContent := s.getXForwardedTLSClientCertInfos(r.TLS.PeerCertificates) + r.Header.Set(xForwardedTLSClientCertInfos, url.QueryEscape(headerContent)) + } else { + log.Warn("Try to extract certificate on a request without TLS") + } + } +} diff --git a/middlewares/tlsClientHeaders_test.go b/middlewares/tlsClientHeaders_test.go new file mode 100644 index 000000000..583da2bb6 --- /dev/null +++ b/middlewares/tlsClientHeaders_test.go @@ -0,0 +1,799 @@ +package middlewares + +import ( + "crypto/tls" + "crypto/x509" + "encoding/pem" + "net" + "net/http" + "net/http/httptest" + "net/url" + "regexp" + "strings" + "testing" + + "github.com/containous/traefik/testhelpers" + "github.com/containous/traefik/types" + "github.com/stretchr/testify/require" +) + +const ( + rootCrt = `-----BEGIN CERTIFICATE----- +MIIDhjCCAm6gAwIBAgIJAIKZlW9a3VrYMA0GCSqGSIb3DQEBCwUAMFgxCzAJBgNV +BAYTAkZSMRMwEQYDVQQIDApTb21lLVN0YXRlMREwDwYDVQQHDAhUb3Vsb3VzZTEh +MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMB4XDTE4MDcxNzIwMzQz +OFoXDTE4MDgxNjIwMzQzOFowWDELMAkGA1UEBhMCRlIxEzARBgNVBAgMClNvbWUt +U3RhdGUxETAPBgNVBAcMCFRvdWxvdXNlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn +aXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1P8GJ +H9LkIxIIqK9MyUpushnjmjwccpSMB3OecISKYLy62QDIcAw6NzGcSe8hMwciMJr+ +CdCjJlohybnaRI9hrJ3GPnI++UT/MMthf2IIcjmJxmD4k9L1fgs1V6zSTlo0+o0x +0gkAGlWvRkgA+3nt555ee84XQZuneKKeRRIlSA1ygycewFobZ/pGYijIEko+gYkV +sF3LnRGxNl673w+EQsvI7+z29T1nzjmM/xE7WlvnsrVd1/N61jAohLota0YTufwd +ioJZNryzuPejHBCiQRGMbJ7uEEZLiSCN6QiZEfqhS3AulykjgFXQQHn4zoVljSBR +UyLV0prIn5Scbks/AgMBAAGjUzBRMB0GA1UdDgQWBBTroRRnSgtkV+8dumtcftb/ +lwIkATAfBgNVHSMEGDAWgBTroRRnSgtkV+8dumtcftb/lwIkATAPBgNVHRMBAf8E +BTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAJ67U5cLa0ZFa/7zQQT4ldkY6YOEgR +0LNoTu51hc+ozaXSvF8YIBzkEpEnbGS3x4xodrwEBZjK2LFhNu/33gkCAuhmedgk +KwZrQM6lqRFGHGVOlkVz+QrJ2EsKYaO4SCUIwVjijXRLA7A30G5C/CIh66PsMgBY +6QHXVPEWm/v1d1Q/DfFfFzSOa1n1rIUw03qVJsxqSwfwYcegOF8YvS/eH4HUr2gF +cEujh6CCnylf35ExHa45atr3+xxbOVdNjobISkYADtbhAAn4KjLS4v8W6445vxxj +G5EIZLjOHyWg1sGaHaaAPkVpZQg8EKm21c4hrEEMfel60AMSSzad/a/V +-----END CERTIFICATE-----` + + minimalCert = `-----BEGIN CERTIFICATE----- +MIIDGTCCAgECCQCqLd75YLi2kDANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJG +UjETMBEGA1UECAwKU29tZS1TdGF0ZTERMA8GA1UEBwwIVG91bG91c2UxITAfBgNV +BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0xODA3MTgwODI4MTZaFw0x +ODA4MTcwODI4MTZaMEUxCzAJBgNVBAYTAkZSMRMwEQYDVQQIDApTb21lLVN0YXRl +MSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQC/+frDMMTLQyXG34F68BPhQq0kzK4LIq9Y0/gl +FjySZNn1C0QDWA1ubVCAcA6yY204I9cxcQDPNrhC7JlS5QA8Y5rhIBrqQlzZizAi +Rj3NTrRjtGUtOScnHuJaWjLy03DWD+aMwb7q718xt5SEABmmUvLwQK+EjW2MeDwj +y8/UEIpvrRDmdhGaqv7IFpIDkcIF7FowJ/hwDvx3PMc+z/JWK0ovzpvgbx69AVbw +ZxCimeha65rOqVi+lEetD26le+WnOdYsdJ2IkmpPNTXGdfb15xuAc+gFXfMCh7Iw +3Ynl6dZtZM/Ok2kiA7/OsmVnRKkWrtBfGYkI9HcNGb3zrk6nAgMBAAEwDQYJKoZI +hvcNAQELBQADggEBAC/R+Yvhh1VUhcbK49olWsk/JKqfS3VIDQYZg1Eo+JCPbwgS +I1BSYVfMcGzuJTX6ua3m/AHzGF3Tap4GhF4tX12jeIx4R4utnjj7/YKkTvuEM2f4 +xT56YqI7zalGScIB0iMeyNz1QcimRl+M/49au8ow9hNX8C2tcA2cwd/9OIj/6T8q +SBRHc6ojvbqZSJCO0jziGDT1L3D+EDgTjED4nd77v/NRdP+egb0q3P0s4dnQ/5AV +aQlQADUn61j3ScbGJ4NSeZFFvsl38jeRi/MEzp0bGgNBcPj6JHi7qbbauZcZfQ05 +jECvgAY7Nfd9mZ1KtyNaW31is+kag7NsvjxU/kM= +-----END CERTIFICATE-----` + + completeCert = `Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=FR, ST=Some-State, L=Toulouse, O=Internet Widgits Pty Ltd + Validity + Not Before: Jul 18 08:00:16 2018 GMT + Not After : Jul 18 08:00:16 2019 GMT + Subject: C=FR, ST=SomeState, L=Toulouse, O=Cheese, CN=*.cheese.org + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:a6:1f:96:7c:c1:cc:b8:1c:b5:91:5d:b8:bf:70: + bc:f7:b8:04:4f:2a:42:de:ea:c5:c3:19:0b:03:04: + ec:ef:a1:24:25:de:ad:05:e7:26:ea:89:6c:59:60: + 10:18:0c:73:f1:bf:d3:cc:7b:ed:6b:9c:ea:1d:88: + e2:ee:14:81:d7:07:ee:87:95:3d:36:df:9c:38:b7: + 7b:1e:2b:51:9c:4a:1f:d0:cc:5b:af:5d:6c:5c:35: + 49:32:e4:01:5b:f9:8c:71:cf:62:48:5a:ea:b7:31: + 58:e2:c6:d0:5b:1c:50:b5:5c:6d:5a:6f:da:41:5e: + d5:4c:6e:1a:21:f3:40:f9:9e:52:76:50:25:3e:03: + 9b:87:19:48:5b:47:87:d3:67:c6:25:69:77:29:8e: + 56:97:45:d9:6f:64:a8:4e:ad:35:75:2e:fc:6a:2e: + 47:87:76:fc:4e:3e:44:e9:16:b2:c7:f0:23:98:13: + a2:df:15:23:cb:0c:3d:fd:48:5e:c7:2c:86:70:63: + 8b:c6:c8:89:17:52:d5:a7:8e:cb:4e:11:9d:69:8e: + 8e:59:cc:7e:a3:bd:a1:11:88:d7:cf:7b:8c:19:46: + 9c:1b:7a:c9:39:81:4c:58:08:1f:c7:ce:b0:0e:79: + 64:d3:11:72:65:e6:dd:bd:00:7f:22:30:46:9b:66: + 9c:b9 + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + X509v3 Subject Alternative Name: + DNS:*.cheese.org, DNS:*.cheese.net, DNS:cheese.in, IP Address:10.0.1.0, IP Address:10.0.1.2, email:test@cheese.org, email:test@cheese.net + X509v3 Subject Key Identifier: + AB:6B:89:25:11:FC:5E:7B:D4:B0:F7:D4:B6:D9:EB:D0:30:93:E5:58 + Signature Algorithm: sha1WithRSAEncryption + ad:87:84:a0:88:a3:4c:d9:0a:c0:14:e4:2d:9a:1d:bb:57:b7: + 12:ef:3a:fb:8b:b2:ce:32:b8:04:e6:59:c8:4f:14:6a:b5:12: + 46:e9:c9:0a:11:64:ea:a1:86:20:96:0e:a7:40:e3:aa:e5:98: + 91:36:89:77:b6:b9:73:7e:1a:58:19:ae:d1:14:83:1e:c1:5f: + a5:a0:32:bb:52:68:b4:8d:a3:1d:b3:08:d7:45:6e:3b:87:64: + 7e:ef:46:e6:6f:d5:79:d7:1d:57:68:67:d8:18:39:61:5b:8b: + 1a:7f:88:da:0a:51:9b:3d:6c:5d:b1:cf:b7:e9:1e:06:65:8e: + 96:d3:61:96:f8:a2:61:f9:40:5e:fa:bc:76:b9:64:0e:6f:90: + 37:de:ac:6d:7f:36:84:35:19:88:8c:26:af:3e:c3:6a:1a:03: + ed:d7:90:89:ed:18:4c:9e:94:1f:d8:ae:6c:61:36:17:72:f9: + bb:de:0a:56:9a:79:b4:7d:4a:9d:cb:4a:7d:71:9f:38:e7:8d: + f0:87:24:21:0a:24:1f:82:9a:6b:67:ce:7d:af:cb:91:6b:8a: + de:e6:d8:6f:a1:37:b9:2d:d0:cb:e8:4e:f4:43:af:ad:90:13: + 7d:61:7a:ce:86:48:fc:00:8c:37:fb:e0:31:6b:e2:18:ad:fd: + 1e:df:08:db +-----BEGIN CERTIFICATE----- +MIIDvTCCAqWgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJGUjET +MBEGA1UECAwKU29tZS1TdGF0ZTERMA8GA1UEBwwIVG91bG91c2UxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0xODA3MTgwODAwMTZaFw0xOTA3 +MTgwODAwMTZaMFwxCzAJBgNVBAYTAkZSMRIwEAYDVQQIDAlTb21lU3RhdGUxETAP +BgNVBAcMCFRvdWxvdXNlMQ8wDQYDVQQKDAZDaGVlc2UxFTATBgNVBAMMDCouY2hl +ZXNlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKYflnzBzLgc +tZFduL9wvPe4BE8qQt7qxcMZCwME7O+hJCXerQXnJuqJbFlgEBgMc/G/08x77Wuc +6h2I4u4UgdcH7oeVPTbfnDi3ex4rUZxKH9DMW69dbFw1STLkAVv5jHHPYkha6rcx +WOLG0FscULVcbVpv2kFe1UxuGiHzQPmeUnZQJT4Dm4cZSFtHh9NnxiVpdymOVpdF +2W9kqE6tNXUu/GouR4d2/E4+ROkWssfwI5gTot8VI8sMPf1IXscshnBji8bIiRdS +1aeOy04RnWmOjlnMfqO9oRGI1897jBlGnBt6yTmBTFgIH8fOsA55ZNMRcmXm3b0A +fyIwRptmnLkCAwEAAaOBjTCBijAJBgNVHRMEAjAAMF4GA1UdEQRXMFWCDCouY2hl +ZXNlLm9yZ4IMKi5jaGVlc2UubmV0ggljaGVlc2UuaW6HBAoAAQCHBAoAAQKBD3Rl +c3RAY2hlZXNlLm9yZ4EPdGVzdEBjaGVlc2UubmV0MB0GA1UdDgQWBBSra4klEfxe +e9Sw99S22evQMJPlWDANBgkqhkiG9w0BAQUFAAOCAQEArYeEoIijTNkKwBTkLZod +u1e3Eu86+4uyzjK4BOZZyE8UarUSRunJChFk6qGGIJYOp0DjquWYkTaJd7a5c34a +WBmu0RSDHsFfpaAyu1JotI2jHbMI10VuO4dkfu9G5m/VedcdV2hn2Bg5YVuLGn+I +2gpRmz1sXbHPt+keBmWOltNhlviiYflAXvq8drlkDm+QN96sbX82hDUZiIwmrz7D +ahoD7deQie0YTJ6UH9iubGE2F3L5u94KVpp5tH1KnctKfXGfOOeN8IckIQokH4Ka +a2fOfa/LkWuK3ubYb6E3uS3Qy+hO9EOvrZATfWF6zoZI/ACMN/vgMWviGK39Ht8I +2w== +-----END CERTIFICATE----- +` +) + +func getCleanCertContents(certContents []string) string { + var re = regexp.MustCompile("-----BEGIN CERTIFICATE-----(?s)(.*)") + + var cleanedCertContent []string + for _, certContent := range certContents { + cert := re.FindString(string(certContent)) + cleanedCertContent = append(cleanedCertContent, sanitize([]byte(cert))) + } + + return strings.Join(cleanedCertContent, ",") +} + +func getCertificate(certContent string) *x509.Certificate { + roots := x509.NewCertPool() + ok := roots.AppendCertsFromPEM([]byte(rootCrt)) + if !ok { + panic("failed to parse root certificate") + } + + block, _ := pem.Decode([]byte(certContent)) + if block == nil { + panic("failed to parse certificate PEM") + } + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + panic("failed to parse certificate: " + err.Error()) + } + + return cert +} + +func buildTLSWith(certContents []string) *tls.ConnectionState { + var peerCertificates []*x509.Certificate + + for _, certContent := range certContents { + peerCertificates = append(peerCertificates, getCertificate(certContent)) + } + + return &tls.ConnectionState{PeerCertificates: peerCertificates} +} + +var myPassTLSClientCustomHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("bar")) +}) + +func getExpectedSanitized(s string) string { + return url.QueryEscape(strings.Replace(s, "\n", "", -1)) +} + +func TestSanitize(t *testing.T) { + testCases := []struct { + desc string + toSanitize []byte + expected string + }{ + { + desc: "Empty", + }, + { + desc: "With a minimal cert", + toSanitize: []byte(minimalCert), + expected: getExpectedSanitized(`MIIDGTCCAgECCQCqLd75YLi2kDANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJG +UjETMBEGA1UECAwKU29tZS1TdGF0ZTERMA8GA1UEBwwIVG91bG91c2UxITAfBgNV +BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0xODA3MTgwODI4MTZaFw0x +ODA4MTcwODI4MTZaMEUxCzAJBgNVBAYTAkZSMRMwEQYDVQQIDApTb21lLVN0YXRl +MSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQC/+frDMMTLQyXG34F68BPhQq0kzK4LIq9Y0/gl +FjySZNn1C0QDWA1ubVCAcA6yY204I9cxcQDPNrhC7JlS5QA8Y5rhIBrqQlzZizAi +Rj3NTrRjtGUtOScnHuJaWjLy03DWD+aMwb7q718xt5SEABmmUvLwQK+EjW2MeDwj +y8/UEIpvrRDmdhGaqv7IFpIDkcIF7FowJ/hwDvx3PMc+z/JWK0ovzpvgbx69AVbw +ZxCimeha65rOqVi+lEetD26le+WnOdYsdJ2IkmpPNTXGdfb15xuAc+gFXfMCh7Iw +3Ynl6dZtZM/Ok2kiA7/OsmVnRKkWrtBfGYkI9HcNGb3zrk6nAgMBAAEwDQYJKoZI +hvcNAQELBQADggEBAC/R+Yvhh1VUhcbK49olWsk/JKqfS3VIDQYZg1Eo+JCPbwgS +I1BSYVfMcGzuJTX6ua3m/AHzGF3Tap4GhF4tX12jeIx4R4utnjj7/YKkTvuEM2f4 +xT56YqI7zalGScIB0iMeyNz1QcimRl+M/49au8ow9hNX8C2tcA2cwd/9OIj/6T8q +SBRHc6ojvbqZSJCO0jziGDT1L3D+EDgTjED4nd77v/NRdP+egb0q3P0s4dnQ/5AV +aQlQADUn61j3ScbGJ4NSeZFFvsl38jeRi/MEzp0bGgNBcPj6JHi7qbbauZcZfQ05 +jECvgAY7Nfd9mZ1KtyNaW31is+kag7NsvjxU/kM=`), + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + require.Equal(t, test.expected, sanitize(test.toSanitize), "The sanitized certificates should be equal") + }) + } + +} + +func TestTlsClientheadersWithPEM(t *testing.T) { + testCases := []struct { + desc string + certContents []string // set the request TLS attribute if defined + tlsClientCertHeaders *types.TLSClientHeaders + expectedHeader string + }{ + { + desc: "No TLS, no option", + }, + { + desc: "TLS, no option", + certContents: []string{minimalCert}, + }, + { + desc: "No TLS, with pem option true", + tlsClientCertHeaders: &types.TLSClientHeaders{PEM: true}, + }, + { + desc: "TLS with simple certificate, with pem option true", + certContents: []string{minimalCert}, + tlsClientCertHeaders: &types.TLSClientHeaders{PEM: true}, + expectedHeader: getCleanCertContents([]string{minimalCert}), + }, + { + desc: "TLS with complete certificate, with pem option true", + certContents: []string{completeCert}, + tlsClientCertHeaders: &types.TLSClientHeaders{PEM: true}, + expectedHeader: getCleanCertContents([]string{completeCert}), + }, + { + desc: "TLS with two certificate, with pem option true", + certContents: []string{minimalCert, completeCert}, + tlsClientCertHeaders: &types.TLSClientHeaders{PEM: true}, + expectedHeader: getCleanCertContents([]string{minimalCert, completeCert}), + }, + } + + for _, test := range testCases { + tlsClientHeaders := NewTLSClientHeaders(&types.Frontend{PassTLSClientCert: test.tlsClientCertHeaders}) + + res := httptest.NewRecorder() + req := testhelpers.MustNewRequest(http.MethodGet, "http://example.com/foo", nil) + + if test.certContents != nil && len(test.certContents) > 0 { + req.TLS = buildTLSWith(test.certContents) + } + + tlsClientHeaders.ServeHTTP(res, req, myPassTLSClientCustomHandler) + + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + require.Equal(t, http.StatusOK, res.Code, "Http Status should be OK") + require.Equal(t, "bar", res.Body.String(), "Should be the expected body") + + if test.expectedHeader != "" { + require.Equal(t, getCleanCertContents(test.certContents), req.Header.Get(xForwardedTLSClientCert), "The request header should contain the cleaned certificate") + } else { + require.Empty(t, req.Header.Get(xForwardedTLSClientCert)) + } + require.Empty(t, res.Header().Get(xForwardedTLSClientCert), "The response header should be always empty") + }) + } + +} + +func TestGetSans(t *testing.T) { + urlFoo, err := url.Parse("my.foo.com") + require.NoError(t, err) + urlBar, err := url.Parse("my.bar.com") + require.NoError(t, err) + + testCases := []struct { + desc string + cert *x509.Certificate // set the request TLS attribute if defined + expected []string + }{ + { + desc: "With nil", + }, + { + desc: "Certificate without Sans", + cert: &x509.Certificate{}, + }, + { + desc: "Certificate with all Sans", + cert: &x509.Certificate{ + DNSNames: []string{"foo", "bar"}, + EmailAddresses: []string{"test@test.com", "test2@test.com"}, + IPAddresses: []net.IP{net.IPv4(10, 0, 0, 1), net.IPv4(10, 0, 0, 2)}, + URIs: []*url.URL{urlFoo, urlBar}, + }, + expected: []string{"foo", "bar", "test@test.com", "test2@test.com", "10.0.0.1", "10.0.0.2", urlFoo.String(), urlBar.String()}, + }, + } + + for _, test := range testCases { + sans := getSANs(test.cert) + test := test + + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + if len(test.expected) > 0 { + for i, expected := range test.expected { + require.Equal(t, expected, sans[i]) + } + } else { + require.Empty(t, sans) + } + }) + } + +} + +func TestTlsClientheadersWithCertInfos(t *testing.T) { + minimalCertAllInfos := `Subject="C=FR,ST=Some-State,O=Internet Widgits Pty Ltd",NB=1531902496,NA=1534494496,SAN=` + completeCertAllInfos := `Subject="C=FR,ST=SomeState,L=Toulouse,O=Cheese,CN=*.cheese.org",NB=1531900816,NA=1563436816,SAN=*.cheese.org,*.cheese.net,cheese.in,test@cheese.org,test@cheese.net,10.0.1.0,10.0.1.2` + + testCases := []struct { + desc string + certContents []string // set the request TLS attribute if defined + tlsClientCertHeaders *types.TLSClientHeaders + expectedHeader string + }{ + { + desc: "No TLS, no option", + }, + { + desc: "TLS, no option", + certContents: []string{minimalCert}, + }, + { + desc: "No TLS, with pem option true", + tlsClientCertHeaders: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + Subject: &types.TLSCLientCertificateSubjectInfos{ + CommonName: true, + Organization: true, + Locality: true, + Province: true, + Country: true, + SerialNumber: true, + }, + }, + }, + }, + { + desc: "No TLS, with pem option true with no flag", + tlsClientCertHeaders: &types.TLSClientHeaders{ + PEM: false, + Infos: &types.TLSClientCertificateInfos{ + Subject: &types.TLSCLientCertificateSubjectInfos{}, + }, + }, + }, + { + desc: "TLS with simple certificate, with all infos", + certContents: []string{minimalCert}, + tlsClientCertHeaders: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + NotAfter: true, + NotBefore: true, + Subject: &types.TLSCLientCertificateSubjectInfos{ + CommonName: true, + Organization: true, + Locality: true, + Province: true, + Country: true, + SerialNumber: true, + }, + Sans: true, + }, + }, + expectedHeader: url.QueryEscape(minimalCertAllInfos), + }, + { + desc: "TLS with simple certificate, with some infos", + certContents: []string{minimalCert}, + tlsClientCertHeaders: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + NotAfter: true, + Subject: &types.TLSCLientCertificateSubjectInfos{ + Organization: true, + }, + Sans: true, + }, + }, + expectedHeader: url.QueryEscape(`Subject="O=Internet Widgits Pty Ltd",NA=1534494496,SAN=`), + }, + { + desc: "TLS with complete certificate, with all infos", + certContents: []string{completeCert}, + tlsClientCertHeaders: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + NotAfter: true, + NotBefore: true, + Subject: &types.TLSCLientCertificateSubjectInfos{ + CommonName: true, + Organization: true, + Locality: true, + Province: true, + Country: true, + SerialNumber: true, + }, + Sans: true, + }, + }, + expectedHeader: url.QueryEscape(completeCertAllInfos), + }, + { + desc: "TLS with 2 certificates, with all infos", + certContents: []string{minimalCert, completeCert}, + tlsClientCertHeaders: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + NotAfter: true, + NotBefore: true, + Subject: &types.TLSCLientCertificateSubjectInfos{ + CommonName: true, + Organization: true, + Locality: true, + Province: true, + Country: true, + SerialNumber: true, + }, + Sans: true, + }, + }, + expectedHeader: url.QueryEscape(strings.Join([]string{minimalCertAllInfos, completeCertAllInfos}, ";")), + }, + } + + for _, test := range testCases { + tlsClientHeaders := NewTLSClientHeaders(&types.Frontend{PassTLSClientCert: test.tlsClientCertHeaders}) + + res := httptest.NewRecorder() + req := testhelpers.MustNewRequest(http.MethodGet, "http://example.com/foo", nil) + + if test.certContents != nil && len(test.certContents) > 0 { + req.TLS = buildTLSWith(test.certContents) + } + + tlsClientHeaders.ServeHTTP(res, req, myPassTLSClientCustomHandler) + + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + require.Equal(t, http.StatusOK, res.Code, "Http Status should be OK") + require.Equal(t, "bar", res.Body.String(), "Should be the expected body") + + if test.expectedHeader != "" { + require.Equal(t, test.expectedHeader, req.Header.Get(xForwardedTLSClientCertInfos), "The request header should contain the cleaned certificate") + } else { + require.Empty(t, req.Header.Get(xForwardedTLSClientCertInfos)) + } + require.Empty(t, res.Header().Get(xForwardedTLSClientCertInfos), "The response header should be always empty") + }) + } + +} + +func TestNewTLSClientHeadersFromStruct(t *testing.T) { + testCases := []struct { + desc string + frontend *types.Frontend + expected *TLSClientHeaders + }{ + { + desc: "Without frontend", + }, + { + desc: "frontend without the option", + frontend: &types.Frontend{}, + expected: &TLSClientHeaders{}, + }, + { + desc: "frontend with the pem set false", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + PEM: false, + }, + }, + expected: &TLSClientHeaders{PEM: false}, + }, + { + desc: "frontend with the pem set true", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + PEM: true, + }, + }, + expected: &TLSClientHeaders{PEM: true}, + }, + { + desc: "frontend with the Infos with no flag", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + NotAfter: false, + NotBefore: false, + Sans: false, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{}, + }, + }, + { + desc: "frontend with the Infos basic", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + NotAfter: true, + NotBefore: true, + Sans: true, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{ + NotBefore: true, + NotAfter: true, + Sans: true, + }, + }, + }, + { + desc: "frontend with the Infos NotAfter", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + NotAfter: true, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{ + NotAfter: true, + }, + }, + }, + { + desc: "frontend with the Infos NotBefore", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + NotBefore: true, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{ + NotBefore: true, + }, + }, + }, + { + desc: "frontend with the Infos Sans", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + Sans: true, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{ + Sans: true, + }, + }, + }, + { + desc: "frontend with the Infos Subject Organization", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + Subject: &types.TLSCLientCertificateSubjectInfos{ + Organization: true, + }, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{ + Subject: &TLSCLientCertificateSubjectInfos{ + Organization: true, + }, + }, + }, + }, + { + desc: "frontend with the Infos Subject Country", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + Subject: &types.TLSCLientCertificateSubjectInfos{ + Country: true, + }, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{ + Subject: &TLSCLientCertificateSubjectInfos{ + Country: true, + }, + }, + }, + }, + { + desc: "frontend with the Infos Subject SerialNumber", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + Subject: &types.TLSCLientCertificateSubjectInfos{ + SerialNumber: true, + }, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{ + Subject: &TLSCLientCertificateSubjectInfos{ + SerialNumber: true, + }, + }, + }, + }, + { + desc: "frontend with the Infos Subject Province", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + Subject: &types.TLSCLientCertificateSubjectInfos{ + Province: true, + }, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{ + Subject: &TLSCLientCertificateSubjectInfos{ + Province: true, + }, + }, + }, + }, + { + desc: "frontend with the Infos Subject Locality", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + Subject: &types.TLSCLientCertificateSubjectInfos{ + Locality: true, + }, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{ + Subject: &TLSCLientCertificateSubjectInfos{ + Locality: true, + }, + }, + }, + }, + { + desc: "frontend with the Infos Subject CommonName", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + Subject: &types.TLSCLientCertificateSubjectInfos{ + CommonName: true, + }, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{ + Subject: &TLSCLientCertificateSubjectInfos{ + CommonName: true, + }, + }, + }, + }, + { + desc: "frontend with the Infos NotBefore", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + Sans: true, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{ + Sans: true, + }, + }, + }, + { + desc: "frontend with the Infos all", + frontend: &types.Frontend{ + PassTLSClientCert: &types.TLSClientHeaders{ + Infos: &types.TLSClientCertificateInfos{ + NotAfter: true, + NotBefore: true, + Subject: &types.TLSCLientCertificateSubjectInfos{ + CommonName: true, + Country: true, + Locality: true, + Organization: true, + Province: true, + SerialNumber: true, + }, + Sans: true, + }, + }, + }, + expected: &TLSClientHeaders{ + PEM: false, + Infos: &TLSClientCertificateInfos{ + NotBefore: true, + NotAfter: true, + Sans: true, + Subject: &TLSCLientCertificateSubjectInfos{ + Province: true, + Organization: true, + Locality: true, + Country: true, + CommonName: true, + SerialNumber: true, + }, + }}, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + require.Equal(t, test.expected, NewTLSClientHeaders(test.frontend)) + }) + } + +} diff --git a/provider/acme/provider.go b/provider/acme/provider.go index 9db5e0657..3d846eb94 100644 --- a/provider/acme/provider.go +++ b/provider/acme/provider.go @@ -338,7 +338,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/provider/consulcatalog/config.go b/provider/consulcatalog/config.go index 6db4045b9..0afe05947 100644 --- a/provider/consulcatalog/config.go +++ b/provider/consulcatalog/config.go @@ -44,6 +44,7 @@ func (p *Provider) buildConfiguration(catalog []catalogUpdate) *types.Configurat "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 441bf9245..4c1a4089d 100644 --- a/provider/consulcatalog/config_test.go +++ b/provider/consulcatalog/config_test.go @@ -423,6 +423,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", @@ -540,6 +551,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 8b5e2d674..3c6381869 100644 --- a/provider/docker/config.go +++ b/provider/docker/config.go @@ -42,19 +42,20 @@ func (p *Provider) buildConfiguration(containersInspected []dockerData) *types.C "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 47eead526..ccff446bc 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{ @@ -387,6 +450,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 985ebe3f0..538d615ec 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 53d6cf91a..81011b086 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", @@ -368,6 +444,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 d7e6eef1f..0af5784da 100644 --- a/provider/ecs/config.go +++ b/provider/ecs/config.go @@ -31,20 +31,21 @@ func (p *Provider) buildConfiguration(instances []ecsInstance) (*types.Configura "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 133f3fbe5..bfcb80080 100644 --- a/provider/ecs/config_segment_test.go +++ b/provider/ecs/config_segment_test.go @@ -321,6 +321,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", @@ -391,6 +402,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 1d5b05bd4..14874a887 100644 --- a/provider/ecs/config_test.go +++ b/provider/ecs/config_test.go @@ -356,6 +356,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"), @@ -489,6 +500,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/ecs/ecs.go b/provider/ecs/ecs.go index bb9e4f5c8..c34e5a76a 100644 --- a/provider/ecs/ecs.go +++ b/provider/ecs/ecs.go @@ -380,7 +380,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 de856c28c..7a1fa0348 100644 --- a/provider/kubernetes/kubernetes_test.go +++ b/provider/kubernetes/kubernetes_test.go @@ -516,37 +516,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", }, @@ -608,23 +608,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/kv/keynames.go b/provider/kv/keynames.go index b714e3f1e..df3a5fa29 100644 --- a/provider/kv/keynames.go +++ b/provider/kv/keynames.go @@ -24,15 +24,28 @@ const ( pathBackendBufferingMemRequestBodyBytes = pathBackendBuffering + "memrequestbodybytes" pathBackendBufferingRetryExpression = pathBackendBuffering + "retryexpression" - pathFrontends = "/frontends/" - pathFrontendBackend = "/backend" - pathFrontendPriority = "/priority" - pathFrontendPassHostHeader = "/passhostheader" - pathFrontendPassTLSCert = "/passtlscert" - pathFrontendWhiteListSourceRange = "/whitelist/sourcerange" - pathFrontendWhiteListIPStrategy = "/whitelist/ipstrategy" - pathFrontendWhiteListIPStrategyDepth = pathFrontendWhiteListIPStrategy + "/depth" - pathFrontendWhiteListIPStrategyExcludedIPs = pathFrontendWhiteListIPStrategy + "/excludedips" + pathFrontends = "/frontends/" + pathFrontendBackend = "/backend" + pathFrontendPriority = "/priority" + 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" + pathFrontendWhiteListIPStrategy = "/whitelist/ipstrategy" + pathFrontendWhiteListIPStrategyDepth = pathFrontendWhiteListIPStrategy + "/depth" + pathFrontendWhiteListIPStrategyExcludedIPs = pathFrontendWhiteListIPStrategy + "/excludedips" pathFrontendAuth = "/auth/" pathFrontendAuthBasic = pathFrontendAuth + "basic/" diff --git a/provider/kv/kv_config.go b/provider/kv/kv_config.go index a154544cc..030e80f30 100644 --- a/provider/kv/kv_config.go +++ b/provider/kv/kv_config.go @@ -41,18 +41,19 @@ func (p *Provider) buildConfiguration() *types.Configuration { "getTLSSection": p.getTLSSection, // Frontend functions - "getBackendName": p.getFuncString(pathFrontendBackend, ""), - "getPriority": p.getFuncInt(pathFrontendPriority, label.DefaultFrontendPriority), - "getPassHostHeader": p.getFuncBool(pathFrontendPassHostHeader, label.DefaultPassHostHeader), - "getPassTLSCert": p.getFuncBool(pathFrontendPassTLSCert, label.DefaultPassTLSCert), - "getEntryPoints": p.getFuncList(pathFrontendEntryPoints), - "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.getFuncBool(pathFrontendPassHostHeader, label.DefaultPassHostHeader), + "getPassTLSCert": p.getFuncBool(pathFrontendPassTLSCert, label.DefaultPassTLSCert), + "getPassTLSClientCert": p.getTLSClientCert, + "getEntryPoints": p.getFuncList(pathFrontendEntryPoints), + "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, @@ -334,6 +335,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 +} + // GetAuth Create auth from path func (p *Provider) getAuth(rootPath string) *types.Auth { if p.hasPrefix(rootPath, pathFrontendAuth) { diff --git a/provider/kv/kv_config_test.go b/provider/kv/kv_config_test.go index bba8be163..1dbab1e6d 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"), @@ -403,6 +415,22 @@ func TestProviderBuildConfiguration(t *testing.T) { ExcludedIPs: []string{"1.1.1.1/24", "1234:abcd::42/32"}, }, }, + 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 894c05df4..1062a8917 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" - 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" - SuffixFrontendWhiteList = "frontend.whiteList." - SuffixFrontendWhiteListSourceRange = SuffixFrontendWhiteList + "sourceRange" - SuffixFrontendWhiteListIPStrategy = SuffixFrontendWhiteList + "ipStrategy" - SuffixFrontendWhiteListIPStrategyDepth = SuffixFrontendWhiteListIPStrategy + ".depth" - SuffixFrontendWhiteListIPStrategyExcludedIPS = SuffixFrontendWhiteListIPStrategy + ".excludedIPs" - 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 - 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 - TraefikFrontendWhiteListIPStrategy = Prefix + SuffixFrontendWhiteListIPStrategy - TraefikFrontendWhiteListIPStrategyDepth = Prefix + SuffixFrontendWhiteListIPStrategyDepth - TraefikFrontendWhiteListIPStrategyExcludedIPS = Prefix + SuffixFrontendWhiteListIPStrategyExcludedIPS - 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" + 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" + SuffixFrontendWhiteList = "frontend.whiteList." + SuffixFrontendWhiteListSourceRange = SuffixFrontendWhiteList + "sourceRange" + SuffixFrontendWhiteListIPStrategy = SuffixFrontendWhiteList + "ipStrategy" + SuffixFrontendWhiteListIPStrategyDepth = SuffixFrontendWhiteListIPStrategy + ".depth" + SuffixFrontendWhiteListIPStrategyExcludedIPS = SuffixFrontendWhiteListIPStrategy + ".excludedIPs" + 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 + 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 + TraefikFrontendWhiteListIPStrategy = Prefix + SuffixFrontendWhiteListIPStrategy + TraefikFrontendWhiteListIPStrategyDepth = Prefix + SuffixFrontendWhiteListIPStrategyDepth + TraefikFrontendWhiteListIPStrategyExcludedIPS = Prefix + SuffixFrontendWhiteListIPStrategyExcludedIPS + 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 e01a137de..0b98c6703 100644 --- a/provider/label/partial.go +++ b/provider/label/partial.go @@ -62,6 +62,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 ff17ffebb..836903836 100644 --- a/provider/label/partial_test.go +++ b/provider/label/partial_test.go @@ -815,3 +815,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 fc4117836..824a7f770 100644 --- a/provider/marathon/config.go +++ b/provider/marathon/config.go @@ -44,6 +44,7 @@ func (p *Provider) buildConfiguration(applications *marathon.Applications) *type "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 06236cd83..f38bc7bb7 100644 --- a/provider/marathon/config_test.go +++ b/provider/marathon/config_test.go @@ -373,6 +373,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"), @@ -455,6 +466,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{ @@ -767,6 +794,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 +885,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 4ec8df3e9..ab72c8cbd 100644 --- a/provider/mesos/config.go +++ b/provider/mesos/config.go @@ -48,6 +48,7 @@ func (p *Provider) buildConfiguration(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 e37e2406d..f0c6a8acb 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"), @@ -418,6 +429,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{ @@ -687,6 +714,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"), @@ -768,6 +806,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/mesos.go b/provider/mesos/mesos.go index 85a8beaaf..4f44040f2 100644 --- a/provider/mesos/mesos.go +++ b/provider/mesos/mesos.go @@ -128,7 +128,7 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s func detectMasters(zk string, masters []string) <-chan []string { changed := make(chan []string, 1) if zk != "" { - log.Debugf("Starting master detector for ZK ", zk) + log.Debugf("Starting master detector for ZK %s", zk) if md, err := detector.New(zk); err != nil { log.Errorf("Failed to create master detector: %v", err) } else if err := md.Detect(detect.NewMasters(masters, changed)); err != nil { diff --git a/provider/rancher/config.go b/provider/rancher/config.go index 027624bca..dd069e2d5 100644 --- a/provider/rancher/config.go +++ b/provider/rancher/config.go @@ -28,19 +28,20 @@ func (p *Provider) buildConfiguration(services []rancherData) *types.Configurati "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 250071f1e..b0049efb9 100644 --- a/provider/rancher/config_test.go +++ b/provider/rancher/config_test.go @@ -58,6 +58,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{ @@ -293,6 +320,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", @@ -373,6 +411,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{ @@ -689,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/metadata.go b/provider/rancher/metadata.go index 92d02edc6..8de2f255d 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/rules/rules.go b/rules/rules.go index 4a5658cd7..093d031d1 100644 --- a/rules/rules.go +++ b/rules/rules.go @@ -8,7 +8,6 @@ import ( "sort" "strings" - "github.com/BurntSushi/ty/fun" "github.com/containous/mux" "github.com/containous/traefik/hostresolver" "github.com/containous/traefik/log" @@ -288,9 +287,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 @@ -299,5 +300,18 @@ func (r *Rules) ParseDomains(expression string) ([]string, error) { return nil, fmt.Errorf("error parsing domains: %v", err) } - return fun.Map(strings.ToLower, domains).([]string), nil + var cleanDomains []string + for _, domain := range domains { + canonicalDomain := strings.ToLower(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 7578cedca..0db618218 100644 --- a/rules/rules_test.go +++ b/rules/rules_test.go @@ -64,24 +64,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, }, } @@ -91,7 +105,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 e9b64ac26..b08278b25 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" @@ -244,7 +243,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 @@ -427,7 +428,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_configuration.go b/server/server_configuration.go index 70d44eafc..e4c819e74 100644 --- a/server/server_configuration.go +++ b/server/server_configuration.go @@ -424,8 +424,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) + } } } }) @@ -515,6 +517,8 @@ func (s *Server) postLoadConfiguration() { domains, err := rls.ParseDomains(route.Rule) if err != nil { log.Errorf("Error parsing domains: %v", err) + } else if len(domains) == 0 { + log.Debugf("No domain parsed in rule %q", route.Rule) } else { s.globalConfiguration.ACME.LoadCertificateForDomains(domains) } diff --git a/server/server_loadbalancer.go b/server/server_loadbalancer.go index 721f93c26..a6f41a47e 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 { @@ -308,7 +307,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 2061dddeb..413684b47 100644 --- a/server/server_middlewares.go +++ b/server/server_middlewares.go @@ -104,6 +104,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/server/server_signals.go b/server/server_signals.go index fb8514c9a..8472696d7 100644 --- a/server/server_signals.go +++ b/server/server_signals.go @@ -13,21 +13,25 @@ func (s *Server) configureSignals() { signal.Notify(s.signals, syscall.SIGUSR1) } -func (s *Server) listenSignals() { +func (s *Server) listenSignals(stop chan bool) { for { - sig := <-s.signals - switch sig { - case syscall.SIGUSR1: - log.Infof("Closing and re-opening log files for rotation: %+v", sig) + select { + case <-stop: + return + case sig := <-s.signals: + switch sig { + case syscall.SIGUSR1: + log.Infof("Closing and re-opening log files for rotation: %+v", sig) - if s.accessLoggerMiddleware != nil { - if err := s.accessLoggerMiddleware.Rotate(); err != nil { - log.Errorf("Error rotating access log: %v", err) + if s.accessLoggerMiddleware != nil { + if err := s.accessLoggerMiddleware.Rotate(); err != nil { + log.Errorf("Error rotating access log: %v", err) + } } - } - if err := log.RotateFile(); err != nil { - log.Errorf("Error rotating traefik log: %v", err) + if err := log.RotateFile(); err != nil { + log.Errorf("Error rotating traefik log: %v", err) + } } } } diff --git a/server/server_signals_windows.go b/server/server_signals_windows.go index 674896b43..05cf4eace 100644 --- a/server/server_signals_windows.go +++ b/server/server_signals_windows.go @@ -4,4 +4,4 @@ package server func (s *Server) configureSignals() {} -func (s *Server) listenSignals() {} +func (s *Server) listenSignals(stop chan bool) {} diff --git a/templates/consul_catalog.tmpl b/templates/consul_catalog.tmpl index fbeac5e56..7d8964e27 100644 --- a/templates/consul_catalog.tmpl +++ b/templates/consul_catalog.tmpl @@ -73,8 +73,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 10f94de62..09ac62f0b 100644 --- a/templates/docker.tmpl +++ b/templates/docker.tmpl @@ -74,6 +74,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 a072798a9..d66dbb623 100644 --- a/templates/ecs.tmpl +++ b/templates/ecs.tmpl @@ -75,6 +75,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 3698df0f8..d851372f9 100644 --- a/templates/kv.tmpl +++ b/templates/kv.tmpl @@ -73,6 +73,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 67da135d1..1ecfcc206 100644 --- a/templates/marathon.tmpl +++ b/templates/marathon.tmpl @@ -76,7 +76,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 a4f71a256..b8c9dabcc 100644 --- a/templates/mesos.tmpl +++ b/templates/mesos.tmpl @@ -76,6 +76,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 b731b24a7..9d0ef07f0 100644 --- a/templates/rancher.tmpl +++ b/templates/rancher.tmpl @@ -74,6 +74,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 525523730..e5301cb4a 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 } @@ -30,8 +30,8 @@ type TLS struct { 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 { @@ -41,7 +41,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() @@ -52,30 +52,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 625242927..c8158c893 100644 --- a/types/types.go +++ b/types/types.go @@ -178,18 +178,19 @@ func (h *Headers) HasSecureHeadersDefined() bool { // Frontend holds frontend configuration. type Frontend struct { - EntryPoints []string `json:"entryPoints,omitempty" hash:"ignore"` - Backend string `json:"backend,omitempty"` - Routes map[string]Route `json:"routes,omitempty" hash:"ignore"` - PassHostHeader bool `json:"passHostHeader,omitempty"` - PassTLSCert bool `json:"passTLSCert,omitempty"` - Priority int `json:"priority"` - WhiteList *WhiteList `json:"whiteList,omitempty"` - Headers *Headers `json:"headers,omitempty"` - Errors map[string]*ErrorPage `json:"errors,omitempty"` - RateLimit *RateLimit `json:"ratelimit,omitempty"` - Redirect *Redirect `json:"redirect,omitempty"` - Auth *Auth `json:"auth,omitempty"` + EntryPoints []string `json:"entryPoints,omitempty" hash:"ignore"` + Backend string `json:"backend,omitempty"` + Routes map[string]Route `json:"routes,omitempty" hash:"ignore"` + PassHostHeader bool `json:"passHostHeader,omitempty"` + PassTLSCert bool `json:"passTLSCert,omitempty"` // Deprecated use PassTLSClientCert instead + PassTLSClientCert *TLSClientHeaders `json:"passTLSClientCert,omitempty"` + Priority int `json:"priority"` + WhiteList *WhiteList `json:"whiteList,omitempty"` + Headers *Headers `json:"headers,omitempty"` + Errors map[string]*ErrorPage `json:"errors,omitempty"` + RateLimit *RateLimit `json:"ratelimit,omitempty"` + Redirect *Redirect `json:"redirect,omitempty"` + Auth *Auth `json:"auth,omitempty"` } // Hash returns the hash value of a Frontend struct. @@ -645,3 +646,27 @@ func (s *IPStrategy) Get() (ip.Strategy, error) { return &ip.RemoteAddrStrategy{}, nil } + +// 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"` +} diff --git a/vendor/github.com/vulcand/oxy/buffer/buffer.go b/vendor/github.com/vulcand/oxy/buffer/buffer.go index d2bbe40ce..68b387925 100644 --- a/vendor/github.com/vulcand/oxy/buffer/buffer.go +++ b/vendor/github.com/vulcand/oxy/buffer/buffer.go @@ -383,7 +383,14 @@ func (b *bufferWriter) Header() http.Header { } func (b *bufferWriter) Write(buf []byte) (int, error) { - return b.buffer.Write(buf) + length, err := b.buffer.Write(buf) + if err != nil { + // Since go1.11 (https://github.com/golang/go/commit/8f38f28222abccc505b9a1992deecfe3e2cb85de) + // if the writer returns an error, the reverse proxy panics + b.log.Error(err) + length = len(buf) + } + return length, nil } // WriteHeader sets rw.Code. @@ -410,7 +417,7 @@ func (b *bufferWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { return conn, rw, err } b.log.Warningf("Upstream ResponseWriter of type %v does not implement http.Hijacker. Returning dummy channel.", reflect.TypeOf(b.responseWriter)) - return nil, nil, fmt.Errorf("The response writer that was wrapped in this proxy, does not implement http.Hijacker. It is of type: %v", reflect.TypeOf(b.responseWriter)) + return nil, nil, fmt.Errorf("the response writer wrapped in this proxy does not implement http.Hijacker. Its type is: %v”", reflect.TypeOf(b.responseWriter)) } // SizeErrHandler Size error handler