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
|
||||
|
||||
## [v1.7.0-rc4](https://github.com/containous/traefik/tree/v1.7.0-rc4) (2018-09-07)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.0-rc3...v1.7.0-rc4)
|
||||
|
||||
**Enhancements:**
|
||||
- **[acme]** Use official Pebble Image. ([#3708](https://github.com/containous/traefik/pull/3708) by [ldez](https://github.com/ldez))
|
||||
- **[consulcatalog]** Multiple frontends for consulcatalog ([#3796](https://github.com/containous/traefik/pull/3796) by [hsmade](https://github.com/hsmade))
|
||||
- **[ecs]** Add segment support for ECS ([#3817](https://github.com/containous/traefik/pull/3817) by [mmatur](https://github.com/mmatur))
|
||||
- **[k8s]** Remove unnecessary loop ([#3799](https://github.com/containous/traefik/pull/3799) by [ZloyDyadka](https://github.com/ZloyDyadka))
|
||||
- **[middleware,consulcatalog,docker,ecs,kv,marathon,mesos,rancher]** Pass the TLS Cert infos in headers ([#3826](https://github.com/containous/traefik/pull/3826) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme,cluster]** StoreConfig always initializes the account if it is missing ([#3844](https://github.com/containous/traefik/pull/3844) by [geraldcroes](https://github.com/geraldcroes))
|
||||
- **[acme]** Set a keyType to ACME if the account is stored with no KeyType ([#3733](https://github.com/containous/traefik/pull/3733) by [nmengin](https://github.com/nmengin))
|
||||
- **[authentication,consulcatalog,docker,ecs,k8s,kv,marathon,mesos,rancher]** Auth Forward with certificates in templates. ([#3804](https://github.com/containous/traefik/pull/3804) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Prevent unparsable strings from being rendered in the Kubernetes template ([#3753](https://github.com/containous/traefik/pull/3753) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Don't merge kubernetes ingresses when priority is set ([#3743](https://github.com/containous/traefik/pull/3743) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[kv]** Include missing key in error message for KV store ([#3779](https://github.com/containous/traefik/pull/3779) by [camelpunch](https://github.com/camelpunch))
|
||||
- **[metrics]** Avoid a panic during Prometheus registering ([#3717](https://github.com/containous/traefik/pull/3717) by [nmengin](https://github.com/nmengin))
|
||||
- **[middleware,websocket]** Enable retry on websocket ([#3825](https://github.com/containous/traefik/pull/3825) by [Juliens](https://github.com/Juliens))
|
||||
- **[middleware]** Extend https redirection tests, and fix incorrect behavior ([#3742](https://github.com/containous/traefik/pull/3742) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[oxy]** Handle Te header when http2 ([#3824](https://github.com/containous/traefik/pull/3824) by [Juliens](https://github.com/Juliens))
|
||||
- **[server]** Avoid goroutine leak in server ([#3851](https://github.com/containous/traefik/pull/3851) by [nmengin](https://github.com/nmengin))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** Fix documentation for route53 acme provider ([#3811](https://github.com/containous/traefik/pull/3811) by [A-Shleifman](https://github.com/A-Shleifman))
|
||||
- **[acme]** Update ACME documentation about TLS-ALPN challenge ([#3756](https://github.com/containous/traefik/pull/3756) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Change syntax in quick start guide ([#3726](https://github.com/containous/traefik/pull/3726) by [trotro](https://github.com/trotro))
|
||||
- **[docker]** Improve the wording in the documentation for Docker and fix title for Docker User Guide ([#3797](https://github.com/containous/traefik/pull/3797) by [dduportal](https://github.com/dduportal))
|
||||
- **[docker]** Typo in docker-and-lets-encrypt.md ([#3724](https://github.com/containous/traefik/pull/3724) by [A-Shleifman](https://github.com/A-Shleifman))
|
||||
- **[k8s]** Update kubernetes docs to reflect https options ([#3807](https://github.com/containous/traefik/pull/3807) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Update kubernetes.md ([#3719](https://github.com/containous/traefik/pull/3719) by [kmaris](https://github.com/kmaris))
|
||||
- **[k8s]** Improve Connection Limit Kubernetes Documentation ([#3711](https://github.com/containous/traefik/pull/3711) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[provider]** Typo in auth labels. ([#3730](https://github.com/containous/traefik/pull/3730) by [ldez](https://github.com/ldez))
|
||||
- **[tracing]** Simple documentation grammar update in tracing ([#3720](https://github.com/containous/traefik/pull/3720) by [loadstar81](https://github.com/loadstar81))
|
||||
- Make the "base domain" on all providers ([#3835](https://github.com/containous/traefik/pull/3835) by [dduportal](https://github.com/dduportal))
|
||||
|
||||
**Misc:**
|
||||
- Merge v1.6.6 into v1.7 ([#3802](https://github.com/containous/traefik/pull/3802) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v1.6.6](https://github.com/containous/traefik/tree/v1.6.6) (2018-08-20)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.6.5...v1.6.6)
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ You need to run the `binary` target. This will create binaries for Linux platfor
|
|||
$ make binary
|
||||
docker build -t "traefik-dev:no-more-godep-ever" -f build.Dockerfile .
|
||||
Sending build context to Docker daemon 295.3 MB
|
||||
Step 0 : FROM golang:1.10-alpine
|
||||
Step 0 : FROM golang:1.11-alpine
|
||||
---> 8c6473912976
|
||||
Step 1 : RUN go get github.com/golang/dep/cmd/dep
|
||||
[...]
|
||||
|
|
2
Gopkg.lock
generated
2
Gopkg.lock
generated
|
@ -1263,7 +1263,7 @@
|
|||
"roundrobin",
|
||||
"utils"
|
||||
]
|
||||
revision = "f6bbeac6d5c4c06f88ba07ed42983ff36a5b407e"
|
||||
revision = "77148e9694210e5f5610328f1cd7cf65583014c2"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/vulcand/predicate"
|
||||
|
|
|
@ -51,7 +51,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
|||
{CertFile: "CertFile 2", KeyFile: "KeyFile 2"},
|
||||
},
|
||||
ClientCA: traefiktls.ClientCA{
|
||||
Files: []string{"foo ClientCAFiles 1", "foo ClientCAFiles 2", "foo ClientCAFiles 3"},
|
||||
Files: traefiktls.FilesOrContents{"foo ClientCAFiles 1", "foo ClientCAFiles 2", "foo ClientCAFiles 3"},
|
||||
Optional: false,
|
||||
},
|
||||
},
|
||||
|
@ -100,7 +100,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
|||
{CertFile: "CertFile 2", KeyFile: "KeyFile 2"},
|
||||
},
|
||||
ClientCA: traefiktls.ClientCA{
|
||||
Files: []string{"fii ClientCAFiles 1", "fii ClientCAFiles 2", "fii ClientCAFiles 3"},
|
||||
Files: traefiktls.FilesOrContents{"fii ClientCAFiles 1", "fii ClientCAFiles 2", "fii ClientCAFiles 3"},
|
||||
Optional: false,
|
||||
},
|
||||
},
|
||||
|
@ -185,7 +185,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
|||
config.ProvidersThrottleDuration = parse.Duration(666 * time.Second)
|
||||
config.MaxIdleConnsPerHost = 666
|
||||
config.InsecureSkipVerify = true
|
||||
config.RootCAs = traefiktls.RootCAs{"RootCAs 1", "RootCAs 2", "RootCAs 3"}
|
||||
config.RootCAs = traefiktls.FilesOrContents{"RootCAs 1", "RootCAs 2", "RootCAs 3"}
|
||||
config.Retry = &configuration.Retry{
|
||||
Attempts: 666,
|
||||
}
|
||||
|
|
|
@ -129,8 +129,30 @@ var _templatesConsul_catalogTmpl = []byte(`[backends]
|
|||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $auth := getAuth $service.TraefikLabels }}
|
||||
{{ $tlsClientCert := getPassTLSClientCert $service.TraefikLabels }}
|
||||
{{if $tlsClientCert }}
|
||||
[frontends."frontend-{{ $service.ServiceName }}".passTLSClientCert]
|
||||
pem = {{ $tlsClientCert.PEM }}
|
||||
{{ $infos := $tlsClientCert.Infos }}
|
||||
{{if $infos }}
|
||||
[frontends."frontend-{{ $service.ServiceName }}".passTLSClientCert.infos]
|
||||
notAfter = {{ $infos.NotAfter }}
|
||||
notBefore = {{ $infos.NotBefore }}
|
||||
sans = {{ $infos.Sans }}
|
||||
{{ $subject := $infos.Subject }}
|
||||
{{if $subject }}
|
||||
[frontends."frontend-{{ $service.ServiceName }}".passTLSClientCert.infos.subject]
|
||||
country = {{ $subject.Country }}
|
||||
province = {{ $subject.Province }}
|
||||
locality = {{ $subject.Locality }}
|
||||
organization = {{ $subject.Organization }}
|
||||
commonName = {{ $subject.CommonName }}
|
||||
serialNumber = {{ $subject.SerialNumber }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $auth := getAuth $service.TraefikLabels }}
|
||||
{{if $auth }}
|
||||
[frontends."frontend-{{ $service.ServiceName }}".auth]
|
||||
headerField = "{{ $auth.HeaderField }}"
|
||||
|
@ -375,6 +397,29 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
|
|||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $tlsClientCert := getPassTLSClientCert $container.SegmentLabels }}
|
||||
{{if $tlsClientCert }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert]
|
||||
pem = {{ $tlsClientCert.PEM }}
|
||||
{{ $infos := $tlsClientCert.Infos }}
|
||||
{{if $infos }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos]
|
||||
notAfter = {{ $infos.NotAfter }}
|
||||
notBefore = {{ $infos.NotBefore }}
|
||||
sans = {{ $infos.Sans }}
|
||||
{{ $subject := $infos.Subject }}
|
||||
{{if $subject }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos.subject]
|
||||
country = {{ $subject.Country }}
|
||||
province = {{ $subject.Province }}
|
||||
locality = {{ $subject.Locality }}
|
||||
organization = {{ $subject.Organization }}
|
||||
commonName = {{ $subject.CommonName }}
|
||||
serialNumber = {{ $subject.SerialNumber }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $auth := getAuth $container.SegmentLabels }}
|
||||
{{if $auth }}
|
||||
[frontends."frontend-{{ $frontendName }}".auth]
|
||||
|
@ -622,6 +667,29 @@ var _templatesEcsTmpl = []byte(`[backends]
|
|||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $tlsClientCert := getPassTLSClientCert $instance.SegmentLabels }}
|
||||
{{if $tlsClientCert }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert]
|
||||
pem = {{ $tlsClientCert.PEM }}
|
||||
{{ $infos := $tlsClientCert.Infos }}
|
||||
{{if $infos }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos]
|
||||
notAfter = {{ $infos.NotAfter }}
|
||||
notBefore = {{ $infos.NotBefore }}
|
||||
sans = {{ $infos.Sans }}
|
||||
{{ $subject := $infos.Subject }}
|
||||
{{if $subject }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos.subject]
|
||||
country = {{ $subject.Country }}
|
||||
province = {{ $subject.Province }}
|
||||
locality = {{ $subject.Locality }}
|
||||
organization = {{ $subject.Organization }}
|
||||
commonName = {{ $subject.CommonName }}
|
||||
serialNumber = {{ $subject.SerialNumber }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $auth := getAuth $instance.SegmentLabels }}
|
||||
{{if $auth }}
|
||||
[frontends."frontend-{{ $frontendName }}".auth]
|
||||
|
@ -1125,6 +1193,29 @@ var _templatesKvTmpl = []byte(`[backends]
|
|||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $tlsClientCert := getPassTLSClientCert $frontend }}
|
||||
{{if $tlsClientCert }}
|
||||
[frontends."{{ $frontendName }}".passTLSClientCert]
|
||||
pem = {{ $tlsClientCert.PEM }}
|
||||
{{ $infos := $tlsClientCert.Infos }}
|
||||
{{if $infos }}
|
||||
[frontends."{{ $frontendName }}".passTLSClientCert.infos]
|
||||
notAfter = {{ $infos.NotAfter }}
|
||||
notBefore = {{ $infos.NotBefore }}
|
||||
sans = {{ $infos.Sans }}
|
||||
{{ $subject := $infos.Subject }}
|
||||
{{if $subject }}
|
||||
[frontends."{{ $frontendName }}".passTLSClientCert.infos.subject]
|
||||
country = {{ $subject.Country }}
|
||||
province = {{ $subject.Province }}
|
||||
locality = {{ $subject.Locality }}
|
||||
organization = {{ $subject.Organization }}
|
||||
commonName = {{ $subject.CommonName }}
|
||||
serialNumber = {{ $subject.SerialNumber }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $auth := getAuth $frontend }}
|
||||
{{if $auth }}
|
||||
[frontends."{{ $frontendName }}".auth]
|
||||
|
@ -1387,6 +1478,29 @@ var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }}
|
|||
"{{.}}",
|
||||
{{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 }}
|
||||
{{if $auth }}
|
||||
[frontends."{{ $frontendName }}".auth]
|
||||
|
@ -1634,6 +1748,29 @@ var _templatesMesosTmpl = []byte(`[backends]
|
|||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $tlsClientCert := getPassTLSClientCert $app.TraefikLabels }}
|
||||
{{if $tlsClientCert }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert]
|
||||
pem = {{ $tlsClientCert.PEM }}
|
||||
{{ $infos := $tlsClientCert.Infos }}
|
||||
{{if $infos }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos]
|
||||
notAfter = {{ $infos.NotAfter }}
|
||||
notBefore = {{ $infos.NotBefore }}
|
||||
sans = {{ $infos.Sans }}
|
||||
{{ $subject := $infos.Subject }}
|
||||
{{if $subject }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos.subject]
|
||||
country = {{ $subject.Country }}
|
||||
province = {{ $subject.Province }}
|
||||
locality = {{ $subject.Locality }}
|
||||
organization = {{ $subject.Organization }}
|
||||
commonName = {{ $subject.CommonName }}
|
||||
serialNumber = {{ $subject.SerialNumber }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $auth := getAuth $app.TraefikLabels }}
|
||||
{{if $auth }}
|
||||
[frontends."frontend-{{ $frontendName }}".auth]
|
||||
|
@ -1903,6 +2040,29 @@ var _templatesRancherTmpl = []byte(`{{ $backendServers := .Backends }}
|
|||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $tlsClientCert := getPassTLSClientCert $service.SegmentLabels }}
|
||||
{{if $tlsClientCert }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert]
|
||||
pem = {{ $tlsClientCert.PEM }}
|
||||
{{ $infos := $tlsClientCert.Infos }}
|
||||
{{if $infos }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos]
|
||||
notAfter = {{ $infos.NotAfter }}
|
||||
notBefore = {{ $infos.NotBefore }}
|
||||
sans = {{ $infos.Sans }}
|
||||
{{ $subject := $infos.Subject }}
|
||||
{{if $subject }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos.subject]
|
||||
country = {{ $subject.Country }}
|
||||
province = {{ $subject.Province }}
|
||||
locality = {{ $subject.Locality }}
|
||||
organization = {{ $subject.Organization }}
|
||||
commonName = {{ $subject.CommonName }}
|
||||
serialNumber = {{ $subject.SerialNumber }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $auth := getAuth $service.SegmentLabels }}
|
||||
{{if $auth }}
|
||||
[frontends."frontend-{{ $frontendName }}".auth]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM golang:1.10-alpine
|
||||
FROM golang:1.11-alpine
|
||||
|
||||
RUN apk --update upgrade \
|
||||
&& apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar \
|
||||
|
|
|
@ -34,7 +34,7 @@ func Test_createReport(t *testing.T) {
|
|||
File: &file.Provider{
|
||||
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
|
||||
if traefikConfiguration.GlobalConfiguration.ACME.OverrideCertificates {
|
||||
if traefikConfiguration.GlobalConfiguration.ACME.OverrideCertificates || !accountInitialized {
|
||||
|
||||
// Store the ACME Account into the KV Store
|
||||
// Certificates in KV Store will be overridden
|
||||
|
@ -114,6 +119,15 @@ func Run(kv *staert.KvSource, traefikConfiguration *cmd.TraefikConfiguration) fu
|
|||
}
|
||||
}
|
||||
|
||||
func keyExists(source *staert.KvSource, key string) (bool, error) {
|
||||
list, err := source.List(key, nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return len(list) > 0, nil
|
||||
}
|
||||
|
||||
// migrateACMEData allows migrating data from acme.json file to KV store in function of the file format
|
||||
func migrateACMEData(fileName string) (*acme.Account, error) {
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ Complete documentation is available at https://traefik.io`,
|
|||
// add custom parsers
|
||||
f.AddParser(reflect.TypeOf(configuration.EntryPoints{}), &configuration.EntryPoints{})
|
||||
f.AddParser(reflect.TypeOf(configuration.DefaultEntryPoints{}), &configuration.DefaultEntryPoints{})
|
||||
f.AddParser(reflect.TypeOf(traefiktls.RootCAs{}), &traefiktls.RootCAs{})
|
||||
f.AddParser(reflect.TypeOf(traefiktls.FilesOrContents{}), &traefiktls.FilesOrContents{})
|
||||
f.AddParser(reflect.TypeOf(types.Constraints{}), &types.Constraints{})
|
||||
f.AddParser(reflect.TypeOf(kubernetes.Namespaces{}), &kubernetes.Namespaces{})
|
||||
f.AddParser(reflect.TypeOf(ecs.Clusters{}), &ecs.Clusters{})
|
||||
|
|
|
@ -75,7 +75,7 @@ type GlobalConfiguration struct {
|
|||
ProvidersThrottleDuration parse.Duration `description:"Backends throttle duration: minimum duration between 2 events from providers before applying a new configuration. It avoids unnecessary reloads if multiples events are sent in a short amount of time." export:"true"`
|
||||
MaxIdleConnsPerHost int `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used" export:"true"`
|
||||
InsecureSkipVerify bool `description:"Disable SSL certificate verification" export:"true"`
|
||||
RootCAs tls.RootCAs `description:"Add cert file for self-signed certificate"`
|
||||
RootCAs tls.FilesOrContents `description:"Add cert file for self-signed certificate"`
|
||||
Retry *Retry `description:"Enable retry sending request if network error" export:"true"`
|
||||
HealthCheck *HealthCheckConfig `description:"Health check parameters" export:"true"`
|
||||
RespondingTimeouts *RespondingTimeouts `description:"Timeouts for incoming requests to the Traefik instance" export:"true"`
|
||||
|
|
|
@ -248,7 +248,8 @@ func makeEntryPointTLS(result map[string]string) (*tls.TLS, error) {
|
|||
|
||||
if configTLS != nil {
|
||||
if len(result["ca"]) > 0 {
|
||||
files := strings.Split(result["ca"], ",")
|
||||
files := tls.FilesOrContents{}
|
||||
files.Set(result["ca"])
|
||||
optional := toBool(result, "ca_optional")
|
||||
configTLS.ClientCA = tls.ClientCA{
|
||||
Files: files,
|
||||
|
|
|
@ -232,7 +232,7 @@ func TestEntryPoints_Set(t *testing.T) {
|
|||
},
|
||||
},
|
||||
ClientCA: tls.ClientCA{
|
||||
Files: []string{"car"},
|
||||
Files: tls.FilesOrContents{"car"},
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
|
@ -352,7 +352,7 @@ func TestEntryPoints_Set(t *testing.T) {
|
|||
},
|
||||
},
|
||||
ClientCA: tls.ClientCA{
|
||||
Files: []string{"car"},
|
||||
Files: tls.FilesOrContents{"car"},
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -122,7 +122,7 @@ In order to use regular expressions with Host and Path matchers, you must declar
|
|||
The variable has no special meaning; however, it is required by the [gorilla/mux](https://github.com/gorilla/mux) dependency which embeds the regular expression and defines the syntax.
|
||||
|
||||
You can optionally enable `passHostHeader` to forward client `Host` header to the backend.
|
||||
You can also optionally enable `passTLSCert` to forward TLS Client certificates to the backend.
|
||||
You can also optionally configure the `passTLSClientCert` option to pass the Client certificates to the backend in a specific header.
|
||||
|
||||
##### Path Matcher Usage Guidelines
|
||||
|
||||
|
@ -157,7 +157,8 @@ Here is an example of frontends definition:
|
|||
[frontends.frontend2]
|
||||
backend = "backend1"
|
||||
passHostHeader = true
|
||||
passTLSCert = true
|
||||
[frontends.frontend2.passTLSClientCert]
|
||||
pem = true
|
||||
priority = 10
|
||||
entrypoints = ["https"] # overrides defaultEntryPoints
|
||||
[frontends.frontend2.routes.test_1]
|
||||
|
|
|
@ -31,7 +31,7 @@ exposedByDefault = false
|
|||
#
|
||||
stale = false
|
||||
|
||||
# Default domain used.
|
||||
# Default base domain used for the frontend rules.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
|
@ -95,7 +95,7 @@ Additional settings can be defined using Consul Catalog tags.
|
|||
The default prefix is `traefik`.
|
||||
|
||||
| Label | Description |
|
||||
|--------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
|----------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `<prefix>.enable=false` | Disables this container in Træfik. |
|
||||
| `<prefix>.protocol=https` | Overrides the default `http` protocol. |
|
||||
| `<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>.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.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.priority=10` | Overrides default frontend priority. |
|
||||
| `<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.
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
|---------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.docker.network` | Overrides the default docker network to use for connections to the container. [1] |
|
||||
| `traefik.domain` | Sets the default base domain for the frontend rules. For more information, check the [Container Labels section's of the user guide "Let's Encrypt & Docker"](/user-guide/docker-and-lets-encrypt/#container-labels) |
|
||||
| `traefik.enable=false` | Disables this container in Træfik. |
|
||||
|
@ -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>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.notAfter=true` | Add the noAfter field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.notBefore=true` | Add the noBefore field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.sans=true` | Add the sans field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.commonName=true` | Add the subject.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.country=true` | Add the subject.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.locality=true` | Add the subject.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.organization=true`| Add the subject.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.province=true` | Add the subject.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Add the subject.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.pem=true` | Pass the escaped pem in the `X-Forwarded-Ssl-Client-Cert` header. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend (DEPRECATED). |
|
||||
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<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.
|
||||
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------------------------|----------------------------------------------------------------|
|
||||
|------------------------------------------------------------------------------------|------------------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||
| `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>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||
| `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.priority=10` | Same as `traefik.frontend.priority` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
||||
|
|
|
@ -26,7 +26,7 @@ clusters = ["default"]
|
|||
#
|
||||
watch = true
|
||||
|
||||
# Default domain used.
|
||||
# Default base domain used for the frontend rules.
|
||||
# Can be overridden by setting the "traefik.domain" label.
|
||||
#
|
||||
# Optional
|
||||
|
@ -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:
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
|---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.domain` | Sets the default domain for frontend rules. |
|
||||
| `traefik.enable=false` | Disables this container in Træfik. |
|
||||
| `traefik.port=80` | Overrides the default `port` value. Overrides `NetworkBindings` from Docker Container |
|
||||
|
@ -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.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
|
||||
| `traefik.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. |
|
||||
| `traefik.frontend.auth.basic.usersfile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
|
||||
| `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. |
|
||||
| `traefik.frontend.auth.digest.usersfile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). |
|
||||
|
@ -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.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. |
|
||||
| `traefik.frontend.auth.removeHeader=true` | If set to true, removes the Authorization header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.notAfter=true` | Add the noAfter field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.notBefore=true` | Add the noBefore field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.sans=true` | Add the sans field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.commonName=true` | Add the subject.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.country=true` | Add the subject.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.locality=true` | Add the subject.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.organization=true`| Add the subject.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.province=true` | Add the subject.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Add the subject.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.pem=true` | Pass the escaped pem in the `X-Forwarded-Ssl-Client-Cert` header. |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `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. |
|
||||
|
@ -233,7 +243,7 @@ You can define as many segments as ports exposed in an application.
|
|||
Segment labels override the default behavior.
|
||||
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------------------------|----------------------------------------------------------------|
|
||||
|-------------------------------------------------------------------------------------|-------------------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||
| `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>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||
| `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.priority=10` | Same as `traefik.frontend.priority` |
|
||||
| `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"]
|
||||
backend = "backend1"
|
||||
passHostHeader = true
|
||||
passTLSCert = true
|
||||
priority = 42
|
||||
|
||||
[frontends.frontend1.passTLSClientCert]
|
||||
# Pass the escaped pem in a `X-Forwarded-Ssl-Client-Cert` header
|
||||
pem = true
|
||||
# Pass the escaped client cert infos selected below in a `X-Forwarded-Ssl-Client-Cert-Infos` header
|
||||
# The unescaped header is like `Subject="C=%s,ST=%s,L=%s,O=%s,CN=%s",NB=%d,NA=%d,SAN=%s`
|
||||
# It there is more than one certificates, their are separated by a `;`
|
||||
[frontends.frontend-server.passTLSClientCert.infos]
|
||||
notBefore = true
|
||||
notAfter = true
|
||||
[frontends.frontend-server.passTLSClientCert.infos.subject]
|
||||
country = true
|
||||
province = true
|
||||
locality = true
|
||||
organization = true
|
||||
commonName = true
|
||||
serialNumber = true
|
||||
[frontends.frontend1.auth]
|
||||
headerField = "X-WebAuth-User"
|
||||
[frontends.frontend1.auth.basic]
|
||||
|
|
|
@ -31,7 +31,7 @@ endpoint = "http://127.0.0.1:8080"
|
|||
#
|
||||
watch = true
|
||||
|
||||
# Default domain used.
|
||||
# Default base domain used for the frontend rules.
|
||||
# Can be overridden by setting the "traefik.domain" label on an application.
|
||||
#
|
||||
# Required
|
||||
|
@ -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.
|
||||
|
||||
| Label | Description |
|
||||
|-----------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.domain` | Sets the default domain used for the frontend rules. |
|
||||
|---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.domain` | Sets the default base domain used for the frontend rules. |
|
||||
| `traefik.enable=false` | Disables this container in Træfik. |
|
||||
| `traefik.port=80` | Registers this port. Useful when the container exposes multiples ports. |
|
||||
| `traefik.portIndex=1` | Registers port by index in the application's ports array. Useful when the application exposes multiple ports. |
|
||||
|
@ -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>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.notAfter=true` | Add the noAfter field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.notBefore=true` | Add the noBefore field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.sans=true` | Add the sans field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.commonName=true` | Add the subject.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.country=true` | Add the subject.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.locality=true` | Add the subject.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.organization=true`| Add the subject.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.province=true` | Add the subject.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Add the subject.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.pem=true` | Pass the escaped pem in the `X-Forwarded-Ssl-Client-Cert` header. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
||||
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
|
@ -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>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||
| `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.priority=10` | Same as `traefik.frontend.priority` |
|
||||
| `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
|
||||
|
||||
# Default domain used.
|
||||
# Default base domain used for the frontend rules.
|
||||
# Can be overridden by setting the "traefik.domain" label on an application.
|
||||
#
|
||||
# Required
|
||||
|
@ -154,6 +154,16 @@ The following labels can be defined on Mesos tasks. They adjust the behavior for
|
|||
| `traefik.frontend.errors.<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.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.notAfter=true` | Add the noAfter field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.notBefore=true` | Add the noBefore field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.sans=true` | Add the sans field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.commonName=true` | Add the subject.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.country=true` | Add the subject.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.locality=true` | Add the subject.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.organization=true`| Add the subject.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.province=true` | Add the subject.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Add the subject.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.pem=true` | Pass the escaped pem in the `X-Forwarded-Ssl-Client-Cert` header. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
||||
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
|
@ -242,6 +252,16 @@ Segment labels override the default behavior.
|
|||
| `traefik.<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.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.priority=10` | Same as `traefik.frontend.priority` |
|
||||
| `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.
|
||||
[rancher]
|
||||
|
||||
# Default domain used.
|
||||
# Default base domain used for the frontend rules.
|
||||
# Can be overridden by setting the "traefik.domain" label on an service.
|
||||
#
|
||||
# Required
|
||||
|
@ -139,7 +139,7 @@ secretKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|||
Labels can be used on task containers to override default behavior:
|
||||
|
||||
| Label | Description |
|
||||
|-----------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
|---------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.domain` | Sets the default domain for the frontend rules. |
|
||||
| `traefik.enable=false` | Disables this container in Træfik. |
|
||||
| `traefik.port=80` | Registers this port. Useful when the container exposes multiple ports. |
|
||||
|
@ -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>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.notAfter=true` | Add the noAfter field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.notBefore=true` | Add the noBefore field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.sans=true` | Add the sans field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.commonName=true` | Add the subject.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.country=true` | Add the subject.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.locality=true` | Add the subject.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.organization=true`| Add the subject.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.province=true` | Add the subject.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Add the subject.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.pem=true` | Pass the escaped pem in the `X-Forwarded-Ssl-Client-Cert` header. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
||||
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
|
@ -240,7 +250,7 @@ You can define as many segments as ports exposed in a container.
|
|||
Segment labels override the default behavior.
|
||||
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------------------------|----------------------------------------------------------------|
|
||||
|------------------------------------------------------------------------------------|------------------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||
| `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>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||
| `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.priority=10` | Same as `traefik.frontend.priority` |
|
||||
| `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]
|
||||
backend = "backend1"
|
||||
passHostHeader = true
|
||||
passTLSCert = true
|
||||
entrypoints = ["https"] # overrides defaultEntryPoints
|
||||
[frontends.frontend2.routes.test_1]
|
||||
rule = "Host:{subdomain:[a-z]+}.localhost"
|
||||
|
|
|
@ -118,7 +118,7 @@ func (hc *HealthCheck) execute(ctx context.Context, backend *BackendConfig) {
|
|||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
log.Debug("Stopping current health check goroutines of backend: %s", backend.name)
|
||||
log.Debugf("Stopping current health check goroutines of backend: %s", backend.name)
|
||||
return
|
||||
case <-ticker.C:
|
||||
log.Debugf("Refreshing health check for backend: %s", backend.name)
|
||||
|
|
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(&RetrySuite{})
|
||||
check.Suite(&SimpleSuite{})
|
||||
check.Suite(&TLSClientHeadersSuite{})
|
||||
check.Suite(&TimeoutSuite{})
|
||||
check.Suite(&TracingSuite{})
|
||||
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/go-check/check"
|
||||
"github.com/gorilla/websocket"
|
||||
checker "github.com/vdemeester/shakers"
|
||||
)
|
||||
|
||||
|
@ -38,3 +39,29 @@ func (s *RetrySuite) TestRetry(c *check.C) {
|
|||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *RetrySuite) TestRetryWebsocket(c *check.C) {
|
||||
whoamiEndpoint := s.composeProject.Container(c, "whoami").NetworkSettings.IPAddress
|
||||
file := s.adaptFile(c, "fixtures/retry/simple.toml", struct {
|
||||
WhoamiEndpoint string
|
||||
}{whoamiEndpoint})
|
||||
defer os.Remove(file)
|
||||
|
||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||
defer display(c)
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 60*time.Second, try.BodyContains("PathPrefix:/"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// This simulates a DialTimeout when connecting to the backend server.
|
||||
_, response, err := websocket.DefaultDialer.Dial("ws://127.0.0.1:8000/echo", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(response.StatusCode, checker.Equals, http.StatusSwitchingProtocols)
|
||||
|
||||
_, response, err = websocket.DefaultDialer.Dial("ws://127.0.0.1:8000/echo", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(response.StatusCode, checker.Equals, http.StatusSwitchingProtocols)
|
||||
}
|
||||
|
|
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
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package middlewares
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
|
@ -41,11 +42,8 @@ func (retry *Retry) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
|||
attempts := 1
|
||||
for {
|
||||
attemptsExhausted := attempts >= retry.attempts
|
||||
// Websocket requests can't be retried at this point in time.
|
||||
// This is due to the fact that gorilla/websocket doesn't use the request
|
||||
// context and so we don't get httptrace information.
|
||||
// Websocket clients should however retry on their own anyway.
|
||||
shouldRetry := !attemptsExhausted && !isWebsocketRequest(r)
|
||||
|
||||
shouldRetry := !attemptsExhausted
|
||||
retryResponseWriter := newRetryResponseWriter(rw, shouldRetry)
|
||||
|
||||
// Disable retries when the backend already received request data
|
||||
|
@ -128,7 +126,7 @@ func (rr *retryResponseWriterWithoutCloseNotify) Header() http.Header {
|
|||
|
||||
func (rr *retryResponseWriterWithoutCloseNotify) Write(buf []byte) (int, error) {
|
||||
if rr.ShouldRetry() {
|
||||
return 0, nil
|
||||
return len(buf), nil
|
||||
}
|
||||
return rr.responseWriter.Write(buf)
|
||||
}
|
||||
|
@ -150,7 +148,11 @@ func (rr *retryResponseWriterWithoutCloseNotify) WriteHeader(code int) {
|
|||
}
|
||||
|
||||
func (rr *retryResponseWriterWithoutCloseNotify) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
return rr.responseWriter.(http.Hijacker).Hijack()
|
||||
hijacker, ok := rr.responseWriter.(http.Hijacker)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("%T is not a http.Hijacker", rr.responseWriter)
|
||||
}
|
||||
return hijacker.Hijack()
|
||||
}
|
||||
|
||||
func (rr *retryResponseWriterWithoutCloseNotify) Flush() {
|
||||
|
|
|
@ -3,10 +3,13 @@ package middlewares
|
|||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/containous/traefik/testhelpers"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/vulcand/oxy/forward"
|
||||
"github.com/vulcand/oxy/roundrobin"
|
||||
)
|
||||
|
@ -18,7 +21,6 @@ func TestRetry(t *testing.T) {
|
|||
wantRetryAttempts int
|
||||
wantResponseStatus int
|
||||
amountFaultyEndpoints int
|
||||
isWebsocketHandshakeRequest bool
|
||||
}{
|
||||
{
|
||||
desc: "no retry on success",
|
||||
|
@ -55,14 +57,6 @@ func TestRetry(t *testing.T) {
|
|||
wantResponseStatus: http.StatusInternalServerError,
|
||||
amountFaultyEndpoints: 3,
|
||||
},
|
||||
{
|
||||
desc: "websocket request should not be retried",
|
||||
maxRequestAttempts: 3,
|
||||
wantRetryAttempts: 0,
|
||||
wantResponseStatus: http.StatusBadGateway,
|
||||
amountFaultyEndpoints: 1,
|
||||
isWebsocketHandshakeRequest: true,
|
||||
},
|
||||
}
|
||||
|
||||
backendServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
|
@ -75,10 +69,10 @@ func TestRetry(t *testing.T) {
|
|||
t.Fatalf("Error creating forwarder: %s", err)
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
loadBalancer, err := roundrobin.New(forwarder)
|
||||
|
@ -87,7 +81,7 @@ func TestRetry(t *testing.T) {
|
|||
}
|
||||
|
||||
basePort := 33444
|
||||
for i := 0; i < tc.amountFaultyEndpoints; i++ {
|
||||
for i := 0; i < test.amountFaultyEndpoints; i++ {
|
||||
// 192.0.2.0 is a non-routable IP for testing purposes.
|
||||
// See: https://stackoverflow.com/questions/528538/non-routable-ip-address/18436928#18436928
|
||||
// We only use the port specification here because the URL is used as identifier
|
||||
|
@ -101,24 +95,91 @@ func TestRetry(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
retryListener := &countingRetryListener{}
|
||||
retry := NewRetry(tc.maxRequestAttempts, loadBalancer, retryListener)
|
||||
retry := NewRetry(test.maxRequestAttempts, loadBalancer, retryListener)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodGet, "http://localhost:3000/ok", nil)
|
||||
|
||||
if tc.isWebsocketHandshakeRequest {
|
||||
req.Header.Add("Connection", "Upgrade")
|
||||
req.Header.Add("Upgrade", "websocket")
|
||||
}
|
||||
|
||||
retry.ServeHTTP(recorder, req)
|
||||
|
||||
if tc.wantResponseStatus != recorder.Code {
|
||||
t.Errorf("got status code %d, want %d", recorder.Code, tc.wantResponseStatus)
|
||||
assert.Equal(t, test.wantResponseStatus, recorder.Code)
|
||||
assert.Equal(t, test.wantRetryAttempts, retryListener.timesCalled)
|
||||
})
|
||||
}
|
||||
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 {
|
||||
log.Debugf("No domain parsed in rule %q", route.Rule)
|
||||
log.Debugf("No domain parsed in rule %q in provider ACME", route.Rule)
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ func (p *Provider) buildConfiguration(catalog []catalogUpdate) *types.Configurat
|
|||
"getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
||||
"getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
|
||||
"getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||
"getPassTLSClientCert": label.GetTLSClientCert,
|
||||
"getWhiteList": label.GetWhiteList,
|
||||
"getRedirect": label.GetRedirect,
|
||||
"getErrorPages": label.GetErrorPages,
|
||||
|
|
|
@ -423,6 +423,17 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
|||
label.TraefikBackendBufferingMemRequestBodyBytes + "=2097152",
|
||||
label.TraefikBackendBufferingRetryExpression + "=IsNetworkError() && Attempts() <= 2",
|
||||
|
||||
label.TraefikFrontendPassTLSClientCertPem + "=true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosNotBefore + "=true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosNotAfter + "=true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSans + "=true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectCommonName + "=true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectCountry + "=true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectLocality + "=true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectOrganization + "=true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectProvince + "=true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber + "=true",
|
||||
|
||||
label.TraefikFrontendAuthBasic + "=test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
label.TraefikFrontendAuthBasicRemoveHeader + "=true",
|
||||
label.TraefikFrontendAuthBasicUsers + "=test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
|
@ -540,6 +551,22 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
|||
PassHostHeader: true,
|
||||
PassTLSCert: true,
|
||||
Priority: 666,
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
PEM: true,
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
NotBefore: true,
|
||||
Sans: true,
|
||||
NotAfter: true,
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
CommonName: true,
|
||||
Country: true,
|
||||
Locality: true,
|
||||
Organization: true,
|
||||
Province: true,
|
||||
SerialNumber: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Auth: &types.Auth{
|
||||
HeaderField: "X-WebAuth-User",
|
||||
Basic: &types.Basic{
|
||||
|
|
|
@ -46,6 +46,7 @@ func (p *Provider) buildConfiguration(containersInspected []dockerData) *types.C
|
|||
"getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
||||
"getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
|
||||
"getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||
"getPassTLSClientCert": label.GetTLSClientCert,
|
||||
"getEntryPoints": label.GetFuncSliceString(label.TraefikFrontendEntryPoints),
|
||||
"getBasicAuth": label.GetFuncSliceString(label.TraefikFrontendAuthBasic), // Deprecated
|
||||
"getAuth": label.GetAuth,
|
||||
|
|
|
@ -111,6 +111,69 @@ func TestDockerBuildConfiguration(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "when pass tls client certificate",
|
||||
containers: []docker.ContainerJSON{
|
||||
containerJSON(
|
||||
name("test"),
|
||||
labels(map[string]string{
|
||||
label.TraefikFrontendPassTLSClientCertPem: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosNotBefore: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosNotAfter: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSans: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectCommonName: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectCountry: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectLocality: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectOrganization: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectProvince: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber: "true",
|
||||
}),
|
||||
ports(nat.PortMap{
|
||||
"80/tcp": {},
|
||||
}),
|
||||
withNetwork("bridge", ipv4("127.0.0.1")),
|
||||
),
|
||||
},
|
||||
expectedFrontends: map[string]*types.Frontend{
|
||||
"frontend-Host-test-docker-localhost-0": {
|
||||
Backend: "backend-test",
|
||||
PassHostHeader: true,
|
||||
EntryPoints: []string{},
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
PEM: true,
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
NotBefore: true,
|
||||
Sans: true,
|
||||
NotAfter: true,
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
CommonName: true,
|
||||
Country: true,
|
||||
Locality: true,
|
||||
Organization: true,
|
||||
Province: true,
|
||||
SerialNumber: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Routes: map[string]types.Route{
|
||||
"route-frontend-Host-test-docker-localhost-0": {
|
||||
Rule: "Host:test.docker.localhost",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedBackends: map[string]*types.Backend{
|
||||
"backend-test": {
|
||||
Servers: map[string]types.Server{
|
||||
"server-test-842895ca2aca17f6ee36ddb2f621194d": {
|
||||
URL: "http://127.0.0.1:80",
|
||||
Weight: label.DefaultWeight,
|
||||
},
|
||||
},
|
||||
CircuitBreaker: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "when frontend basic auth backward compatibility",
|
||||
containers: []docker.ContainerJSON{
|
||||
|
@ -387,6 +450,17 @@ func TestDockerBuildConfiguration(t *testing.T) {
|
|||
label.TraefikBackendBufferingMemRequestBodyBytes: "2097152",
|
||||
label.TraefikBackendBufferingRetryExpression: "IsNetworkError() && Attempts() <= 2",
|
||||
|
||||
label.TraefikFrontendPassTLSClientCertPem: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosNotBefore: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosNotAfter: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSans: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectCommonName: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectCountry: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectLocality: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectOrganization: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectProvince: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber: "true",
|
||||
|
||||
label.TraefikFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
label.TraefikFrontendAuthBasicRemoveHeader: "true",
|
||||
label.TraefikFrontendAuthBasicUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
|
@ -475,6 +549,22 @@ func TestDockerBuildConfiguration(t *testing.T) {
|
|||
PassHostHeader: true,
|
||||
PassTLSCert: true,
|
||||
Priority: 666,
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
PEM: true,
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
NotBefore: true,
|
||||
Sans: true,
|
||||
NotAfter: true,
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
CommonName: true,
|
||||
Country: true,
|
||||
Locality: true,
|
||||
Organization: true,
|
||||
Province: true,
|
||||
SerialNumber: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Auth: &types.Auth{
|
||||
HeaderField: "X-WebAuth-User",
|
||||
Basic: &types.Basic{
|
||||
|
|
|
@ -93,6 +93,72 @@ func TestSwarmBuildConfiguration(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "when pass tls client cert configuration",
|
||||
services: []swarm.Service{
|
||||
swarmService(
|
||||
serviceName("test"),
|
||||
serviceLabels(map[string]string{
|
||||
label.TraefikPort: "80",
|
||||
label.TraefikFrontendPassTLSClientCertPem: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosNotBefore: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosNotAfter: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSans: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectCommonName: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectCountry: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectLocality: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectOrganization: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectProvince: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber: "true",
|
||||
}),
|
||||
withEndpointSpec(modeVIP),
|
||||
withEndpoint(virtualIP("1", "127.0.0.1/24")),
|
||||
),
|
||||
},
|
||||
expectedFrontends: map[string]*types.Frontend{
|
||||
"frontend-Host-test-docker-localhost-0": {
|
||||
Backend: "backend-test",
|
||||
PassHostHeader: true,
|
||||
EntryPoints: []string{},
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
PEM: true,
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
NotBefore: true,
|
||||
Sans: true,
|
||||
NotAfter: true,
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
CommonName: true,
|
||||
Country: true,
|
||||
Locality: true,
|
||||
Organization: true,
|
||||
Province: true,
|
||||
SerialNumber: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Routes: map[string]types.Route{
|
||||
"route-frontend-Host-test-docker-localhost-0": {
|
||||
Rule: "Host:test.docker.localhost",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedBackends: map[string]*types.Backend{
|
||||
"backend-test": {
|
||||
Servers: map[string]types.Server{
|
||||
"server-test-842895ca2aca17f6ee36ddb2f621194d": {
|
||||
URL: "http://127.0.0.1:80",
|
||||
Weight: label.DefaultWeight,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
networks: map[string]*docker.NetworkResource{
|
||||
"1": {
|
||||
Name: "foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "when frontend basic auth configuration",
|
||||
services: []swarm.Service{
|
||||
|
|
|
@ -65,6 +65,71 @@ func TestSegmentBuildConfiguration(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "pass tls client cert",
|
||||
containers: []docker.ContainerJSON{
|
||||
containerJSON(
|
||||
name("foo"),
|
||||
labels(map[string]string{
|
||||
"traefik.sauternes.port": "2503",
|
||||
"traefik.sauternes.frontend.entryPoints": "http,https",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertPem: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosNotAfter: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosNotBefore: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSans: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectCommonName: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectCountry: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectLocality: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectOrganization: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectProvince: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectSerialNumber: "true",
|
||||
}),
|
||||
ports(nat.PortMap{
|
||||
"80/tcp": {},
|
||||
}),
|
||||
withNetwork("bridge", ipv4("127.0.0.1")),
|
||||
),
|
||||
},
|
||||
expectedFrontends: map[string]*types.Frontend{
|
||||
"frontend-sauternes-foo-sauternes": {
|
||||
Backend: "backend-foo-sauternes",
|
||||
PassHostHeader: true,
|
||||
EntryPoints: []string{"http", "https"},
|
||||
Routes: map[string]types.Route{
|
||||
"route-frontend-sauternes-foo-sauternes": {
|
||||
Rule: "Host:foo.docker.localhost",
|
||||
},
|
||||
},
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
PEM: true,
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
NotBefore: true,
|
||||
Sans: true,
|
||||
NotAfter: true,
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
CommonName: true,
|
||||
Country: true,
|
||||
Locality: true,
|
||||
Organization: true,
|
||||
Province: true,
|
||||
SerialNumber: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedBackends: map[string]*types.Backend{
|
||||
"backend-foo-sauternes": {
|
||||
Servers: map[string]types.Server{
|
||||
"server-foo-863563a2e23c95502862016417ee95ea": {
|
||||
URL: "http://127.0.0.1:2503",
|
||||
Weight: label.DefaultWeight,
|
||||
},
|
||||
},
|
||||
CircuitBreaker: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "auth basic",
|
||||
containers: []docker.ContainerJSON{
|
||||
|
@ -286,6 +351,17 @@ func TestSegmentBuildConfiguration(t *testing.T) {
|
|||
label.Prefix + "sauternes." + label.SuffixProtocol: "https",
|
||||
label.Prefix + "sauternes." + label.SuffixWeight: "12",
|
||||
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertPem: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosNotAfter: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosNotBefore: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSans: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectCommonName: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectCountry: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectLocality: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectOrganization: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectProvince: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectSerialNumber: "true",
|
||||
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendAuthBasicRemoveHeader: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendAuthBasicUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendAuthBasicUsersFile: ".htpasswd",
|
||||
|
@ -368,6 +444,22 @@ func TestSegmentBuildConfiguration(t *testing.T) {
|
|||
PassHostHeader: true,
|
||||
PassTLSCert: true,
|
||||
Priority: 666,
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
PEM: true,
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
NotBefore: true,
|
||||
Sans: true,
|
||||
NotAfter: true,
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
CommonName: true,
|
||||
Country: true,
|
||||
Locality: true,
|
||||
Organization: true,
|
||||
Province: true,
|
||||
SerialNumber: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Auth: &types.Auth{
|
||||
HeaderField: "X-WebAuth-User",
|
||||
Basic: &types.Basic{
|
||||
|
|
|
@ -36,6 +36,7 @@ func (p *Provider) buildConfiguration(instances []ecsInstance) (*types.Configura
|
|||
"getFrontendName": p.getFrontendName,
|
||||
"getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
|
||||
"getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||
"getPassTLSClientCert": label.GetTLSClientCert,
|
||||
"getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
||||
"getBasicAuth": label.GetFuncSliceString(label.TraefikFrontendAuthBasic), // Deprecated
|
||||
"getAuth": label.GetAuth,
|
||||
|
|
|
@ -321,6 +321,17 @@ func TestSegmentBuildConfiguration(t *testing.T) {
|
|||
label.Prefix + "sauternes." + label.SuffixFrontendAuthForwardTLSInsecureSkipVerify: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendAuthHeaderField: "X-WebAuth-User",
|
||||
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertPem: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosNotBefore: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosNotAfter: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSans: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectCommonName: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectCountry: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectLocality: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectOrganization: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectProvince: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectSerialNumber: "true",
|
||||
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendEntryPoints: "http,https",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassHostHeader: "true",
|
||||
|
@ -391,6 +402,22 @@ func TestSegmentBuildConfiguration(t *testing.T) {
|
|||
PassHostHeader: true,
|
||||
PassTLSCert: true,
|
||||
Priority: 666,
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
PEM: true,
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
NotBefore: true,
|
||||
Sans: true,
|
||||
NotAfter: true,
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
CommonName: true,
|
||||
Country: true,
|
||||
Locality: true,
|
||||
Organization: true,
|
||||
Province: true,
|
||||
SerialNumber: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Auth: &types.Auth{
|
||||
HeaderField: "X-WebAuth-User",
|
||||
Basic: &types.Basic{
|
||||
|
|
|
@ -356,6 +356,17 @@ func TestBuildConfiguration(t *testing.T) {
|
|||
label.TraefikBackendBufferingMemRequestBodyBytes: aws.String("2097152"),
|
||||
label.TraefikBackendBufferingRetryExpression: aws.String("IsNetworkError() && Attempts() <= 2"),
|
||||
|
||||
label.TraefikFrontendPassTLSClientCertPem: aws.String("true"),
|
||||
label.TraefikFrontendPassTLSClientCertInfosNotBefore: aws.String("true"),
|
||||
label.TraefikFrontendPassTLSClientCertInfosNotAfter: aws.String("true"),
|
||||
label.TraefikFrontendPassTLSClientCertInfosSans: aws.String("true"),
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectCommonName: aws.String("true"),
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectCountry: aws.String("true"),
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectLocality: aws.String("true"),
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectOrganization: aws.String("true"),
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectProvince: aws.String("true"),
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber: aws.String("true"),
|
||||
|
||||
label.TraefikFrontendAuthBasic: aws.String("test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
||||
label.TraefikFrontendAuthBasicRemoveHeader: aws.String("true"),
|
||||
label.TraefikFrontendAuthBasicUsers: aws.String("test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
||||
|
@ -489,6 +500,22 @@ func TestBuildConfiguration(t *testing.T) {
|
|||
PassHostHeader: true,
|
||||
PassTLSCert: true,
|
||||
Priority: 666,
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
PEM: true,
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
NotBefore: true,
|
||||
Sans: true,
|
||||
NotAfter: true,
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
CommonName: true,
|
||||
Country: true,
|
||||
Locality: true,
|
||||
Organization: true,
|
||||
Province: true,
|
||||
SerialNumber: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Auth: &types.Auth{
|
||||
HeaderField: "X-WebAuth-User",
|
||||
Basic: &types.Basic{
|
||||
|
|
|
@ -380,7 +380,7 @@ func (p *Provider) lookupEc2Instances(ctx context.Context, client *awsClient, cl
|
|||
})
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("Unable to describe instances [%s]: %v", err)
|
||||
log.Errorf("Unable to describe instances: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,19 @@ const (
|
|||
pathFrontendBackend = "/backend"
|
||||
pathFrontendPriority = "/priority"
|
||||
pathFrontendPassHostHeader = "/passhostheader"
|
||||
pathFrontendPassTLSClientCert = "/passTLSClientCert"
|
||||
pathFrontendPassTLSClientCertPem = pathFrontendPassTLSClientCert + "/pem"
|
||||
pathFrontendPassTLSClientCertInfos = pathFrontendPassTLSClientCert + "/infos"
|
||||
pathFrontendPassTLSClientCertInfosNotAfter = pathFrontendPassTLSClientCertInfos + "/notAfter"
|
||||
pathFrontendPassTLSClientCertInfosNotBefore = pathFrontendPassTLSClientCertInfos + "/notBefore"
|
||||
pathFrontendPassTLSClientCertInfosSans = pathFrontendPassTLSClientCertInfos + "/sans"
|
||||
pathFrontendPassTLSClientCertInfosSubject = pathFrontendPassTLSClientCertInfos + "/subject"
|
||||
pathFrontendPassTLSClientCertInfosSubjectCommonName = pathFrontendPassTLSClientCertInfosSubject + "/commonName"
|
||||
pathFrontendPassTLSClientCertInfosSubjectCountry = pathFrontendPassTLSClientCertInfosSubject + "/country"
|
||||
pathFrontendPassTLSClientCertInfosSubjectLocality = pathFrontendPassTLSClientCertInfosSubject + "/locality"
|
||||
pathFrontendPassTLSClientCertInfosSubjectOrganization = pathFrontendPassTLSClientCertInfosSubject + "/organization"
|
||||
pathFrontendPassTLSClientCertInfosSubjectProvince = pathFrontendPassTLSClientCertInfosSubject + "/province"
|
||||
pathFrontendPassTLSClientCertInfosSubjectSerialNumber = pathFrontendPassTLSClientCertInfosSubject + "/serialNumber"
|
||||
pathFrontendPassTLSCert = "/passtlscert"
|
||||
pathFrontendWhiteListSourceRange = "/whitelist/sourcerange"
|
||||
pathFrontendWhiteListIPStrategy = "/whitelist/ipstrategy"
|
||||
|
|
|
@ -45,6 +45,7 @@ func (p *Provider) buildConfiguration() *types.Configuration {
|
|||
"getPriority": p.getFuncInt(pathFrontendPriority, label.DefaultFrontendPriority),
|
||||
"getPassHostHeader": p.getFuncBool(pathFrontendPassHostHeader, label.DefaultPassHostHeader),
|
||||
"getPassTLSCert": p.getFuncBool(pathFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||
"getPassTLSClientCert": p.getTLSClientCert,
|
||||
"getEntryPoints": p.getFuncList(pathFrontendEntryPoints),
|
||||
"getAuth": p.getAuth,
|
||||
"getRoutes": p.getRoutes,
|
||||
|
@ -334,6 +335,39 @@ func (p *Provider) getTLSSection(prefix string) []*tls.Configuration {
|
|||
return tlsSection
|
||||
}
|
||||
|
||||
// getTLSClientCert create TLS client header configuration from labels
|
||||
func (p *Provider) getTLSClientCert(rootPath string) *types.TLSClientHeaders {
|
||||
if !p.hasPrefix(rootPath, pathFrontendPassTLSClientCert) {
|
||||
return nil
|
||||
}
|
||||
|
||||
tlsClientHeaders := &types.TLSClientHeaders{
|
||||
PEM: p.getBool(false, rootPath, pathFrontendPassTLSClientCertPem),
|
||||
}
|
||||
|
||||
if p.hasPrefix(rootPath, pathFrontendPassTLSClientCertInfos) {
|
||||
infos := &types.TLSClientCertificateInfos{
|
||||
NotAfter: p.getBool(false, rootPath, pathFrontendPassTLSClientCertInfosNotAfter),
|
||||
NotBefore: p.getBool(false, rootPath, pathFrontendPassTLSClientCertInfosNotBefore),
|
||||
Sans: p.getBool(false, rootPath, pathFrontendPassTLSClientCertInfosSans),
|
||||
}
|
||||
|
||||
if p.hasPrefix(rootPath, pathFrontendPassTLSClientCertInfosSubject) {
|
||||
subject := &types.TLSCLientCertificateSubjectInfos{
|
||||
CommonName: p.getBool(false, rootPath, pathFrontendPassTLSClientCertInfosSubjectCommonName),
|
||||
Country: p.getBool(false, rootPath, pathFrontendPassTLSClientCertInfosSubjectCountry),
|
||||
Locality: p.getBool(false, rootPath, pathFrontendPassTLSClientCertInfosSubjectLocality),
|
||||
Organization: p.getBool(false, rootPath, pathFrontendPassTLSClientCertInfosSubjectOrganization),
|
||||
Province: p.getBool(false, rootPath, pathFrontendPassTLSClientCertInfosSubjectProvince),
|
||||
SerialNumber: p.getBool(false, rootPath, pathFrontendPassTLSClientCertInfosSubjectSerialNumber),
|
||||
}
|
||||
infos.Subject = subject
|
||||
}
|
||||
tlsClientHeaders.Infos = infos
|
||||
}
|
||||
return tlsClientHeaders
|
||||
}
|
||||
|
||||
// GetAuth Create auth from path
|
||||
func (p *Provider) getAuth(rootPath string) *types.Auth {
|
||||
if p.hasPrefix(rootPath, pathFrontendAuth) {
|
||||
|
|
|
@ -277,6 +277,18 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
|||
withPair(pathFrontendBackend, "backend1"),
|
||||
withPair(pathFrontendPriority, "6"),
|
||||
withPair(pathFrontendPassHostHeader, "false"),
|
||||
|
||||
withPair(pathFrontendPassTLSClientCertPem, "true"),
|
||||
withPair(pathFrontendPassTLSClientCertInfosNotBefore, "true"),
|
||||
withPair(pathFrontendPassTLSClientCertInfosNotAfter, "true"),
|
||||
withPair(pathFrontendPassTLSClientCertInfosSans, "true"),
|
||||
withPair(pathFrontendPassTLSClientCertInfosSubjectCommonName, "true"),
|
||||
withPair(pathFrontendPassTLSClientCertInfosSubjectCountry, "true"),
|
||||
withPair(pathFrontendPassTLSClientCertInfosSubjectLocality, "true"),
|
||||
withPair(pathFrontendPassTLSClientCertInfosSubjectOrganization, "true"),
|
||||
withPair(pathFrontendPassTLSClientCertInfosSubjectProvince, "true"),
|
||||
withPair(pathFrontendPassTLSClientCertInfosSubjectSerialNumber, "true"),
|
||||
|
||||
withPair(pathFrontendPassTLSCert, "true"),
|
||||
withList(pathFrontendEntryPoints, "http", "https"),
|
||||
withList(pathFrontendWhiteListSourceRange, "1.1.1.1/24", "1234:abcd::42/32"),
|
||||
|
@ -403,6 +415,22 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
|||
ExcludedIPs: []string{"1.1.1.1/24", "1234:abcd::42/32"},
|
||||
},
|
||||
},
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
PEM: true,
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
NotBefore: true,
|
||||
Sans: true,
|
||||
NotAfter: true,
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
CommonName: true,
|
||||
Country: true,
|
||||
Locality: true,
|
||||
Organization: true,
|
||||
Province: true,
|
||||
SerialNumber: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Auth: &types.Auth{
|
||||
HeaderField: "X-WebAuth-User",
|
||||
Basic: &types.Basic{
|
||||
|
|
|
@ -78,7 +78,20 @@ const (
|
|||
SuffixFrontendHeadersReferrerPolicy = SuffixFrontendHeaders + "referrerPolicy"
|
||||
SuffixFrontendHeadersIsDevelopment = SuffixFrontendHeaders + "isDevelopment"
|
||||
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"
|
||||
SuffixFrontendRateLimitExtractorFunc = "frontend.rateLimit.extractorFunc"
|
||||
SuffixFrontendRedirectEntryPoint = "frontend.redirect.entryPoint"
|
||||
|
@ -143,7 +156,20 @@ const (
|
|||
TraefikFrontendAuthHeaderField = Prefix + SuffixFrontendAuthHeaderField
|
||||
TraefikFrontendEntryPoints = Prefix + SuffixFrontendEntryPoints
|
||||
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
|
||||
TraefikFrontendRateLimitExtractorFunc = Prefix + SuffixFrontendRateLimitExtractorFunc
|
||||
TraefikFrontendRedirectEntryPoint = Prefix + SuffixFrontendRedirectEntryPoint
|
||||
|
|
|
@ -62,6 +62,39 @@ func GetRedirect(labels map[string]string) *types.Redirect {
|
|||
return nil
|
||||
}
|
||||
|
||||
// GetTLSClientCert create TLS client header configuration from labels
|
||||
func GetTLSClientCert(labels map[string]string) *types.TLSClientHeaders {
|
||||
if !HasPrefix(labels, TraefikFrontendPassTLSClientCert) {
|
||||
return nil
|
||||
}
|
||||
|
||||
tlsClientHeaders := &types.TLSClientHeaders{
|
||||
PEM: GetBoolValue(labels, TraefikFrontendPassTLSClientCertPem, false),
|
||||
}
|
||||
|
||||
if HasPrefix(labels, TraefikFrontendPassTLSClientCertInfos) {
|
||||
infos := &types.TLSClientCertificateInfos{
|
||||
NotAfter: GetBoolValue(labels, TraefikFrontendPassTLSClientCertInfosNotAfter, false),
|
||||
NotBefore: GetBoolValue(labels, TraefikFrontendPassTLSClientCertInfosNotBefore, false),
|
||||
Sans: GetBoolValue(labels, TraefikFrontendPassTLSClientCertInfosSans, false),
|
||||
}
|
||||
|
||||
if HasPrefix(labels, TraefikFrontendPassTLSClientCertInfosSubject) {
|
||||
subject := &types.TLSCLientCertificateSubjectInfos{
|
||||
CommonName: GetBoolValue(labels, TraefikFrontendPassTLSClientCertInfosSubjectCommonName, false),
|
||||
Country: GetBoolValue(labels, TraefikFrontendPassTLSClientCertInfosSubjectCountry, false),
|
||||
Locality: GetBoolValue(labels, TraefikFrontendPassTLSClientCertInfosSubjectLocality, false),
|
||||
Organization: GetBoolValue(labels, TraefikFrontendPassTLSClientCertInfosSubjectOrganization, false),
|
||||
Province: GetBoolValue(labels, TraefikFrontendPassTLSClientCertInfosSubjectProvince, false),
|
||||
SerialNumber: GetBoolValue(labels, TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber, false),
|
||||
}
|
||||
infos.Subject = subject
|
||||
}
|
||||
tlsClientHeaders.Infos = infos
|
||||
}
|
||||
return tlsClientHeaders
|
||||
}
|
||||
|
||||
// GetAuth Create auth from labels
|
||||
func GetAuth(labels map[string]string) *types.Auth {
|
||||
if !HasPrefix(labels, TraefikFrontendAuth) {
|
||||
|
|
|
@ -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,
|
||||
"getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
|
||||
"getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||
"getPassTLSClientCert": label.GetTLSClientCert,
|
||||
"getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
||||
"getEntryPoints": label.GetFuncSliceString(label.TraefikFrontendEntryPoints),
|
||||
"getBasicAuth": label.GetFuncSliceString(label.TraefikFrontendAuthBasic), // Deprecated
|
||||
|
|
|
@ -373,6 +373,17 @@ func TestBuildConfiguration(t *testing.T) {
|
|||
withLabel(label.TraefikBackendBufferingMemRequestBodyBytes, "2097152"),
|
||||
withLabel(label.TraefikBackendBufferingRetryExpression, "IsNetworkError() && Attempts() <= 2"),
|
||||
|
||||
withLabel(label.TraefikFrontendPassTLSClientCertPem, "true"),
|
||||
withLabel(label.TraefikFrontendPassTLSClientCertInfosNotBefore, "true"),
|
||||
withLabel(label.TraefikFrontendPassTLSClientCertInfosNotAfter, "true"),
|
||||
withLabel(label.TraefikFrontendPassTLSClientCertInfosSans, "true"),
|
||||
withLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectCommonName, "true"),
|
||||
withLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectCountry, "true"),
|
||||
withLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectLocality, "true"),
|
||||
withLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectOrganization, "true"),
|
||||
withLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectProvince, "true"),
|
||||
withLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber, "true"),
|
||||
|
||||
withLabel(label.TraefikFrontendAuthBasic, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
||||
withLabel(label.TraefikFrontendAuthBasicRemoveHeader, "true"),
|
||||
withLabel(label.TraefikFrontendAuthBasicUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
||||
|
@ -455,6 +466,22 @@ func TestBuildConfiguration(t *testing.T) {
|
|||
PassHostHeader: true,
|
||||
PassTLSCert: true,
|
||||
Priority: 666,
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
PEM: true,
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
NotBefore: true,
|
||||
Sans: true,
|
||||
NotAfter: true,
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
CommonName: true,
|
||||
Country: true,
|
||||
Locality: true,
|
||||
Organization: true,
|
||||
Province: true,
|
||||
SerialNumber: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Auth: &types.Auth{
|
||||
HeaderField: "X-WebAuth-User",
|
||||
Basic: &types.Basic{
|
||||
|
@ -767,6 +794,17 @@ func TestBuildConfigurationSegments(t *testing.T) {
|
|||
withSegmentLabel(label.TraefikProtocol, "https", "containous"),
|
||||
withSegmentLabel(label.TraefikWeight, "12", "containous"),
|
||||
|
||||
withSegmentLabel(label.TraefikFrontendPassTLSClientCertPem, "true", "containous"),
|
||||
withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosNotBefore, "true", "containous"),
|
||||
withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosNotAfter, "true", "containous"),
|
||||
withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSans, "true", "containous"),
|
||||
withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectCommonName, "true", "containous"),
|
||||
withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectCountry, "true", "containous"),
|
||||
withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectLocality, "true", "containous"),
|
||||
withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectOrganization, "true", "containous"),
|
||||
withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectProvince, "true", "containous"),
|
||||
withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber, "true", "containous"),
|
||||
|
||||
withSegmentLabel(label.TraefikFrontendAuthBasic, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", "containous"),
|
||||
withSegmentLabel(label.TraefikFrontendAuthBasicRemoveHeader, "true", "containous"),
|
||||
withSegmentLabel(label.TraefikFrontendAuthBasicUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", "containous"),
|
||||
|
@ -847,6 +885,22 @@ func TestBuildConfigurationSegments(t *testing.T) {
|
|||
PassHostHeader: true,
|
||||
PassTLSCert: true,
|
||||
Priority: 666,
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
PEM: true,
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
NotBefore: true,
|
||||
Sans: true,
|
||||
NotAfter: true,
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
CommonName: true,
|
||||
Country: true,
|
||||
Locality: true,
|
||||
Organization: true,
|
||||
Province: true,
|
||||
SerialNumber: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Auth: &types.Auth{
|
||||
HeaderField: "X-WebAuth-User",
|
||||
Basic: &types.Basic{
|
||||
|
|
|
@ -48,6 +48,7 @@ func (p *Provider) buildConfiguration(tasks []state.Task) *types.Configuration {
|
|||
"getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
||||
"getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
|
||||
"getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||
"getPassTLSClientCert": label.GetTLSClientCert,
|
||||
"getFrontendRule": p.getFrontendRule,
|
||||
"getRedirect": label.GetRedirect,
|
||||
"getErrorPages": label.GetErrorPages,
|
||||
|
|
|
@ -330,6 +330,17 @@ func TestBuildConfiguration(t *testing.T) {
|
|||
withLabel(label.TraefikBackendBufferingMemRequestBodyBytes, "2097152"),
|
||||
withLabel(label.TraefikBackendBufferingRetryExpression, "IsNetworkError() && Attempts() <= 2"),
|
||||
|
||||
withLabel(label.TraefikFrontendPassTLSClientCertPem, "true"),
|
||||
withLabel(label.TraefikFrontendPassTLSClientCertInfosNotBefore, "true"),
|
||||
withLabel(label.TraefikFrontendPassTLSClientCertInfosNotAfter, "true"),
|
||||
withLabel(label.TraefikFrontendPassTLSClientCertInfosSans, "true"),
|
||||
withLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectCommonName, "true"),
|
||||
withLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectCountry, "true"),
|
||||
withLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectLocality, "true"),
|
||||
withLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectOrganization, "true"),
|
||||
withLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectProvince, "true"),
|
||||
withLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber, "true"),
|
||||
|
||||
withLabel(label.TraefikFrontendAuthBasic, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
||||
withLabel(label.TraefikFrontendAuthBasicRemoveHeader, "true"),
|
||||
withLabel(label.TraefikFrontendAuthBasicUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"),
|
||||
|
@ -418,6 +429,22 @@ func TestBuildConfiguration(t *testing.T) {
|
|||
PassHostHeader: true,
|
||||
PassTLSCert: true,
|
||||
Priority: 666,
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
PEM: true,
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
NotBefore: true,
|
||||
Sans: true,
|
||||
NotAfter: true,
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
CommonName: true,
|
||||
Country: true,
|
||||
Locality: true,
|
||||
Organization: true,
|
||||
Province: true,
|
||||
SerialNumber: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Auth: &types.Auth{
|
||||
HeaderField: "X-WebAuth-User",
|
||||
Basic: &types.Basic{
|
||||
|
@ -687,6 +714,17 @@ func TestBuildConfigurationSegments(t *testing.T) {
|
|||
withSegmentLabel(label.TraefikProtocol, "https", "containous"),
|
||||
withSegmentLabel(label.TraefikWeight, "12", "containous"),
|
||||
|
||||
withSegmentLabel(label.TraefikFrontendPassTLSClientCertPem, "true", "containous"),
|
||||
withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosNotBefore, "true", "containous"),
|
||||
withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosNotAfter, "true", "containous"),
|
||||
withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSans, "true", "containous"),
|
||||
withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectCommonName, "true", "containous"),
|
||||
withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectCountry, "true", "containous"),
|
||||
withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectLocality, "true", "containous"),
|
||||
withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectOrganization, "true", "containous"),
|
||||
withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectProvince, "true", "containous"),
|
||||
withSegmentLabel(label.TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber, "true", "containous"),
|
||||
|
||||
withSegmentLabel(label.TraefikFrontendAuthBasic, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", "containous"),
|
||||
withSegmentLabel(label.TraefikFrontendAuthBasicRemoveHeader, "true", "containous"),
|
||||
withSegmentLabel(label.TraefikFrontendAuthBasicUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", "containous"),
|
||||
|
@ -768,6 +806,22 @@ func TestBuildConfigurationSegments(t *testing.T) {
|
|||
PassHostHeader: true,
|
||||
PassTLSCert: true,
|
||||
Priority: 666,
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
PEM: true,
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
NotBefore: true,
|
||||
Sans: true,
|
||||
NotAfter: true,
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
CommonName: true,
|
||||
Country: true,
|
||||
Locality: true,
|
||||
Organization: true,
|
||||
Province: true,
|
||||
SerialNumber: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Auth: &types.Auth{
|
||||
HeaderField: "X-WebAuth-User",
|
||||
Basic: &types.Basic{
|
||||
|
|
|
@ -128,7 +128,7 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s
|
|||
func detectMasters(zk string, masters []string) <-chan []string {
|
||||
changed := make(chan []string, 1)
|
||||
if zk != "" {
|
||||
log.Debugf("Starting master detector for ZK ", zk)
|
||||
log.Debugf("Starting master detector for ZK %s", zk)
|
||||
if md, err := detector.New(zk); err != nil {
|
||||
log.Errorf("Failed to create master detector: %v", err)
|
||||
} else if err := md.Detect(detect.NewMasters(masters, changed)); err != nil {
|
||||
|
|
|
@ -33,6 +33,7 @@ func (p *Provider) buildConfiguration(services []rancherData) *types.Configurati
|
|||
"getPriority": label.GetFuncInt(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
||||
"getPassHostHeader": label.GetFuncBool(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
|
||||
"getPassTLSCert": label.GetFuncBool(label.TraefikFrontendPassTLSCert, label.DefaultPassTLSCert),
|
||||
"getPassTLSClientCert": label.GetTLSClientCert,
|
||||
"getEntryPoints": label.GetFuncSliceString(label.TraefikFrontendEntryPoints),
|
||||
"getBasicAuth": label.GetFuncSliceString(label.TraefikFrontendAuthBasic), // Deprecated
|
||||
"getAuth": label.GetAuth,
|
||||
|
|
|
@ -58,6 +58,17 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
|||
label.TraefikBackendBufferingMemRequestBodyBytes: "2097152",
|
||||
label.TraefikBackendBufferingRetryExpression: "IsNetworkError() && Attempts() <= 2",
|
||||
|
||||
label.TraefikFrontendPassTLSClientCertPem: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosNotBefore: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosNotAfter: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSans: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectCommonName: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectCountry: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectLocality: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectOrganization: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectProvince: "true",
|
||||
label.TraefikFrontendPassTLSClientCertInfosSubjectSerialNumber: "true",
|
||||
|
||||
label.TraefikFrontendAuthBasic: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
label.TraefikFrontendAuthBasicRemoveHeader: "true",
|
||||
label.TraefikFrontendAuthBasicUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
|
@ -144,6 +155,22 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
|||
PassHostHeader: true,
|
||||
PassTLSCert: true,
|
||||
Priority: 666,
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
PEM: true,
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
NotBefore: true,
|
||||
Sans: true,
|
||||
NotAfter: true,
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
CommonName: true,
|
||||
Country: true,
|
||||
Locality: true,
|
||||
Organization: true,
|
||||
Province: true,
|
||||
SerialNumber: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Auth: &types.Auth{
|
||||
HeaderField: "X-WebAuth-User",
|
||||
Basic: &types.Basic{
|
||||
|
@ -293,6 +320,17 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
|||
label.Prefix + "sauternes." + label.SuffixProtocol: "https",
|
||||
label.Prefix + "sauternes." + label.SuffixWeight: "12",
|
||||
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertPem: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosNotAfter: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosNotBefore: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSans: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectCommonName: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectCountry: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectLocality: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectOrganization: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectProvince: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendPassTLSClientCertInfosSubjectSerialNumber: "true",
|
||||
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendRule: "Host:traefik.wtf",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendAuthBasicRemoveHeader: "true",
|
||||
label.Prefix + "sauternes." + label.SuffixFrontendAuthBasicUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
|
@ -373,6 +411,22 @@ func TestProviderBuildConfiguration(t *testing.T) {
|
|||
PassHostHeader: true,
|
||||
PassTLSCert: true,
|
||||
Priority: 666,
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
PEM: true,
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
NotBefore: true,
|
||||
Sans: true,
|
||||
NotAfter: true,
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
CommonName: true,
|
||||
Country: true,
|
||||
Locality: true,
|
||||
Organization: true,
|
||||
Province: true,
|
||||
SerialNumber: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Auth: &types.Auth{
|
||||
HeaderField: "X-WebAuth-User",
|
||||
Basic: &types.Basic{
|
||||
|
|
|
@ -29,7 +29,7 @@ func (p *Provider) metadataProvide(configurationChan chan<- types.ConfigMessage,
|
|||
operation := func() error {
|
||||
client, err := rancher.NewClientAndWait(metadataServiceURL)
|
||||
if err != nil {
|
||||
log.Errorln("Failed to create Rancher metadata service client: %s", err)
|
||||
log.Errorf("Failed to create Rancher metadata service client: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ func (p *Provider) metadataProvide(configurationChan chan<- types.ConfigMessage,
|
|||
|
||||
stacks, err := client.GetStacks()
|
||||
if err != nil {
|
||||
log.Errorf("Failed to query Rancher metadata service: %s", err)
|
||||
log.Errorf("Failed to query Rancher metadata service: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/BurntSushi/ty/fun"
|
||||
"github.com/containous/mux"
|
||||
"github.com/containous/traefik/hostresolver"
|
||||
"github.com/containous/traefik/log"
|
||||
|
@ -288,9 +287,11 @@ func (r *Rules) Parse(expression string) (*mux.Route, error) {
|
|||
// ParseDomains parses rules expressions and returns domains
|
||||
func (r *Rules) ParseDomains(expression string) ([]string, error) {
|
||||
var domains []string
|
||||
isHostRule := false
|
||||
|
||||
err := r.parseRules(expression, func(functionName string, function interface{}, arguments []string) error {
|
||||
if functionName == "Host" {
|
||||
isHostRule = true
|
||||
domains = append(domains, arguments...)
|
||||
}
|
||||
return nil
|
||||
|
@ -299,5 +300,18 @@ func (r *Rules) ParseDomains(expression string) ([]string, error) {
|
|||
return nil, fmt.Errorf("error parsing domains: %v", err)
|
||||
}
|
||||
|
||||
return fun.Map(strings.ToLower, domains).([]string), nil
|
||||
var cleanDomains []string
|
||||
for _, domain := range domains {
|
||||
canonicalDomain := strings.ToLower(domain)
|
||||
if len(canonicalDomain) > 0 {
|
||||
cleanDomains = append(cleanDomains, canonicalDomain)
|
||||
}
|
||||
}
|
||||
|
||||
// Return an error if an Host rule is detected but no domain are parsed
|
||||
if isHostRule && len(cleanDomains) == 0 {
|
||||
return nil, fmt.Errorf("unable to parse correctly the domains in the Host rule from %q", expression)
|
||||
}
|
||||
|
||||
return cleanDomains, nil
|
||||
}
|
||||
|
|
|
@ -64,24 +64,38 @@ func TestParseDomains(t *testing.T) {
|
|||
rules := &Rules{}
|
||||
|
||||
tests := []struct {
|
||||
description string
|
||||
expression string
|
||||
domain []string
|
||||
errorExpected bool
|
||||
}{
|
||||
{
|
||||
description: "Many host rules",
|
||||
expression: "Host:foo.bar,test.bar",
|
||||
domain: []string{"foo.bar", "test.bar"},
|
||||
errorExpected: false,
|
||||
},
|
||||
{
|
||||
description: "No host rule",
|
||||
expression: "Path:/test",
|
||||
domain: []string{},
|
||||
errorExpected: false,
|
||||
},
|
||||
{
|
||||
description: "Host rule and another rule",
|
||||
expression: "Host:foo.bar;Path:/test",
|
||||
domain: []string{"foo.bar"},
|
||||
errorExpected: false,
|
||||
},
|
||||
{
|
||||
description: "Host rule to trim and another rule",
|
||||
expression: "Host: Foo.Bar ;Path:/test",
|
||||
domain: []string{"foo.bar"},
|
||||
errorExpected: false,
|
||||
},
|
||||
{
|
||||
description: "Host rule with no domain",
|
||||
expression: "Host: ;Path:/test",
|
||||
errorExpected: true,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -91,7 +105,12 @@ func TestParseDomains(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
domains, err := rules.ParseDomains(test.expression)
|
||||
|
||||
if test.errorExpected {
|
||||
require.Errorf(t, err, "unable to parse correctly the domains in the Host rule from %q", test.expression)
|
||||
} else {
|
||||
require.NoError(t, err, "%s: Error while parsing domain.", test.expression)
|
||||
}
|
||||
|
||||
assert.EqualValues(t, test.domain, domains, "%s: Error parsing domains from expression.", test.expression)
|
||||
})
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
stdlog "log"
|
||||
"net"
|
||||
"net/http"
|
||||
|
@ -244,7 +243,9 @@ func (s *Server) Start() {
|
|||
s.listenConfigurations(stop)
|
||||
})
|
||||
s.startProvider()
|
||||
go s.listenSignals()
|
||||
s.routinesPool.Go(func(stop chan bool) {
|
||||
s.listenSignals(stop)
|
||||
})
|
||||
}
|
||||
|
||||
// StartWithContext starts the server and Stop/Close it when context is Done
|
||||
|
@ -427,7 +428,7 @@ func (s *Server) createTLSConfig(entryPointName string, tlsOption *traefiktls.TL
|
|||
if len(tlsOption.ClientCA.Files) > 0 {
|
||||
pool := x509.NewCertPool()
|
||||
for _, caFile := range tlsOption.ClientCA.Files {
|
||||
data, err := ioutil.ReadFile(caFile)
|
||||
data, err := caFile.Read()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -424,10 +424,12 @@ func (s *Server) throttleProviderConfigReload(throttle time.Duration, publish ch
|
|||
case <-stop:
|
||||
return
|
||||
case nextConfig := <-ring.Out():
|
||||
publish <- nextConfig.(types.ConfigMessage)
|
||||
if config, ok := nextConfig.(types.ConfigMessage); ok {
|
||||
publish <- config
|
||||
time.Sleep(throttle)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
for {
|
||||
|
@ -515,6 +517,8 @@ func (s *Server) postLoadConfiguration() {
|
|||
domains, err := rls.ParseDomains(route.Rule)
|
||||
if err != nil {
|
||||
log.Errorf("Error parsing domains: %v", err)
|
||||
} else if len(domains) == 0 {
|
||||
log.Debugf("No domain parsed in rule %q", route.Rule)
|
||||
} else {
|
||||
s.globalConfiguration.ACME.LoadCertificateForDomains(domains)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
@ -280,7 +279,7 @@ func createHTTPTransport(globalConfiguration configuration.GlobalConfiguration)
|
|||
return transport, nil
|
||||
}
|
||||
|
||||
func createRootCACertPool(rootCAs traefiktls.RootCAs) *x509.CertPool {
|
||||
func createRootCACertPool(rootCAs traefiktls.FilesOrContents) *x509.CertPool {
|
||||
roots := x509.NewCertPool()
|
||||
|
||||
for _, cert := range rootCAs {
|
||||
|
@ -308,7 +307,7 @@ func createClientTLSConfig(entryPointName string, tlsOption *traefiktls.TLS) (*t
|
|||
if len(tlsOption.ClientCA.Files) > 0 {
|
||||
pool := x509.NewCertPool()
|
||||
for _, caFile := range tlsOption.ClientCA.Files {
|
||||
data, err := ioutil.ReadFile(caFile)
|
||||
data, err := caFile.Read()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -104,6 +104,15 @@ func (s *Server) buildMiddlewares(frontendName string, frontend *types.Frontend,
|
|||
middle = append(middle, handler)
|
||||
}
|
||||
|
||||
// TLSClientHeaders
|
||||
tlsClientHeadersMiddleware := middlewares.NewTLSClientHeaders(frontend)
|
||||
if tlsClientHeadersMiddleware != nil {
|
||||
log.Debugf("Adding TLSClientHeaders middleware for frontend %s", frontendName)
|
||||
|
||||
handler := s.tracingMiddleware.NewNegroniHandlerWrapper("TLSClientHeaders", tlsClientHeadersMiddleware, false)
|
||||
middle = append(middle, handler)
|
||||
}
|
||||
|
||||
return middle, buildModifyResponse(secureMiddleware, headerMiddleware), postConfig, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -13,9 +13,12 @@ func (s *Server) configureSignals() {
|
|||
signal.Notify(s.signals, syscall.SIGUSR1)
|
||||
}
|
||||
|
||||
func (s *Server) listenSignals() {
|
||||
func (s *Server) listenSignals(stop chan bool) {
|
||||
for {
|
||||
sig := <-s.signals
|
||||
select {
|
||||
case <-stop:
|
||||
return
|
||||
case sig := <-s.signals:
|
||||
switch sig {
|
||||
case syscall.SIGUSR1:
|
||||
log.Infof("Closing and re-opening log files for rotation: %+v", sig)
|
||||
|
@ -31,4 +34,5 @@ func (s *Server) listenSignals() {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,4 +4,4 @@ package server
|
|||
|
||||
func (s *Server) configureSignals() {}
|
||||
|
||||
func (s *Server) listenSignals() {}
|
||||
func (s *Server) listenSignals(stop chan bool) {}
|
||||
|
|
|
@ -73,8 +73,30 @@
|
|||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $auth := getAuth $service.TraefikLabels }}
|
||||
{{ $tlsClientCert := getPassTLSClientCert $service.TraefikLabels }}
|
||||
{{if $tlsClientCert }}
|
||||
[frontends."frontend-{{ $service.ServiceName }}".passTLSClientCert]
|
||||
pem = {{ $tlsClientCert.PEM }}
|
||||
{{ $infos := $tlsClientCert.Infos }}
|
||||
{{if $infos }}
|
||||
[frontends."frontend-{{ $service.ServiceName }}".passTLSClientCert.infos]
|
||||
notAfter = {{ $infos.NotAfter }}
|
||||
notBefore = {{ $infos.NotBefore }}
|
||||
sans = {{ $infos.Sans }}
|
||||
{{ $subject := $infos.Subject }}
|
||||
{{if $subject }}
|
||||
[frontends."frontend-{{ $service.ServiceName }}".passTLSClientCert.infos.subject]
|
||||
country = {{ $subject.Country }}
|
||||
province = {{ $subject.Province }}
|
||||
locality = {{ $subject.Locality }}
|
||||
organization = {{ $subject.Organization }}
|
||||
commonName = {{ $subject.CommonName }}
|
||||
serialNumber = {{ $subject.SerialNumber }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $auth := getAuth $service.TraefikLabels }}
|
||||
{{if $auth }}
|
||||
[frontends."frontend-{{ $service.ServiceName }}".auth]
|
||||
headerField = "{{ $auth.HeaderField }}"
|
||||
|
|
|
@ -74,6 +74,29 @@
|
|||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $tlsClientCert := getPassTLSClientCert $container.SegmentLabels }}
|
||||
{{if $tlsClientCert }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert]
|
||||
pem = {{ $tlsClientCert.PEM }}
|
||||
{{ $infos := $tlsClientCert.Infos }}
|
||||
{{if $infos }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos]
|
||||
notAfter = {{ $infos.NotAfter }}
|
||||
notBefore = {{ $infos.NotBefore }}
|
||||
sans = {{ $infos.Sans }}
|
||||
{{ $subject := $infos.Subject }}
|
||||
{{if $subject }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos.subject]
|
||||
country = {{ $subject.Country }}
|
||||
province = {{ $subject.Province }}
|
||||
locality = {{ $subject.Locality }}
|
||||
organization = {{ $subject.Organization }}
|
||||
commonName = {{ $subject.CommonName }}
|
||||
serialNumber = {{ $subject.SerialNumber }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $auth := getAuth $container.SegmentLabels }}
|
||||
{{if $auth }}
|
||||
[frontends."frontend-{{ $frontendName }}".auth]
|
||||
|
|
|
@ -75,6 +75,29 @@
|
|||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $tlsClientCert := getPassTLSClientCert $instance.SegmentLabels }}
|
||||
{{if $tlsClientCert }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert]
|
||||
pem = {{ $tlsClientCert.PEM }}
|
||||
{{ $infos := $tlsClientCert.Infos }}
|
||||
{{if $infos }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos]
|
||||
notAfter = {{ $infos.NotAfter }}
|
||||
notBefore = {{ $infos.NotBefore }}
|
||||
sans = {{ $infos.Sans }}
|
||||
{{ $subject := $infos.Subject }}
|
||||
{{if $subject }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos.subject]
|
||||
country = {{ $subject.Country }}
|
||||
province = {{ $subject.Province }}
|
||||
locality = {{ $subject.Locality }}
|
||||
organization = {{ $subject.Organization }}
|
||||
commonName = {{ $subject.CommonName }}
|
||||
serialNumber = {{ $subject.SerialNumber }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $auth := getAuth $instance.SegmentLabels }}
|
||||
{{if $auth }}
|
||||
[frontends."frontend-{{ $frontendName }}".auth]
|
||||
|
|
|
@ -73,6 +73,29 @@
|
|||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $tlsClientCert := getPassTLSClientCert $frontend }}
|
||||
{{if $tlsClientCert }}
|
||||
[frontends."{{ $frontendName }}".passTLSClientCert]
|
||||
pem = {{ $tlsClientCert.PEM }}
|
||||
{{ $infos := $tlsClientCert.Infos }}
|
||||
{{if $infos }}
|
||||
[frontends."{{ $frontendName }}".passTLSClientCert.infos]
|
||||
notAfter = {{ $infos.NotAfter }}
|
||||
notBefore = {{ $infos.NotBefore }}
|
||||
sans = {{ $infos.Sans }}
|
||||
{{ $subject := $infos.Subject }}
|
||||
{{if $subject }}
|
||||
[frontends."{{ $frontendName }}".passTLSClientCert.infos.subject]
|
||||
country = {{ $subject.Country }}
|
||||
province = {{ $subject.Province }}
|
||||
locality = {{ $subject.Locality }}
|
||||
organization = {{ $subject.Organization }}
|
||||
commonName = {{ $subject.CommonName }}
|
||||
serialNumber = {{ $subject.SerialNumber }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $auth := getAuth $frontend }}
|
||||
{{if $auth }}
|
||||
[frontends."{{ $frontendName }}".auth]
|
||||
|
|
|
@ -76,6 +76,29 @@
|
|||
"{{.}}",
|
||||
{{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 }}
|
||||
{{if $auth }}
|
||||
[frontends."{{ $frontendName }}".auth]
|
||||
|
|
|
@ -76,6 +76,29 @@
|
|||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $tlsClientCert := getPassTLSClientCert $app.TraefikLabels }}
|
||||
{{if $tlsClientCert }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert]
|
||||
pem = {{ $tlsClientCert.PEM }}
|
||||
{{ $infos := $tlsClientCert.Infos }}
|
||||
{{if $infos }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos]
|
||||
notAfter = {{ $infos.NotAfter }}
|
||||
notBefore = {{ $infos.NotBefore }}
|
||||
sans = {{ $infos.Sans }}
|
||||
{{ $subject := $infos.Subject }}
|
||||
{{if $subject }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos.subject]
|
||||
country = {{ $subject.Country }}
|
||||
province = {{ $subject.Province }}
|
||||
locality = {{ $subject.Locality }}
|
||||
organization = {{ $subject.Organization }}
|
||||
commonName = {{ $subject.CommonName }}
|
||||
serialNumber = {{ $subject.SerialNumber }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $auth := getAuth $app.TraefikLabels }}
|
||||
{{if $auth }}
|
||||
[frontends."frontend-{{ $frontendName }}".auth]
|
||||
|
|
|
@ -74,6 +74,29 @@
|
|||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $tlsClientCert := getPassTLSClientCert $service.SegmentLabels }}
|
||||
{{if $tlsClientCert }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert]
|
||||
pem = {{ $tlsClientCert.PEM }}
|
||||
{{ $infos := $tlsClientCert.Infos }}
|
||||
{{if $infos }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos]
|
||||
notAfter = {{ $infos.NotAfter }}
|
||||
notBefore = {{ $infos.NotBefore }}
|
||||
sans = {{ $infos.Sans }}
|
||||
{{ $subject := $infos.Subject }}
|
||||
{{if $subject }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos.subject]
|
||||
country = {{ $subject.Country }}
|
||||
province = {{ $subject.Province }}
|
||||
locality = {{ $subject.Locality }}
|
||||
organization = {{ $subject.Organization }}
|
||||
commonName = {{ $subject.CommonName }}
|
||||
serialNumber = {{ $subject.SerialNumber }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $auth := getAuth $service.SegmentLabels }}
|
||||
{{if $auth }}
|
||||
[frontends."frontend-{{ $frontendName }}".auth]
|
||||
|
|
34
tls/tls.go
34
tls/tls.go
|
@ -16,7 +16,7 @@ const (
|
|||
// ClientCA defines traefik CA files for a entryPoint
|
||||
// and it indicates if they are mandatory or have just to be analyzed if provided
|
||||
type ClientCA struct {
|
||||
Files []string
|
||||
Files FilesOrContents
|
||||
Optional bool
|
||||
}
|
||||
|
||||
|
@ -30,8 +30,8 @@ type TLS struct {
|
|||
SniStrict bool `export:"true"`
|
||||
}
|
||||
|
||||
// RootCAs hold the CA we want to have in root
|
||||
type RootCAs []FileOrContent
|
||||
// FilesOrContents hold the CA we want to have in root
|
||||
type FilesOrContents []FileOrContent
|
||||
|
||||
// Configuration allows mapping a TLS certificate to a list of entrypoints
|
||||
type Configuration struct {
|
||||
|
@ -41,7 +41,7 @@ type Configuration struct {
|
|||
|
||||
// String is the method to format the flag's value, part of the flag.Value interface.
|
||||
// The String method's output will be used in diagnostics.
|
||||
func (r *RootCAs) String() string {
|
||||
func (r *FilesOrContents) String() string {
|
||||
sliceOfString := make([]string, len([]FileOrContent(*r)))
|
||||
for key, value := range *r {
|
||||
sliceOfString[key] = value.String()
|
||||
|
@ -52,30 +52,30 @@ func (r *RootCAs) String() string {
|
|||
// Set is the method to set the flag value, part of the flag.Value interface.
|
||||
// Set's argument is a string to be parsed to set the flag.
|
||||
// It's a comma-separated list, so we split it.
|
||||
func (r *RootCAs) Set(value string) error {
|
||||
rootCAs := strings.Split(value, ",")
|
||||
if len(rootCAs) == 0 {
|
||||
return fmt.Errorf("bad RootCAs format: %s", value)
|
||||
func (r *FilesOrContents) Set(value string) error {
|
||||
filesOrContents := strings.Split(value, ",")
|
||||
if len(filesOrContents) == 0 {
|
||||
return fmt.Errorf("bad FilesOrContents format: %s", value)
|
||||
}
|
||||
for _, rootCA := range rootCAs {
|
||||
*r = append(*r, FileOrContent(rootCA))
|
||||
for _, fileOrContent := range filesOrContents {
|
||||
*r = append(*r, FileOrContent(fileOrContent))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get return the RootCAs list
|
||||
func (r *RootCAs) Get() interface{} {
|
||||
// Get return the FilesOrContents list
|
||||
func (r *FilesOrContents) Get() interface{} {
|
||||
return *r
|
||||
}
|
||||
|
||||
// SetValue sets the RootCAs with val
|
||||
func (r *RootCAs) SetValue(val interface{}) {
|
||||
*r = val.(RootCAs)
|
||||
// SetValue sets the FilesOrContents with val
|
||||
func (r *FilesOrContents) SetValue(val interface{}) {
|
||||
*r = val.(FilesOrContents)
|
||||
}
|
||||
|
||||
// Type is type of the struct
|
||||
func (r *RootCAs) Type() string {
|
||||
return "rootcas"
|
||||
func (r *FilesOrContents) Type() string {
|
||||
return "filesorcontents"
|
||||
}
|
||||
|
||||
// SortTLSPerEntryPoints converts TLS configuration sorted by Certificates into TLS configuration sorted by EntryPoints
|
||||
|
|
|
@ -182,7 +182,8 @@ type Frontend struct {
|
|||
Backend string `json:"backend,omitempty"`
|
||||
Routes map[string]Route `json:"routes,omitempty" hash:"ignore"`
|
||||
PassHostHeader bool `json:"passHostHeader,omitempty"`
|
||||
PassTLSCert bool `json:"passTLSCert,omitempty"`
|
||||
PassTLSCert bool `json:"passTLSCert,omitempty"` // Deprecated use PassTLSClientCert instead
|
||||
PassTLSClientCert *TLSClientHeaders `json:"passTLSClientCert,omitempty"`
|
||||
Priority int `json:"priority"`
|
||||
WhiteList *WhiteList `json:"whiteList,omitempty"`
|
||||
Headers *Headers `json:"headers,omitempty"`
|
||||
|
@ -645,3 +646,27 @@ func (s *IPStrategy) Get() (ip.Strategy, error) {
|
|||
|
||||
return &ip.RemoteAddrStrategy{}, nil
|
||||
}
|
||||
|
||||
// TLSClientHeaders holds the TLS client cert headers configuration.
|
||||
type TLSClientHeaders struct {
|
||||
PEM bool `description:"Enable header with escaped client pem" json:"pem"`
|
||||
Infos *TLSClientCertificateInfos `description:"Enable header with configured client cert infos" json:"infos,omitempty"`
|
||||
}
|
||||
|
||||
// TLSClientCertificateInfos holds the client TLS certificate infos configuration
|
||||
type TLSClientCertificateInfos struct {
|
||||
NotAfter bool `description:"Add NotAfter info in header" json:"notAfter"`
|
||||
NotBefore bool `description:"Add NotBefore info in header" json:"notBefore"`
|
||||
Subject *TLSCLientCertificateSubjectInfos `description:"Add Subject info in header" json:"subject,omitempty"`
|
||||
Sans bool `description:"Add Sans info in header" json:"sans"`
|
||||
}
|
||||
|
||||
// TLSCLientCertificateSubjectInfos holds the client TLS certificate subject infos configuration
|
||||
type TLSCLientCertificateSubjectInfos struct {
|
||||
Country bool `description:"Add Country info in header" json:"country"`
|
||||
Province bool `description:"Add Province info in header" json:"province"`
|
||||
Locality bool `description:"Add Locality info in header" json:"locality"`
|
||||
Organization bool `description:"Add Organization info in header" json:"organization"`
|
||||
CommonName bool `description:"Add CommonName info in header" json:"commonName"`
|
||||
SerialNumber bool `description:"Add SerialNumber info in header" json:"serialNumber"`
|
||||
}
|
||||
|
|
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) {
|
||||
return b.buffer.Write(buf)
|
||||
length, err := b.buffer.Write(buf)
|
||||
if err != nil {
|
||||
// Since go1.11 (https://github.com/golang/go/commit/8f38f28222abccc505b9a1992deecfe3e2cb85de)
|
||||
// if the writer returns an error, the reverse proxy panics
|
||||
b.log.Error(err)
|
||||
length = len(buf)
|
||||
}
|
||||
return length, nil
|
||||
}
|
||||
|
||||
// WriteHeader sets rw.Code.
|
||||
|
@ -410,7 +417,7 @@ func (b *bufferWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
|||
return conn, rw, err
|
||||
}
|
||||
b.log.Warningf("Upstream ResponseWriter of type %v does not implement http.Hijacker. Returning dummy channel.", reflect.TypeOf(b.responseWriter))
|
||||
return nil, nil, fmt.Errorf("The response writer that was wrapped in this proxy, does not implement http.Hijacker. It is of type: %v", reflect.TypeOf(b.responseWriter))
|
||||
return nil, nil, fmt.Errorf("the response writer wrapped in this proxy does not implement http.Hijacker. Its type is: %v”", reflect.TypeOf(b.responseWriter))
|
||||
}
|
||||
|
||||
// SizeErrHandler Size error handler
|
||||
|
|
Loading…
Reference in a new issue