diff --git a/.travis.yml b/.travis.yml index d119e792c..2680b1e9e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ env: script: - echo "Skipping tests... (Tests are executed on SemaphoreCI)" +- if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then make docs-verify; fi before_deploy: - > diff --git a/CHANGELOG.md b/CHANGELOG.md index 270ef022b..c62480e59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # Change Log +## [v1.7.0-rc2](https://github.com/containous/traefik/tree/v1.7.0-rc2) (2018-07-17) +[All Commits](https://github.com/containous/traefik/compare/v1.7.0-rc1...v1.7.0-rc2) + + +**Bug fixes:** +- **[acme,provider]** Create init method on provider interface ([#3580](https://github.com/containous/traefik/pull/3580) by [Juliens](https://github.com/Juliens)) +- **[acme]** Serve TLS-Challenge certificate in first ([#3605](https://github.com/containous/traefik/pull/3605) by [nmengin](https://github.com/nmengin)) +- **[api,authentication,webui]** Auth section in web UI. ([#3628](https://github.com/containous/traefik/pull/3628) by [ldez](https://github.com/ldez)) +- **[authentication,middleware,provider]** Don't pass the Authorization header to the backends ([#3606](https://github.com/containous/traefik/pull/3606) by [jbdoumenjou](https://github.com/jbdoumenjou)) +- **[ecs]** Fix 400 bad request on AWS ECS API ([#3629](https://github.com/containous/traefik/pull/3629) by [mmatur](https://github.com/mmatur)) +- **[k8s]** Fix rewrite-target Annotation behavior ([#3582](https://github.com/containous/traefik/pull/3582) by [dtomcej](https://github.com/dtomcej)) +- **[k8s]** Correct App-Root kubernetes behavior ([#3592](https://github.com/containous/traefik/pull/3592) by [dtomcej](https://github.com/dtomcej)) +- **[k8s]** Add more K8s Unit Tests ([#3583](https://github.com/containous/traefik/pull/3583) by [dtomcej](https://github.com/dtomcej)) +- **[kv]** KV and authentication ([#3615](https://github.com/containous/traefik/pull/3615) by [ldez](https://github.com/ldez)) +- **[middleware]** Send 'Retry-After' to comply with RFC6585. ([#3593](https://github.com/containous/traefik/pull/3593) by [ldez](https://github.com/ldez)) + +**Documentation:** +- **[k8s]** Correct Modifier in Kubernetes Documentation ([#3610](https://github.com/containous/traefik/pull/3610) by [dtomcej](https://github.com/dtomcej)) + +**Misc:** +- Merge v1.6.5 into v1.7 ([#3595](https://github.com/containous/traefik/pull/3595) by [ldez](https://github.com/ldez)) + ## [v1.6.5](https://github.com/containous/traefik/tree/v1.6.5) (2018-07-09) [All Commits](https://github.com/containous/traefik/compare/v1.6.4...v1.6.5) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 688b88e0c..1f6a4ac5e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -160,9 +160,11 @@ Integration tests must be run from the `integration/` directory and require the The [documentation site](http://docs.traefik.io/) is built with [mkdocs](http://mkdocs.org/) -### Method 1: `Docker` and `make` +### Building Documentation -You can test documentation using the `docs` target. +#### Method 1: `Docker` and `make` + +You can build the documentation and serve it locally with livereloading, using the `docs` target: ```bash $ make docs @@ -177,11 +179,18 @@ docker run --rm -v /home/user/go/github/containous/traefik:/mkdocs -p 8000:8000 And go to [http://127.0.0.1:8000](http://127.0.0.1:8000). -### Method 2: `mkdocs` +If you only want to build the documentation without serving it locally, you can use the following command: + +```bash +$ make docs-build +... +``` + +#### Method 2: `mkdocs` First make sure you have python and pip installed -```shell +```bash $ python --version Python 2.7.2 $ pip --version @@ -190,22 +199,42 @@ pip 1.5.2 Then install mkdocs with pip -```shell +```bash pip install --user -r requirements.txt ``` -To test documentation locally run `mkdocs serve` in the root directory, this should start a server locally to preview your changes. +To build documentation locally and serve it locally, +run `mkdocs serve` in the root directory, +this should start a server locally to preview your changes. -```shell +```bash $ mkdocs serve INFO - Building documentation... -WARNING - Config value: 'theme'. Warning: The theme 'united' will be removed in an upcoming MkDocs release. See http://www.mkdocs.org/about/release-notes/ for more details INFO - Cleaning site directory [I 160505 22:31:24 server:281] Serving on http://127.0.0.1:8000 [I 160505 22:31:24 handlers:59] Start watching changes [I 160505 22:31:24 handlers:61] Start detecting changes ``` +### Verify Documentation + +You can verify that the documentation meets some expectations, as checking for dead links, html markup validity. + +```bash +$ make docs-verify +docker build -t traefik-docs-verify ./script/docs-verify-docker-image ## Build Validator image +... +docker run --rm -v /home/travis/build/containous/traefik:/app traefik-docs-verify ## Check for dead links and w3c compliance +=== Checking HTML content... +Running ["HtmlCheck", "ImageCheck", "ScriptCheck", "LinkCheck"] on /app/site/basics/index.html on *.html... +``` + +If you recently changed the documentation, do not forget to clean it to have it rebuilt: + +```bash +$ make docs-clean docs-verify +... +``` ## How to Write a Good Issue diff --git a/Gopkg.lock b/Gopkg.lock index 8c7a21475..c2a3bbbc8 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -276,10 +276,10 @@ version = "v3.1.1" [[projects]] - branch = "init-provider" name = "github.com/containous/traefik-extra-service-fabric" packages = ["."] - revision = "eb4d5cf161b3213bf45be611dc1f56e8b2161e46" + revision = "6e90a9eef2ac9d320e55d6e994d169673a8d8b0f" + version = "v1.3.0" [[projects]] name = "github.com/coreos/bbolt" @@ -781,7 +781,6 @@ revision = "9b66602d496a139e4722bdde32f0f1ac1c12d4a8" [[projects]] - branch = "master" name = "github.com/jjcollinge/servicefabric" packages = ["."] revision = "8eebe170fa1ba25d3dfb928b3f86a7313b13b9fe" @@ -1756,6 +1755,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "c228c6029e36e15b6c74bdfa587ee0fa39787af0dc0d4047752d80ee2fb690c1" + inputs-digest = "2b7ffb1d01d8a14224fcc9964900fb5a39fbf38cfacba45f49b931136e4fee9b" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index a5ade8e33..3daa4cf4e 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -66,7 +66,7 @@ [[constraint]] name = "github.com/containous/traefik-extra-service-fabric" - branch = "init-provider" + version = "1.3.0" [[constraint]] name = "github.com/coreos/go-systemd" diff --git a/Makefile b/Makefile index bba47bc7e..02200df1e 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: all +.PHONY: all docs-verify docs docs-clean docs-build TRAEFIK_ENVS := \ -e OS_ARCH_ARG \ @@ -22,6 +22,7 @@ REPONAME := $(shell echo $(REPO) | tr '[:upper:]' '[:lower:]') TRAEFIK_IMAGE := $(if $(REPONAME),$(REPONAME),"containous/traefik") INTEGRATION_OPTS := $(if $(MAKE_DOCKER_HOST),-e "DOCKER_HOST=$(MAKE_DOCKER_HOST)", -e "TEST_CONTAINER=1" -v "/var/run/docker.sock:/var/run/docker.sock") TRAEFIK_DOC_IMAGE := traefik-docs +TRAEFIK_DOC_VERIFY_IMAGE := $(TRAEFIK_DOC_IMAGE)-verify DOCKER_BUILD_ARGS := $(if $(DOCKER_VERSION), "--build-arg=DOCKER_VERSION=$(DOCKER_VERSION)",) DOCKER_RUN_OPTS := $(TRAEFIK_ENVS) $(TRAEFIK_MOUNT) "$(TRAEFIK_DEV_IMAGE)" @@ -94,11 +95,23 @@ image-dirty: binary ## build a docker traefik image image: clear-static binary ## clean up static directory and build a docker traefik image docker build -t $(TRAEFIK_IMAGE) . +docs-image: + docker build -t $(TRAEFIK_DOC_IMAGE) -f docs.Dockerfile . + docs: docs-image docker run $(DOCKER_RUN_DOC_OPTS) $(TRAEFIK_DOC_IMAGE) mkdocs serve -docs-image: - docker build -t $(TRAEFIK_DOC_IMAGE) -f docs.Dockerfile . +docs-build: site + +docs-verify: site + docker build -t $(TRAEFIK_DOC_VERIFY_IMAGE) ./script/docs-verify-docker-image ## Build Validator image + docker run --rm -v $(CURDIR):/app $(TRAEFIK_DOC_VERIFY_IMAGE) ## Check for dead links and w3c compliance + +site: docs-image + docker run $(DOCKER_RUN_DOC_OPTS) $(TRAEFIK_DOC_IMAGE) mkdocs build + +docs-clean: + rm -rf $(CURDIR)/site clear-static: rm -rf static diff --git a/acme/acme.go b/acme/acme.go index 318a8583e..85599bc3e 100644 --- a/acme/acme.go +++ b/acme/acme.go @@ -234,15 +234,15 @@ func (a *ACME) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificat domain := types.CanonicalDomain(clientHello.ServerName) account := a.store.Get().(*Account) - if providedCertificate := a.getProvidedCertificate(domain); providedCertificate != nil { - return providedCertificate, nil - } - if challengeCert, ok := a.challengeTLSProvider.getCertificate(domain); ok { log.Debugf("ACME got challenge %s", domain) return challengeCert, nil } + if providedCertificate := a.getProvidedCertificate(domain); providedCertificate != nil { + return providedCertificate, nil + } + if domainCert, ok := account.DomainsCertificate.getCertificateForDomain(domain); ok { log.Debugf("ACME got domain cert %s", domain) return domainCert.tlsCert, nil diff --git a/autogen/gentemplates/gen.go b/autogen/gentemplates/gen.go index 87647b094..5390fbe2d 100644 --- a/autogen/gentemplates/gen.go +++ b/autogen/gentemplates/gen.go @@ -232,6 +232,7 @@ var _templatesConsul_catalogTmpl = []byte(`[backends] {{if $auth.Basic }} [frontends."frontend-{{ $service.ServiceName }}".auth.basic] + removeHeader = {{ $auth.Basic.RemoveHeader }} {{if $auth.Basic.Users }} users = [{{range $auth.Basic.Users }} "{{.}}", @@ -242,6 +243,7 @@ var _templatesConsul_catalogTmpl = []byte(`[backends] {{if $auth.Digest }} [frontends."frontend-{{ $service.ServiceName }}".auth.digest] + removeHeader = {{ $auth.Digest.RemoveHeader }} {{if $auth.Digest.Users }} users = [{{range $auth.Digest.Users }} "{{.}}", @@ -679,6 +681,7 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}} {{if $auth.Basic }} [frontends."frontend-{{ $frontendName }}".auth.basic] + removeHeader = {{ $auth.Basic.RemoveHeader }} {{if $auth.Basic.Users }} users = [{{range $auth.Basic.Users }} "{{.}}", @@ -689,6 +692,7 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}} {{if $auth.Digest }} [frontends."frontend-{{ $frontendName }}".auth.digest] + removeHeader = {{ $auth.Digest.RemoveHeader }} {{if $auth.Digest.Users }} users = [{{range $auth.Digest.Users }} "{{.}}", @@ -977,6 +981,7 @@ var _templatesEcsTmpl = []byte(`[backends] {{if $auth.Basic }} [frontends."frontend-{{ $serviceName }}".auth.basic] + removeHeader = {{ $auth.Basic.RemoveHeader }} {{if $auth.Basic.Users }} users = [{{range $auth.Basic.Users }} "{{.}}", @@ -987,6 +992,7 @@ var _templatesEcsTmpl = []byte(`[backends] {{if $auth.Digest }} [frontends."frontend-{{ $serviceName }}".auth.digest] + removeHeader = {{ $auth.Digest.RemoveHeader }} {{if $auth.Digest.Users }} users = [{{range $auth.Digest.Users }} "{{.}}", @@ -1217,6 +1223,7 @@ var _templatesKubernetesTmpl = []byte(`[backends] {{if $frontend.Auth.Basic }} [frontends."{{ $frontendName }}".auth.basic] + removeHeader = {{$frontend.Auth.Basic.RemoveHeader}} users = [{{range $frontend.Auth.Basic.Users }} "{{.}}", {{end}}] @@ -1224,6 +1231,7 @@ var _templatesKubernetesTmpl = []byte(`[backends] {{if $frontend.Auth.Digest }} [frontends."{{ $frontendName }}".auth.digest] + removeHeader = {{$frontend.Auth.Digest.RemoveHeader}} users = [{{range $frontend.Auth.Digest.Users }} "{{.}}", {{end}}] @@ -1466,6 +1474,7 @@ var _templatesKvTmpl = []byte(`[backends] {{if $auth.Basic }} [frontends."{{ $frontendName }}".auth.basic] + removeHeader = {{ $auth.Basic.RemoveHeader }} {{if $auth.Basic.Users }} users = [{{range $auth.Basic.Users }} "{{.}}", @@ -1476,6 +1485,7 @@ var _templatesKvTmpl = []byte(`[backends] {{if $auth.Digest }} [frontends."{{ $frontendName }}".auth.digest] + removeHeader = {{ $auth.Digest.RemoveHeader }} {{if $auth.Digest.Users }} users = [{{range $auth.Digest.Users }} "{{.}}", @@ -1806,6 +1816,7 @@ var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }} {{if $auth.Basic }} [frontends."{{ $frontendName }}".auth.basic] + removeHeader = {{ $auth.Basic.RemoveHeader }} {{if $auth.Basic.Users }} users = [{{range $auth.Basic.Users }} "{{.}}", @@ -1816,6 +1827,7 @@ var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }} {{if $auth.Digest }} [frontends."{{ $frontendName }}".auth.digest] + removeHeader = {{ $auth.Digest.RemoveHeader }} {{if $auth.Digest.Users }} users = [{{range $auth.Digest.Users }} "{{.}}", @@ -2090,6 +2102,7 @@ var _templatesMesosTmpl = []byte(`[backends] {{if $auth.Basic }} [frontends."frontend-{{ $frontendName }}".auth.basic] + removeHeader = {{ $auth.Basic.RemoveHeader}} {{if $auth.Basic.Users }} users = [{{range $auth.Basic.Users }} "{{.}}", @@ -2100,6 +2113,7 @@ var _templatesMesosTmpl = []byte(`[backends] {{if $auth.Digest }} [frontends."frontend-{{ $frontendName }}".auth.digest] + removeHeader = {{ $auth.Digest.RemoveHeader}} {{if $auth.Digest.Users }} users = [{{range $auth.Digest.Users }} "{{.}}", @@ -2427,6 +2441,7 @@ var _templatesRancherTmpl = []byte(`{{ $backendServers := .Backends }} {{if $auth.Basic }} [frontends."frontend-{{ $frontendName }}".auth.basic] + removeHeader = {{ $auth.Basic.RemoveHeader }} {{if $auth.Basic.Users }} users = [{{range $auth.Basic.Users }} "{{.}}", @@ -2437,6 +2452,7 @@ var _templatesRancherTmpl = []byte(`{{ $backendServers := .Backends }} {{if $auth.Digest }} [frontends."frontend-{{ $frontendName }}".auth.digest] + removeHeader = {{ $auth.Digest.RemoveHeader }} {{if $auth.Digest.Users }} users = [{{range $auth.Digest.Users }} "{{.}}", diff --git a/configuration/configuration.go b/configuration/configuration.go index 125f65c3a..0a347e334 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -58,19 +58,19 @@ const ( // GlobalConfiguration holds global configuration (with providers, etc.). // It's populated from the traefik configuration file passed as an argument to the binary. type GlobalConfiguration struct { - LifeCycle *LifeCycle `description:"Timeouts influencing the server life cycle" export:"true"` - GraceTimeOut flaeg.Duration `short:"g" description:"(Deprecated) Duration to give active requests a chance to finish before Traefik stops" export:"true"` // Deprecated - Debug bool `short:"d" description:"Enable debug mode" export:"true"` - CheckNewVersion bool `description:"Periodically check if a new version has been released" export:"true"` - SendAnonymousUsage bool `description:"send periodically anonymous usage statistics" export:"true"` - AccessLogsFile string `description:"(Deprecated) Access logs file" export:"true"` // Deprecated - AccessLog *types.AccessLog `description:"Access log settings" export:"true"` - TraefikLogsFile string `description:"(Deprecated) Traefik logs file. Stdout is used when omitted or empty" export:"true"` // Deprecated - TraefikLog *types.TraefikLog `description:"Traefik log settings" export:"true"` - Tracing *tracing.Tracing `description:"OpenTracing configuration" export:"true"` - LogLevel string `short:"l" description:"Log level" export:"true"` - EntryPoints EntryPoints `description:"Entrypoints definition using format: --entryPoints='Name:http Address::8000 Redirect.EntryPoint:https' --entryPoints='Name:https Address::4442 TLS:tests/traefik.crt,tests/traefik.key;prod/traefik.crt,prod/traefik.key'" export:"true"` - Cluster *types.Cluster `description:"Enable clustering" export:"true"` + LifeCycle *LifeCycle `description:"Timeouts influencing the server life cycle" export:"true"` + GraceTimeOut flaeg.Duration `short:"g" description:"(Deprecated) Duration to give active requests a chance to finish before Traefik stops" export:"true"` // Deprecated + Debug bool `short:"d" description:"Enable debug mode" export:"true"` + CheckNewVersion bool `description:"Periodically check if a new version has been released" export:"true"` + SendAnonymousUsage bool `description:"send periodically anonymous usage statistics" export:"true"` + AccessLogsFile string `description:"(Deprecated) Access logs file" export:"true"` // Deprecated + AccessLog *types.AccessLog `description:"Access log settings" export:"true"` + TraefikLogsFile string `description:"(Deprecated) Traefik logs file. Stdout is used when omitted or empty" export:"true"` // Deprecated + TraefikLog *types.TraefikLog `description:"Traefik log settings" export:"true"` + Tracing *tracing.Tracing `description:"OpenTracing configuration" export:"true"` + LogLevel string `short:"l" description:"Log level" export:"true"` + EntryPoints EntryPoints `description:"Entrypoints definition using format: --entryPoints='Name:http Address::8000 Redirect.EntryPoint:https' --entryPoints='Name:https Address::4442 TLS:tests/traefik.crt,tests/traefik.key;prod/traefik.crt,prod/traefik.key'" export:"true"` + Cluster *types.Cluster Constraints types.Constraints `description:"Filter services by constraint, matching with service tags" export:"true"` ACME *acme.ACME `description:"Enable ACME (Let's Encrypt): automatic SSL" export:"true"` DefaultEntryPoints DefaultEntryPoints `description:"Entrypoints to be used by frontends that do not specify any entrypoint" export:"true"` diff --git a/configuration/entrypoints.go b/configuration/entrypoints.go index 7a5f679b8..abb0eebc9 100644 --- a/configuration/entrypoints.go +++ b/configuration/entrypoints.go @@ -106,14 +106,16 @@ func makeEntryPointAuth(result map[string]string) *types.Auth { var basic *types.Basic if v, ok := result["auth_basic_users"]; ok { basic = &types.Basic{ - Users: strings.Split(v, ","), + Users: strings.Split(v, ","), + RemoveHeader: toBool(result, "auth_basic_removeheader"), } } var digest *types.Digest if v, ok := result["auth_digest_users"]; ok { digest = &types.Digest{ - Users: strings.Split(v, ","), + Users: strings.Split(v, ","), + RemoveHeader: toBool(result, "auth_digest_removeheader"), } } diff --git a/configuration/entrypoints_test.go b/configuration/entrypoints_test.go index 74911b994..214ace662 100644 --- a/configuration/entrypoints_test.go +++ b/configuration/entrypoints_test.go @@ -33,7 +33,9 @@ func Test_parseEntryPointsConfiguration(t *testing.T) { "ProxyProtocol.TrustedIPs:192.168.0.1 " + "ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24 " + "Auth.Basic.Users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0 " + + "Auth.Basic.RemoveHeader:true " + "Auth.Digest.Users:test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e " + + "Auth.Digest.RemoveHeader:true " + "Auth.HeaderField:X-WebAuth-User " + "Auth.Forward.Address:https://authserver.com/auth " + "Auth.Forward.AuthResponseHeaders:X-Auth,X-Test,X-Secret " + @@ -49,7 +51,9 @@ func Test_parseEntryPointsConfiguration(t *testing.T) { expectedResult: map[string]string{ "address": ":8000", "auth_basic_users": "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", + "auth_basic_removeheader": "true", "auth_digest_users": "test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e", + "auth_digest_removeheader": "true", "auth_forward_address": "https://authserver.com/auth", "auth_forward_authresponseheaders": "X-Auth,X-Test,X-Secret", "auth_forward_tls_ca": "path/to/local.crt", @@ -190,7 +194,9 @@ func TestEntryPoints_Set(t *testing.T) { "ProxyProtocol.TrustedIPs:192.168.0.1 " + "ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24 " + "Auth.Basic.Users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0 " + + "Auth.Basic.RemoveHeader:true " + "Auth.Digest.Users:test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e " + + "Auth.Digest.RemoveHeader:true " + "Auth.HeaderField:X-WebAuth-User " + "Auth.Forward.Address:https://authserver.com/auth " + "Auth.Forward.AuthResponseHeaders:X-Auth,X-Test,X-Secret " + @@ -232,12 +238,14 @@ func TestEntryPoints_Set(t *testing.T) { }, Auth: &types.Auth{ Basic: &types.Basic{ + RemoveHeader: true, Users: types.Users{ "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", }, }, Digest: &types.Digest{ + RemoveHeader: true, Users: types.Users{ "test:traefik:a2688e031edb4be6a3797f3882655c05", "test2:traefik:518845800f9e2bfb1f1f740ec24f074e", diff --git a/docs.Dockerfile b/docs.Dockerfile index cf0d30835..946c26391 100644 --- a/docs.Dockerfile +++ b/docs.Dockerfile @@ -1,11 +1,10 @@ -FROM alpine +FROM alpine:3.7 ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.local/bin COPY requirements.txt /mkdocs/ WORKDIR /mkdocs +VOLUME /mkdocs -RUN apk --update upgrade \ -&& apk --no-cache --no-progress add py-pip \ -&& rm -rf /var/cache/apk/* \ -&& pip install --user -r requirements.txt +RUN apk --no-cache --no-progress add py-pip \ + && pip install --user -r requirements.txt diff --git a/docs/configuration/acme.md b/docs/configuration/acme.md index fd4501bae..0ffee45cc 100644 --- a/docs/configuration/acme.md +++ b/docs/configuration/acme.md @@ -371,7 +371,7 @@ For example, the rule `Host:test1.traefik.io,test2.traefik.io` will request a ce !!! warning `onHostRule` option can not be used to generate wildcard certificates. - Refer to [wildcard generation](/configuration/acme/#wildcard-domain) for further information. + Refer to [wildcard generation](/configuration/acme/#wildcard-domains) for further information. ### `storage` diff --git a/docs/configuration/api.md b/docs/configuration/api.md index b2db8517a..eda514c98 100644 --- a/docs/configuration/api.md +++ b/docs/configuration/api.md @@ -21,7 +21,7 @@ # Enable debug mode. # This will install HTTP handlers to expose Go expvars under /debug/vars and - # pprof profiling data under /debug/pprof. + # pprof profiling data under /debug/pprof/. # Additionally, the log level will be set to DEBUG. # # Optional @@ -30,7 +30,7 @@ debug = true ``` -For more customization, see [entry points](/configuration/entrypoints/) documentation and [examples](/user-guide/examples/#ping-health-check). +For more customization, see [entry points](/configuration/entrypoints/) documentation and the examples below. ## Web UI diff --git a/docs/configuration/backends/consulcatalog.md b/docs/configuration/backends/consulcatalog.md index 7b34e9835..be3a6b9ea 100644 --- a/docs/configuration/backends/consulcatalog.md +++ b/docs/configuration/backends/consulcatalog.md @@ -118,8 +118,10 @@ Additional settings can be defined using Consul Catalog tags. | `.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | | `.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | | `.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). | +| `.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | | `.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. | | `.frontend.auth.basic.usersfile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | | `.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | | `.frontend.auth.digest.usersfile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | | `.frontend.auth.forward.address=https://example.com`| Sets the URL of the authentication server. | diff --git a/docs/configuration/backends/docker.md b/docs/configuration/backends/docker.md index b6092db66..6b89ff9fe 100644 --- a/docs/configuration/backends/docker.md +++ b/docs/configuration/backends/docker.md @@ -236,8 +236,10 @@ Labels can be used on containers to override default behavior. | `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | | `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | | `traefik.frontend.auth.basic=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` [2] (DEPRECATED). | +| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | | `traefik.frontend.auth.basic.users=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` [2]. | | `traefik.frontend.auth.basic.usersfile=/path/.htpasswd` | Sets the basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | | `traefik.frontend.auth.digest.users=EXPR` | Sets the digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | | `traefik.frontend.auth.digest.usersfile=/path/.htdigest` | Sets the digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | | `traefik.frontend.auth.forward.address=https://example.com`| Sets the URL of the authentication server. | @@ -326,8 +328,10 @@ Segment labels override the default behavior. | `traefik..protocol=http` | Same as `traefik.protocol` | | `traefik..weight=10` | Same as `traefik.weight` | | `traefik..frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` | +| `traefik..frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` | | `traefik..frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` | | `traefik..frontend.auth.basic.usersfile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersfile` | +| `traefik..frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` | | `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | | `traefik..frontend.auth.digest.usersfile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersfile` | | `traefik..frontend.auth.forward.address=https://example.com`| Same as `traefik.frontend.auth.forward.address` | diff --git a/docs/configuration/backends/ecs.md b/docs/configuration/backends/ecs.md index dbcaec8d4..7c248409b 100644 --- a/docs/configuration/backends/ecs.md +++ b/docs/configuration/backends/ecs.md @@ -163,8 +163,10 @@ Labels can be used on task containers to override default behaviour: | `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | | `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | | `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). | +| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | | `traefik.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. | | `traefik.frontend.auth.basic.usersfile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | | `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | | `traefik.frontend.auth.digest.usersfile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | | `traefik.frontend.auth.forward.address=https://example.com`| Sets the URL of the authentication server. | @@ -175,6 +177,7 @@ Labels can be used on task containers to override default behaviour: | `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | | `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | | `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. | +| `traefik.frontend.auth.removeHeader=true` | If set to true, removes the Authorization header. | | `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | | `traefik.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | | `traefik.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | diff --git a/docs/configuration/backends/file.md b/docs/configuration/backends/file.md index e85dd8a27..beb4e0b5e 100644 --- a/docs/configuration/backends/file.md +++ b/docs/configuration/backends/file.md @@ -65,12 +65,14 @@ Træfik can be configured with a file. [frontends.frontend1.auth] headerField = "X-WebAuth-User" [frontends.frontend1.auth.basic] + removeHeader = true users = [ "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", ] usersFile = "/path/to/.htpasswd" [frontends.frontend1.auth.digest] + removeHeader = true users = [ "test:traefik:a2688e031edb4be6a3797f3882655c05", "test2:traefik:518845800f9e2bfb1f1f740ec24f074e", diff --git a/docs/configuration/backends/kubernetes.md b/docs/configuration/backends/kubernetes.md index e11ca7325..b574942d1 100644 --- a/docs/configuration/backends/kubernetes.md +++ b/docs/configuration/backends/kubernetes.md @@ -155,7 +155,7 @@ The following general annotations are applicable on the Ingress object: | `traefik.ingress.kubernetes.io/redirect-replacement: http://mydomain/$1` | Redirect to another URL for that frontend. Must be set with `traefik.ingress.kubernetes.io/redirect-regex`. | | `traefik.ingress.kubernetes.io/rewrite-target: /users` | Replaces each matched Ingress path with the specified one, and adds the old path to the `X-Replaced-Path` header. | | `traefik.ingress.kubernetes.io/rule-type: PathPrefixStrip` | Override the default frontend rule type. Only path related matchers can be used [(`Path`, `PathPrefix`, `PathStrip`, `PathPrefixStrip`)](/basics/#path-matcher-usage-guidelines). Note: ReplacePath is deprecated in this annotation, use the `traefik.ingress.kubernetes.io/request-modifier` annotation instead. Default: `PathPrefix`. | -| `traefik.ingress.kubernetes.io/request-modifier: AddPath: /users` | Add a [request modifier](/basics/#modifiers) to the backend request. | +| `traefik.ingress.kubernetes.io/request-modifier: AddPrefix: /users` | Add a [request modifier](/basics/#modifiers) to the backend request. | | `traefik.ingress.kubernetes.io/whitelist-source-range: "1.2.3.0/24, fe80::/16"` | A comma-separated list of IP ranges permitted for access (6). | | `ingress.kubernetes.io/whitelist-x-forwarded-for: "true"` | Use `X-Forwarded-For` header as valid source of IP for the white list. | | `traefik.ingress.kubernetes.io/app-root: "/index.html"` | Redirects all requests for `/` to the defined path. (4) | @@ -205,8 +205,9 @@ retryexpression: IsNetworkError() && Attempts() <= 2 <4> `traefik.ingress.kubernetes.io/app-root`: Non-root paths will not be affected by this annotation and handled normally. -This annotation may not be combined with the `ReplacePath` rule type or any other annotation leveraging that rule type. -Trying to do so leads to an error and the corresponding Ingress object being ignored. +This annotation may not be combined with other redirect annotations. +Trying to do so will result in the other redirects being ignored. +This annotation can be used in combination with `traefik.ingress.kubernetes.io/redirect-permanent` to configure whether the `app-root` redirect is a 301 or a 302. <5> `traefik.ingress.kubernetes.io/service-weights`: Service weights enable to split traffic across multiple backing services in a fine-grained manner. @@ -303,6 +304,7 @@ The source of the authentication is a Secret object that contains the credential |----------------------------------------------------------------------|-------|--------|---------|-------------------------------------------------------------------------------------------------------------| | `ingress.kubernetes.io/auth-type: basic` | x | x | x | Contains the authentication type: `basic`, `digest`, `forward`. | | `ingress.kubernetes.io/auth-secret: mysecret` | x | x | | Name of Secret containing the username and password with access to the paths defined in the Ingress object. | +| `ingress.kubernetes.io/auth-remove-header: true` | x | x | | If set to `true` removes the `Authorization` header. | | `ingress.kubernetes.io/auth-header-field: X-WebAuth-User` | x | x | | Pass Authenticated user to application via headers. | | `ingress.kubernetes.io/auth-url: https://example.com` | | | x | [The URL of the authentication server](/configuration/entrypoints/#forward-authentication). | | `ingress.kubernetes.io/auth-trust-headers: false` | | | x | Trust `X-Forwarded-*` headers. | diff --git a/docs/configuration/backends/marathon.md b/docs/configuration/backends/marathon.md index 775de201a..e6ad58d19 100644 --- a/docs/configuration/backends/marathon.md +++ b/docs/configuration/backends/marathon.md @@ -221,10 +221,12 @@ The following labels can be defined on Marathon applications. They adjust the be | `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | | `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | | `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). | +| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | | `traefik.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. | -| `traefik.frontend.auth.basic.usersfile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.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). | @@ -233,6 +235,7 @@ The following labels can be defined on Marathon applications. They adjust the be | `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | | `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | | `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. | +| `traefik.frontend.auth.removeHeader=true` | If set to true, removes the Authorization header. | | `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | | `traefik.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | | `traefik.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | @@ -293,33 +296,48 @@ You can define as many segments as ports exposed in an application. Segment labels override the default behavior. -| Label | Description | -|---------------------------------------------------------------------------|-------------------------------------------------------------| -| `traefik..backend=BACKEND` | Same as `traefik.backend` | -| `traefik..domain=DOMAIN` | Same as `traefik.domain` | -| `traefik..portIndex=1` | Same as `traefik.portIndex` | -| `traefik..port=PORT` | Same as `traefik.port` | -| `traefik..protocol=http` | Same as `traefik.protocol` | -| `traefik..weight=10` | Same as `traefik.weight` | -| `traefik..frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` | -| `traefik..frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` | -| `traefik..frontend.errors..backend=NAME` | Same as `traefik.frontend.errors..backend` | -| `traefik..frontend.errors..query=PATH` | Same as `traefik.frontend.errors..query` | -| `traefik..frontend.errors..status=RANGE` | Same as `traefik.frontend.errors..status` | -| `traefik..frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` | -| `traefik..frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` | -| `traefik..frontend.priority=10` | Same as `traefik.frontend.priority` | -| `traefik..frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` | -| `traefik..frontend.rateLimit.rateSet..period=6` | Same as `traefik.frontend.rateLimit.rateSet..period` | -| `traefik..frontend.rateLimit.rateSet..average=6` | Same as `traefik.frontend.rateLimit.rateSet..average` | -| `traefik..frontend.rateLimit.rateSet..burst=6` | Same as `traefik.frontend.rateLimit.rateSet..burst` | -| `traefik..frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` | -| `traefik..frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` | -| `traefik..frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` | -| `traefik..frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` | -| `traefik..frontend.rule=EXP` | Same as `traefik.frontend.rule` | -| `traefik..frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` | -| `traefik..frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` | +| Label | Description | +|---------------------------------------------------------------------------|----------------------------------------------------------------| +| `traefik..backend=BACKEND` | Same as `traefik.backend` | +| `traefik..domain=DOMAIN` | Same as `traefik.domain` | +| `traefik..portIndex=1` | Same as `traefik.portIndex` | +| `traefik..port=PORT` | Same as `traefik.port` | +| `traefik..protocol=http` | Same as `traefik.protocol` | +| `traefik..weight=10` | Same as `traefik.weight` | +| `traefik..frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` | +| `traefik..frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` | +| `traefik..frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` | +| `traefik..frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` | +| `traefik..frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` | +| `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | +| `traefik..frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` | +| `traefik..frontend.auth.forward.address=https://example.com`| Same as `traefik.frontend.auth.forward.address` | +| `traefik..frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` | +| `traefik..frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` | +| `traefik..frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` | +| `traefik..frontend.auth.forward.tls.insecureSkipVerify=true`| Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` | +| `traefik..frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` | +| `traefik..frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` | +| `traefik..frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` | +| `traefik..frontend.auth.removeHeader=true` | Same as `traefik.frontend.auth.removeHeader` | +| `traefik..frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` | +| `traefik..frontend.errors..backend=NAME` | Same as `traefik.frontend.errors..backend` | +| `traefik..frontend.errors..query=PATH` | Same as `traefik.frontend.errors..query` | +| `traefik..frontend.errors..status=RANGE` | Same as `traefik.frontend.errors..status` | +| `traefik..frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` | +| `traefik..frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` | +| `traefik..frontend.priority=10` | Same as `traefik.frontend.priority` | +| `traefik..frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` | +| `traefik..frontend.rateLimit.rateSet..period=6` | Same as `traefik.frontend.rateLimit.rateSet..period` | +| `traefik..frontend.rateLimit.rateSet..average=6` | Same as `traefik.frontend.rateLimit.rateSet..average` | +| `traefik..frontend.rateLimit.rateSet..burst=6` | Same as `traefik.frontend.rateLimit.rateSet..burst` | +| `traefik..frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` | +| `traefik..frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` | +| `traefik..frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` | +| `traefik..frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` | +| `traefik..frontend.rule=EXP` | Same as `traefik.frontend.rule` | +| `traefik..frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` | +| `traefik..frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` | #### Custom Headers diff --git a/docs/configuration/backends/mesos.md b/docs/configuration/backends/mesos.md index 51734e523..e1b83e85a 100644 --- a/docs/configuration/backends/mesos.md +++ b/docs/configuration/backends/mesos.md @@ -135,9 +135,11 @@ The following labels can be defined on Mesos tasks. They adjust the behavior for | `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | | `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). | | `traefik.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. | -| `traefik.frontend.auth.basic.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.removeHeader=true` | If set to `true`, removes the `Authorization` header. | +| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | | `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | -| `traefik.frontend.auth.digest.usersfile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.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). | @@ -146,6 +148,7 @@ The following labels can be defined on Mesos tasks. They adjust the behavior for | `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. | | `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. | | `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. | +| `traefik.frontend.auth.removeHeader=true` | If set to true, removes the Authorization header. | | `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.
Overrides `defaultEntryPoints` | | `traefik.frontend.errors..backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | | `traefik.frontend.errors..query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. | @@ -207,34 +210,49 @@ Additionally, if a segment name matches a named port, that port will be used unl Segment labels override the default behavior. -| Label | Description | -|---------------------------------------------------------------------------|-------------------------------------------------------------| -| `traefik..backend=BACKEND` | Same as `traefik.backend` | -| `traefik..domain=DOMAIN` | Same as `traefik.domain` | -| `traefik..portIndex=1` | Same as `traefik.portIndex` | -| `traefik..portName=web` | Same as `traefik.portName` | -| `traefik..port=PORT` | Same as `traefik.port` | -| `traefik..protocol=http` | Same as `traefik.protocol` | -| `traefik..weight=10` | Same as `traefik.weight` | -| `traefik..frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` | -| `traefik..frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` | -| `traefik..frontend.errors..backend=NAME` | Same as `traefik.frontend.errors..backend` | -| `traefik..frontend.errors..query=PATH` | Same as `traefik.frontend.errors..query` | -| `traefik..frontend.errors..status=RANGE` | Same as `traefik.frontend.errors..status` | -| `traefik..frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` | -| `traefik..frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` | -| `traefik..frontend.priority=10` | Same as `traefik.frontend.priority` | -| `traefik..frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` | -| `traefik..frontend.rateLimit.rateSet..period=6` | Same as `traefik.frontend.rateLimit.rateSet..period` | -| `traefik..frontend.rateLimit.rateSet..average=6` | Same as `traefik.frontend.rateLimit.rateSet..average` | -| `traefik..frontend.rateLimit.rateSet..burst=6` | Same as `traefik.frontend.rateLimit.rateSet..burst` | -| `traefik..frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` | -| `traefik..frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` | -| `traefik..frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` | -| `traefik..frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` | -| `traefik..frontend.rule=EXP` | Same as `traefik.frontend.rule` | -| `traefik..frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` | -| `traefik..frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` | +| Label | Description | +|---------------------------------------------------------------------------|----------------------------------------------------------------| +| `traefik..backend=BACKEND` | Same as `traefik.backend` | +| `traefik..domain=DOMAIN` | Same as `traefik.domain` | +| `traefik..portIndex=1` | Same as `traefik.portIndex` | +| `traefik..portName=web` | Same as `traefik.portName` | +| `traefik..port=PORT` | Same as `traefik.port` | +| `traefik..protocol=http` | Same as `traefik.protocol` | +| `traefik..weight=10` | Same as `traefik.weight` | +| `traefik..frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` | +| `traefik..frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` | +| `traefik..frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` | +| `traefik..frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` | +| `traefik..frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` | +| `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | +| `traefik..frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` | +| `traefik..frontend.auth.forward.address=https://example.com`| Same as `traefik.frontend.auth.forward.address` | +| `traefik..frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` | +| `traefik..frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` | +| `traefik..frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` | +| `traefik..frontend.auth.forward.tls.insecureSkipVerify=true`| Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` | +| `traefik..frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` | +| `traefik..frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` | +| `traefik..frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` | +| `traefik..frontend.auth.removeHeader=true` | Same as `traefik.frontend.auth.removeHeader` | +| `traefik..frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` | +| `traefik..frontend.errors..backend=NAME` | Same as `traefik.frontend.errors..backend` | +| `traefik..frontend.errors..query=PATH` | Same as `traefik.frontend.errors..query` | +| `traefik..frontend.errors..status=RANGE` | Same as `traefik.frontend.errors..status` | +| `traefik..frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` | +| `traefik..frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` | +| `traefik..frontend.priority=10` | Same as `traefik.frontend.priority` | +| `traefik..frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` | +| `traefik..frontend.rateLimit.rateSet..period=6` | Same as `traefik.frontend.rateLimit.rateSet..period` | +| `traefik..frontend.rateLimit.rateSet..average=6` | Same as `traefik.frontend.rateLimit.rateSet..average` | +| `traefik..frontend.rateLimit.rateSet..burst=6` | Same as `traefik.frontend.rateLimit.rateSet..burst` | +| `traefik..frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` | +| `traefik..frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` | +| `traefik..frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` | +| `traefik..frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` | +| `traefik..frontend.rule=EXP` | Same as `traefik.frontend.rule` | +| `traefik..frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` | +| `traefik..frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` | #### Custom Headers diff --git a/docs/configuration/backends/rancher.md b/docs/configuration/backends/rancher.md index 74e8266fe..23d925ea6 100644 --- a/docs/configuration/backends/rancher.md +++ b/docs/configuration/backends/rancher.md @@ -142,7 +142,7 @@ Labels can be used on task containers to override default behavior: |------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `traefik.domain` | Sets the default domain for the frontend rules. | | `traefik.enable=false` | Disables this container in Træfik. | -| `traefik.port=80` | Registers this port. Useful when the container exposes multiple ports. | +| `traefik.port=80` | Registers this port. Useful when the container exposes multiple ports. | | `traefik.protocol=https` | Overrides the default `http` protocol. | | `traefik.weight=10` | Assigns this weight to the container. | | `traefik.backend=foo` | Gives the name `foo` to the generated backend for this container. | @@ -165,8 +165,10 @@ Labels can be used on task containers to override default behavior: | `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.
Must be used in conjunction with the below label to take effect. | | `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.
Must be used in conjunction with the above label to take effect. | | `traefik.frontend.auth.basic=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). | +| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. | | `traefik.frontend.auth.basic.users=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` . | | `traefik.frontend.auth.basic.usersfile=/path/.htpasswd` | Sets the basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | +| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. | | `traefik.frontend.auth.digest.users=EXPR` | Sets the digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. | | `traefik.frontend.auth.digest.usersfile=/path/.htdigest` | Sets the digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. | | `traefik.frontend.auth.forward.address=https://example.com`| Sets the URL of the authentication server. | @@ -244,8 +246,10 @@ Segment labels override the default behavior. | `traefik..protocol=http` | Same as `traefik.protocol` | | `traefik..weight=10` | Same as `traefik.weight` | | `traefik..frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` | +| `traefik..frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` | | `traefik..frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` | | `traefik..frontend.auth.basic.usersfile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersfile` | +| `traefik..frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` | | `traefik..frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` | | `traefik..frontend.auth.digest.usersfile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersfile` | | `traefik..frontend.auth.forward.address=https://example.com`| Same as `traefik.frontend.auth.forward.address` | diff --git a/docs/configuration/commons.md b/docs/configuration/commons.md index c7d80af00..082281892 100644 --- a/docs/configuration/commons.md +++ b/docs/configuration/commons.md @@ -18,7 +18,7 @@ # Enable debug mode. # This will install HTTP handlers to expose Go expvars under /debug/vars and -# pprof profiling data under /debug/pprof. +# pprof profiling data under /debug/pprof/. # The log level will be set to DEBUG unless `logLevel` is specified. # # Optional diff --git a/docs/configuration/entrypoints.md b/docs/configuration/entrypoints.md index 5704c27d2..1aa81b454 100644 --- a/docs/configuration/entrypoints.md +++ b/docs/configuration/entrypoints.md @@ -5,6 +5,11 @@ ### TOML ```toml +defaultEntryPoints = ["http", "https"] + +# ... +# ... + [entryPoints] [entryPoints.http] address = ":80" @@ -40,12 +45,14 @@ [entryPoints.http.auth] headerField = "X-WebAuth-User" [entryPoints.http.auth.basic] + removeHeader = true users = [ "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", ] usersFile = "/path/to/.htpasswd" [entryPoints.http.auth.digest] + removeHeader = true users = [ "test:traefik:a2688e031edb4be6a3797f3882655c05", "test2:traefik:518845800f9e2bfb1f1f740ec24f074e", @@ -127,7 +134,9 @@ ProxyProtocol.TrustedIPs:192.168.0.1 ProxyProtocol.Insecure:true ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24 Auth.Basic.Users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0 +Auth.Basic.Removeheader:true Auth.Digest.Users:test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e +Auth.Digest.Removeheader:true Auth.HeaderField:X-WebAuth-User Auth.Forward.Address:https://authserver.com/auth Auth.Forward.AuthResponseHeaders:X-Auth,X-Test,X-Secret @@ -275,18 +284,32 @@ Users can be specified directly in the TOML file, or indirectly by referencing a usersFile = "/path/to/.htpasswd" ``` -Optionally, you can pass authenticated user to application via headers +Optionally, you can: + +- pass authenticated user to application via headers ```toml [entryPoints] [entryPoints.http] address = ":80" [entryPoints.http.auth] - headerField = "X-WebAuth-User" # <-- + headerField = "X-WebAuth-User" # <-- header for the authenticated user [entryPoints.http.auth.basic] users = ["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"] ``` +- remove the Authorization header + +```toml +[entryPoints] + [entryPoints.http] + address = ":80" + [entryPoints.http.auth] + [entryPoints.http.auth.basic] + removeHeader = true # <-- remove the Authorization header + users = ["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"] +``` + ### Digest Authentication You can use `htdigest` to generate them. @@ -304,18 +327,32 @@ Users can be specified directly in the TOML file, or indirectly by referencing a usersFile = "/path/to/.htdigest" ``` -Optionally, you can pass authenticated user to application via headers +Optionally, you can! + +- pass authenticated user to application via headers. ```toml [entryPoints] [entryPoints.http] address = ":80" [entryPoints.http.auth] - headerField = "X-WebAuth-User" # <-- + headerField = "X-WebAuth-User" # <-- header for the authenticated user [entryPoints.http.auth.digest] users = ["test:traefik:a2688e031edb4be6a3797f3882655c05", "test2:traefik:518845800f9e2bfb1f1f740ec24f074e"] ``` +- remove the Authorization header. + +```toml +[entryPoints] + [entryPoints.http] + address = ":80" + [entryPoints.http.auth] + [entryPoints.http.auth.digest] + removeHeader = true # <-- remove the Authorization header + users = ["test:traefik:a2688e031edb4be6a3797f3882655c05", "test2:traefik:518845800f9e2bfb1f1f740ec24f074e"] +``` + ### Forward Authentication This configuration will first forward the request to `http://authserver.com/auth`. diff --git a/docs/index.md b/docs/index.md index 52f85d639..e466c3f6d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,7 +3,7 @@

