Merge branch 'v1.7' into master
This commit is contained in:
commit
bd4846aa9c
82 changed files with 3573 additions and 877 deletions
39
CHANGELOG.md
39
CHANGELOG.md
|
@ -1,5 +1,44 @@
|
||||||
# Change Log
|
# 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)
|
## [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)
|
[All Commits](https://github.com/containous/traefik/compare/v1.6.5...v1.6.6)
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ You need to run the `binary` target. This will create binaries for Linux platfor
|
||||||
$ make binary
|
$ make binary
|
||||||
docker build -t "traefik-dev:no-more-godep-ever" -f build.Dockerfile .
|
docker build -t "traefik-dev:no-more-godep-ever" -f build.Dockerfile .
|
||||||
Sending build context to Docker daemon 295.3 MB
|
Sending build context to Docker daemon 295.3 MB
|
||||||
Step 0 : FROM golang:1.10-alpine
|
Step 0 : FROM golang:1.11-alpine
|
||||||
---> 8c6473912976
|
---> 8c6473912976
|
||||||
Step 1 : RUN go get github.com/golang/dep/cmd/dep
|
Step 1 : RUN go get github.com/golang/dep/cmd/dep
|
||||||
[...]
|
[...]
|
||||||
|
|
2
Gopkg.lock
generated
2
Gopkg.lock
generated
|
@ -1263,7 +1263,7 @@
|
||||||
"roundrobin",
|
"roundrobin",
|
||||||
"utils"
|
"utils"
|
||||||
]
|
]
|
||||||
revision = "f6bbeac6d5c4c06f88ba07ed42983ff36a5b407e"
|
revision = "77148e9694210e5f5610328f1cd7cf65583014c2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/vulcand/predicate"
|
name = "github.com/vulcand/predicate"
|
||||||
|
|
|
@ -51,7 +51,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||||
{CertFile: "CertFile 2", KeyFile: "KeyFile 2"},
|
{CertFile: "CertFile 2", KeyFile: "KeyFile 2"},
|
||||||
},
|
},
|
||||||
ClientCA: traefiktls.ClientCA{
|
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,
|
Optional: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -100,7 +100,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||||
{CertFile: "CertFile 2", KeyFile: "KeyFile 2"},
|
{CertFile: "CertFile 2", KeyFile: "KeyFile 2"},
|
||||||
},
|
},
|
||||||
ClientCA: traefiktls.ClientCA{
|
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,
|
Optional: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -185,7 +185,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||||
config.ProvidersThrottleDuration = parse.Duration(666 * time.Second)
|
config.ProvidersThrottleDuration = parse.Duration(666 * time.Second)
|
||||||
config.MaxIdleConnsPerHost = 666
|
config.MaxIdleConnsPerHost = 666
|
||||||
config.InsecureSkipVerify = true
|
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{
|
config.Retry = &configuration.Retry{
|
||||||
Attempts: 666,
|
Attempts: 666,
|
||||||
}
|
}
|
||||||
|
|
|
@ -129,8 +129,30 @@ var _templatesConsul_catalogTmpl = []byte(`[backends]
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{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 }}
|
{{if $auth }}
|
||||||
[frontends."frontend-{{ $service.ServiceName }}".auth]
|
[frontends."frontend-{{ $service.ServiceName }}".auth]
|
||||||
headerField = "{{ $auth.HeaderField }}"
|
headerField = "{{ $auth.HeaderField }}"
|
||||||
|
@ -375,6 +397,29 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{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 }}
|
{{ $auth := getAuth $container.SegmentLabels }}
|
||||||
{{if $auth }}
|
{{if $auth }}
|
||||||
[frontends."frontend-{{ $frontendName }}".auth]
|
[frontends."frontend-{{ $frontendName }}".auth]
|
||||||
|
@ -622,6 +667,29 @@ var _templatesEcsTmpl = []byte(`[backends]
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{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 }}
|
{{ $auth := getAuth $instance.SegmentLabels }}
|
||||||
{{if $auth }}
|
{{if $auth }}
|
||||||
[frontends."frontend-{{ $frontendName }}".auth]
|
[frontends."frontend-{{ $frontendName }}".auth]
|
||||||
|
@ -1125,6 +1193,29 @@ var _templatesKvTmpl = []byte(`[backends]
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{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 }}
|
{{ $auth := getAuth $frontend }}
|
||||||
{{if $auth }}
|
{{if $auth }}
|
||||||
[frontends."{{ $frontendName }}".auth]
|
[frontends."{{ $frontendName }}".auth]
|
||||||
|
@ -1387,6 +1478,29 @@ var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
|
{{ $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 }}
|
{{ $auth := getAuth $app.SegmentLabels }}
|
||||||
{{if $auth }}
|
{{if $auth }}
|
||||||
[frontends."{{ $frontendName }}".auth]
|
[frontends."{{ $frontendName }}".auth]
|
||||||
|
@ -1634,6 +1748,29 @@ var _templatesMesosTmpl = []byte(`[backends]
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{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 }}
|
{{ $auth := getAuth $app.TraefikLabels }}
|
||||||
{{if $auth }}
|
{{if $auth }}
|
||||||
[frontends."frontend-{{ $frontendName }}".auth]
|
[frontends."frontend-{{ $frontendName }}".auth]
|
||||||
|
@ -1903,6 +2040,29 @@ var _templatesRancherTmpl = []byte(`{{ $backendServers := .Backends }}
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{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 }}
|
{{ $auth := getAuth $service.SegmentLabels }}
|
||||||
{{if $auth }}
|
{{if $auth }}
|
||||||
[frontends."frontend-{{ $frontendName }}".auth]
|
[frontends."frontend-{{ $frontendName }}".auth]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM golang:1.10-alpine
|
FROM golang:1.11-alpine
|
||||||
|
|
||||||
RUN apk --update upgrade \
|
RUN apk --update upgrade \
|
||||||
&& apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar \
|
&& apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar \
|
||||||
|
|
|
@ -34,7 +34,7 @@ func Test_createReport(t *testing.T) {
|
||||||
File: &file.Provider{
|
File: &file.Provider{
|
||||||
Directory: "BAR",
|
Directory: "BAR",
|
||||||
},
|
},
|
||||||
RootCAs: tls.RootCAs{"fllf"},
|
RootCAs: tls.FilesOrContents{"fllf"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
// 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
|
// Store the ACME Account into the KV Store
|
||||||
// Certificates in KV Store will be overridden
|
// 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
|
// migrateACMEData allows migrating data from acme.json file to KV store in function of the file format
|
||||||
func migrateACMEData(fileName string) (*acme.Account, error) {
|
func migrateACMEData(fileName string) (*acme.Account, error) {
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,7 @@ Complete documentation is available at https://traefik.io`,
|
||||||
// add custom parsers
|
// add custom parsers
|
||||||
f.AddParser(reflect.TypeOf(configuration.EntryPoints{}), &configuration.EntryPoints{})
|
f.AddParser(reflect.TypeOf(configuration.EntryPoints{}), &configuration.EntryPoints{})
|
||||||
f.AddParser(reflect.TypeOf(configuration.DefaultEntryPoints{}), &configuration.DefaultEntryPoints{})
|
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(types.Constraints{}), &types.Constraints{})
|
||||||
f.AddParser(reflect.TypeOf(kubernetes.Namespaces{}), &kubernetes.Namespaces{})
|
f.AddParser(reflect.TypeOf(kubernetes.Namespaces{}), &kubernetes.Namespaces{})
|
||||||
f.AddParser(reflect.TypeOf(ecs.Clusters{}), &ecs.Clusters{})
|
f.AddParser(reflect.TypeOf(ecs.Clusters{}), &ecs.Clusters{})
|
||||||
|
|
|
@ -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"`
|
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"`
|
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"`
|
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"`
|
Retry *Retry `description:"Enable retry sending request if network error" export:"true"`
|
||||||
HealthCheck *HealthCheckConfig `description:"Health check parameters" export:"true"`
|
HealthCheck *HealthCheckConfig `description:"Health check parameters" export:"true"`
|
||||||
RespondingTimeouts *RespondingTimeouts `description:"Timeouts for incoming requests to the Traefik instance" export:"true"`
|
RespondingTimeouts *RespondingTimeouts `description:"Timeouts for incoming requests to the Traefik instance" export:"true"`
|
||||||
|
|
|
@ -248,7 +248,8 @@ func makeEntryPointTLS(result map[string]string) (*tls.TLS, error) {
|
||||||
|
|
||||||
if configTLS != nil {
|
if configTLS != nil {
|
||||||
if len(result["ca"]) > 0 {
|
if len(result["ca"]) > 0 {
|
||||||
files := strings.Split(result["ca"], ",")
|
files := tls.FilesOrContents{}
|
||||||
|
files.Set(result["ca"])
|
||||||
optional := toBool(result, "ca_optional")
|
optional := toBool(result, "ca_optional")
|
||||||
configTLS.ClientCA = tls.ClientCA{
|
configTLS.ClientCA = tls.ClientCA{
|
||||||
Files: files,
|
Files: files,
|
||||||
|
|
|
@ -232,7 +232,7 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ClientCA: tls.ClientCA{
|
ClientCA: tls.ClientCA{
|
||||||
Files: []string{"car"},
|
Files: tls.FilesOrContents{"car"},
|
||||||
Optional: true,
|
Optional: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -352,7 +352,7 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ClientCA: tls.ClientCA{
|
ClientCA: tls.ClientCA{
|
||||||
Files: []string{"car"},
|
Files: tls.FilesOrContents{"car"},
|
||||||
Optional: true,
|
Optional: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -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.
|
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 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
|
##### Path Matcher Usage Guidelines
|
||||||
|
|
||||||
|
@ -157,7 +157,8 @@ Here is an example of frontends definition:
|
||||||
[frontends.frontend2]
|
[frontends.frontend2]
|
||||||
backend = "backend1"
|
backend = "backend1"
|
||||||
passHostHeader = true
|
passHostHeader = true
|
||||||
passTLSCert = true
|
[frontends.frontend2.passTLSClientCert]
|
||||||
|
pem = true
|
||||||
priority = 10
|
priority = 10
|
||||||
entrypoints = ["https"] # overrides defaultEntryPoints
|
entrypoints = ["https"] # overrides defaultEntryPoints
|
||||||
[frontends.frontend2.routes.test_1]
|
[frontends.frontend2.routes.test_1]
|
||||||
|
|
|
@ -31,7 +31,7 @@ exposedByDefault = false
|
||||||
#
|
#
|
||||||
stale = false
|
stale = false
|
||||||
|
|
||||||
# Default domain used.
|
# Default base domain used for the frontend rules.
|
||||||
#
|
#
|
||||||
# Optional
|
# Optional
|
||||||
#
|
#
|
||||||
|
@ -95,7 +95,7 @@ Additional settings can be defined using Consul Catalog tags.
|
||||||
The default prefix is `traefik`.
|
The default prefix is `traefik`.
|
||||||
|
|
||||||
| Label | Description |
|
| Label | Description |
|
||||||
|--------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|----------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `<prefix>.enable=false` | Disables this container in Træfik. |
|
| `<prefix>.enable=false` | Disables this container in Træfik. |
|
||||||
| `<prefix>.protocol=https` | Overrides the default `http` protocol. |
|
| `<prefix>.protocol=https` | Overrides the default `http` protocol. |
|
||||||
| `<prefix>.weight=10` | Assigns this weight to the container. |
|
| `<prefix>.weight=10` | Assigns this weight to the container. |
|
||||||
|
@ -136,6 +136,16 @@ Additional settings can be defined using Consul Catalog tags.
|
||||||
| `<prefix>.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `<prefix>.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||||
| `<prefix>.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `<prefix>.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||||
| `<prefix>.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
| `<prefix>.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
||||||
|
| `<prefix>.frontend.passTLSClientCert.infos.notAfter=true` | Add the noAfter field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||||
|
| `<prefix>.frontend.passTLSClientCert.infos.notBefore=true` | Add the noBefore field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||||
|
| `<prefix>.frontend.passTLSClientCert.infos.sans=true` | Add the sans field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||||
|
| `<prefix>.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. |
|
||||||
|
| `<prefix>.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. |
|
||||||
|
| `<prefix>.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. |
|
||||||
|
| `<prefix>.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. |
|
||||||
|
| `<prefix>.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. |
|
||||||
|
| `<prefix>.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. |
|
||||||
|
| `<prefix>.frontend.passTLSClientCert.pem=true` | Pass the escaped pem in the `X-Forwarded-Ssl-Client-Cert` header. |
|
||||||
| `<prefix>.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
| `<prefix>.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
||||||
| `<prefix>.frontend.priority=10` | Overrides default frontend priority. |
|
| `<prefix>.frontend.priority=10` | Overrides default frontend priority. |
|
||||||
| `<prefix>.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `<prefix>.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||||
|
|
|
@ -208,7 +208,7 @@ services:
|
||||||
Labels can be used on containers to override default behavior.
|
Labels can be used on containers to override default behavior.
|
||||||
|
|
||||||
| Label | Description |
|
| Label | Description |
|
||||||
|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|---------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `traefik.docker.network` | Overrides the default docker network to use for connections to the container. [1] |
|
| `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.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.enable=false` | Disables this container in Træfik. |
|
||||||
|
@ -254,7 +254,17 @@ Labels can be used on containers to override default behavior.
|
||||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.frontend.errors.<name>.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.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
||||||
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates 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.priority=10` | Overrides default frontend priority |
|
||||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||||
|
@ -321,7 +331,7 @@ You can define as many segments as ports exposed in a container.
|
||||||
Segment labels override the default behavior.
|
Segment labels override the default behavior.
|
||||||
|
|
||||||
| Label | Description |
|
| Label | Description |
|
||||||
|------------------------------------------------------------------------------|----------------------------------------------------------------|
|
|------------------------------------------------------------------------------------|------------------------------------------------------------------------|
|
||||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||||
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
||||||
|
@ -347,6 +357,16 @@ Segment labels override the default behavior.
|
||||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||||
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notAfter=true` | Same as `traefik.frontend.passTLSClientCert.infos.notAfter` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notBefore=true` | Same as `traefik.frontend.passTLSClientCert.infos.notBefore` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.sans=true` | Same as `traefik.frontend.passTLSClientCert.infos.sans` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.commonName=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.commonName` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.country=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.country` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.locality=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.locality` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.organization=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.organization`|
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.province=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.province` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.serialNumber`|
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.pem=true` | Same as `traefik.frontend.passTLSClientCert.infos.pem` |
|
||||||
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
||||||
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
||||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
||||||
|
|
|
@ -26,7 +26,7 @@ clusters = ["default"]
|
||||||
#
|
#
|
||||||
watch = true
|
watch = true
|
||||||
|
|
||||||
# Default domain used.
|
# Default base domain used for the frontend rules.
|
||||||
# Can be overridden by setting the "traefik.domain" label.
|
# Can be overridden by setting the "traefik.domain" label.
|
||||||
#
|
#
|
||||||
# Optional
|
# Optional
|
||||||
|
@ -131,7 +131,7 @@ Træfik needs the following policy to read ECS information:
|
||||||
Labels can be used on task containers to override default behavior:
|
Labels can be used on task containers to override default behavior:
|
||||||
|
|
||||||
| Label | Description |
|
| Label | Description |
|
||||||
|-------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `traefik.domain` | Sets the default domain for frontend rules. |
|
| `traefik.domain` | Sets the default domain for frontend rules. |
|
||||||
| `traefik.enable=false` | Disables this container in Træfik. |
|
| `traefik.enable=false` | Disables this container in Træfik. |
|
||||||
| `traefik.port=80` | Overrides the default `port` value. Overrides `NetworkBindings` from Docker Container |
|
| `traefik.port=80` | Overrides the default `port` value. Overrides `NetworkBindings` from Docker Container |
|
||||||
|
@ -158,10 +158,10 @@ Labels can be used on task containers to override default behavior:
|
||||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). |
|
| `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.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.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.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.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.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.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.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.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.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). |
|
||||||
|
@ -171,6 +171,16 @@ Labels can be used on task containers to override default behavior:
|
||||||
| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. |
|
| `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.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.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`.<br>Overrides `defaultEntryPoints` |
|
| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||||
|
@ -233,7 +243,7 @@ You can define as many segments as ports exposed in an application.
|
||||||
Segment labels override the default behavior.
|
Segment labels override the default behavior.
|
||||||
|
|
||||||
| Label | Description |
|
| Label | Description |
|
||||||
|------------------------------------------------------------------------------|----------------------------------------------------------------|
|
|-------------------------------------------------------------------------------------|-------------------------------------------------------------------------|
|
||||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||||
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
||||||
|
@ -260,6 +270,16 @@ Segment labels override the default behavior.
|
||||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||||
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notAfter=true` | Same as `traefik.frontend.passTLSClientCert.infos.notAfter` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notBefore=true` | Same as `traefik.frontend.passTLSClientCert.infos.notBefore` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.sans=true` | Same as `traefik.frontend.passTLSClientCert.infos.sans` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.commonName=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.commonName` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.country=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.country` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.locality=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.locality` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.organization=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.organization` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.province=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.province` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.serialNumber=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.serialNumber` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.pem=true` | Same as `traefik.frontend.passTLSClientCert.infos.pem` |
|
||||||
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
||||||
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
||||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
||||||
|
|
|
@ -53,9 +53,24 @@ Træfik can be configured with a file.
|
||||||
entryPoints = ["http", "https"]
|
entryPoints = ["http", "https"]
|
||||||
backend = "backend1"
|
backend = "backend1"
|
||||||
passHostHeader = true
|
passHostHeader = true
|
||||||
passTLSCert = true
|
|
||||||
priority = 42
|
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]
|
[frontends.frontend1.auth]
|
||||||
headerField = "X-WebAuth-User"
|
headerField = "X-WebAuth-User"
|
||||||
[frontends.frontend1.auth.basic]
|
[frontends.frontend1.auth.basic]
|
||||||
|
|
|
@ -31,7 +31,7 @@ endpoint = "http://127.0.0.1:8080"
|
||||||
#
|
#
|
||||||
watch = true
|
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.
|
# Can be overridden by setting the "traefik.domain" label on an application.
|
||||||
#
|
#
|
||||||
# Required
|
# Required
|
||||||
|
@ -194,8 +194,8 @@ 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.
|
The following labels can be defined on Marathon applications. They adjust the behavior for the entire application.
|
||||||
|
|
||||||
| Label | Description |
|
| Label | Description |
|
||||||
|-----------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `traefik.domain` | Sets the default domain used for the frontend rules. |
|
| `traefik.domain` | Sets the default base domain used for the frontend rules. |
|
||||||
| `traefik.enable=false` | Disables this container in Træfik. |
|
| `traefik.enable=false` | Disables this container in Træfik. |
|
||||||
| `traefik.port=80` | Registers this port. Useful when the container exposes multiples ports. |
|
| `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.portIndex=1` | Registers port by index in the application's ports array. Useful when the application exposes multiple ports. |
|
||||||
|
@ -240,6 +240,16 @@ The following labels can be defined on Marathon applications. They adjust the be
|
||||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.frontend.errors.<name>.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.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.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
||||||
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
||||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||||
|
@ -326,6 +336,16 @@ Segment labels override the default behavior.
|
||||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||||
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notAfter=true` | Same as `traefik.frontend.passTLSClientCert.infos.notAfter` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notBefore=true` | Same as `traefik.frontend.passTLSClientCert.infos.notBefore` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.sans=true` | Same as `traefik.frontend.passTLSClientCert.infos.sans` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.commonName=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.commonName` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.country=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.country` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.locality=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.locality` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.organization=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.organization`|
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.province=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.province` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.serialNumber`|
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.pem=true` | Same as `traefik.frontend.passTLSClientCert.infos.pem` |
|
||||||
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
||||||
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
||||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
||||||
|
|
|
@ -27,7 +27,7 @@ endpoint = "http://127.0.0.1:8080"
|
||||||
#
|
#
|
||||||
watch = true
|
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.
|
# Can be overridden by setting the "traefik.domain" label on an application.
|
||||||
#
|
#
|
||||||
# Required
|
# Required
|
||||||
|
@ -154,6 +154,16 @@ The following labels can be defined on Mesos tasks. They adjust the behavior for
|
||||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.frontend.errors.<name>.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.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.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
||||||
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
||||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||||
|
@ -242,6 +252,16 @@ Segment labels override the default behavior.
|
||||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||||
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notAfter=true` | Same as `traefik.frontend.passTLSClientCert.infos.notAfter` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notBefore=true` | Same as `traefik.frontend.passTLSClientCert.infos.notBefore` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.sans=true` | Same as `traefik.frontend.passTLSClientCert.infos.sans` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.commonName=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.commonName` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.country=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.country` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.locality=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.locality` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.organization=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.organization`|
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.province=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.province` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.serialNumber`|
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.pem=true` | Same as `traefik.frontend.passTLSClientCert.infos.pem` |
|
||||||
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
||||||
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
||||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
||||||
|
|
|
@ -12,7 +12,7 @@ Træfik can be configured to use Rancher as a provider.
|
||||||
# Enable Rancher Provider.
|
# Enable Rancher Provider.
|
||||||
[rancher]
|
[rancher]
|
||||||
|
|
||||||
# Default domain used.
|
# Default base domain used for the frontend rules.
|
||||||
# Can be overridden by setting the "traefik.domain" label on an service.
|
# Can be overridden by setting the "traefik.domain" label on an service.
|
||||||
#
|
#
|
||||||
# Required
|
# Required
|
||||||
|
@ -139,7 +139,7 @@ secretKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||||
Labels can be used on task containers to override default behavior:
|
Labels can be used on task containers to override default behavior:
|
||||||
|
|
||||||
| Label | Description |
|
| Label | Description |
|
||||||
|-----------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|---------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `traefik.domain` | Sets the default domain for the frontend rules. |
|
| `traefik.domain` | Sets the default domain for the frontend rules. |
|
||||||
| `traefik.enable=false` | Disables this container in Træfik. |
|
| `traefik.enable=false` | Disables this container in Træfik. |
|
||||||
| `traefik.port=80` | Registers this port. Useful when the container exposes multiple ports. |
|
| `traefik.port=80` | Registers this port. Useful when the container exposes multiple ports. |
|
||||||
|
@ -183,6 +183,16 @@ Labels can be used on task containers to override default behavior:
|
||||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
| `traefik.frontend.errors.<name>.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.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.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
||||||
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
||||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||||
|
@ -240,7 +250,7 @@ You can define as many segments as ports exposed in a container.
|
||||||
Segment labels override the default behavior.
|
Segment labels override the default behavior.
|
||||||
|
|
||||||
| Label | Description |
|
| Label | Description |
|
||||||
|------------------------------------------------------------------------------|----------------------------------------------------------------|
|
|------------------------------------------------------------------------------------|------------------------------------------------------------------------|
|
||||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||||
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
||||||
|
@ -266,6 +276,16 @@ Segment labels override the default behavior.
|
||||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||||
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notAfter=true` | Same as `traefik.frontend.passTLSClientCert.infos.notAfter` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notBefore=true` | Same as `traefik.frontend.passTLSClientCert.infos.notBefore` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.sans=true` | Same as `traefik.frontend.passTLSClientCert.infos.sans` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.commonName=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.commonName` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.country=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.country` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.locality=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.locality` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.organization=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.organization`|
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.province=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.province` |
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.serialNumber`|
|
||||||
|
| `traefik.<segment_name>.frontend.passTLSClientCert.pem=true` | Same as `traefik.frontend.passTLSClientCert.infos.pem` |
|
||||||
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
||||||
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
||||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
||||||
|
|
|
@ -311,7 +311,6 @@ The `consul` provider contains the configuration.
|
||||||
[frontends.frontend2]
|
[frontends.frontend2]
|
||||||
backend = "backend1"
|
backend = "backend1"
|
||||||
passHostHeader = true
|
passHostHeader = true
|
||||||
passTLSCert = true
|
|
||||||
entrypoints = ["https"] # overrides defaultEntryPoints
|
entrypoints = ["https"] # overrides defaultEntryPoints
|
||||||
[frontends.frontend2.routes.test_1]
|
[frontends.frontend2.routes.test_1]
|
||||||
rule = "Host:{subdomain:[a-z]+}.localhost"
|
rule = "Host:{subdomain:[a-z]+}.localhost"
|
||||||
|
|
|
@ -118,7 +118,7 @@ func (hc *HealthCheck) execute(ctx context.Context, backend *BackendConfig) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
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
|
return
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
log.Debugf("Refreshing health check for backend: %s", backend.name)
|
log.Debugf("Refreshing health check for backend: %s", backend.name)
|
||||||
|
|
21
integration/fixtures/tlsclientheaders/root.pem
Normal file
21
integration/fixtures/tlsclientheaders/root.pem
Normal file
|
@ -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-----
|
27
integration/fixtures/tlsclientheaders/server.key
Normal file
27
integration/fixtures/tlsclientheaders/server.key
Normal file
|
@ -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-----
|
19
integration/fixtures/tlsclientheaders/server.pem
Normal file
19
integration/fixtures/tlsclientheaders/server.pem
Normal file
|
@ -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-----
|
24
integration/fixtures/tlsclientheaders/simple.toml
Normal file
24
integration/fixtures/tlsclientheaders/simple.toml
Normal file
|
@ -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
|
|
@ -59,6 +59,7 @@ func init() {
|
||||||
check.Suite(&RateLimitSuite{})
|
check.Suite(&RateLimitSuite{})
|
||||||
check.Suite(&RetrySuite{})
|
check.Suite(&RetrySuite{})
|
||||||
check.Suite(&SimpleSuite{})
|
check.Suite(&SimpleSuite{})
|
||||||
|
check.Suite(&TLSClientHeadersSuite{})
|
||||||
check.Suite(&TimeoutSuite{})
|
check.Suite(&TimeoutSuite{})
|
||||||
check.Suite(&TracingSuite{})
|
check.Suite(&TracingSuite{})
|
||||||
check.Suite(&WebsocketSuite{})
|
check.Suite(&WebsocketSuite{})
|
||||||
|
|
6
integration/resources/compose/tlsclientheaders.yml
Normal file
6
integration/resources/compose/tlsclientheaders.yml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
whoami:
|
||||||
|
image: containous/whoami
|
||||||
|
labels:
|
||||||
|
- traefik.frontend.passTLSClientCert.pem=true
|
||||||
|
- traefik.frontend.rule=PathPrefix:/
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/containous/traefik/integration/try"
|
"github.com/containous/traefik/integration/try"
|
||||||
"github.com/go-check/check"
|
"github.com/go-check/check"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
checker "github.com/vdemeester/shakers"
|
checker "github.com/vdemeester/shakers"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -38,3 +39,29 @@ func (s *RetrySuite) TestRetry(c *check.C) {
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil)
|
||||||
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
|
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)
|
||||||
|
}
|
||||||
|
|
71
integration/tls_client_headers_test.go
Normal file
71
integration/tls_client_headers_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
|
@ -48,7 +48,7 @@ func (wl *IPWhiteLister) handle(w http.ResponseWriter, r *http.Request, next htt
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Debugf("Accept %s: %+v", wl.strategy.GetIP(r), r)
|
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)
|
next.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package middlewares
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -41,11 +42,8 @@ func (retry *Retry) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||||
attempts := 1
|
attempts := 1
|
||||||
for {
|
for {
|
||||||
attemptsExhausted := attempts >= retry.attempts
|
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
|
shouldRetry := !attemptsExhausted
|
||||||
// context and so we don't get httptrace information.
|
|
||||||
// Websocket clients should however retry on their own anyway.
|
|
||||||
shouldRetry := !attemptsExhausted && !isWebsocketRequest(r)
|
|
||||||
retryResponseWriter := newRetryResponseWriter(rw, shouldRetry)
|
retryResponseWriter := newRetryResponseWriter(rw, shouldRetry)
|
||||||
|
|
||||||
// Disable retries when the backend already received request data
|
// 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) {
|
func (rr *retryResponseWriterWithoutCloseNotify) Write(buf []byte) (int, error) {
|
||||||
if rr.ShouldRetry() {
|
if rr.ShouldRetry() {
|
||||||
return 0, nil
|
return len(buf), nil
|
||||||
}
|
}
|
||||||
return rr.responseWriter.Write(buf)
|
return rr.responseWriter.Write(buf)
|
||||||
}
|
}
|
||||||
|
@ -150,7 +148,11 @@ func (rr *retryResponseWriterWithoutCloseNotify) WriteHeader(code int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rr *retryResponseWriterWithoutCloseNotify) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
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() {
|
func (rr *retryResponseWriterWithoutCloseNotify) Flush() {
|
||||||
|
|
|
@ -3,10 +3,13 @@ package middlewares
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/containous/traefik/testhelpers"
|
"github.com/containous/traefik/testhelpers"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/vulcand/oxy/forward"
|
"github.com/vulcand/oxy/forward"
|
||||||
"github.com/vulcand/oxy/roundrobin"
|
"github.com/vulcand/oxy/roundrobin"
|
||||||
)
|
)
|
||||||
|
@ -18,7 +21,6 @@ func TestRetry(t *testing.T) {
|
||||||
wantRetryAttempts int
|
wantRetryAttempts int
|
||||||
wantResponseStatus int
|
wantResponseStatus int
|
||||||
amountFaultyEndpoints int
|
amountFaultyEndpoints int
|
||||||
isWebsocketHandshakeRequest bool
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
desc: "no retry on success",
|
desc: "no retry on success",
|
||||||
|
@ -55,14 +57,6 @@ func TestRetry(t *testing.T) {
|
||||||
wantResponseStatus: http.StatusInternalServerError,
|
wantResponseStatus: http.StatusInternalServerError,
|
||||||
amountFaultyEndpoints: 3,
|
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) {
|
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)
|
t.Fatalf("Error creating forwarder: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, test := range testCases {
|
||||||
tc := tc
|
test := test
|
||||||
|
|
||||||
t.Run(tc.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
loadBalancer, err := roundrobin.New(forwarder)
|
loadBalancer, err := roundrobin.New(forwarder)
|
||||||
|
@ -87,7 +81,7 @@ func TestRetry(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
basePort := 33444
|
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.
|
// 192.0.2.0 is a non-routable IP for testing purposes.
|
||||||
// See: https://stackoverflow.com/questions/528538/non-routable-ip-address/18436928#18436928
|
// 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
|
// 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)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
retryListener := &countingRetryListener{}
|
retryListener := &countingRetryListener{}
|
||||||
retry := NewRetry(tc.maxRequestAttempts, loadBalancer, retryListener)
|
retry := NewRetry(test.maxRequestAttempts, loadBalancer, retryListener)
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
req := httptest.NewRequest(http.MethodGet, "http://localhost:3000/ok", nil)
|
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)
|
retry.ServeHTTP(recorder, req)
|
||||||
|
|
||||||
if tc.wantResponseStatus != recorder.Code {
|
assert.Equal(t, test.wantResponseStatus, recorder.Code)
|
||||||
t.Errorf("got status code %d, want %d", recorder.Code, tc.wantResponseStatus)
|
assert.Equal(t, test.wantRetryAttempts, retryListener.timesCalled)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if tc.wantRetryAttempts != retryListener.timesCalled {
|
}
|
||||||
t.Errorf("retry listener called %d time(s), want %d time(s)", retryListener.timesCalled, tc.wantRetryAttempts)
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
251
middlewares/tlsClientHeaders.go
Normal file
251
middlewares/tlsClientHeaders.go
Normal file
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
799
middlewares/tlsClientHeaders_test.go
Normal file
799
middlewares/tlsClientHeaders_test.go
Normal file
|
@ -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))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -338,7 +338,7 @@ func (p *Provider) watchNewDomains() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(domains) == 0 {
|
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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ func (p *Provider) buildConfiguration(catalog []catalogUpdate) *types.Configurat
|
||||||
"getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
"getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
||||||
"getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
|
"getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
|
||||||
"getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
"getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||||
|
"getPassTLSClientCert": label.GetTLSClientCert,
|
||||||
"getWhiteList": label.GetWhiteList,
|
"getWhiteList": label.GetWhiteList,
|
||||||
"getRedirect": label.GetRedirect,
|
"getRedirect": label.GetRedirect,
|
||||||
"getErrorPages": label.GetErrorPages,
|
"getErrorPages": label.GetErrorPages,
|
||||||
|
|
|
@ -423,6 +423,17 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
||||||
label.TraefikBackendBufferingMemRequestBodyBytes + "=2097152",
|
label.TraefikBackendBufferingMemRequestBodyBytes + "=2097152",
|
||||||
label.TraefikBackendBufferingRetryExpression + "=IsNetworkError() && Attempts() <= 2",
|
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.TraefikFrontendAuthBasic + "=test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
label.TraefikFrontendAuthBasicRemoveHeader + "=true",
|
label.TraefikFrontendAuthBasicRemoveHeader + "=true",
|
||||||
label.TraefikFrontendAuthBasicUsers + "=test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
label.TraefikFrontendAuthBasicUsers + "=test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
|
@ -540,6 +551,22 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
PassTLSCert: true,
|
PassTLSCert: true,
|
||||||
Priority: 666,
|
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{
|
Auth: &types.Auth{
|
||||||
HeaderField: "X-WebAuth-User",
|
HeaderField: "X-WebAuth-User",
|
||||||
Basic: &types.Basic{
|
Basic: &types.Basic{
|
||||||
|
|
|
@ -46,6 +46,7 @@ func (p *Provider) buildConfiguration(containersInspected []dockerData) *types.C
|
||||||
"getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
"getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
||||||
"getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
|
"getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
|
||||||
"getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
"getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||||
|
"getPassTLSClientCert": label.GetTLSClientCert,
|
||||||
"getEntryPoints": label.GetFuncSliceString(label.TraefikFrontendEntryPoints),
|
"getEntryPoints": label.GetFuncSliceString(label.TraefikFrontendEntryPoints),
|
||||||
"getBasicAuth": label.GetFuncSliceString(label.TraefikFrontendAuthBasic), // Deprecated
|
"getBasicAuth": label.GetFuncSliceString(label.TraefikFrontendAuthBasic), // Deprecated
|
||||||
"getAuth": label.GetAuth,
|
"getAuth": label.GetAuth,
|
||||||
|
|
|
@ -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",
|
desc: "when frontend basic auth backward compatibility",
|
||||||
containers: []docker.ContainerJSON{
|
containers: []docker.ContainerJSON{
|
||||||
|
@ -387,6 +450,17 @@ func TestDockerBuildConfiguration(t *testing.T) {
|
||||||
label.TraefikBackendBufferingMemRequestBodyBytes: "2097152",
|
label.TraefikBackendBufferingMemRequestBodyBytes: "2097152",
|
||||||
label.TraefikBackendBufferingRetryExpression: "IsNetworkError() && Attempts() <= 2",
|
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.TraefikFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
label.TraefikFrontendAuthBasicRemoveHeader: "true",
|
label.TraefikFrontendAuthBasicRemoveHeader: "true",
|
||||||
label.TraefikFrontendAuthBasicUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
label.TraefikFrontendAuthBasicUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
|
@ -475,6 +549,22 @@ func TestDockerBuildConfiguration(t *testing.T) {
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
PassTLSCert: true,
|
PassTLSCert: true,
|
||||||
Priority: 666,
|
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{
|
Auth: &types.Auth{
|
||||||
HeaderField: "X-WebAuth-User",
|
HeaderField: "X-WebAuth-User",
|
||||||
Basic: &types.Basic{
|
Basic: &types.Basic{
|
||||||
|
|
|
@ -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",
|
desc: "when frontend basic auth configuration",
|
||||||
services: []swarm.Service{
|
services: []swarm.Service{
|
||||||
|
|
|
@ -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",
|
desc: "auth basic",
|
||||||
containers: []docker.ContainerJSON{
|
containers: []docker.ContainerJSON{
|
||||||
|
@ -286,6 +351,17 @@ func TestSegmentBuildConfiguration(t *testing.T) {
|
||||||
label.Prefix + "sauternes." + label.SuffixProtocol: "https",
|
label.Prefix + "sauternes." + label.SuffixProtocol: "https",
|
||||||
label.Prefix + "sauternes." + label.SuffixWeight: "12",
|
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.SuffixFrontendAuthBasicRemoveHeader: "true",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendAuthBasicUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
label.Prefix + "sauternes." + label.SuffixFrontendAuthBasicUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendAuthBasicUsersFile: ".htpasswd",
|
label.Prefix + "sauternes." + label.SuffixFrontendAuthBasicUsersFile: ".htpasswd",
|
||||||
|
@ -368,6 +444,22 @@ func TestSegmentBuildConfiguration(t *testing.T) {
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
PassTLSCert: true,
|
PassTLSCert: true,
|
||||||
Priority: 666,
|
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{
|
Auth: &types.Auth{
|
||||||
HeaderField: "X-WebAuth-User",
|
HeaderField: "X-WebAuth-User",
|
||||||
Basic: &types.Basic{
|
Basic: &types.Basic{
|
||||||
|
|
|
@ -36,6 +36,7 @@ func (p *Provider) buildConfiguration(instances []ecsInstance) (*types.Configura
|
||||||
"getFrontendName": p.getFrontendName,
|
"getFrontendName": p.getFrontendName,
|
||||||
"getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
|
"getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
|
||||||
"getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
"getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||||
|
"getPassTLSClientCert": label.GetTLSClientCert,
|
||||||
"getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
"getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
||||||
"getBasicAuth": label.GetFuncSliceString(label.TraefikFrontendAuthBasic), // Deprecated
|
"getBasicAuth": label.GetFuncSliceString(label.TraefikFrontendAuthBasic), // Deprecated
|
||||||
"getAuth": label.GetAuth,
|
"getAuth": label.GetAuth,
|
||||||
|
|
|
@ -321,6 +321,17 @@ func TestSegmentBuildConfiguration(t *testing.T) {
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendAuthForwardTLSInsecureSkipVerify: "true",
|
label.Prefix + "sauternes." + label.SuffixFrontendAuthForwardTLSInsecureSkipVerify: "true",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendAuthHeaderField: "X-WebAuth-User",
|
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.SuffixFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendEntryPoints: "http,https",
|
label.Prefix + "sauternes." + label.SuffixFrontendEntryPoints: "http,https",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendPassHostHeader: "true",
|
label.Prefix + "sauternes." + label.SuffixFrontendPassHostHeader: "true",
|
||||||
|
@ -391,6 +402,22 @@ func TestSegmentBuildConfiguration(t *testing.T) {
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
PassTLSCert: true,
|
PassTLSCert: true,
|
||||||
Priority: 666,
|
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{
|
Auth: &types.Auth{
|
||||||
HeaderField: "X-WebAuth-User",
|
HeaderField: "X-WebAuth-User",
|
||||||
Basic: &types.Basic{
|
Basic: &types.Basic{
|
||||||
|
|
|
@ -356,6 +356,17 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
label.TraefikBackendBufferingMemRequestBodyBytes: aws.String("2097152"),
|
label.TraefikBackendBufferingMemRequestBodyBytes: aws.String("2097152"),
|
||||||
label.TraefikBackendBufferingRetryExpression: aws.String("IsNetworkError() && Attempts() <= 2"),
|
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.TraefikFrontendAuthBasic: aws.String("test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
||||||
label.TraefikFrontendAuthBasicRemoveHeader: aws.String("true"),
|
label.TraefikFrontendAuthBasicRemoveHeader: aws.String("true"),
|
||||||
label.TraefikFrontendAuthBasicUsers: aws.String("test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
label.TraefikFrontendAuthBasicUsers: aws.String("test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
||||||
|
@ -489,6 +500,22 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
PassTLSCert: true,
|
PassTLSCert: true,
|
||||||
Priority: 666,
|
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{
|
Auth: &types.Auth{
|
||||||
HeaderField: "X-WebAuth-User",
|
HeaderField: "X-WebAuth-User",
|
||||||
Basic: &types.Basic{
|
Basic: &types.Basic{
|
||||||
|
|
|
@ -380,7 +380,7 @@ func (p *Provider) lookupEc2Instances(ctx context.Context, client *awsClient, cl
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Unable to describe instances [%s]: %v", err)
|
log.Errorf("Unable to describe instances: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,19 @@ const (
|
||||||
pathFrontendBackend = "/backend"
|
pathFrontendBackend = "/backend"
|
||||||
pathFrontendPriority = "/priority"
|
pathFrontendPriority = "/priority"
|
||||||
pathFrontendPassHostHeader = "/passhostheader"
|
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"
|
pathFrontendPassTLSCert = "/passtlscert"
|
||||||
pathFrontendWhiteListSourceRange = "/whitelist/sourcerange"
|
pathFrontendWhiteListSourceRange = "/whitelist/sourcerange"
|
||||||
pathFrontendWhiteListIPStrategy = "/whitelist/ipstrategy"
|
pathFrontendWhiteListIPStrategy = "/whitelist/ipstrategy"
|
||||||
|
|
|
@ -45,6 +45,7 @@ func (p *Provider) buildConfiguration() *types.Configuration {
|
||||||
"getPriority": p.getFuncInt(pathFrontendPriority, label.DefaultFrontendPriority),
|
"getPriority": p.getFuncInt(pathFrontendPriority, label.DefaultFrontendPriority),
|
||||||
"getPassHostHeader": p.getFuncBool(pathFrontendPassHostHeader, label.DefaultPassHostHeader),
|
"getPassHostHeader": p.getFuncBool(pathFrontendPassHostHeader, label.DefaultPassHostHeader),
|
||||||
"getPassTLSCert": p.getFuncBool(pathFrontendPassTLSCert, label.DefaultPassTLSCert),
|
"getPassTLSCert": p.getFuncBool(pathFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||||
|
"getPassTLSClientCert": p.getTLSClientCert,
|
||||||
"getEntryPoints": p.getFuncList(pathFrontendEntryPoints),
|
"getEntryPoints": p.getFuncList(pathFrontendEntryPoints),
|
||||||
"getAuth": p.getAuth,
|
"getAuth": p.getAuth,
|
||||||
"getRoutes": p.getRoutes,
|
"getRoutes": p.getRoutes,
|
||||||
|
@ -334,6 +335,39 @@ func (p *Provider) getTLSSection(prefix string) []*tls.Configuration {
|
||||||
return tlsSection
|
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
|
// GetAuth Create auth from path
|
||||||
func (p *Provider) getAuth(rootPath string) *types.Auth {
|
func (p *Provider) getAuth(rootPath string) *types.Auth {
|
||||||
if p.hasPrefix(rootPath, pathFrontendAuth) {
|
if p.hasPrefix(rootPath, pathFrontendAuth) {
|
||||||
|
|
|
@ -277,6 +277,18 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
||||||
withPair(pathFrontendBackend, "backend1"),
|
withPair(pathFrontendBackend, "backend1"),
|
||||||
withPair(pathFrontendPriority, "6"),
|
withPair(pathFrontendPriority, "6"),
|
||||||
withPair(pathFrontendPassHostHeader, "false"),
|
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"),
|
withPair(pathFrontendPassTLSCert, "true"),
|
||||||
withList(pathFrontendEntryPoints, "http", "https"),
|
withList(pathFrontendEntryPoints, "http", "https"),
|
||||||
withList(pathFrontendWhiteListSourceRange, "1.1.1.1/24", "1234:abcd::42/32"),
|
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"},
|
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{
|
Auth: &types.Auth{
|
||||||
HeaderField: "X-WebAuth-User",
|
HeaderField: "X-WebAuth-User",
|
||||||
Basic: &types.Basic{
|
Basic: &types.Basic{
|
||||||
|
|
|
@ -78,7 +78,20 @@ const (
|
||||||
SuffixFrontendHeadersReferrerPolicy = SuffixFrontendHeaders + "referrerPolicy"
|
SuffixFrontendHeadersReferrerPolicy = SuffixFrontendHeaders + "referrerPolicy"
|
||||||
SuffixFrontendHeadersIsDevelopment = SuffixFrontendHeaders + "isDevelopment"
|
SuffixFrontendHeadersIsDevelopment = SuffixFrontendHeaders + "isDevelopment"
|
||||||
SuffixFrontendPassHostHeader = "frontend.passHostHeader"
|
SuffixFrontendPassHostHeader = "frontend.passHostHeader"
|
||||||
SuffixFrontendPassTLSCert = "frontend.passTLSCert"
|
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"
|
SuffixFrontendPriority = "frontend.priority"
|
||||||
SuffixFrontendRateLimitExtractorFunc = "frontend.rateLimit.extractorFunc"
|
SuffixFrontendRateLimitExtractorFunc = "frontend.rateLimit.extractorFunc"
|
||||||
SuffixFrontendRedirectEntryPoint = "frontend.redirect.entryPoint"
|
SuffixFrontendRedirectEntryPoint = "frontend.redirect.entryPoint"
|
||||||
|
@ -143,7 +156,20 @@ const (
|
||||||
TraefikFrontendAuthHeaderField = Prefix + SuffixFrontendAuthHeaderField
|
TraefikFrontendAuthHeaderField = Prefix + SuffixFrontendAuthHeaderField
|
||||||
TraefikFrontendEntryPoints = Prefix + SuffixFrontendEntryPoints
|
TraefikFrontendEntryPoints = Prefix + SuffixFrontendEntryPoints
|
||||||
TraefikFrontendPassHostHeader = Prefix + SuffixFrontendPassHostHeader
|
TraefikFrontendPassHostHeader = Prefix + SuffixFrontendPassHostHeader
|
||||||
TraefikFrontendPassTLSCert = Prefix + SuffixFrontendPassTLSCert
|
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
|
TraefikFrontendPriority = Prefix + SuffixFrontendPriority
|
||||||
TraefikFrontendRateLimitExtractorFunc = Prefix + SuffixFrontendRateLimitExtractorFunc
|
TraefikFrontendRateLimitExtractorFunc = Prefix + SuffixFrontendRateLimitExtractorFunc
|
||||||
TraefikFrontendRedirectEntryPoint = Prefix + SuffixFrontendRedirectEntryPoint
|
TraefikFrontendRedirectEntryPoint = Prefix + SuffixFrontendRedirectEntryPoint
|
||||||
|
|
|
@ -62,6 +62,39 @@ func GetRedirect(labels map[string]string) *types.Redirect {
|
||||||
return nil
|
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
|
// GetAuth Create auth from labels
|
||||||
func GetAuth(labels map[string]string) *types.Auth {
|
func GetAuth(labels map[string]string) *types.Auth {
|
||||||
if !HasPrefix(labels, TraefikFrontendAuth) {
|
if !HasPrefix(labels, TraefikFrontendAuth) {
|
||||||
|
|
|
@ -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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ func (p *Provider) buildConfiguration(applications *marathon.Applications) *type
|
||||||
"getFrontendName": p.getFrontendName,
|
"getFrontendName": p.getFrontendName,
|
||||||
"getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
|
"getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
|
||||||
"getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
"getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||||
|
"getPassTLSClientCert": label.GetTLSClientCert,
|
||||||
"getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
"getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
||||||
"getEntryPoints": label.GetFuncSliceString(label.TraefikFrontendEntryPoints),
|
"getEntryPoints": label.GetFuncSliceString(label.TraefikFrontendEntryPoints),
|
||||||
"getBasicAuth": label.GetFuncSliceString(label.TraefikFrontendAuthBasic), // Deprecated
|
"getBasicAuth": label.GetFuncSliceString(label.TraefikFrontendAuthBasic), // Deprecated
|
||||||
|
|
|
@ -373,6 +373,17 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
withLabel(label.TraefikBackendBufferingMemRequestBodyBytes, "2097152"),
|
withLabel(label.TraefikBackendBufferingMemRequestBodyBytes, "2097152"),
|
||||||
withLabel(label.TraefikBackendBufferingRetryExpression, "IsNetworkError() && Attempts() <= 2"),
|
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.TraefikFrontendAuthBasic, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
||||||
withLabel(label.TraefikFrontendAuthBasicRemoveHeader, "true"),
|
withLabel(label.TraefikFrontendAuthBasicRemoveHeader, "true"),
|
||||||
withLabel(label.TraefikFrontendAuthBasicUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
withLabel(label.TraefikFrontendAuthBasicUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
||||||
|
@ -455,6 +466,22 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
PassTLSCert: true,
|
PassTLSCert: true,
|
||||||
Priority: 666,
|
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{
|
Auth: &types.Auth{
|
||||||
HeaderField: "X-WebAuth-User",
|
HeaderField: "X-WebAuth-User",
|
||||||
Basic: &types.Basic{
|
Basic: &types.Basic{
|
||||||
|
@ -767,6 +794,17 @@ func TestBuildConfigurationSegments(t *testing.T) {
|
||||||
withSegmentLabel(label.TraefikProtocol, "https", "containous"),
|
withSegmentLabel(label.TraefikProtocol, "https", "containous"),
|
||||||
withSegmentLabel(label.TraefikWeight, "12", "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.TraefikFrontendAuthBasic, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", "containous"),
|
||||||
withSegmentLabel(label.TraefikFrontendAuthBasicRemoveHeader, "true", "containous"),
|
withSegmentLabel(label.TraefikFrontendAuthBasicRemoveHeader, "true", "containous"),
|
||||||
withSegmentLabel(label.TraefikFrontendAuthBasicUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", "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,
|
PassHostHeader: true,
|
||||||
PassTLSCert: true,
|
PassTLSCert: true,
|
||||||
Priority: 666,
|
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{
|
Auth: &types.Auth{
|
||||||
HeaderField: "X-WebAuth-User",
|
HeaderField: "X-WebAuth-User",
|
||||||
Basic: &types.Basic{
|
Basic: &types.Basic{
|
||||||
|
|
|
@ -48,6 +48,7 @@ func (p *Provider) buildConfiguration(tasks []state.Task) *types.Configuration {
|
||||||
"getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
"getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
||||||
"getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
|
"getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
|
||||||
"getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
"getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||||
|
"getPassTLSClientCert": label.GetTLSClientCert,
|
||||||
"getFrontendRule": p.getFrontendRule,
|
"getFrontendRule": p.getFrontendRule,
|
||||||
"getRedirect": label.GetRedirect,
|
"getRedirect": label.GetRedirect,
|
||||||
"getErrorPages": label.GetErrorPages,
|
"getErrorPages": label.GetErrorPages,
|
||||||
|
|
|
@ -330,6 +330,17 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
withLabel(label.TraefikBackendBufferingMemRequestBodyBytes, "2097152"),
|
withLabel(label.TraefikBackendBufferingMemRequestBodyBytes, "2097152"),
|
||||||
withLabel(label.TraefikBackendBufferingRetryExpression, "IsNetworkError() && Attempts() <= 2"),
|
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.TraefikFrontendAuthBasic, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
||||||
withLabel(label.TraefikFrontendAuthBasicRemoveHeader, "true"),
|
withLabel(label.TraefikFrontendAuthBasicRemoveHeader, "true"),
|
||||||
withLabel(label.TraefikFrontendAuthBasicUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
withLabel(label.TraefikFrontendAuthBasicUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
||||||
|
@ -418,6 +429,22 @@ func TestBuildConfiguration(t *testing.T) {
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
PassTLSCert: true,
|
PassTLSCert: true,
|
||||||
Priority: 666,
|
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{
|
Auth: &types.Auth{
|
||||||
HeaderField: "X-WebAuth-User",
|
HeaderField: "X-WebAuth-User",
|
||||||
Basic: &types.Basic{
|
Basic: &types.Basic{
|
||||||
|
@ -687,6 +714,17 @@ func TestBuildConfigurationSegments(t *testing.T) {
|
||||||
withSegmentLabel(label.TraefikProtocol, "https", "containous"),
|
withSegmentLabel(label.TraefikProtocol, "https", "containous"),
|
||||||
withSegmentLabel(label.TraefikWeight, "12", "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.TraefikFrontendAuthBasic, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", "containous"),
|
||||||
withSegmentLabel(label.TraefikFrontendAuthBasicRemoveHeader, "true", "containous"),
|
withSegmentLabel(label.TraefikFrontendAuthBasicRemoveHeader, "true", "containous"),
|
||||||
withSegmentLabel(label.TraefikFrontendAuthBasicUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", "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,
|
PassHostHeader: true,
|
||||||
PassTLSCert: true,
|
PassTLSCert: true,
|
||||||
Priority: 666,
|
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{
|
Auth: &types.Auth{
|
||||||
HeaderField: "X-WebAuth-User",
|
HeaderField: "X-WebAuth-User",
|
||||||
Basic: &types.Basic{
|
Basic: &types.Basic{
|
||||||
|
|
|
@ -128,7 +128,7 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s
|
||||||
func detectMasters(zk string, masters []string) <-chan []string {
|
func detectMasters(zk string, masters []string) <-chan []string {
|
||||||
changed := make(chan []string, 1)
|
changed := make(chan []string, 1)
|
||||||
if zk != "" {
|
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 {
|
if md, err := detector.New(zk); err != nil {
|
||||||
log.Errorf("Failed to create master detector: %v", err)
|
log.Errorf("Failed to create master detector: %v", err)
|
||||||
} else if err := md.Detect(detect.NewMasters(masters, changed)); err != nil {
|
} else if err := md.Detect(detect.NewMasters(masters, changed)); err != nil {
|
||||||
|
|
|
@ -33,6 +33,7 @@ func (p *Provider) buildConfiguration(services []rancherData) *types.Configurati
|
||||||
"getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
"getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
||||||
"getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
|
"getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
|
||||||
"getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
"getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||||
|
"getPassTLSClientCert": label.GetTLSClientCert,
|
||||||
"getEntryPoints": label.GetFuncSliceString(label.TraefikFrontendEntryPoints),
|
"getEntryPoints": label.GetFuncSliceString(label.TraefikFrontendEntryPoints),
|
||||||
"getBasicAuth": label.GetFuncSliceString(label.TraefikFrontendAuthBasic), // Deprecated
|
"getBasicAuth": label.GetFuncSliceString(label.TraefikFrontendAuthBasic), // Deprecated
|
||||||
"getAuth": label.GetAuth,
|
"getAuth": label.GetAuth,
|
||||||
|
|
|
@ -58,6 +58,17 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
||||||
label.TraefikBackendBufferingMemRequestBodyBytes: "2097152",
|
label.TraefikBackendBufferingMemRequestBodyBytes: "2097152",
|
||||||
label.TraefikBackendBufferingRetryExpression: "IsNetworkError() && Attempts() <= 2",
|
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.TraefikFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
label.TraefikFrontendAuthBasicRemoveHeader: "true",
|
label.TraefikFrontendAuthBasicRemoveHeader: "true",
|
||||||
label.TraefikFrontendAuthBasicUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
label.TraefikFrontendAuthBasicUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
|
@ -144,6 +155,22 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
PassTLSCert: true,
|
PassTLSCert: true,
|
||||||
Priority: 666,
|
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{
|
Auth: &types.Auth{
|
||||||
HeaderField: "X-WebAuth-User",
|
HeaderField: "X-WebAuth-User",
|
||||||
Basic: &types.Basic{
|
Basic: &types.Basic{
|
||||||
|
@ -293,6 +320,17 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
||||||
label.Prefix + "sauternes." + label.SuffixProtocol: "https",
|
label.Prefix + "sauternes." + label.SuffixProtocol: "https",
|
||||||
label.Prefix + "sauternes." + label.SuffixWeight: "12",
|
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.SuffixFrontendRule: "Host:traefik.wtf",
|
||||||
label.Prefix + "sauternes." + label.SuffixFrontendAuthBasicRemoveHeader: "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.SuffixFrontendAuthBasicUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
|
@ -373,6 +411,22 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
||||||
PassHostHeader: true,
|
PassHostHeader: true,
|
||||||
PassTLSCert: true,
|
PassTLSCert: true,
|
||||||
Priority: 666,
|
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{
|
Auth: &types.Auth{
|
||||||
HeaderField: "X-WebAuth-User",
|
HeaderField: "X-WebAuth-User",
|
||||||
Basic: &types.Basic{
|
Basic: &types.Basic{
|
||||||
|
|
|
@ -29,7 +29,7 @@ func (p *Provider) metadataProvide(configurationChan chan<- types.ConfigMessage,
|
||||||
operation := func() error {
|
operation := func() error {
|
||||||
client, err := rancher.NewClientAndWait(metadataServiceURL)
|
client, err := rancher.NewClientAndWait(metadataServiceURL)
|
||||||
if err != nil {
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ func (p *Provider) metadataProvide(configurationChan chan<- types.ConfigMessage,
|
||||||
|
|
||||||
stacks, err := client.GetStacks()
|
stacks, err := client.GetStacks()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Failed to query Rancher metadata service: %s", err)
|
log.Errorf("Failed to query Rancher metadata service: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/BurntSushi/ty/fun"
|
|
||||||
"github.com/containous/mux"
|
"github.com/containous/mux"
|
||||||
"github.com/containous/traefik/hostresolver"
|
"github.com/containous/traefik/hostresolver"
|
||||||
"github.com/containous/traefik/log"
|
"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
|
// ParseDomains parses rules expressions and returns domains
|
||||||
func (r *Rules) ParseDomains(expression string) ([]string, error) {
|
func (r *Rules) ParseDomains(expression string) ([]string, error) {
|
||||||
var domains []string
|
var domains []string
|
||||||
|
isHostRule := false
|
||||||
|
|
||||||
err := r.parseRules(expression, func(functionName string, function interface{}, arguments []string) error {
|
err := r.parseRules(expression, func(functionName string, function interface{}, arguments []string) error {
|
||||||
if functionName == "Host" {
|
if functionName == "Host" {
|
||||||
|
isHostRule = true
|
||||||
domains = append(domains, arguments...)
|
domains = append(domains, arguments...)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -299,5 +300,18 @@ func (r *Rules) ParseDomains(expression string) ([]string, error) {
|
||||||
return nil, fmt.Errorf("error parsing domains: %v", err)
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,24 +64,38 @@ func TestParseDomains(t *testing.T) {
|
||||||
rules := &Rules{}
|
rules := &Rules{}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
description string
|
||||||
expression string
|
expression string
|
||||||
domain []string
|
domain []string
|
||||||
|
errorExpected bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
description: "Many host rules",
|
||||||
expression: "Host:foo.bar,test.bar",
|
expression: "Host:foo.bar,test.bar",
|
||||||
domain: []string{"foo.bar", "test.bar"},
|
domain: []string{"foo.bar", "test.bar"},
|
||||||
|
errorExpected: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
description: "No host rule",
|
||||||
expression: "Path:/test",
|
expression: "Path:/test",
|
||||||
domain: []string{},
|
errorExpected: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
description: "Host rule and another rule",
|
||||||
expression: "Host:foo.bar;Path:/test",
|
expression: "Host:foo.bar;Path:/test",
|
||||||
domain: []string{"foo.bar"},
|
domain: []string{"foo.bar"},
|
||||||
|
errorExpected: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
description: "Host rule to trim and another rule",
|
||||||
expression: "Host: Foo.Bar ;Path:/test",
|
expression: "Host: Foo.Bar ;Path:/test",
|
||||||
domain: []string{"foo.bar"},
|
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()
|
t.Parallel()
|
||||||
|
|
||||||
domains, err := rules.ParseDomains(test.expression)
|
domains, err := rules.ParseDomains(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)
|
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)
|
assert.EqualValues(t, test.domain, domains, "%s: Error parsing domains from expression.", test.expression)
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
stdlog "log"
|
stdlog "log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -244,7 +243,9 @@ func (s *Server) Start() {
|
||||||
s.listenConfigurations(stop)
|
s.listenConfigurations(stop)
|
||||||
})
|
})
|
||||||
s.startProvider()
|
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
|
// 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 {
|
if len(tlsOption.ClientCA.Files) > 0 {
|
||||||
pool := x509.NewCertPool()
|
pool := x509.NewCertPool()
|
||||||
for _, caFile := range tlsOption.ClientCA.Files {
|
for _, caFile := range tlsOption.ClientCA.Files {
|
||||||
data, err := ioutil.ReadFile(caFile)
|
data, err := caFile.Read()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -424,10 +424,12 @@ func (s *Server) throttleProviderConfigReload(throttle time.Duration, publish ch
|
||||||
case <-stop:
|
case <-stop:
|
||||||
return
|
return
|
||||||
case nextConfig := <-ring.Out():
|
case nextConfig := <-ring.Out():
|
||||||
publish <- nextConfig.(types.ConfigMessage)
|
if config, ok := nextConfig.(types.ConfigMessage); ok {
|
||||||
|
publish <- config
|
||||||
time.Sleep(throttle)
|
time.Sleep(throttle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
@ -515,6 +517,8 @@ func (s *Server) postLoadConfiguration() {
|
||||||
domains, err := rls.ParseDomains(route.Rule)
|
domains, err := rls.ParseDomains(route.Rule)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error parsing domains: %v", err)
|
log.Errorf("Error parsing domains: %v", err)
|
||||||
|
} else if len(domains) == 0 {
|
||||||
|
log.Debugf("No domain parsed in rule %q", route.Rule)
|
||||||
} else {
|
} else {
|
||||||
s.globalConfiguration.ACME.LoadCertificateForDomains(domains)
|
s.globalConfiguration.ACME.LoadCertificateForDomains(domains)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -280,7 +279,7 @@ func createHTTPTransport(globalConfiguration configuration.GlobalConfiguration)
|
||||||
return transport, nil
|
return transport, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createRootCACertPool(rootCAs traefiktls.RootCAs) *x509.CertPool {
|
func createRootCACertPool(rootCAs traefiktls.FilesOrContents) *x509.CertPool {
|
||||||
roots := x509.NewCertPool()
|
roots := x509.NewCertPool()
|
||||||
|
|
||||||
for _, cert := range rootCAs {
|
for _, cert := range rootCAs {
|
||||||
|
@ -308,7 +307,7 @@ func createClientTLSConfig(entryPointName string, tlsOption *traefiktls.TLS) (*t
|
||||||
if len(tlsOption.ClientCA.Files) > 0 {
|
if len(tlsOption.ClientCA.Files) > 0 {
|
||||||
pool := x509.NewCertPool()
|
pool := x509.NewCertPool()
|
||||||
for _, caFile := range tlsOption.ClientCA.Files {
|
for _, caFile := range tlsOption.ClientCA.Files {
|
||||||
data, err := ioutil.ReadFile(caFile)
|
data, err := caFile.Read()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,6 +104,15 @@ func (s *Server) buildMiddlewares(frontendName string, frontend *types.Frontend,
|
||||||
middle = append(middle, handler)
|
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
|
return middle, buildModifyResponse(secureMiddleware, headerMiddleware), postConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,9 +13,12 @@ func (s *Server) configureSignals() {
|
||||||
signal.Notify(s.signals, syscall.SIGUSR1)
|
signal.Notify(s.signals, syscall.SIGUSR1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) listenSignals() {
|
func (s *Server) listenSignals(stop chan bool) {
|
||||||
for {
|
for {
|
||||||
sig := <-s.signals
|
select {
|
||||||
|
case <-stop:
|
||||||
|
return
|
||||||
|
case sig := <-s.signals:
|
||||||
switch sig {
|
switch sig {
|
||||||
case syscall.SIGUSR1:
|
case syscall.SIGUSR1:
|
||||||
log.Infof("Closing and re-opening log files for rotation: %+v", sig)
|
log.Infof("Closing and re-opening log files for rotation: %+v", sig)
|
||||||
|
@ -31,4 +34,5 @@ func (s *Server) listenSignals() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,4 +4,4 @@ package server
|
||||||
|
|
||||||
func (s *Server) configureSignals() {}
|
func (s *Server) configureSignals() {}
|
||||||
|
|
||||||
func (s *Server) listenSignals() {}
|
func (s *Server) listenSignals(stop chan bool) {}
|
||||||
|
|
|
@ -73,8 +73,30 @@
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{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 }}
|
{{if $auth }}
|
||||||
[frontends."frontend-{{ $service.ServiceName }}".auth]
|
[frontends."frontend-{{ $service.ServiceName }}".auth]
|
||||||
headerField = "{{ $auth.HeaderField }}"
|
headerField = "{{ $auth.HeaderField }}"
|
||||||
|
|
|
@ -74,6 +74,29 @@
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{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 }}
|
{{ $auth := getAuth $container.SegmentLabels }}
|
||||||
{{if $auth }}
|
{{if $auth }}
|
||||||
[frontends."frontend-{{ $frontendName }}".auth]
|
[frontends."frontend-{{ $frontendName }}".auth]
|
||||||
|
|
|
@ -75,6 +75,29 @@
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{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 }}
|
{{ $auth := getAuth $instance.SegmentLabels }}
|
||||||
{{if $auth }}
|
{{if $auth }}
|
||||||
[frontends."frontend-{{ $frontendName }}".auth]
|
[frontends."frontend-{{ $frontendName }}".auth]
|
||||||
|
|
|
@ -73,6 +73,29 @@
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{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 }}
|
{{ $auth := getAuth $frontend }}
|
||||||
{{if $auth }}
|
{{if $auth }}
|
||||||
[frontends."{{ $frontendName }}".auth]
|
[frontends."{{ $frontendName }}".auth]
|
||||||
|
|
|
@ -76,6 +76,29 @@
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{end}}]
|
||||||
|
|
||||||
|
{{ $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 }}
|
{{ $auth := getAuth $app.SegmentLabels }}
|
||||||
{{if $auth }}
|
{{if $auth }}
|
||||||
[frontends."{{ $frontendName }}".auth]
|
[frontends."{{ $frontendName }}".auth]
|
||||||
|
|
|
@ -76,6 +76,29 @@
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{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 }}
|
{{ $auth := getAuth $app.TraefikLabels }}
|
||||||
{{if $auth }}
|
{{if $auth }}
|
||||||
[frontends."frontend-{{ $frontendName }}".auth]
|
[frontends."frontend-{{ $frontendName }}".auth]
|
||||||
|
|
|
@ -74,6 +74,29 @@
|
||||||
"{{.}}",
|
"{{.}}",
|
||||||
{{end}}]
|
{{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 }}
|
{{ $auth := getAuth $service.SegmentLabels }}
|
||||||
{{if $auth }}
|
{{if $auth }}
|
||||||
[frontends."frontend-{{ $frontendName }}".auth]
|
[frontends."frontend-{{ $frontendName }}".auth]
|
||||||
|
|
34
tls/tls.go
34
tls/tls.go
|
@ -16,7 +16,7 @@ const (
|
||||||
// ClientCA defines traefik CA files for a entryPoint
|
// ClientCA defines traefik CA files for a entryPoint
|
||||||
// and it indicates if they are mandatory or have just to be analyzed if provided
|
// and it indicates if they are mandatory or have just to be analyzed if provided
|
||||||
type ClientCA struct {
|
type ClientCA struct {
|
||||||
Files []string
|
Files FilesOrContents
|
||||||
Optional bool
|
Optional bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,8 +30,8 @@ type TLS struct {
|
||||||
SniStrict bool `export:"true"`
|
SniStrict bool `export:"true"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// RootCAs hold the CA we want to have in root
|
// FilesOrContents hold the CA we want to have in root
|
||||||
type RootCAs []FileOrContent
|
type FilesOrContents []FileOrContent
|
||||||
|
|
||||||
// Configuration allows mapping a TLS certificate to a list of entrypoints
|
// Configuration allows mapping a TLS certificate to a list of entrypoints
|
||||||
type Configuration struct {
|
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.
|
// 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.
|
// 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)))
|
sliceOfString := make([]string, len([]FileOrContent(*r)))
|
||||||
for key, value := range *r {
|
for key, value := range *r {
|
||||||
sliceOfString[key] = value.String()
|
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 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.
|
// Set's argument is a string to be parsed to set the flag.
|
||||||
// It's a comma-separated list, so we split it.
|
// It's a comma-separated list, so we split it.
|
||||||
func (r *RootCAs) Set(value string) error {
|
func (r *FilesOrContents) Set(value string) error {
|
||||||
rootCAs := strings.Split(value, ",")
|
filesOrContents := strings.Split(value, ",")
|
||||||
if len(rootCAs) == 0 {
|
if len(filesOrContents) == 0 {
|
||||||
return fmt.Errorf("bad RootCAs format: %s", value)
|
return fmt.Errorf("bad FilesOrContents format: %s", value)
|
||||||
}
|
}
|
||||||
for _, rootCA := range rootCAs {
|
for _, fileOrContent := range filesOrContents {
|
||||||
*r = append(*r, FileOrContent(rootCA))
|
*r = append(*r, FileOrContent(fileOrContent))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get return the RootCAs list
|
// Get return the FilesOrContents list
|
||||||
func (r *RootCAs) Get() interface{} {
|
func (r *FilesOrContents) Get() interface{} {
|
||||||
return *r
|
return *r
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetValue sets the RootCAs with val
|
// SetValue sets the FilesOrContents with val
|
||||||
func (r *RootCAs) SetValue(val interface{}) {
|
func (r *FilesOrContents) SetValue(val interface{}) {
|
||||||
*r = val.(RootCAs)
|
*r = val.(FilesOrContents)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type is type of the struct
|
// Type is type of the struct
|
||||||
func (r *RootCAs) Type() string {
|
func (r *FilesOrContents) Type() string {
|
||||||
return "rootcas"
|
return "filesorcontents"
|
||||||
}
|
}
|
||||||
|
|
||||||
// SortTLSPerEntryPoints converts TLS configuration sorted by Certificates into TLS configuration sorted by EntryPoints
|
// SortTLSPerEntryPoints converts TLS configuration sorted by Certificates into TLS configuration sorted by EntryPoints
|
||||||
|
|
|
@ -182,7 +182,8 @@ type Frontend struct {
|
||||||
Backend string `json:"backend,omitempty"`
|
Backend string `json:"backend,omitempty"`
|
||||||
Routes map[string]Route `json:"routes,omitempty" hash:"ignore"`
|
Routes map[string]Route `json:"routes,omitempty" hash:"ignore"`
|
||||||
PassHostHeader bool `json:"passHostHeader,omitempty"`
|
PassHostHeader bool `json:"passHostHeader,omitempty"`
|
||||||
PassTLSCert bool `json:"passTLSCert,omitempty"`
|
PassTLSCert bool `json:"passTLSCert,omitempty"` // Deprecated use PassTLSClientCert instead
|
||||||
|
PassTLSClientCert *TLSClientHeaders `json:"passTLSClientCert,omitempty"`
|
||||||
Priority int `json:"priority"`
|
Priority int `json:"priority"`
|
||||||
WhiteList *WhiteList `json:"whiteList,omitempty"`
|
WhiteList *WhiteList `json:"whiteList,omitempty"`
|
||||||
Headers *Headers `json:"headers,omitempty"`
|
Headers *Headers `json:"headers,omitempty"`
|
||||||
|
@ -645,3 +646,27 @@ func (s *IPStrategy) Get() (ip.Strategy, error) {
|
||||||
|
|
||||||
return &ip.RemoteAddrStrategy{}, nil
|
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"`
|
||||||
|
}
|
||||||
|
|
11
vendor/github.com/vulcand/oxy/buffer/buffer.go
generated
vendored
11
vendor/github.com/vulcand/oxy/buffer/buffer.go
generated
vendored
|
@ -383,7 +383,14 @@ func (b *bufferWriter) Header() http.Header {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *bufferWriter) Write(buf []byte) (int, error) {
|
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.
|
// WriteHeader sets rw.Code.
|
||||||
|
@ -410,7 +417,7 @@ func (b *bufferWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||||
return conn, rw, err
|
return conn, rw, err
|
||||||
}
|
}
|
||||||
b.log.Warningf("Upstream ResponseWriter of type %v does not implement http.Hijacker. Returning dummy channel.", reflect.TypeOf(b.responseWriter))
|
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
|
// SizeErrHandler Size error handler
|
||||||
|
|
Loading…
Reference in a new issue