From 7ff9193cf5193efc517bb2d2a81c09d234bd10b5 Mon Sep 17 00:00:00 2001 From: Daniel Tomcej Date: Thu, 12 Jul 2018 07:20:04 -0600 Subject: [PATCH 01/13] Correct App-Root kubernetes behavior --- docs/configuration/backends/kubernetes.md | 5 ++-- provider/kubernetes/kubernetes.go | 31 ++++++++++------------- provider/kubernetes/kubernetes_test.go | 3 ++- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/docs/configuration/backends/kubernetes.md b/docs/configuration/backends/kubernetes.md index e11ca7325..bc0a68a19 100644 --- a/docs/configuration/backends/kubernetes.md +++ b/docs/configuration/backends/kubernetes.md @@ -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. diff --git a/provider/kubernetes/kubernetes.go b/provider/kubernetes/kubernetes.go index c89e244f4..085131488 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 != "" { @@ -859,9 +846,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..2f473d91e 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"), ), ), From a7c158f0e1e3184461e5ebb583a1065c52d14a43 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 12 Jul 2018 17:40:04 +0200 Subject: [PATCH 02/13] Fix bad condition in ECS provider --- provider/ecs/deprecated_config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/ecs/deprecated_config.go b/provider/ecs/deprecated_config.go index 2bde17dec..528a54591 100644 --- a/provider/ecs/deprecated_config.go +++ b/provider/ecs/deprecated_config.go @@ -205,7 +205,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) From 2721c2017c27ed02c06abd908e3c33981a67df51 Mon Sep 17 00:00:00 2001 From: Daniel Tomcej Date: Thu, 12 Jul 2018 09:42:05 -0600 Subject: [PATCH 03/13] Correct Modifier in Kubernetes Documentation --- docs/configuration/backends/kubernetes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration/backends/kubernetes.md b/docs/configuration/backends/kubernetes.md index bc0a68a19..7746aff3d 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) | From f0ab2721a5db096ee686496cd9b6c3a391951c6d Mon Sep 17 00:00:00 2001 From: Jonathan Ballet Date: Thu, 12 Jul 2018 17:58:02 +0200 Subject: [PATCH 04/13] Fix path to the debug pprof API --- docs/configuration/api.md | 2 +- docs/configuration/commons.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/configuration/api.md b/docs/configuration/api.md index b2db8517a..9da05c7e8 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 diff --git a/docs/configuration/commons.md b/docs/configuration/commons.md index 481843362..aa53a5940 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 From 3ef6bf2118cd1544a7a58435b7d75b02c7157621 Mon Sep 17 00:00:00 2001 From: Damien Duportal Date: Thu, 12 Jul 2018 18:26:03 +0200 Subject: [PATCH 05/13] Documentation: Introduces a check stage to validate HTML and links --- .travis.yml | 1 + CONTRIBUTING.md | 45 +++++++++++++++++---- Makefile | 19 +++++++-- docs.Dockerfile | 9 ++--- docs/configuration/acme.md | 2 +- docs/configuration/api.md | 2 +- docs/index.md | 2 +- docs/user-guide/cluster.md | 4 +- docs/user-guide/examples.md | 2 +- docs/user-guide/grpc.md | 4 +- docs/user-guide/kv-config.md | 4 +- docs/user-guide/marathon.md | 2 +- docs/user-guide/swarm-mode.md | 2 +- docs/user-guide/swarm.md | 2 +- script/docs-verify-docker-image/Dockerfile | 20 +++++++++ script/docs-verify-docker-image/validate.sh | 26 ++++++++++++ 16 files changed, 116 insertions(+), 30 deletions(-) create mode 100644 script/docs-verify-docker-image/Dockerfile create mode 100644 script/docs-verify-docker-image/validate.sh 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/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/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/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..a5d6f5f25 100644 --- a/docs/configuration/api.md +++ b/docs/configuration/api.md @@ -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/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/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." From 14b7152bf0cecfd9f374fc149c9ee899cb9e2308 Mon Sep 17 00:00:00 2001 From: NicoMen Date: Thu, 12 Jul 2018 19:10:03 +0200 Subject: [PATCH 06/13] Serve TLS-Challenge certificate in first --- acme/acme.go | 8 ++-- integration/acme_test.go | 40 ++++++++++++++----- .../acme/{acme-base.toml => acme_base.toml} | 0 integration/fixtures/acme/acme_tls.toml | 4 ++ server/server.go | 10 ++--- 5 files changed, 42 insertions(+), 20 deletions(-) rename integration/fixtures/acme/{acme-base.toml => acme_base.toml} (100%) 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/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/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) From 5afc8f2b1205b1d6ec6b44223e06a35ac22004d1 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Fri, 13 Jul 2018 17:24:03 +0200 Subject: [PATCH 07/13] KV and authentication --- provider/kv/kv_config.go | 23 +++++++++++++++++++---- provider/kv/kv_config_test.go | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/provider/kv/kv_config.go b/provider/kv/kv_config.go index a2242ac6b..d08878318 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) } @@ -588,6 +588,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..b259ec5b7 100644 --- a/provider/kv/kv_config_test.go +++ b/provider/kv/kv_config_test.go @@ -62,13 +62,12 @@ 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"), withList(pathFrontendAuthBasicUsers, "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"), - withPair(pathFrontendAuthBasicUsersFile, ".htpasswd"), ), backend("backend"), ), @@ -90,6 +89,38 @@ func TestProviderBuildConfiguration(t *testing.T) { Basic: &types.Basic{ 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"), + ), + 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{ UsersFile: ".htpasswd", }, }, From dbe720f0f10369d60fd8a81f2d62bfd33db4fa99 Mon Sep 17 00:00:00 2001 From: Daniel Tomcej Date: Fri, 13 Jul 2018 09:32:03 -0600 Subject: [PATCH 08/13] Remove unusable `--cluster` flag --- configuration/configuration.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/configuration/configuration.go b/configuration/configuration.go index 7dfb43e01..77be95986 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"` From 5774d100c1857438272824b77aee90cd1868eec9 Mon Sep 17 00:00:00 2001 From: SALLEYRON Julien Date: Fri, 13 Jul 2018 18:04:03 +0200 Subject: [PATCH 09/13] Update service fabric --- Gopkg.lock | 7 ++--- Gopkg.toml | 2 +- .../servicefabric.go | 30 +++++++++++-------- 3 files changed, 21 insertions(+), 18 deletions(-) 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/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 } From 9ce444b91ae85fcdcbad47c92e0fffdb943d5901 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Doumenjou Date: Mon, 16 Jul 2018 13:52:03 +0200 Subject: [PATCH 10/13] Don't pass the Authorization header to the backends --- autogen/gentemplates/gen.go | 16 ++++ configuration/entrypoints.go | 6 +- configuration/entrypoints_test.go | 8 ++ docs/configuration/backends/consulcatalog.md | 2 + docs/configuration/backends/docker.md | 4 + docs/configuration/backends/ecs.md | 3 + docs/configuration/backends/file.md | 2 + docs/configuration/backends/kubernetes.md | 1 + docs/configuration/backends/marathon.md | 76 ++++++++++------ docs/configuration/backends/mesos.md | 78 ++++++++++------ docs/configuration/backends/rancher.md | 6 +- docs/configuration/entrypoints.md | 45 ++++++++- middlewares/auth/authenticator.go | 12 +++ middlewares/auth/authenticator_test.go | 91 ++++++++++++++++--- provider/consulcatalog/config_test.go | 5 + .../docker/config_container_docker_test.go | 15 ++- .../docker/config_container_swarm_test.go | 19 ++-- provider/docker/config_segment_test.go | 27 ++++-- provider/ecs/config_test.go | 19 ++-- provider/kubernetes/annotations.go | 1 + provider/kubernetes/kubernetes.go | 9 +- provider/kubernetes/kubernetes_test.go | 6 +- provider/kv/keynames.go | 2 + provider/kv/kv_config.go | 8 +- provider/kv/kv_config_test.go | 9 ++ provider/label/names.go | 4 + provider/label/partial.go | 8 +- provider/label/partial_test.go | 18 ++-- provider/marathon/config_test.go | 25 +++++ provider/mesos/config_test.go | 10 ++ provider/rancher/config_test.go | 14 ++- templates/consul_catalog.tmpl | 2 + templates/docker.tmpl | 2 + templates/ecs.tmpl | 2 + templates/kubernetes.tmpl | 2 + templates/kv.tmpl | 2 + templates/marathon.tmpl | 2 + templates/mesos.tmpl | 2 + templates/rancher.tmpl | 2 + types/types.go | 10 +- 40 files changed, 445 insertions(+), 130 deletions(-) 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/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/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 7746aff3d..b574942d1 100644 --- a/docs/configuration/backends/kubernetes.md +++ b/docs/configuration/backends/kubernetes.md @@ -304,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/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/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/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/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 085131488..d29e52309 100644 --- a/provider/kubernetes/kubernetes.go +++ b/provider/kubernetes/kubernetes.go @@ -737,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) { @@ -746,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) { diff --git a/provider/kubernetes/kubernetes_test.go b/provider/kubernetes/kubernetes_test.go index 2f473d91e..df1cc5953 100644 --- a/provider/kubernetes/kubernetes_test.go +++ b/provider/kubernetes/kubernetes_test.go @@ -2048,6 +2048,7 @@ func TestLoadIngressesBasicAuth(t *testing.T) { iNamespace("testing"), iAnnotation(annotationKubernetesAuthType, "basic"), iAnnotation(annotationKubernetesAuthSecret, "mySecret"), + iAnnotation(annotationKubernetesAuthRemoveHeader, "true"), iRules( iRule( iHost("basic"), @@ -2096,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 d08878318..ed7680780 100644 --- a/provider/kv/kv_config.go +++ b/provider/kv/kv_config.go @@ -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), } } diff --git a/provider/kv/kv_config_test.go b/provider/kv/kv_config_test.go index b259ec5b7..e1f604d24 100644 --- a/provider/kv/kv_config_test.go +++ b/provider/kv/kv_config_test.go @@ -67,6 +67,7 @@ func TestProviderBuildConfiguration(t *testing.T) { 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"), @@ -87,6 +88,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"}, }, @@ -166,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"), ), @@ -187,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", @@ -279,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"), @@ -398,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", @@ -2157,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/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 9c7e076d7..1b3c7b333 100644 --- a/types/types.go +++ b/types/types.go @@ -401,14 +401,16 @@ type Users []string // Basic HTTP basic authentication type Basic struct { - Users `mapstructure:","` - UsersFile string + Users `mapstructure:","` + UsersFile string + RemoveHeader bool } // Digest HTTP authentication type Digest struct { - Users `mapstructure:","` - UsersFile string + Users `mapstructure:","` + UsersFile string + RemoveHeader bool } // Forward authentication From a953d3ad893a3ffe39edf2c8860498e7ad5e45f6 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Tue, 17 Jul 2018 12:02:03 +0200 Subject: [PATCH 11/13] Auth section in web UI. --- middlewares/retry.go | 2 +- types/types.go | 38 ++++----- .../providers/providers.component.html | 81 ++++++++++++++++++- 3 files changed, 97 insertions(+), 24 deletions(-) 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/types/types.go b/types/types.go index 1b3c7b333..1ab95ac86 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,24 +401,24 @@ type Users []string // Basic HTTP basic authentication type Basic struct { - Users `mapstructure:","` - UsersFile string - RemoveHeader bool + 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 - RemoveHeader bool + 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 +501,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/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 }} +
+
From ff2e2d50264a1329994b5803c89d458cb73bca48 Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 17 Jul 2018 12:26:03 +0200 Subject: [PATCH 12/13] Fix 400 bad request on AWS ECS API --- provider/ecs/ecs.go | 84 +++++++++++++++++++++++--------------- provider/ecs/ecs_test.go | 88 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 32 deletions(-) create mode 100644 provider/ecs/ecs_test.go 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) + }) + } +} From a1911a960867e139965d32cd2793c9ef5f5c5165 Mon Sep 17 00:00:00 2001 From: NicoMen Date: Tue, 17 Jul 2018 14:30:03 +0200 Subject: [PATCH 13/13] Prepare release 1.7.0 rc2 --- CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) 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)