[![Build Status SemaphoreCI](https://semaphoreci.com/api/v1/containous/traefik/branches/master/shields_badge.svg)](https://semaphoreci.com/containous/traefik) -[![Docs](https://img.shields.io/badge/docs-current-brightgreen.svg)](https://docs.traefik.io) +[![Docs](https://img.shields.io/badge/docs-current-brightgreen.svg)](/) [![Go Report Card](https://goreportcard.com/badge/github.com/containous/traefik)](https://goreportcard.com/report/github.com/containous/traefik) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/containous/traefik/blob/master/LICENSE.md) [![Join the chat at https://slack.traefik.io](https://img.shields.io/badge/style-register-green.svg?style=social&label=Slack)](https://slack.traefik.io) diff --git a/docs/user-guide/cluster.md b/docs/user-guide/cluster.md index 111527641..8c03d2798 100644 --- a/docs/user-guide/cluster.md +++ b/docs/user-guide/cluster.md @@ -26,8 +26,8 @@ If this instance fails, another manager will be automatically elected. ## Træfik cluster and Let's Encrypt -**In cluster mode, ACME certificates have to be stored in [a KV Store entry](/configuration/acme/#storage-kv-entry).** +**In cluster mode, ACME certificates have to be stored in [a KV Store entry](/configuration/acme/#as-a-key-value-store-entry).** Thanks to the Træfik cluster mode algorithm (based on [the Raft Consensus Algorithm](https://raft.github.io/)), only one instance will contact Let's encrypt to solve the challenges. -The others instances will get ACME certificate from the KV Store entry. \ No newline at end of file +The others instances will get ACME certificate from the KV Store entry. diff --git a/docs/user-guide/examples.md b/docs/user-guide/examples.md index 70a6f5895..51bc7c1be 100644 --- a/docs/user-guide/examples.md +++ b/docs/user-guide/examples.md @@ -223,7 +223,7 @@ These variables have to be set on the machine/container that host Træfik. These variables are described [in this section](/configuration/acme/#provider). -More information about wildcard certificates are available [in this section](/configuration/acme/#wildcard-domain). +More information about wildcard certificates are available [in this section](/configuration/acme/#wildcard-domains). ### onHostRule option and provided certificates (with HTTP challenge) diff --git a/docs/user-guide/grpc.md b/docs/user-guide/grpc.md index a4bda1bc7..55ba25bc9 100644 --- a/docs/user-guide/grpc.md +++ b/docs/user-guide/grpc.md @@ -45,9 +45,7 @@ We don't need specific configuration to use gRPC in Træfik, we just need to use This section explains how to use Traefik as reverse proxy for gRPC application with self-signed certificates. -

-gRPC architecture -

+![gRPC architecture](/img/grpc.svg) ### gRPC Server certificate diff --git a/docs/user-guide/kv-config.md b/docs/user-guide/kv-config.md index 3b8ad9661..7fe28ee73 100644 --- a/docs/user-guide/kv-config.md +++ b/docs/user-guide/kv-config.md @@ -56,7 +56,7 @@ whoami4: ### Upload the configuration in the Key-value store -We should now fill the store with the Træfik global configuration, as we do with a [TOML file configuration](/toml). +We should now fill the store with the Træfik global configuration. To do that, we can send the Key-value pairs via [curl commands](https://www.consul.io/intro/getting-started/kv.html) or via the [Web UI](https://www.consul.io/intro/getting-started/ui.html). Fortunately, Træfik allows automation of this process using the `storeconfig` subcommand. @@ -445,4 +445,4 @@ Then remove the line `storageFile = "acme.json"` from your TOML config file. That's it! -![](https://i.giphy.com/ujUdrdpX7Ok5W.gif) +![GIF Magica](https://i.giphy.com/ujUdrdpX7Ok5W.gif) diff --git a/docs/user-guide/marathon.md b/docs/user-guide/marathon.md index 961687575..d437ff1d5 100644 --- a/docs/user-guide/marathon.md +++ b/docs/user-guide/marathon.md @@ -30,7 +30,7 @@ Following is the order by which Traefik tries to identify the port (the first on ## Applications with multiple ports -Some Marathon applications may expose multiple ports. Traefik supports creating one so-called _service_ per port using [specific labels](/configuration/backends/marathon#service-level). +Some Marathon applications may expose multiple ports. Traefik supports creating one so-called _segment_ per port using [segment labels](/configuration/backends/marathon#applications-with-multiple-ports-segment-labels). For instance, assume that a Marathon application exposes a web API on port 80 and an admin interface on port 8080. It would then be possible to make each service available by specifying the following Marathon labels: diff --git a/docs/user-guide/swarm-mode.md b/docs/user-guide/swarm-mode.md index 3d713127c..0ffbbf799 100644 --- a/docs/user-guide/swarm-mode.md +++ b/docs/user-guide/swarm-mode.md @@ -330,4 +330,4 @@ X-Forwarded-Proto: http X-Forwarded-Server: 77fc29c69fe4 ``` -![](https://i.giphy.com/ujUdrdpX7Ok5W.gif) +![GIF Magica](https://i.giphy.com/ujUdrdpX7Ok5W.gif) diff --git a/docs/user-guide/swarm.md b/docs/user-guide/swarm.md index 638a11300..fa018476e 100644 --- a/docs/user-guide/swarm.md +++ b/docs/user-guide/swarm.md @@ -178,4 +178,4 @@ X-Forwarded-Proto: http X-Forwarded-Server: 8fbc39271b4c ``` -![](https://i.giphy.com/ujUdrdpX7Ok5W.gif) +![GIF Magica](https://i.giphy.com/ujUdrdpX7Ok5W.gif) diff --git a/integration/acme_test.go b/integration/acme_test.go index 7aba14da6..6f10f81e8 100644 --- a/integration/acme_test.go +++ b/integration/acme_test.go @@ -122,7 +122,7 @@ func (s *AcmeSuite) TearDownSuite(c *check.C) { func (s *AcmeSuite) TestHTTP01DomainsAtStart(c *check.C) { testCase := acmeTestCase{ - traefikConfFilePath: "fixtures/acme/acme-base.toml", + traefikConfFilePath: "fixtures/acme/acme_base.toml", template: templateModel{ Acme: acme.Configuration{ HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, @@ -140,7 +140,7 @@ func (s *AcmeSuite) TestHTTP01DomainsAtStart(c *check.C) { func (s *AcmeSuite) TestHTTP01DomainsInSANAtStart(c *check.C) { testCase := acmeTestCase{ - traefikConfFilePath: "fixtures/acme/acme-base.toml", + traefikConfFilePath: "fixtures/acme/acme_base.toml", template: templateModel{ Acme: acme.Configuration{ HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, @@ -159,7 +159,7 @@ func (s *AcmeSuite) TestHTTP01DomainsInSANAtStart(c *check.C) { func (s *AcmeSuite) TestHTTP01OnHostRule(c *check.C) { testCase := acmeTestCase{ - traefikConfFilePath: "fixtures/acme/acme-base.toml", + traefikConfFilePath: "fixtures/acme/acme_base.toml", template: templateModel{ Acme: acme.Configuration{ HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, @@ -175,7 +175,7 @@ func (s *AcmeSuite) TestHTTP01OnHostRule(c *check.C) { func (s *AcmeSuite) TestHTTP01OnHostRuleECDSA(c *check.C) { testCase := acmeTestCase{ - traefikConfFilePath: "fixtures/acme/acme-base.toml", + traefikConfFilePath: "fixtures/acme/acme_base.toml", template: templateModel{ Acme: acme.Configuration{ HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, @@ -192,7 +192,7 @@ func (s *AcmeSuite) TestHTTP01OnHostRuleECDSA(c *check.C) { func (s *AcmeSuite) TestHTTP01OnHostRuleInvalidAlgo(c *check.C) { testCase := acmeTestCase{ - traefikConfFilePath: "fixtures/acme/acme-base.toml", + traefikConfFilePath: "fixtures/acme/acme_base.toml", template: templateModel{ Acme: acme.Configuration{ HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, @@ -257,7 +257,7 @@ func (s *AcmeSuite) TestHTTP01OnHostRuleDynamicCertificatesWithWildcard(c *check func (s *AcmeSuite) TestHTTP01OnDemand(c *check.C) { testCase := acmeTestCase{ - traefikConfFilePath: "fixtures/acme/acme-base.toml", + traefikConfFilePath: "fixtures/acme/acme_base.toml", template: templateModel{ Acme: acme.Configuration{ HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, @@ -305,7 +305,7 @@ func (s *AcmeSuite) TestHTTP01OnDemandDynamicCertificatesWithWildcard(c *check.C func (s *AcmeSuite) TestTLSALPN01OnHostRule(c *check.C) { testCase := acmeTestCase{ - traefikConfFilePath: "fixtures/acme/acme-base.toml", + traefikConfFilePath: "fixtures/acme/acme_base.toml", template: templateModel{ Acme: acme.Configuration{ TLSChallenge: &acme.TLSChallenge{}, @@ -321,7 +321,7 @@ func (s *AcmeSuite) TestTLSALPN01OnHostRule(c *check.C) { func (s *AcmeSuite) TestTLSALPN01OnDemand(c *check.C) { testCase := acmeTestCase{ - traefikConfFilePath: "fixtures/acme/acme-base.toml", + traefikConfFilePath: "fixtures/acme/acme_base.toml", template: templateModel{ Acme: acme.Configuration{ TLSChallenge: &acme.TLSChallenge{}, @@ -337,7 +337,7 @@ func (s *AcmeSuite) TestTLSALPN01OnDemand(c *check.C) { func (s *AcmeSuite) TestTLSALPN01DomainsAtStart(c *check.C) { testCase := acmeTestCase{ - traefikConfFilePath: "fixtures/acme/acme-base.toml", + traefikConfFilePath: "fixtures/acme/acme_base.toml", template: templateModel{ Acme: acme.Configuration{ TLSChallenge: &acme.TLSChallenge{}, @@ -355,7 +355,7 @@ func (s *AcmeSuite) TestTLSALPN01DomainsAtStart(c *check.C) { func (s *AcmeSuite) TestTLSALPN01DomainsInSANAtStart(c *check.C) { testCase := acmeTestCase{ - traefikConfFilePath: "fixtures/acme/acme-base.toml", + traefikConfFilePath: "fixtures/acme/acme_base.toml", template: templateModel{ Acme: acme.Configuration{ TLSChallenge: &acme.TLSChallenge{}, @@ -372,9 +372,27 @@ func (s *AcmeSuite) TestTLSALPN01DomainsInSANAtStart(c *check.C) { s.retrieveAcmeCertificate(c, testCase) } +func (s *AcmeSuite) TestTLSALPN01DomainsWithProvidedWildcardDomainAtStart(c *check.C) { + testCase := acmeTestCase{ + traefikConfFilePath: "fixtures/acme/acme_tls.toml", + template: templateModel{ + Acme: acme.Configuration{ + TLSChallenge: &acme.TLSChallenge{}, + Domains: types.Domains{types.Domain{ + Main: "traefik.acme.wtf", + }}, + }, + }, + expectedCommonName: "traefik.acme.wtf", + expectedAlgorithm: x509.RSA, + } + + s.retrieveAcmeCertificate(c, testCase) +} + // Test Let's encrypt down func (s *AcmeSuite) TestNoValidLetsEncryptServer(c *check.C) { - file := s.adaptFile(c, "fixtures/acme/acme-base.toml", templateModel{ + file := s.adaptFile(c, "fixtures/acme/acme_base.toml", templateModel{ Acme: acme.Configuration{ CAServer: "http://wrongurl:4001/directory", HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"}, diff --git a/integration/fixtures/acme/acme-base.toml b/integration/fixtures/acme/acme_base.toml similarity index 100% rename from integration/fixtures/acme/acme-base.toml rename to integration/fixtures/acme/acme_base.toml diff --git a/integration/fixtures/acme/acme_tls.toml b/integration/fixtures/acme/acme_tls.toml index 29532fe94..061554ff5 100644 --- a/integration/fixtures/acme/acme_tls.toml +++ b/integration/fixtures/acme/acme_tls.toml @@ -27,6 +27,10 @@ defaultEntryPoints = ["http", "https"] entryPoint = "{{ .Acme.HTTPChallenge.EntryPoint }}" {{end}} + {{if .Acme.TLSChallenge }} + [acme.tlsChallenge] + {{end}} + {{range .Acme.Domains}} [[acme.domains]] main = "{{ .Main }}" diff --git a/middlewares/auth/authenticator.go b/middlewares/auth/authenticator.go index 20e29635c..ea223049e 100644 --- a/middlewares/auth/authenticator.go +++ b/middlewares/auth/authenticator.go @@ -26,6 +26,10 @@ type tracingAuthenticator struct { clientSpanKind bool } +const ( + authorizationHeader = "Authorization" +) + // NewAuthenticator builds a new Authenticator given a config func NewAuthenticator(authConfig *types.Auth, tracingMiddleware *tracing.Tracing) (*Authenticator, error) { if authConfig == nil { @@ -86,6 +90,10 @@ func createAuthDigestHandler(digestAuth *goauth.DigestAuth, authConfig *types.Au if authConfig.HeaderField != "" { r.Header[authConfig.HeaderField] = []string{username} } + if authConfig.Digest.RemoveHeader { + log.Debugf("Remove the Authorization header from the Digest auth") + r.Header.Del(authorizationHeader) + } next.ServeHTTP(w, r) } }) @@ -101,6 +109,10 @@ func createAuthBasicHandler(basicAuth *goauth.BasicAuth, authConfig *types.Auth) if authConfig.HeaderField != "" { r.Header[authConfig.HeaderField] = []string{username} } + if authConfig.Basic.RemoveHeader { + log.Debugf("Remove the Authorization header from the Basic auth") + r.Header.Del(authorizationHeader) + } next.ServeHTTP(w, r) } }) diff --git a/middlewares/auth/authenticator_test.go b/middlewares/auth/authenticator_test.go index 0de79dfb2..360148600 100644 --- a/middlewares/auth/authenticator_test.go +++ b/middlewares/auth/authenticator_test.go @@ -12,6 +12,7 @@ import ( "github.com/containous/traefik/testhelpers" "github.com/containous/traefik/types" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/urfave/negroni" ) @@ -51,13 +52,16 @@ func TestAuthUsersFromFile(t *testing.T) { t.Run(test.authType, func(t *testing.T) { t.Parallel() usersFile, err := ioutil.TempFile("", "auth-users") - assert.NoError(t, err, "there should be no error") + require.NoError(t, err) defer os.Remove(usersFile.Name()) + _, err = usersFile.Write([]byte(test.usersStr)) - assert.NoError(t, err, "there should be no error") + require.NoError(t, err) + users, err := test.parserFunc(usersFile.Name()) - assert.NoError(t, err, "there should be no error") + require.NoError(t, err) assert.Equal(t, 2, len(users), "they should be equal") + _, ok := users[test.userKeys[0]] assert.True(t, ok, "user test should be found") _, ok = users[test.userKeys[1]] @@ -79,7 +83,7 @@ func TestBasicAuthFail(t *testing.T) { Users: []string{"test:test"}, }, }, &tracing.Tracing{}) - assert.NoError(t, err, "there should be no error") + require.NoError(t, err) handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "traefik") @@ -93,7 +97,7 @@ func TestBasicAuthFail(t *testing.T) { req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil) req.SetBasicAuth("test", "test") res, err := client.Do(req) - assert.NoError(t, err, "there should be no error") + require.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, res.StatusCode, "they should be equal") } @@ -103,7 +107,7 @@ func TestBasicAuthSuccess(t *testing.T) { Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"}, }, }, &tracing.Tracing{}) - assert.NoError(t, err, "there should be no error") + require.NoError(t, err) handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "traefik") @@ -117,11 +121,11 @@ func TestBasicAuthSuccess(t *testing.T) { req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil) req.SetBasicAuth("test", "test") res, err := client.Do(req) - assert.NoError(t, err, "there should be no error") + require.NoError(t, err) assert.Equal(t, http.StatusOK, res.StatusCode, "they should be equal") body, err := ioutil.ReadAll(res.Body) - assert.NoError(t, err, "there should be no error") + require.NoError(t, err) assert.Equal(t, "traefik\n", string(body), "they should be equal") } @@ -138,7 +142,7 @@ func TestDigestAuthFail(t *testing.T) { Users: []string{"test:traefik:test"}, }, }, &tracing.Tracing{}) - assert.NoError(t, err, "there should be no error") + require.NoError(t, err) assert.NotNil(t, authMiddleware, "this should not be nil") handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -153,7 +157,7 @@ func TestDigestAuthFail(t *testing.T) { req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil) req.SetBasicAuth("test", "test") res, err := client.Do(req) - assert.NoError(t, err, "there should be no error") + require.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, res.StatusCode, "they should be equal") } @@ -164,7 +168,7 @@ func TestBasicAuthUserHeader(t *testing.T) { }, HeaderField: "X-Webauth-User", }, &tracing.Tracing{}) - assert.NoError(t, err, "there should be no error") + require.NoError(t, err) handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { assert.Equal(t, "test", r.Header["X-Webauth-User"][0], "auth user should be set") @@ -179,11 +183,72 @@ func TestBasicAuthUserHeader(t *testing.T) { req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil) req.SetBasicAuth("test", "test") res, err := client.Do(req) - assert.NoError(t, err, "there should be no error") + require.NoError(t, err) assert.Equal(t, http.StatusOK, res.StatusCode, "they should be equal") body, err := ioutil.ReadAll(res.Body) - assert.NoError(t, err, "there should be no error") + require.NoError(t, err) + assert.Equal(t, "traefik\n", string(body), "they should be equal") +} + +func TestBasicAuthHeaderRemoved(t *testing.T) { + middleware, err := NewAuthenticator(&types.Auth{ + Basic: &types.Basic{ + RemoveHeader: true, + Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"}, + }, + }, &tracing.Tracing{}) + require.NoError(t, err) + + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Empty(t, r.Header.Get(authorizationHeader)) + fmt.Fprintln(w, "traefik") + }) + n := negroni.New(middleware) + n.UseHandler(handler) + ts := httptest.NewServer(n) + defer ts.Close() + + client := &http.Client{} + req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil) + req.SetBasicAuth("test", "test") + res, err := client.Do(req) + require.NoError(t, err) + + assert.Equal(t, http.StatusOK, res.StatusCode, "they should be equal") + + body, err := ioutil.ReadAll(res.Body) + require.NoError(t, err) + assert.Equal(t, "traefik\n", string(body), "they should be equal") +} + +func TestBasicAuthHeaderPresent(t *testing.T) { + middleware, err := NewAuthenticator(&types.Auth{ + Basic: &types.Basic{ + Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"}, + }, + }, &tracing.Tracing{}) + require.NoError(t, err) + + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.NotEmpty(t, r.Header.Get(authorizationHeader)) + fmt.Fprintln(w, "traefik") + }) + n := negroni.New(middleware) + n.UseHandler(handler) + ts := httptest.NewServer(n) + defer ts.Close() + + client := &http.Client{} + req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil) + req.SetBasicAuth("test", "test") + res, err := client.Do(req) + require.NoError(t, err) + + assert.Equal(t, http.StatusOK, res.StatusCode, "they should be equal") + + body, err := ioutil.ReadAll(res.Body) + require.NoError(t, err) assert.Equal(t, "traefik\n", string(body), "they should be equal") } diff --git a/middlewares/retry.go b/middlewares/retry.go index e4146e9bb..6ecbdf1cd 100644 --- a/middlewares/retry.go +++ b/middlewares/retry.go @@ -137,7 +137,7 @@ func (rr *retryResponseWriterWithoutCloseNotify) WriteHeader(code int) { if rr.ShouldRetry() && code == http.StatusServiceUnavailable { // We get a 503 HTTP Status Code when there is no backend server in the pool // to which the request could be sent. Also, note that rr.ShouldRetry() - // will never return true in case there was a connetion established to + // will never return true in case there was a connection established to // the backend server and so we can be sure that the 503 was produced // inside Traefik already and we don't have to retry in this cases. rr.DisableRetries() diff --git a/provider/consulcatalog/config_test.go b/provider/consulcatalog/config_test.go index 70bc96f5d..332a14dee 100644 --- a/provider/consulcatalog/config_test.go +++ b/provider/consulcatalog/config_test.go @@ -188,6 +188,7 @@ func TestProviderBuildConfiguration(t *testing.T) { ServiceName: "test", Attributes: []string{ "random.foo=bar", + label.TraefikFrontendAuthDigestRemoveHeader + "=true", label.TraefikFrontendAuthDigestUsers + "=test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", label.TraefikFrontendAuthDigestUsersFile + "=.htpasswd", }, @@ -224,6 +225,7 @@ func TestProviderBuildConfiguration(t *testing.T) { }, Auth: &types.Auth{ Digest: &types.Digest{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", @@ -348,8 +350,10 @@ func TestProviderBuildConfiguration(t *testing.T) { label.TraefikBackendBufferingRetryExpression + "=IsNetworkError() && Attempts() <= 2", 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", label.TraefikFrontendAuthBasicUsersFile + "=.htpasswd", + label.TraefikFrontendAuthDigestRemoveHeader + "=true", label.TraefikFrontendAuthDigestUsers + "=test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", label.TraefikFrontendAuthDigestUsersFile + "=.htpasswd", label.TraefikFrontendAuthForwardAddress + "=auth.server", @@ -464,6 +468,7 @@ func TestProviderBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Basic: &types.Basic{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", diff --git a/provider/docker/config_container_docker_test.go b/provider/docker/config_container_docker_test.go index 7d284d579..457a195ce 100644 --- a/provider/docker/config_container_docker_test.go +++ b/provider/docker/config_container_docker_test.go @@ -69,8 +69,9 @@ func TestDockerBuildConfiguration(t *testing.T) { containerJSON( name("test"), labels(map[string]string{ - label.TraefikFrontendAuthBasicUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", - label.TraefikFrontendAuthBasicUsersFile: ".htpasswd", + label.TraefikFrontendAuthBasicUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", + label.TraefikFrontendAuthBasicUsersFile: ".htpasswd", + label.TraefikFrontendAuthBasicRemoveHeader: "true", }), ports(nat.PortMap{ "80/tcp": {}, @@ -85,6 +86,7 @@ func TestDockerBuildConfiguration(t *testing.T) { EntryPoints: []string{}, Auth: &types.Auth{ Basic: &types.Basic{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", @@ -159,8 +161,9 @@ func TestDockerBuildConfiguration(t *testing.T) { containerJSON( name("test"), labels(map[string]string{ - label.TraefikFrontendAuthDigestUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", - label.TraefikFrontendAuthDigestUsersFile: ".htpasswd", + label.TraefikFrontendAuthDigestRemoveHeader: "true", + label.TraefikFrontendAuthDigestUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", + label.TraefikFrontendAuthDigestUsersFile: ".htpasswd", }), ports(nat.PortMap{ "80/tcp": {}, @@ -175,6 +178,7 @@ func TestDockerBuildConfiguration(t *testing.T) { EntryPoints: []string{}, Auth: &types.Auth{ Digest: &types.Digest{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", @@ -385,8 +389,10 @@ func TestDockerBuildConfiguration(t *testing.T) { label.TraefikBackendBufferingRetryExpression: "IsNetworkError() && Attempts() <= 2", 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", label.TraefikFrontendAuthBasicUsersFile: ".htpasswd", + label.TraefikFrontendAuthDigestRemoveHeader: "true", label.TraefikFrontendAuthDigestUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", label.TraefikFrontendAuthDigestUsersFile: ".htpasswd", label.TraefikFrontendAuthForwardAddress: "auth.server", @@ -472,6 +478,7 @@ func TestDockerBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Basic: &types.Basic{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", diff --git a/provider/docker/config_container_swarm_test.go b/provider/docker/config_container_swarm_test.go index 4c9676951..ced5b0dc2 100644 --- a/provider/docker/config_container_swarm_test.go +++ b/provider/docker/config_container_swarm_test.go @@ -99,9 +99,10 @@ func TestSwarmBuildConfiguration(t *testing.T) { swarmService( serviceName("test"), serviceLabels(map[string]string{ - label.TraefikPort: "80", - label.TraefikFrontendAuthBasicUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", - label.TraefikFrontendAuthBasicUsersFile: ".htpasswd", + label.TraefikPort: "80", + label.TraefikFrontendAuthBasicUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", + label.TraefikFrontendAuthBasicUsersFile: ".htpasswd", + label.TraefikFrontendAuthBasicRemoveHeader: "true", }), withEndpointSpec(modeVIP), withEndpoint(virtualIP("1", "127.0.0.1/24")), @@ -114,6 +115,7 @@ func TestSwarmBuildConfiguration(t *testing.T) { EntryPoints: []string{}, Auth: &types.Auth{ Basic: &types.Basic{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", @@ -195,9 +197,10 @@ func TestSwarmBuildConfiguration(t *testing.T) { swarmService( serviceName("test"), serviceLabels(map[string]string{ - label.TraefikPort: "80", - label.TraefikFrontendAuthDigestUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", - label.TraefikFrontendAuthDigestUsersFile: ".htpasswd", + label.TraefikPort: "80", + label.TraefikFrontendAuthDigestUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", + label.TraefikFrontendAuthDigestUsersFile: ".htpasswd", + label.TraefikFrontendAuthDigestRemoveHeader: "true", }), withEndpointSpec(modeVIP), withEndpoint(virtualIP("1", "127.0.0.1/24")), @@ -210,6 +213,7 @@ func TestSwarmBuildConfiguration(t *testing.T) { EntryPoints: []string{}, Auth: &types.Auth{ Digest: &types.Digest{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", @@ -329,8 +333,10 @@ func TestSwarmBuildConfiguration(t *testing.T) { label.TraefikBackendBufferingMemRequestBodyBytes: "2097152", label.TraefikBackendBufferingRetryExpression: "IsNetworkError() && Attempts() <= 2", + label.TraefikFrontendAuthBasicRemoveHeader: "true", label.TraefikFrontendAuthBasicUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", label.TraefikFrontendAuthBasicUsersFile: ".htpasswd", + label.TraefikFrontendAuthDigestRemoveHeader: "true", label.TraefikFrontendAuthDigestUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", label.TraefikFrontendAuthDigestUsersFile: ".htpasswd", label.TraefikFrontendAuthForwardAddress: "auth.server", @@ -414,6 +420,7 @@ func TestSwarmBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Basic: &types.Basic{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", diff --git a/provider/docker/config_segment_test.go b/provider/docker/config_segment_test.go index 210663e9e..1452b5fb3 100644 --- a/provider/docker/config_segment_test.go +++ b/provider/docker/config_segment_test.go @@ -71,11 +71,12 @@ func TestSegmentBuildConfiguration(t *testing.T) { containerJSON( name("foo"), labels(map[string]string{ - "traefik.sauternes.port": "2503", - "traefik.sauternes.frontend.entryPoints": "http,https", - label.Prefix + "sauternes." + label.SuffixFrontendAuthHeaderField: "X-WebAuth-User", - label.Prefix + "sauternes." + label.SuffixFrontendAuthBasicUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", - label.Prefix + "sauternes." + label.SuffixFrontendAuthBasicUsersFile: ".htpasswd", + "traefik.sauternes.port": "2503", + "traefik.sauternes.frontend.entryPoints": "http,https", + label.Prefix + "sauternes." + label.SuffixFrontendAuthHeaderField: "X-WebAuth-User", + label.Prefix + "sauternes." + label.SuffixFrontendAuthBasicUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", + label.Prefix + "sauternes." + label.SuffixFrontendAuthBasicUsersFile: ".htpasswd", + label.Prefix + "sauternes." + label.SuffixFrontendAuthBasicRemoveHeader: "true", }), ports(nat.PortMap{ "80/tcp": {}, @@ -96,6 +97,7 @@ func TestSegmentBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Basic: &types.Basic{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", @@ -167,11 +169,12 @@ func TestSegmentBuildConfiguration(t *testing.T) { containerJSON( name("foo"), labels(map[string]string{ - "traefik.sauternes.port": "2503", - "traefik.sauternes.frontend.entryPoints": "http,https", - label.Prefix + "sauternes." + label.SuffixFrontendAuthHeaderField: "X-WebAuth-User", - label.Prefix + "sauternes." + label.SuffixFrontendAuthDigestUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", - label.Prefix + "sauternes." + label.SuffixFrontendAuthDigestUsersFile: ".htpasswd", + "traefik.sauternes.port": "2503", + "traefik.sauternes.frontend.entryPoints": "http,https", + label.Prefix + "sauternes." + label.SuffixFrontendAuthHeaderField: "X-WebAuth-User", + label.Prefix + "sauternes." + label.SuffixFrontendAuthDigestUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", + label.Prefix + "sauternes." + label.SuffixFrontendAuthDigestUsersFile: ".htpasswd", + label.Prefix + "sauternes." + label.SuffixFrontendAuthDigestRemoveHeader: "true", }), ports(nat.PortMap{ "80/tcp": {}, @@ -192,6 +195,7 @@ func TestSegmentBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Digest: &types.Digest{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", @@ -282,8 +286,10 @@ func TestSegmentBuildConfiguration(t *testing.T) { label.Prefix + "sauternes." + label.SuffixProtocol: "https", label.Prefix + "sauternes." + label.SuffixWeight: "12", + 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", + label.Prefix + "sauternes." + label.SuffixFrontendAuthDigestRemoveHeader: "true", label.Prefix + "sauternes." + label.SuffixFrontendAuthDigestUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", label.Prefix + "sauternes." + label.SuffixFrontendAuthDigestUsersFile: ".htpasswd", label.Prefix + "sauternes." + label.SuffixFrontendAuthForwardAddress: "auth.server", @@ -364,6 +370,7 @@ func TestSegmentBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Basic: &types.Basic{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", diff --git a/provider/ecs/config_test.go b/provider/ecs/config_test.go index 57c0ecd85..2e0b620c7 100644 --- a/provider/ecs/config_test.go +++ b/provider/ecs/config_test.go @@ -114,9 +114,10 @@ func TestBuildConfiguration(t *testing.T) { ID: "1", containerDefinition: &ecs.ContainerDefinition{ DockerLabels: map[string]*string{ - label.TraefikFrontendAuthBasicUsers: aws.String("test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), - label.TraefikFrontendAuthBasicUsersFile: aws.String(".htpasswd"), - label.TraefikFrontendAuthHeaderField: aws.String("X-WebAuth-User"), + label.TraefikFrontendAuthBasicUsers: aws.String("test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), + label.TraefikFrontendAuthBasicUsersFile: aws.String(".htpasswd"), + label.TraefikFrontendAuthBasicRemoveHeader: aws.String("true"), + label.TraefikFrontendAuthHeaderField: aws.String("X-WebAuth-User"), }}, machine: &machine{ state: ec2.InstanceStateNameRunning, @@ -147,6 +148,7 @@ func TestBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Basic: &types.Basic{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", @@ -212,9 +214,10 @@ func TestBuildConfiguration(t *testing.T) { ID: "1", containerDefinition: &ecs.ContainerDefinition{ DockerLabels: map[string]*string{ - label.TraefikFrontendAuthDigestUsers: aws.String("test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), - label.TraefikFrontendAuthDigestUsersFile: aws.String(".htpasswd"), - label.TraefikFrontendAuthHeaderField: aws.String("X-WebAuth-User"), + label.TraefikFrontendAuthDigestRemoveHeader: aws.String("true"), + label.TraefikFrontendAuthDigestUsers: aws.String("test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), + label.TraefikFrontendAuthDigestUsersFile: aws.String(".htpasswd"), + label.TraefikFrontendAuthHeaderField: aws.String("X-WebAuth-User"), }}, machine: &machine{ state: ec2.InstanceStateNameRunning, @@ -245,6 +248,7 @@ func TestBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Digest: &types.Digest{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", @@ -350,8 +354,10 @@ func TestBuildConfiguration(t *testing.T) { label.TraefikBackendBufferingRetryExpression: aws.String("IsNetworkError() && Attempts() <= 2"), 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"), label.TraefikFrontendAuthBasicUsersFile: aws.String(".htpasswd"), + label.TraefikFrontendAuthDigestRemoveHeader: aws.String("true"), label.TraefikFrontendAuthDigestUsers: aws.String("test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), label.TraefikFrontendAuthDigestUsersFile: aws.String(".htpasswd"), label.TraefikFrontendAuthForwardAddress: aws.String("auth.server"), @@ -481,6 +487,7 @@ func TestBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Basic: &types.Basic{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", diff --git a/provider/ecs/deprecated_config.go b/provider/ecs/deprecated_config.go index 32dfcad75..06808b7f1 100644 --- a/provider/ecs/deprecated_config.go +++ b/provider/ecs/deprecated_config.go @@ -228,7 +228,7 @@ func getFuncFirstStringValueV1(labelName string, defaultValue string) func(insta // Deprecated func getFuncFirstBoolValueV1(labelName string, defaultValue bool) func(instances []ecsInstance) bool { return func(instances []ecsInstance) bool { - if len(instances) < 0 { + if len(instances) == 0 { return defaultValue } return getBoolValueV1(instances[0], labelName, defaultValue) diff --git a/provider/ecs/ecs.go b/provider/ecs/ecs.go index fb35e0372..89959512d 100644 --- a/provider/ecs/ecs.go +++ b/provider/ecs/ecs.go @@ -347,43 +347,47 @@ func (p *Provider) lookupEc2Instances(ctx context.Context, client *awsClient, cl } } - resp, err := client.ecs.DescribeContainerInstancesWithContext(ctx, &ecs.DescribeContainerInstancesInput{ - ContainerInstances: containerInstancesArns, - Cluster: clusterName, - }) - - if err != nil { - log.Errorf("Unable to describe container instances: %s", err) - return nil, err - } - - for _, container := range resp.ContainerInstances { - instanceIds[aws.StringValue(container.Ec2InstanceId)] = aws.StringValue(container.ContainerInstanceArn) - instanceArns = append(instanceArns, container.Ec2InstanceId) - } - - if len(instanceArns) > 0 { - input := &ec2.DescribeInstancesInput{ - InstanceIds: instanceArns, - } - - err = client.ec2.DescribeInstancesPagesWithContext(ctx, input, func(page *ec2.DescribeInstancesOutput, lastPage bool) bool { - if len(page.Reservations) > 0 { - for _, r := range page.Reservations { - for _, i := range r.Instances { - if i.InstanceId != nil { - ec2Instances[instanceIds[aws.StringValue(i.InstanceId)]] = i - } - } - } - } - return !lastPage + for _, arns := range p.chunkIDs(containerInstancesArns) { + resp, err := client.ecs.DescribeContainerInstancesWithContext(ctx, &ecs.DescribeContainerInstancesInput{ + ContainerInstances: arns, + Cluster: clusterName, }) if err != nil { - log.Errorf("Unable to describe instances: %s", err) + log.Errorf("Unable to describe container instances: %v", err) return nil, err } + + for _, container := range resp.ContainerInstances { + instanceIds[aws.StringValue(container.Ec2InstanceId)] = aws.StringValue(container.ContainerInstanceArn) + instanceArns = append(instanceArns, container.Ec2InstanceId) + } + } + + if len(instanceArns) > 0 { + for _, ids := range p.chunkIDs(instanceArns) { + input := &ec2.DescribeInstancesInput{ + InstanceIds: ids, + } + + err := client.ec2.DescribeInstancesPagesWithContext(ctx, input, func(page *ec2.DescribeInstancesOutput, lastPage bool) bool { + if len(page.Reservations) > 0 { + for _, r := range page.Reservations { + for _, i := range r.Instances { + if i.InstanceId != nil { + ec2Instances[instanceIds[aws.StringValue(i.InstanceId)]] = i + } + } + } + } + return !lastPage + }) + + if err != nil { + log.Errorf("Unable to describe instances [%s]: %v", err) + return nil, err + } + } } return ec2Instances, nil @@ -414,3 +418,19 @@ func (p *Provider) loadECSConfig(ctx context.Context, client *awsClient) (*types return p.buildConfiguration(instances) } + +// chunkIDs ECS expects no more than 100 parameters be passed to a API call; +// thus, pack each string into an array capped at 100 elements +func (p *Provider) chunkIDs(ids []*string) [][]*string { + var chuncked [][]*string + for i := 0; i < len(ids); i += 100 { + sliceEnd := -1 + if i+100 < len(ids) { + sliceEnd = i + 100 + } else { + sliceEnd = len(ids) + } + chuncked = append(chuncked, ids[i:sliceEnd]) + } + return chuncked +} diff --git a/provider/ecs/ecs_test.go b/provider/ecs/ecs_test.go new file mode 100644 index 000000000..f55510305 --- /dev/null +++ b/provider/ecs/ecs_test.go @@ -0,0 +1,88 @@ +package ecs + +import ( + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/stretchr/testify/assert" +) + +func TestChunkIDs(t *testing.T) { + provider := &Provider{} + + testCases := []struct { + desc string + count int + expected []int + }{ + { + desc: "0 element", + count: 0, + expected: []int(nil), + }, + { + desc: "1 element", + count: 1, + expected: []int{1}, + }, + { + desc: "99 elements, 1 chunk", + count: 99, + expected: []int{99}, + }, + { + desc: "100 elements, 1 chunk", + count: 100, + expected: []int{100}, + }, + { + desc: "101 elements, 2 chunks", + count: 101, + expected: []int{100, 1}, + }, + { + desc: "199 elements, 2 chunks", + count: 199, + expected: []int{100, 99}, + }, + { + desc: "200 elements, 2 chunks", + count: 200, + expected: []int{100, 100}, + }, + { + desc: "201 elements, 3 chunks", + count: 201, + expected: []int{100, 100, 1}, + }, + { + desc: "555 elements, 5 chunks", + count: 555, + expected: []int{100, 100, 100, 100, 100, 55}, + }, + { + desc: "1001 elements, 11 chunks", + count: 1001, + expected: []int{100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 1}, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + var IDs []*string + for v := 0; v < test.count; v++ { + IDs = append(IDs, aws.String("a")) + } + + var outCount []int + for _, el := range provider.chunkIDs(IDs) { + outCount = append(outCount, len(el)) + } + + assert.Equal(t, test.expected, outCount) + }) + } +} diff --git a/provider/kubernetes/annotations.go b/provider/kubernetes/annotations.go index f56213704..4b716e395 100644 --- a/provider/kubernetes/annotations.go +++ b/provider/kubernetes/annotations.go @@ -11,6 +11,7 @@ const ( annotationKubernetesAuthSecret = "ingress.kubernetes.io/auth-secret" annotationKubernetesAuthHeaderField = "ingress.kubernetes.io/auth-header-field" annotationKubernetesAuthForwardResponseHeaders = "ingress.kubernetes.io/auth-response-headers" + annotationKubernetesAuthRemoveHeader = "ingress.kubernetes.io/auth-remove-header" annotationKubernetesAuthForwardURL = "ingress.kubernetes.io/auth-url" annotationKubernetesAuthForwardTrustHeaders = "ingress.kubernetes.io/auth-trust-headers" annotationKubernetesAuthForwardTLSSecret = "ingress.kubernetes.io/auth-tls-secret" diff --git a/provider/kubernetes/kubernetes.go b/provider/kubernetes/kubernetes.go index c89e244f4..d29e52309 100644 --- a/provider/kubernetes/kubernetes.go +++ b/provider/kubernetes/kubernetes.go @@ -258,7 +258,7 @@ func (p *Provider) loadIngresses(k8sClient Client) (*types.Configuration, error) Routes: make(map[string]types.Route), Priority: priority, WhiteList: getWhiteList(i), - Redirect: getFrontendRedirect(i), + Redirect: getFrontendRedirect(i, baseName, pa.Path), EntryPoints: entryPoints, Headers: getHeader(i), Errors: getErrorPages(i), @@ -500,7 +500,7 @@ func (p *Provider) addGlobalBackend(cl Client, i *extensionsv1beta1.Ingress, tem Routes: make(map[string]types.Route), Priority: priority, WhiteList: getWhiteList(i), - Redirect: getFrontendRedirect(i), + Redirect: getFrontendRedirect(i, defaultFrontendName, "/"), EntryPoints: entryPoints, Headers: getHeader(i), Errors: getErrorPages(i), @@ -531,25 +531,12 @@ func getRuleForPath(pa extensionsv1beta1.HTTPIngressPath, i *extensionsv1beta1.I rules := []string{ruleType + ":" + pa.Path} - var pathReplaceAnnotation string - if ruleType == ruleTypeReplacePath { - pathReplaceAnnotation = annotationKubernetesRuleType - } - if rewriteTarget := getStringValue(i.Annotations, annotationKubernetesRewriteTarget, ""); rewriteTarget != "" { - if pathReplaceAnnotation != "" { - return "", fmt.Errorf("rewrite-target must not be used together with annotation %q", pathReplaceAnnotation) + if ruleType == ruleTypeReplacePath { + return "", fmt.Errorf("rewrite-target must not be used together with annotation %q", annotationKubernetesRuleType) } rewriteTargetRule := fmt.Sprintf("ReplacePathRegex: ^%s/(.*) %s/$1", pa.Path, strings.TrimRight(rewriteTarget, "/")) rules = append(rules, rewriteTargetRule) - pathReplaceAnnotation = annotationKubernetesRewriteTarget - } - - if rootPath := getStringValue(i.Annotations, annotationKubernetesAppRoot, ""); rootPath != "" && pa.Path == "/" { - if pathReplaceAnnotation != "" { - return "", fmt.Errorf("app-root must not be used together with annotation %q", pathReplaceAnnotation) - } - rules = append(rules, ruleTypeReplacePath+":"+rootPath) } if requestModifier := getStringValue(i.Annotations, annotationKubernetesRequestModifier, ""); requestModifier != "" { @@ -750,7 +737,10 @@ func getBasicAuthConfig(i *extensionsv1beta1.Ingress, k8sClient Client) (*types. return nil, err } - return &types.Basic{Users: credentials}, nil + return &types.Basic{ + Users: credentials, + RemoveHeader: getBoolValue(i.Annotations, annotationKubernetesAuthRemoveHeader, false), + }, nil } func getDigestAuthConfig(i *extensionsv1beta1.Ingress, k8sClient Client) (*types.Digest, error) { @@ -759,7 +749,9 @@ func getDigestAuthConfig(i *extensionsv1beta1.Ingress, k8sClient Client) (*types return nil, err } - return &types.Digest{Users: credentials}, nil + return &types.Digest{Users: credentials, + RemoveHeader: getBoolValue(i.Annotations, annotationKubernetesAuthRemoveHeader, false), + }, nil } func getAuthCredentials(i *extensionsv1beta1.Ingress, k8sClient Client) ([]string, error) { @@ -859,9 +851,17 @@ func loadAuthTLSSecret(namespace, secretName string, k8sClient Client) (string, return getCertificateBlocks(secret, namespace, secretName) } -func getFrontendRedirect(i *extensionsv1beta1.Ingress) *types.Redirect { +func getFrontendRedirect(i *extensionsv1beta1.Ingress, baseName, path string) *types.Redirect { permanent := getBoolValue(i.Annotations, annotationKubernetesRedirectPermanent, false) + if appRoot := getStringValue(i.Annotations, annotationKubernetesAppRoot, ""); appRoot != "" && path == "/" { + return &types.Redirect{ + Regex: fmt.Sprintf("%s$", baseName), + Replacement: fmt.Sprintf("%s/%s", strings.TrimRight(baseName, "/"), strings.TrimLeft(appRoot, "/")), + Permanent: permanent, + } + } + redirectEntryPoint := getStringValue(i.Annotations, annotationKubernetesRedirectEntryPoint, "") if len(redirectEntryPoint) > 0 { return &types.Redirect{ diff --git a/provider/kubernetes/kubernetes_test.go b/provider/kubernetes/kubernetes_test.go index 576d00aad..df1cc5953 100644 --- a/provider/kubernetes/kubernetes_test.go +++ b/provider/kubernetes/kubernetes_test.go @@ -1470,8 +1470,9 @@ rateset: ), frontend("root/", passHostHeader(), + redirectRegex("root/$", "root/root"), routes( - route("/", "PathPrefix:/;ReplacePath:/root"), + route("/", "PathPrefix:/"), route("root", "Host:root"), ), ), @@ -2047,6 +2048,7 @@ func TestLoadIngressesBasicAuth(t *testing.T) { iNamespace("testing"), iAnnotation(annotationKubernetesAuthType, "basic"), iAnnotation(annotationKubernetesAuthSecret, "mySecret"), + iAnnotation(annotationKubernetesAuthRemoveHeader, "true"), iRules( iRule( iHost("basic"), @@ -2095,8 +2097,9 @@ func TestLoadIngressesBasicAuth(t *testing.T) { actual = provider.loadConfig(*actual) require.NotNil(t, actual) - got := actual.Frontends["basic/auth"].Auth.Basic.Users - assert.Equal(t, types.Users{"myUser:myEncodedPW"}, got) + actualBasicAuth := actual.Frontends["basic/auth"].Auth.Basic + assert.Equal(t, types.Users{"myUser:myEncodedPW"}, actualBasicAuth.Users) + assert.True(t, actualBasicAuth.RemoveHeader, "Bad RemoveHeader flag") } func TestLoadIngressesForwardAuth(t *testing.T) { diff --git a/provider/kv/keynames.go b/provider/kv/keynames.go index 7dfb292c2..27c1bdb4e 100644 --- a/provider/kv/keynames.go +++ b/provider/kv/keynames.go @@ -37,9 +37,11 @@ const ( pathFrontendBasicAuth = "/basicauth" // Deprecated pathFrontendAuth = "/auth/" pathFrontendAuthBasic = pathFrontendAuth + "basic/" + pathFrontendAuthBasicRemoveHeader = pathFrontendAuthBasic + "removeheader" pathFrontendAuthBasicUsers = pathFrontendAuthBasic + "users" pathFrontendAuthBasicUsersFile = pathFrontendAuthBasic + "usersfile" pathFrontendAuthDigest = pathFrontendAuth + "digest/" + pathFrontendAuthDigestRemoveHeader = pathFrontendAuthDigest + "removeheader" pathFrontendAuthDigestUsers = pathFrontendAuthDigest + "users" pathFrontendAuthDigestUsersFile = pathFrontendAuthDigest + "usersfile" pathFrontendAuthForward = pathFrontendAuth + "forward/" diff --git a/provider/kv/kv_config.go b/provider/kv/kv_config.go index a2242ac6b..ed7680780 100644 --- a/provider/kv/kv_config.go +++ b/provider/kv/kv_config.go @@ -377,16 +377,16 @@ func (p *Provider) hasDeprecatedBasicAuth(rootPath string) bool { // GetAuth Create auth from path func (p *Provider) getAuth(rootPath string) *types.Auth { hasDeprecatedBasicAuth := p.hasDeprecatedBasicAuth(rootPath) - if len(p.getList(rootPath, pathFrontendAuth)) > 0 || hasDeprecatedBasicAuth { + if p.hasPrefix(rootPath, pathFrontendAuth) || hasDeprecatedBasicAuth { auth := &types.Auth{ HeaderField: p.get("", rootPath, pathFrontendAuthHeaderField), } - if len(p.getList(rootPath, pathFrontendAuthBasic)) > 0 || hasDeprecatedBasicAuth { + if p.hasPrefix(rootPath, pathFrontendAuthBasic) || hasDeprecatedBasicAuth { auth.Basic = p.getAuthBasic(rootPath) - } else if len(p.getList(rootPath, pathFrontendAuthDigest)) > 0 { + } else if p.hasPrefix(rootPath, pathFrontendAuthDigest) { auth.Digest = p.getAuthDigest(rootPath) - } else if len(p.getList(rootPath, pathFrontendAuthForward)) > 0 { + } else if p.hasPrefix(rootPath, pathFrontendAuthForward) { auth.Forward = p.getAuthForward(rootPath) } @@ -398,7 +398,8 @@ func (p *Provider) getAuth(rootPath string) *types.Auth { // getAuthBasic Create Basic Auth from path func (p *Provider) getAuthBasic(rootPath string) *types.Basic { basicAuth := &types.Basic{ - UsersFile: p.get("", rootPath, pathFrontendAuthBasicUsersFile), + UsersFile: p.get("", rootPath, pathFrontendAuthBasicUsersFile), + RemoveHeader: p.getBool(false, rootPath, pathFrontendAuthBasicRemoveHeader), } // backward compatibility @@ -415,8 +416,9 @@ func (p *Provider) getAuthBasic(rootPath string) *types.Basic { // getAuthDigest Create Digest Auth from path func (p *Provider) getAuthDigest(rootPath string) *types.Digest { return &types.Digest{ - Users: p.getList(rootPath, pathFrontendAuthDigestUsers), - UsersFile: p.get("", rootPath, pathFrontendAuthDigestUsersFile), + Users: p.getList(rootPath, pathFrontendAuthDigestUsers), + UsersFile: p.get("", rootPath, pathFrontendAuthDigestUsersFile), + RemoveHeader: p.getBool(false, rootPath, pathFrontendAuthDigestRemoveHeader), } } @@ -588,6 +590,21 @@ func (p *Provider) has(keyParts ...string) bool { return len(value) > 0 } +func (p *Provider) hasPrefix(keyParts ...string) bool { + baseKey := strings.Join(keyParts, "") + if !strings.HasSuffix(baseKey, "/") { + baseKey += "/" + } + + listKeys, err := p.kvClient.List(baseKey, nil) + if err != nil { + log.Debugf("Cannot list keys under %q: %v", baseKey, err) + return false + } + + return len(listKeys) > 0 +} + func (p *Provider) getInt(defaultValue int, keyParts ...string) int { rawValue := p.get("", keyParts...) diff --git a/provider/kv/kv_config_test.go b/provider/kv/kv_config_test.go index bc8bb4e88..e1f604d24 100644 --- a/provider/kv/kv_config_test.go +++ b/provider/kv/kv_config_test.go @@ -62,12 +62,47 @@ func TestProviderBuildConfiguration(t *testing.T) { }, }, { - desc: "basic auth", + desc: "basic auth Users", kvPairs: filler("traefik", frontend("frontend", withPair(pathFrontendBackend, "backend"), withPair(pathFrontendAuthHeaderField, "X-WebAuth-User"), + withPair(pathFrontendAuthBasicRemoveHeader, "true"), withList(pathFrontendAuthBasicUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), + ), + backend("backend"), + ), + expected: &types.Configuration{ + Backends: map[string]*types.Backend{ + "backend": { + LoadBalancer: &types.LoadBalancer{ + Method: "wrr", + }, + }, + }, + Frontends: map[string]*types.Frontend{ + "frontend": { + Backend: "backend", + PassHostHeader: true, + EntryPoints: []string{}, + Auth: &types.Auth{ + HeaderField: "X-WebAuth-User", + Basic: &types.Basic{ + RemoveHeader: true, + Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", + "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, + }, + }, + }, + }, + }, + }, + { + desc: "basic auth UsersFile", + kvPairs: filler("traefik", + frontend("frontend", + withPair(pathFrontendBackend, "backend"), + withPair(pathFrontendAuthHeaderField, "X-WebAuth-User"), withPair(pathFrontendAuthBasicUsersFile, ".htpasswd"), ), backend("backend"), @@ -88,8 +123,6 @@ func TestProviderBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Basic: &types.Basic{ - Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", - "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", }, }, @@ -135,6 +168,7 @@ func TestProviderBuildConfiguration(t *testing.T) { frontend("frontend", withPair(pathFrontendBackend, "backend"), withPair(pathFrontendAuthHeaderField, "X-WebAuth-User"), + withPair(pathFrontendAuthDigestRemoveHeader, "true"), withList(pathFrontendAuthDigestUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), withPair(pathFrontendAuthDigestUsersFile, ".htpasswd"), ), @@ -156,6 +190,7 @@ func TestProviderBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Digest: &types.Digest{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", @@ -248,8 +283,10 @@ func TestProviderBuildConfiguration(t *testing.T) { withPair(pathFrontendWhiteListUseXForwardedFor, "true"), withList(pathFrontendBasicAuth, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), + withPair(pathFrontendAuthBasicRemoveHeader, "true"), withList(pathFrontendAuthBasicUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), withPair(pathFrontendAuthBasicUsersFile, ".htpasswd"), + withPair(pathFrontendAuthDigestRemoveHeader, "true"), withList(pathFrontendAuthDigestUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), withPair(pathFrontendAuthDigestUsersFile, ".htpasswd"), withPair(pathFrontendAuthForwardAddress, "auth.server"), @@ -367,6 +404,7 @@ func TestProviderBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Basic: &types.Basic{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", @@ -2126,12 +2164,14 @@ func TestProviderGetAuth(t *testing.T) { rootPath: "traefik/frontends/foo", kvPairs: filler("traefik", frontend("foo", + withPair(pathFrontendAuthBasicRemoveHeader, "true"), withList(pathFrontendAuthBasicUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), withPair(pathFrontendAuthBasicUsersFile, ".htpasswd"), withPair(pathFrontendAuthHeaderField, "X-WebAuth-User"))), expected: &types.Auth{ HeaderField: "X-WebAuth-User", Basic: &types.Basic{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", diff --git a/provider/label/names.go b/provider/label/names.go index ddb1aacaa..bafa49c61 100644 --- a/provider/label/names.go +++ b/provider/label/names.go @@ -37,9 +37,11 @@ const ( SuffixFrontend = "frontend" SuffixFrontendAuth = SuffixFrontend + ".auth" SuffixFrontendAuthBasic = SuffixFrontendAuth + ".basic" + SuffixFrontendAuthBasicRemoveHeader = SuffixFrontendAuthBasic + ".removeHeader" SuffixFrontendAuthBasicUsers = SuffixFrontendAuthBasic + ".users" SuffixFrontendAuthBasicUsersFile = SuffixFrontendAuthBasic + ".usersFile" SuffixFrontendAuthDigest = SuffixFrontendAuth + ".digest" + SuffixFrontendAuthDigestRemoveHeader = SuffixFrontendAuthDigest + ".removeHeader" SuffixFrontendAuthDigestUsers = SuffixFrontendAuthDigest + ".users" SuffixFrontendAuthDigestUsersFile = SuffixFrontendAuthDigest + ".usersFile" SuffixFrontendAuthForward = SuffixFrontendAuth + ".forward" @@ -123,9 +125,11 @@ const ( TraefikFrontend = Prefix + SuffixFrontend TraefikFrontendAuth = Prefix + SuffixFrontendAuth TraefikFrontendAuthBasic = Prefix + SuffixFrontendAuthBasic + TraefikFrontendAuthBasicRemoveHeader = Prefix + SuffixFrontendAuthBasicRemoveHeader TraefikFrontendAuthBasicUsers = Prefix + SuffixFrontendAuthBasicUsers TraefikFrontendAuthBasicUsersFile = Prefix + SuffixFrontendAuthBasicUsersFile TraefikFrontendAuthDigest = Prefix + SuffixFrontendAuthDigest + TraefikFrontendAuthDigestRemoveHeader = Prefix + SuffixFrontendAuthDigestRemoveHeader TraefikFrontendAuthDigestUsers = Prefix + SuffixFrontendAuthDigestUsers TraefikFrontendAuthDigestUsersFile = Prefix + SuffixFrontendAuthDigestUsersFile TraefikFrontendAuthForward = Prefix + SuffixFrontendAuthForward diff --git a/provider/label/partial.go b/provider/label/partial.go index 0723e6c57..aec443edd 100644 --- a/provider/label/partial.go +++ b/provider/label/partial.go @@ -84,7 +84,8 @@ func GetAuth(labels map[string]string) *types.Auth { // getAuthBasic Create Basic Auth from labels func getAuthBasic(labels map[string]string) *types.Basic { basicAuth := &types.Basic{ - UsersFile: GetStringValue(labels, TraefikFrontendAuthBasicUsersFile, ""), + UsersFile: GetStringValue(labels, TraefikFrontendAuthBasicUsersFile, ""), + RemoveHeader: GetBoolValue(labels, TraefikFrontendAuthBasicRemoveHeader, false), } // backward compatibility @@ -101,8 +102,9 @@ func getAuthBasic(labels map[string]string) *types.Basic { // getAuthDigest Create Digest Auth from labels func getAuthDigest(labels map[string]string) *types.Digest { return &types.Digest{ - Users: GetSliceStringValue(labels, TraefikFrontendAuthDigestUsers), - UsersFile: GetStringValue(labels, TraefikFrontendAuthDigestUsersFile, ""), + Users: GetSliceStringValue(labels, TraefikFrontendAuthDigestUsers), + UsersFile: GetStringValue(labels, TraefikFrontendAuthDigestUsersFile, ""), + RemoveHeader: GetBoolValue(labels, TraefikFrontendAuthDigestRemoveHeader, false), } } diff --git a/provider/label/partial_test.go b/provider/label/partial_test.go index f6d3f9998..796acfe15 100644 --- a/provider/label/partial_test.go +++ b/provider/label/partial_test.go @@ -735,25 +735,27 @@ func TestGetAuth(t *testing.T) { { desc: "should return a basic auth", labels: map[string]string{ - TraefikFrontendAuthHeaderField: "myHeaderField", - TraefikFrontendAuthBasicUsers: "user:pwd,user2:pwd2", - TraefikFrontendAuthBasicUsersFile: "myUsersFile", + TraefikFrontendAuthHeaderField: "myHeaderField", + TraefikFrontendAuthBasicUsers: "user:pwd,user2:pwd2", + TraefikFrontendAuthBasicUsersFile: "myUsersFile", + TraefikFrontendAuthBasicRemoveHeader: "true", }, expected: &types.Auth{ HeaderField: "myHeaderField", - Basic: &types.Basic{UsersFile: "myUsersFile", Users: []string{"user:pwd", "user2:pwd2"}}, + Basic: &types.Basic{UsersFile: "myUsersFile", Users: []string{"user:pwd", "user2:pwd2"}, RemoveHeader: true}, }, }, { desc: "should return a digest auth", labels: map[string]string{ - TraefikFrontendAuthHeaderField: "myHeaderField", - TraefikFrontendAuthDigestUsers: "user:pwd,user2:pwd2", - TraefikFrontendAuthDigestUsersFile: "myUsersFile", + TraefikFrontendAuthDigestRemoveHeader: "true", + TraefikFrontendAuthHeaderField: "myHeaderField", + TraefikFrontendAuthDigestUsers: "user:pwd,user2:pwd2", + TraefikFrontendAuthDigestUsersFile: "myUsersFile", }, expected: &types.Auth{ HeaderField: "myHeaderField", - Digest: &types.Digest{UsersFile: "myUsersFile", Users: []string{"user:pwd", "user2:pwd2"}}, + Digest: &types.Digest{UsersFile: "myUsersFile", Users: []string{"user:pwd", "user2:pwd2"}, RemoveHeader: true}, }, }, { diff --git a/provider/marathon/config_test.go b/provider/marathon/config_test.go index ae9ec6d2e..f6a5d3c9a 100644 --- a/provider/marathon/config_test.go +++ b/provider/marathon/config_test.go @@ -161,6 +161,7 @@ func TestBuildConfiguration(t *testing.T) { appID("/app"), appPorts(80), withLabel(label.TraefikFrontendAuthHeaderField, "X-WebAuth-User"), + withLabel(label.TraefikFrontendAuthBasicRemoveHeader, "true"), withLabel(label.TraefikFrontendAuthBasicUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), withLabel(label.TraefikFrontendAuthBasicUsersFile, ".htpasswd"), withTasks(localhostTask(taskPorts(80))), @@ -176,6 +177,7 @@ func TestBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Basic: &types.Basic{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", @@ -245,6 +247,7 @@ func TestBuildConfiguration(t *testing.T) { appID("/app"), appPorts(80), withLabel(label.TraefikFrontendAuthHeaderField, "X-WebAuth-User"), + withLabel(label.TraefikFrontendAuthDigestRemoveHeader, "true"), withLabel(label.TraefikFrontendAuthDigestUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), withLabel(label.TraefikFrontendAuthDigestUsersFile, ".htpasswd"), withTasks(localhostTask(taskPorts(80))), @@ -260,6 +263,7 @@ func TestBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Digest: &types.Digest{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", @@ -371,8 +375,10 @@ func TestBuildConfiguration(t *testing.T) { withLabel(label.TraefikBackendBufferingRetryExpression, "IsNetworkError() && Attempts() <= 2"), 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"), withLabel(label.TraefikFrontendAuthBasicUsersFile, ".htpasswd"), + withLabel(label.TraefikFrontendAuthDigestRemoveHeader, "true"), withLabel(label.TraefikFrontendAuthDigestUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), withLabel(label.TraefikFrontendAuthDigestUsersFile, ".htpasswd"), withLabel(label.TraefikFrontendAuthForwardAddress, "auth.server"), @@ -452,6 +458,7 @@ func TestBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Basic: &types.Basic{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", @@ -760,6 +767,21 @@ func TestBuildConfigurationSegments(t *testing.T) { withSegmentLabel(label.TraefikWeight, "12", "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"), + withSegmentLabel(label.TraefikFrontendAuthBasicUsersFile, ".htpasswd", "containous"), + withSegmentLabel(label.TraefikFrontendAuthDigestRemoveHeader, "true", "containous"), + withSegmentLabel(label.TraefikFrontendAuthDigestUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", "containous"), + withSegmentLabel(label.TraefikFrontendAuthDigestUsersFile, ".htpasswd", "containous"), + withSegmentLabel(label.TraefikFrontendAuthForwardAddress, "auth.server", "containous"), + withSegmentLabel(label.TraefikFrontendAuthForwardTrustForwardHeader, "true", "containous"), + withSegmentLabel(label.TraefikFrontendAuthForwardTLSCa, "ca.crt", "containous"), + withSegmentLabel(label.TraefikFrontendAuthForwardTLSCaOptional, "true", "containous"), + withSegmentLabel(label.TraefikFrontendAuthForwardTLSCert, "server.crt", "containous"), + withSegmentLabel(label.TraefikFrontendAuthForwardTLSKey, "server.key", "containous"), + withSegmentLabel(label.TraefikFrontendAuthForwardTLSInsecureSkipVerify, "true", "containous"), + withSegmentLabel(label.TraefikFrontendAuthHeaderField, "X-WebAuth-User", "containous"), + withSegmentLabel(label.TraefikFrontendEntryPoints, "http,https", "containous"), withSegmentLabel(label.TraefikFrontendPassHostHeader, "true", "containous"), withSegmentLabel(label.TraefikFrontendPassTLSCert, "true", "containous"), @@ -826,9 +848,12 @@ func TestBuildConfigurationSegments(t *testing.T) { PassTLSCert: true, Priority: 666, Auth: &types.Auth{ + HeaderField: "X-WebAuth-User", Basic: &types.Basic{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, + UsersFile: ".htpasswd", }, }, WhiteList: &types.WhiteList{ diff --git a/provider/mesos/config_test.go b/provider/mesos/config_test.go index 8f52baeab..6b5af71d7 100644 --- a/provider/mesos/config_test.go +++ b/provider/mesos/config_test.go @@ -121,6 +121,7 @@ func TestBuildConfiguration(t *testing.T) { withStatus(withHealthy(true), withState("TASK_RUNNING")), withLabel(label.TraefikFrontendAuthBasicUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), withLabel(label.TraefikFrontendAuthBasicUsersFile, ".htpasswd"), + withLabel(label.TraefikFrontendAuthBasicRemoveHeader, "true"), withLabel(label.TraefikFrontendAuthHeaderField, "X-WebAuth-User"), ), }, @@ -137,6 +138,7 @@ func TestBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Basic: &types.Basic{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", @@ -205,6 +207,7 @@ func TestBuildConfiguration(t *testing.T) { withInfo("name1", withPorts(withPort("TCP", 80, "WEB"))), withStatus(withHealthy(true), withState("TASK_RUNNING")), + withLabel(label.TraefikFrontendAuthDigestRemoveHeader, "true"), withLabel(label.TraefikFrontendAuthDigestUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), withLabel(label.TraefikFrontendAuthDigestUsersFile, ".htpasswd"), withLabel(label.TraefikFrontendAuthHeaderField, "X-WebAuth-User"), @@ -223,6 +226,7 @@ func TestBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Digest: &types.Digest{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", @@ -327,8 +331,10 @@ func TestBuildConfiguration(t *testing.T) { withLabel(label.TraefikBackendBufferingRetryExpression, "IsNetworkError() && Attempts() <= 2"), 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"), withLabel(label.TraefikFrontendAuthBasicUsersFile, ".htpasswd"), + withLabel(label.TraefikFrontendAuthDigestRemoveHeader, "true"), withLabel(label.TraefikFrontendAuthDigestUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), withLabel(label.TraefikFrontendAuthDigestUsersFile, ".htpasswd"), withLabel(label.TraefikFrontendAuthForwardAddress, "auth.server"), @@ -414,6 +420,7 @@ func TestBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Basic: &types.Basic{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", @@ -678,8 +685,10 @@ func TestBuildConfigurationSegments(t *testing.T) { withSegmentLabel(label.TraefikWeight, "12", "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"), withSegmentLabel(label.TraefikFrontendAuthBasicUsersFile, ".htpasswd", "containous"), + withSegmentLabel(label.TraefikFrontendAuthDigestRemoveHeader, "true", "containous"), withSegmentLabel(label.TraefikFrontendAuthDigestUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", "containous"), withSegmentLabel(label.TraefikFrontendAuthDigestUsersFile, ".htpasswd", "containous"), withSegmentLabel(label.TraefikFrontendAuthForwardAddress, "auth.server", "containous"), @@ -760,6 +769,7 @@ func TestBuildConfigurationSegments(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Basic: &types.Basic{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", diff --git a/provider/rancher/config_test.go b/provider/rancher/config_test.go index 57c67ea81..600813e19 100644 --- a/provider/rancher/config_test.go +++ b/provider/rancher/config_test.go @@ -60,8 +60,10 @@ func TestProviderBuildConfiguration(t *testing.T) { label.TraefikBackendBufferingRetryExpression: "IsNetworkError() && Attempts() <= 2", 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", label.TraefikFrontendAuthBasicUsersFile: ".htpasswd", + label.TraefikFrontendAuthDigestRemoveHeader: "true", label.TraefikFrontendAuthDigestUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", label.TraefikFrontendAuthDigestUsersFile: ".htpasswd", label.TraefikFrontendAuthForwardAddress: "auth.server", @@ -145,6 +147,7 @@ func TestProviderBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Basic: &types.Basic{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", @@ -289,8 +292,10 @@ func TestProviderBuildConfiguration(t *testing.T) { label.Prefix + "sauternes." + label.SuffixWeight: "12", 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", label.Prefix + "sauternes." + label.SuffixFrontendAuthBasicUsersFile: ".htpasswd", + label.Prefix + "sauternes." + label.SuffixFrontendAuthDigestRemoveHeader: "true", label.Prefix + "sauternes." + label.SuffixFrontendAuthDigestUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", label.Prefix + "sauternes." + label.SuffixFrontendAuthDigestUsersFile: ".htpasswd", label.Prefix + "sauternes." + label.SuffixFrontendAuthForwardAddress: "auth.server", @@ -370,6 +375,7 @@ func TestProviderBuildConfiguration(t *testing.T) { Auth: &types.Auth{ HeaderField: "X-WebAuth-User", Basic: &types.Basic{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", @@ -564,9 +570,10 @@ func TestProviderBuildConfiguration(t *testing.T) { { Name: "test/service", Labels: map[string]string{ - label.TraefikPort: "80", - label.TraefikFrontendAuthDigestUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", - label.TraefikFrontendAuthDigestUsersFile: ".htpasswd", + label.TraefikPort: "80", + label.TraefikFrontendAuthDigestUsers: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", + label.TraefikFrontendAuthDigestUsersFile: ".htpasswd", + label.TraefikFrontendAuthDigestRemoveHeader: "true", }, Health: "healthy", Containers: []string{"127.0.0.1"}, @@ -579,6 +586,7 @@ func TestProviderBuildConfiguration(t *testing.T) { EntryPoints: []string{}, Auth: &types.Auth{ Digest: &types.Digest{ + RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"}, UsersFile: ".htpasswd", diff --git a/script/docs-verify-docker-image/Dockerfile b/script/docs-verify-docker-image/Dockerfile new file mode 100644 index 000000000..5e4dba6a8 --- /dev/null +++ b/script/docs-verify-docker-image/Dockerfile @@ -0,0 +1,20 @@ +FROM alpine:3.7 + +RUN apk --no-cache --no-progress add \ + ca-certificates \ + curl \ + findutils \ + ruby-bigdecimal \ + ruby-ffi \ + ruby-json \ + ruby-nokogiri \ + tini \ + && gem install --no-document html-proofer + +COPY ./validate.sh /validate.sh + +WORKDIR /app +VOLUME ["/tmp","/app"] + +ENTRYPOINT ["/sbin/tini","-g","sh"] +CMD ["/validate.sh"] diff --git a/script/docs-verify-docker-image/validate.sh b/script/docs-verify-docker-image/validate.sh new file mode 100644 index 000000000..17563a0d3 --- /dev/null +++ b/script/docs-verify-docker-image/validate.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +set -eu + +PATH_TO_SITE="/app/site" +[ -d "${PATH_TO_SITE}" ] + +NUMBER_OF_CPUS="$(grep -c processor /proc/cpuinfo)" + + +echo "=== Checking HTML content..." + +# Search for all HTML files except the theme's partials +# and pipe this to htmlproofer with parallel threads +# (one htmlproofer per vCPU) +find "${PATH_TO_SITE}" -type f -not -path "/app/site/theme/*" \ + -name "*.html" -print0 \ +| xargs -0 -r -P "${NUMBER_OF_CPUS}" -I '{}' \ + htmlproofer \ + --check-html \ + --alt_ignore="/traefik.logo.png/" \ + --url-ignore "/localhost:/,/127.0.0.1:/,/fonts.gstatic.com/,/.minikube/,/github.com\/containous\/traefik\/*edit*/,/github.com\/containous\/traefik\/$/" \ + '{}' +## HTML-proofer options at https://github.com/gjtorikian/html-proofer#configuration + +echo "= Documentation checked successfuly." diff --git a/server/server.go b/server/server.go index 0cd9e6b95..0869b1300 100644 --- a/server/server.go +++ b/server/server.go @@ -274,11 +274,6 @@ func (s *Server) AddListener(listener func(types.Configuration)) { // getCertificate allows to customize tlsConfig.GetCertificate behaviour to get the certificates inserted dynamically func (s *serverEntryPoint) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { - bestCertificate := s.certs.GetBestCertificate(clientHello) - if bestCertificate != nil { - return bestCertificate, nil - } - domainToCheck := types.CanonicalDomain(clientHello.ServerName) if s.tlsALPNGetter != nil { @@ -292,6 +287,11 @@ func (s *serverEntryPoint) getCertificate(clientHello *tls.ClientHelloInfo) (*tl } } + bestCertificate := s.certs.GetBestCertificate(clientHello) + if bestCertificate != nil { + return bestCertificate, nil + } + if s.onDemandListener != nil && len(domainToCheck) > 0 { // Only check for an onDemandCert if there is a domain name return s.onDemandListener(domainToCheck) diff --git a/templates/consul_catalog.tmpl b/templates/consul_catalog.tmpl index 72ebb2357..c7105d55e 100644 --- a/templates/consul_catalog.tmpl +++ b/templates/consul_catalog.tmpl @@ -97,6 +97,7 @@ {{if $auth.Basic }} [frontends."frontend-{{ $service.ServiceName }}".auth.basic] + removeHeader = {{ $auth.Basic.RemoveHeader }} {{if $auth.Basic.Users }} users = [{{range $auth.Basic.Users }} "{{.}}", @@ -107,6 +108,7 @@ {{if $auth.Digest }} [frontends."frontend-{{ $service.ServiceName }}".auth.digest] + removeHeader = {{ $auth.Digest.RemoveHeader }} {{if $auth.Digest.Users }} users = [{{range $auth.Digest.Users }} "{{.}}", diff --git a/templates/docker.tmpl b/templates/docker.tmpl index 0161ac058..ae01d9efe 100644 --- a/templates/docker.tmpl +++ b/templates/docker.tmpl @@ -97,6 +97,7 @@ {{if $auth.Basic }} [frontends."frontend-{{ $frontendName }}".auth.basic] + removeHeader = {{ $auth.Basic.RemoveHeader }} {{if $auth.Basic.Users }} users = [{{range $auth.Basic.Users }} "{{.}}", @@ -107,6 +108,7 @@ {{if $auth.Digest }} [frontends."frontend-{{ $frontendName }}".auth.digest] + removeHeader = {{ $auth.Digest.RemoveHeader }} {{if $auth.Digest.Users }} users = [{{range $auth.Digest.Users }} "{{.}}", diff --git a/templates/ecs.tmpl b/templates/ecs.tmpl index 0e13c31f6..f230b120e 100644 --- a/templates/ecs.tmpl +++ b/templates/ecs.tmpl @@ -96,6 +96,7 @@ {{if $auth.Basic }} [frontends."frontend-{{ $serviceName }}".auth.basic] + removeHeader = {{ $auth.Basic.RemoveHeader }} {{if $auth.Basic.Users }} users = [{{range $auth.Basic.Users }} "{{.}}", @@ -106,6 +107,7 @@ {{if $auth.Digest }} [frontends."frontend-{{ $serviceName }}".auth.digest] + removeHeader = {{ $auth.Digest.RemoveHeader }} {{if $auth.Digest.Users }} users = [{{range $auth.Digest.Users }} "{{.}}", diff --git a/templates/kubernetes.tmpl b/templates/kubernetes.tmpl index 011e3e25c..51c03a748 100644 --- a/templates/kubernetes.tmpl +++ b/templates/kubernetes.tmpl @@ -58,6 +58,7 @@ {{if $frontend.Auth.Basic }} [frontends."{{ $frontendName }}".auth.basic] + removeHeader = {{$frontend.Auth.Basic.RemoveHeader}} users = [{{range $frontend.Auth.Basic.Users }} "{{.}}", {{end}}] @@ -65,6 +66,7 @@ {{if $frontend.Auth.Digest }} [frontends."{{ $frontendName }}".auth.digest] + removeHeader = {{$frontend.Auth.Digest.RemoveHeader}} users = [{{range $frontend.Auth.Digest.Users }} "{{.}}", {{end}}] diff --git a/templates/kv.tmpl b/templates/kv.tmpl index 733a9cba2..03a5dd6e1 100644 --- a/templates/kv.tmpl +++ b/templates/kv.tmpl @@ -96,6 +96,7 @@ {{if $auth.Basic }} [frontends."{{ $frontendName }}".auth.basic] + removeHeader = {{ $auth.Basic.RemoveHeader }} {{if $auth.Basic.Users }} users = [{{range $auth.Basic.Users }} "{{.}}", @@ -106,6 +107,7 @@ {{if $auth.Digest }} [frontends."{{ $frontendName }}".auth.digest] + removeHeader = {{ $auth.Digest.RemoveHeader }} {{if $auth.Digest.Users }} users = [{{range $auth.Digest.Users }} "{{.}}", diff --git a/templates/marathon.tmpl b/templates/marathon.tmpl index 379495ede..6b42630cb 100644 --- a/templates/marathon.tmpl +++ b/templates/marathon.tmpl @@ -99,6 +99,7 @@ {{if $auth.Basic }} [frontends."{{ $frontendName }}".auth.basic] + removeHeader = {{ $auth.Basic.RemoveHeader }} {{if $auth.Basic.Users }} users = [{{range $auth.Basic.Users }} "{{.}}", @@ -109,6 +110,7 @@ {{if $auth.Digest }} [frontends."{{ $frontendName }}".auth.digest] + removeHeader = {{ $auth.Digest.RemoveHeader }} {{if $auth.Digest.Users }} users = [{{range $auth.Digest.Users }} "{{.}}", diff --git a/templates/mesos.tmpl b/templates/mesos.tmpl index 79b3af440..6337cee30 100644 --- a/templates/mesos.tmpl +++ b/templates/mesos.tmpl @@ -99,6 +99,7 @@ {{if $auth.Basic }} [frontends."frontend-{{ $frontendName }}".auth.basic] + removeHeader = {{ $auth.Basic.RemoveHeader}} {{if $auth.Basic.Users }} users = [{{range $auth.Basic.Users }} "{{.}}", @@ -109,6 +110,7 @@ {{if $auth.Digest }} [frontends."frontend-{{ $frontendName }}".auth.digest] + removeHeader = {{ $auth.Digest.RemoveHeader}} {{if $auth.Digest.Users }} users = [{{range $auth.Digest.Users }} "{{.}}", diff --git a/templates/rancher.tmpl b/templates/rancher.tmpl index 13384296b..0b4d38ecb 100644 --- a/templates/rancher.tmpl +++ b/templates/rancher.tmpl @@ -97,6 +97,7 @@ {{if $auth.Basic }} [frontends."frontend-{{ $frontendName }}".auth.basic] + removeHeader = {{ $auth.Basic.RemoveHeader }} {{if $auth.Basic.Users }} users = [{{range $auth.Basic.Users }} "{{.}}", @@ -107,6 +108,7 @@ {{if $auth.Digest }} [frontends."frontend-{{ $frontendName }}".auth.digest] + removeHeader = {{ $auth.Digest.RemoveHeader }} {{if $auth.Digest.Users }} users = [{{range $auth.Digest.Users }} "{{.}}", diff --git a/types/types.go b/types/types.go index 5591afc23..6f48a8655 100644 --- a/types/types.go +++ b/types/types.go @@ -390,10 +390,10 @@ type Cluster struct { // Auth holds authentication configuration (BASIC, DIGEST, users) type Auth struct { - Basic *Basic `export:"true"` - Digest *Digest `export:"true"` - Forward *Forward `export:"true"` - HeaderField string `export:"true"` + Basic *Basic `json:"basic,omitempty" export:"true"` + Digest *Digest `json:"digest,omitempty" export:"true"` + Forward *Forward `json:"forward,omitempty" export:"true"` + HeaderField string `json:"headerField,omitempty" export:"true"` } // Users authentication users @@ -401,22 +401,24 @@ type Users []string // Basic HTTP basic authentication type Basic struct { - Users `mapstructure:","` - UsersFile string + Users `json:"users,omitempty" mapstructure:","` + UsersFile string `json:"usersFile,omitempty"` + RemoveHeader bool `json:"removeHeader,omitempty"` } // Digest HTTP authentication type Digest struct { - Users `mapstructure:","` - UsersFile string + Users `json:"users,omitempty" mapstructure:","` + UsersFile string `json:"usersFile,omitempty"` + RemoveHeader bool `json:"removeHeader,omitempty"` } // Forward authentication type Forward struct { - Address string `description:"Authentication server address"` - TLS *ClientTLS `description:"Enable TLS support" export:"true"` - TrustForwardHeader bool `description:"Trust X-Forwarded-* headers" export:"true"` - AuthResponseHeaders []string `description:"Headers to be forwarded from auth response"` + Address string `description:"Authentication server address" json:"address,omitempty"` + TLS *ClientTLS `description:"Enable TLS support" json:"tls,omitempty" export:"true"` + TrustForwardHeader bool `description:"Trust X-Forwarded-* headers" json:"trustForwardHeader,omitempty" export:"true"` + AuthResponseHeaders []string `description:"Headers to be forwarded from auth response" json:"authResponseHeaders,omitempty"` } // CanonicalDomain returns a lower case domain with trim space @@ -501,11 +503,11 @@ func (b *Buckets) SetValue(val interface{}) { // ClientTLS holds TLS specific configurations as client // CA, Cert and Key can be either path or file contents type ClientTLS struct { - CA string `description:"TLS CA"` - CAOptional bool `description:"TLS CA.Optional"` - Cert string `description:"TLS cert"` - Key string `description:"TLS key"` - InsecureSkipVerify bool `description:"TLS insecure skip verify"` + CA string `description:"TLS CA" json:"ca,omitempty"` + CAOptional bool `description:"TLS CA.Optional" json:"caOptional,omitempty"` + Cert string `description:"TLS cert" json:"cert,omitempty"` + Key string `description:"TLS key" json:"key,omitempty"` + InsecureSkipVerify bool `description:"TLS insecure skip verify" json:"insecureSkipVerify,omitempty"` } // CreateTLSConfig creates a TLS config from ClientTLS structures diff --git a/vendor/github.com/containous/traefik-extra-service-fabric/servicefabric.go b/vendor/github.com/containous/traefik-extra-service-fabric/servicefabric.go index 8ae139884..bb7c3fb37 100644 --- a/vendor/github.com/containous/traefik-extra-service-fabric/servicefabric.go +++ b/vendor/github.com/containous/traefik-extra-service-fabric/servicefabric.go @@ -39,17 +39,16 @@ type Provider struct { AppInsightsKey string `description:"Application Insights Instrumentation Key"` AppInsightsBatchSize int `description:"Number of trace lines per batch, optional"` AppInsightsInterval flaeg.Duration `description:"The interval for sending data to Application Insights, optional"` + sfClient sfClient } // Init the provider func (p *Provider) Init(constraints types.Constraints) error { - p.BaseProvider.Init(constraints) - return nil -} + err := p.BaseProvider.Init(constraints) + if err != nil { + return err + } -// Provide allows the ServiceFabric provider to provide configurations to traefik -// using the given configuration channel. -func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool) error { if p.APIVersion == "" { p.APIVersion = sf.DefaultAPIVersion } @@ -59,7 +58,7 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s return err } - sfClient, err := sf.NewClient(http.DefaultClient, p.ClusterManagementURL, p.APIVersion, tlsConfig) + p.sfClient, err = sf.NewClient(http.DefaultClient, p.ClusterManagementURL, p.APIVersion, tlsConfig) if err != nil { return err } @@ -77,11 +76,16 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s } createAppInsightsHook(p.AppInsightsClientName, p.AppInsightsKey, p.AppInsightsBatchSize, p.AppInsightsInterval) } - - return p.updateConfig(configurationChan, pool, sfClient, time.Duration(p.RefreshSeconds)) + return nil } -func (p *Provider) updateConfig(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, sfClient sfClient, pollInterval time.Duration) error { +// Provide allows the ServiceFabric provider to provide configurations to traefik +// using the given configuration channel. +func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool) error { + return p.updateConfig(configurationChan, pool, time.Duration(p.RefreshSeconds)) +} + +func (p *Provider) updateConfig(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, pollInterval time.Duration) error { pool.Go(func(stop chan bool) { operation := func() error { ticker := time.NewTicker(pollInterval) @@ -96,7 +100,7 @@ func (p *Provider) updateConfig(configurationChan chan<- types.ConfigMessage, po log.Info("Checking service fabric config") } - configuration, err := p.getConfiguration(sfClient) + configuration, err := p.getConfiguration() if err != nil { return err } @@ -120,8 +124,8 @@ func (p *Provider) updateConfig(configurationChan chan<- types.ConfigMessage, po return nil } -func (p *Provider) getConfiguration(sfClient sfClient) (*types.Configuration, error) { - services, err := getClusterServices(sfClient) +func (p *Provider) getConfiguration() (*types.Configuration, error) { + services, err := getClusterServices(p.sfClient) if err != nil { return nil, err } diff --git a/webui/src/app/components/providers/providers.component.html b/webui/src/app/components/providers/providers.component.html index 2039bc571..bcf9df5e5 100644 --- a/webui/src/app/components/providers/providers.component.html +++ b/webui/src/app/components/providers/providers.component.html @@ -161,12 +161,85 @@ -
+

-

Basic Authentication

-
- {{ auth }} +
+

Basic Authentication

+ + + + + + + + + + + + + + + + + + + +
Users File{{ p.auth.basic.usersFile }}
Header Field{{ p.auth.headerField }}
Remove Auth Header{{ !!p.auth.basic.removeHeader }}
Users +
+ {{ user }} +
+
+
+
+

Digest Authentication

+ + + + + + + + + + + + + + + + + + + +
Users File{{ p.auth.digest.usersFile }}
Header Field{{ p.auth.headerField }}
Remove Auth Header{{ !!p.auth.digest.removeHeader }}
Users +
+ {{ user }} +
+
+
+
+

Forward Authentication

+ + + + + + + + + + + + + + + +
Address{{ p.auth.forward.address }}
Trust Forward Header{{ p.auth.forward.trustForwardHeader }}
Response Headers +
+ {{ respHeader }} +
+