From 80a68de91b465823bd372acdd937c6754b4416fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Ch=C3=A1vez?= Date: Wed, 30 Oct 2019 12:46:04 +0100 Subject: [PATCH 01/23] Upgrades zipkin library to avoid errors when using textMap. --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0bc1b3eba..e171de90c 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/opencontainers/runc v1.0.0-rc8 // indirect github.com/opentracing/basictracer-go v1.0.0 // indirect github.com/opentracing/opentracing-go v1.1.0 - github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.3 + github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.4 github.com/openzipkin/zipkin-go v0.2.1 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/philhofer/fwd v1.0.0 // indirect diff --git a/go.sum b/go.sum index dcebb5ef1..94dd6bd84 100644 --- a/go.sum +++ b/go.sum @@ -396,8 +396,8 @@ github.com/opentracing/basictracer-go v1.0.0 h1:YyUAhaEfjoWXclZVJ9sGoNct7j4TVk7l github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.3 h1:XudIMByQMXJ6oDHy4SipNyo35LxjA69Z7v1nL0aAZvA= -github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.3/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.4 h1:bzTJRoOZEN7uI1gq594S5HhMYNSud4FKUEwd4aFbsEI= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.4/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= github.com/openzipkin/zipkin-go v0.2.1 h1:noL5/5Uf1HpVl3wNsfkZhIKbSWCVi5jgqkONNx8PXcA= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= From c37ad5c8bf1ff976a4d158c60e39812ee1610f6b Mon Sep 17 00:00:00 2001 From: Clery <7500492+clery@users.noreply.github.com> Date: Tue, 5 Nov 2019 13:22:04 +0100 Subject: [PATCH 02/23] Double dollar on docker-compose config --- docs/content/middlewares/replacepathregex.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/middlewares/replacepathregex.md b/docs/content/middlewares/replacepathregex.md index 9a88d976e..acc28f857 100644 --- a/docs/content/middlewares/replacepathregex.md +++ b/docs/content/middlewares/replacepathregex.md @@ -15,7 +15,7 @@ The ReplaceRegex replace a path from an url to another with regex matching and r # Replace path with regex labels: - "traefik.http.middlewares.test-replacepathregex.replacepathregex.regex=^/foo/(.*)" - - "traefik.http.middlewares.test-replacepathregex.replacepathregex.replacement=/bar/$1" + - "traefik.http.middlewares.test-replacepathregex.replacepathregex.replacement=/bar/$$1" ``` ```yaml tab="Kubernetes" From 93cf947e2aa7689a6c9ebd8b73caae092b6b7da4 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Tue, 5 Nov 2019 18:10:03 +0100 Subject: [PATCH 03/23] Improve building documentation --- docs/content/contributing/building-testing.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/content/contributing/building-testing.md b/docs/content/contributing/building-testing.md index 65240f216..d55e5971d 100644 --- a/docs/content/contributing/building-testing.md +++ b/docs/content/contributing/building-testing.md @@ -62,6 +62,7 @@ Requirements: - `go` v1.13+ - environment variable `GO111MODULE=on` +- go-bindata `GO111MODULE=off go get -u github.com/containous/go-bindata/...` !!! tip "Source Directory" From 7346b3e326dfb75196c044d46e61ac90fcf51b03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D1=83=D1=81=D0=BB=D0=B0=D0=BD=20=D0=9A=D0=BE=D1=80?= =?UTF-8?q?=D0=BD=D0=B5=D0=B2?= Date: Wed, 6 Nov 2019 14:22:05 +0300 Subject: [PATCH 04/23] Adds missed quotes in api.md --- docs/content/operations/api.md | 20 ++++++++++---------- docs/content/operations/dashboard.md | 18 +++++++++--------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/content/operations/api.md b/docs/content/operations/api.md index f1500293a..636509f9e 100644 --- a/docs/content/operations/api.md +++ b/docs/content/operations/api.md @@ -46,7 +46,7 @@ And then define a routing configuration on Traefik itself with the ```yaml tab="Docker" # Dynamic Configuration labels: - - "traefik.http.routers.api.rule=Host(`traefik.domain.com`) + - "traefik.http.routers.api.rule=Host(`traefik.domain.com`)" - "traefik.http.routers.api.service=api@internal" - "traefik.http.routers.api.middlewares=auth" - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" @@ -64,7 +64,7 @@ labels: ```yaml tab="Rancher" # Dynamic Configuration labels: - - "traefik.http.routers.api.rule=Host(`traefik.domain.com`) + - "traefik.http.routers.api.rule=Host(`traefik.domain.com`)" - "traefik.http.routers.api.service=api@internal" - "traefik.http.routers.api.middlewares=auth" - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" @@ -73,15 +73,15 @@ labels: ```toml tab="File (TOML)" # Dynamic Configuration [http.routers.my-api] - rule="Host(`traefik.domain.com`) - service="api@internal" - middlewares=["auth"] - + rule = "Host(`traefik.domain.com`)" + service = "api@internal" + middlewares = ["auth"] + [http.middlewares.auth.basicAuth] - users = [ - "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", - "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", - ] + users = [ + "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", + "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", + ] ``` ```yaml tab="File (YAML)" diff --git a/docs/content/operations/dashboard.md b/docs/content/operations/dashboard.md index 169e136f2..166ffcb63 100644 --- a/docs/content/operations/dashboard.md +++ b/docs/content/operations/dashboard.md @@ -77,7 +77,7 @@ to allow defining: ```yaml tab="Docker" # Dynamic Configuration labels: - - "traefik.http.routers.api.rule=Host(`traefik.domain.com`) + - "traefik.http.routers.api.rule=Host(`traefik.domain.com`)" - "traefik.http.routers.api.service=api@internal" - "traefik.http.routers.api.middlewares=auth" - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" @@ -95,7 +95,7 @@ to allow defining: ```yaml tab="Rancher" # Dynamic Configuration labels: - - "traefik.http.routers.api.rule=Host(`traefik.domain.com`) + - "traefik.http.routers.api.rule=Host(`traefik.domain.com`)" - "traefik.http.routers.api.service=api@internal" - "traefik.http.routers.api.middlewares=auth" - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" @@ -104,15 +104,15 @@ to allow defining: ```toml tab="File (TOML)" # Dynamic Configuration [http.routers.my-api] - rule="Host(`traefik.domain.com`) - service="api@internal" - middlewares=["auth"] + rule = "Host(`traefik.domain.com`)" + service = "api@internal" + middlewares = ["auth"] [http.middlewares.auth.basicAuth] - users = [ - "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", - "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", - ] + users = [ + "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", + "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", + ] ``` ```yaml tab="File (YAML)" From dbf303d5d6efa1af4676948db9832b21e751a3aa Mon Sep 17 00:00:00 2001 From: Ross Date: Fri, 8 Nov 2019 08:44:04 +0000 Subject: [PATCH 05/23] Fix quickstart link in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4c7574e8e..ee973b0ea 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ _(But if you'd rather configure some of your routes manually, Traefik supports t ## Quickstart -To get your hands on Traefik, you can use the [5-Minute Quickstart](http://docs.traefik.io/#the-traefik-quickstart-using-docker) in our documentation (you will need Docker). +To get your hands on Traefik, you can use the [5-Minute Quickstart](https://docs.traefik.io/getting-started/quick-start/) in our documentation (you will need Docker). ## Web UI From 97873ddb5d126cba85172a32a8d2a7a1ab104fe5 Mon Sep 17 00:00:00 2001 From: Janne Johansson Date: Fri, 8 Nov 2019 14:28:05 +0100 Subject: [PATCH 06/23] slashes ended up in bad place. --- docs/content/routing/services/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/content/routing/services/index.md b/docs/content/routing/services/index.md index 0b582918d..f3e03ebda 100644 --- a/docs/content/routing/services/index.md +++ b/docs/content/routing/services/index.md @@ -599,12 +599,12 @@ This strategy can only be defined with [File](../../providers/file.md). [tcp.services.appv1] [tcp.services.appv1.loadBalancer] [[tcp.services.appv1.loadBalancer.servers]] - address = "private-ip-server-1/:8080" + address = "private-ip-server-1:8080/" [tcp.services.appv2] [tcp.services.appv2.loadBalancer] [[tcp.services.appv2.loadBalancer.servers]] - address = "private-ip-server-2/:8080" + address = "private-ip-server-2:8080/" ``` ```yaml tab="YAML" From 229402594f88205db868fb96ecf3a1caa52c38de Mon Sep 17 00:00:00 2001 From: waiting Date: Fri, 8 Nov 2019 22:00:06 +0800 Subject: [PATCH 07/23] docs: remove field api.entryPoint --- traefik.sample.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/traefik.sample.toml b/traefik.sample.toml index bdc001eaf..1f664ac93 100644 --- a/traefik.sample.toml +++ b/traefik.sample.toml @@ -95,12 +95,12 @@ # Enable API and dashboard [api] - # Name of the related entry point + # Enable the API in insecure mode # # Optional - # Default: "traefik" + # Default: true # - # entryPoint = "traefik" + # insecure = false # Enabled Dashboard # From 7536f5e83c69a8fef743a0f4bb9073231e27d8f2 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Tue, 12 Nov 2019 10:24:05 +0100 Subject: [PATCH 08/23] fix: metric with services LB. --- pkg/metrics/prometheus.go | 6 ++++-- pkg/metrics/prometheus_test.go | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/pkg/metrics/prometheus.go b/pkg/metrics/prometheus.go index ffe236b94..491e6875b 100644 --- a/pkg/metrics/prometheus.go +++ b/pkg/metrics/prometheus.go @@ -229,8 +229,10 @@ func OnConfigurationUpdate(dynConf dynamic.Configurations, entryPoints []string) for serviceName, service := range config.HTTP.Services { dynamicConfig.services[fmt.Sprintf("%s@%s", serviceName, key)] = make(map[string]bool) - for _, server := range service.LoadBalancer.Servers { - dynamicConfig.services[fmt.Sprintf("%s@%s", serviceName, key)][server.URL] = true + if service.LoadBalancer != nil { + for _, server := range service.LoadBalancer.Servers { + dynamicConfig.services[fmt.Sprintf("%s@%s", serviceName, key)][server.URL] = true + } } } } diff --git a/pkg/metrics/prometheus_test.go b/pkg/metrics/prometheus_test.go index 13eb3e874..ccaf2e84b 100644 --- a/pkg/metrics/prometheus_test.go +++ b/pkg/metrics/prometheus_test.go @@ -291,6 +291,11 @@ func TestPrometheusMetricRemoval(t *testing.T) { th.WithLoadBalancerServices(th.WithService("bar", th.WithServers(th.WithServer("http://localhost:9000"))), ), + func(cfg *dynamic.HTTPConfiguration) { + cfg.Services["fii"] = &dynamic.Service{ + Weighted: &dynamic.WeightedRoundRobin{}, + } + }, ), } From 2036518813427e9347e695de43d59591daf24ec7 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Tue, 12 Nov 2019 10:44:05 +0100 Subject: [PATCH 09/23] Use alpine for v2 experimental images. --- exp.Dockerfile | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/exp.Dockerfile b/exp.Dockerfile index d13f64a7a..730db9a40 100644 --- a/exp.Dockerfile +++ b/exp.Dockerfile @@ -7,8 +7,8 @@ RUN mkdir -p $WEBUI_DIR COPY ./webui/ $WEBUI_DIR/ WORKDIR $WEBUI_DIR -RUN npm install +RUN npm install RUN npm run build # BUILD @@ -38,10 +38,12 @@ COPY --from=webui /src/static/ /go/src/github.com/containous/traefik/static/ RUN ./script/make.sh generate binary ## IMAGE -FROM scratch +FROM alpine:3.10 + +RUN apk --no-cache --no-progress add bash curl ca-certificates tzdata \ + && update-ca-certificates \ + && rm -rf /var/cache/apk/* -COPY --from=gobuild /usr/share/zoneinfo /usr/share/zoneinfo -COPY --from=gobuild /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY --from=gobuild /go/src/github.com/containous/traefik/dist/traefik / EXPOSE 80 From e6e026f42016712119d66591b1b43574447aadc2 Mon Sep 17 00:00:00 2001 From: Sylvain Rabot Date: Tue, 12 Nov 2019 11:06:05 +0100 Subject: [PATCH 10/23] Fix rate limiting and SSE --- pkg/middlewares/ratelimiter/rate_limiter.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/pkg/middlewares/ratelimiter/rate_limiter.go b/pkg/middlewares/ratelimiter/rate_limiter.go index 4e51ec3c9..2940c22ae 100644 --- a/pkg/middlewares/ratelimiter/rate_limiter.go +++ b/pkg/middlewares/ratelimiter/rate_limiter.go @@ -5,7 +5,6 @@ import ( "context" "fmt" "net/http" - "sync" "time" "github.com/containous/traefik/v2/pkg/config/dynamic" @@ -35,8 +34,7 @@ type rateLimiter struct { sourceMatcher utils.SourceExtractor next http.Handler - bucketsMu sync.Mutex - buckets *ttlmap.TtlMap // actual buckets, keyed by source. + buckets *ttlmap.TtlMap // actual buckets, keyed by source. } // New returns a rate limiter middleware. @@ -57,7 +55,7 @@ func New(ctx context.Context, next http.Handler, config dynamic.RateLimit, name return nil, err } - buckets, err := ttlmap.NewMap(maxSources) + buckets, err := ttlmap.NewConcurrent(maxSources) if err != nil { return nil, err } @@ -104,9 +102,6 @@ func (rl *rateLimiter) ServeHTTP(w http.ResponseWriter, r *http.Request) { logger.Infof("ignoring token bucket amount > 1: %d", amount) } - rl.bucketsMu.Lock() - defer rl.bucketsMu.Unlock() - var bucket *rate.Limiter if rlSource, exists := rl.buckets.Get(source); exists { bucket = rlSource.(*rate.Limiter) From e30ab074394a33b1db7bc0d0161a27a018ea6c32 Mon Sep 17 00:00:00 2001 From: Damien Duportal Date: Tue, 12 Nov 2019 15:40:05 +0100 Subject: [PATCH 11/23] Dashboard example with swarm --- docs/content/operations/.markdownlint.json | 1 + docs/content/operations/api.md | 58 +--------------- docs/content/operations/dashboard.md | 63 +---------------- .../operations/include-api-examples.md | 69 +++++++++++++++++++ docs/mkdocs.yml | 3 + docs/requirements.txt | 1 + 6 files changed, 78 insertions(+), 117 deletions(-) create mode 100644 docs/content/operations/include-api-examples.md diff --git a/docs/content/operations/.markdownlint.json b/docs/content/operations/.markdownlint.json index 3cd5e9a07..e404a3618 100644 --- a/docs/content/operations/.markdownlint.json +++ b/docs/content/operations/.markdownlint.json @@ -1,4 +1,5 @@ { "extends": "../../.markdownlint.json", + "MD041": false, "MD046": false } diff --git a/docs/content/operations/api.md b/docs/content/operations/api.md index 636509f9e..b8a3c3391 100644 --- a/docs/content/operations/api.md +++ b/docs/content/operations/api.md @@ -43,63 +43,7 @@ api: {} And then define a routing configuration on Traefik itself with the [dynamic configuration](../getting-started/configuration-overview.md#the-dynamic-configuration): -```yaml tab="Docker" -# Dynamic Configuration -labels: - - "traefik.http.routers.api.rule=Host(`traefik.domain.com`)" - - "traefik.http.routers.api.service=api@internal" - - "traefik.http.routers.api.middlewares=auth" - - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" -``` - -```json tab="Marathon" -"labels": { - "traefik.http.routers.api.rule": "Host(`traefik.domain.com`)", - "traefik.http.routers.api.service": "api@internal", - "traefik.http.routers.api.middlewares": "auth", - "traefik.http.middlewares.auth.basicauth.users": "test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" -} -``` - -```yaml tab="Rancher" -# Dynamic Configuration -labels: - - "traefik.http.routers.api.rule=Host(`traefik.domain.com`)" - - "traefik.http.routers.api.service=api@internal" - - "traefik.http.routers.api.middlewares=auth" - - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" -``` - -```toml tab="File (TOML)" -# Dynamic Configuration -[http.routers.my-api] - rule = "Host(`traefik.domain.com`)" - service = "api@internal" - middlewares = ["auth"] - -[http.middlewares.auth.basicAuth] - users = [ - "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", - "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", - ] -``` - -```yaml tab="File (YAML)" -# Dynamic Configuration -http: - routers: - api: - rule: Host(`traefik.domain.com`) - service: api@internal - middlewares: - - auth - middlewares: - auth: - basicAuth: - users: - - "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/" - - "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0" -``` +--8<-- "content/operations/include-api-examples.md" ??? warning "The router's [rule](../../routing/routers#rule) must catch requests for the URI path `/api`" Using an "Host" rule is recommended, by catching all the incoming traffic on this host domain to the API. diff --git a/docs/content/operations/dashboard.md b/docs/content/operations/dashboard.md index 166ffcb63..46fbb9775 100644 --- a/docs/content/operations/dashboard.md +++ b/docs/content/operations/dashboard.md @@ -60,8 +60,8 @@ api: --api.dashboard=true ``` -Then define a routing configuration on Traefik itself, -with a router attached to the service `api@internal` in the +Then define a routing configuration on Traefik itself, +with a router attached to the service `api@internal` in the [dynamic configuration](../getting-started/configuration-overview.md#the-dynamic-configuration), to allow defining: @@ -73,64 +73,7 @@ to allow defining: through Traefik itself (sometimes referred as "Traefik-ception"). ??? example "Dashboard Dynamic Configuration Examples" - - ```yaml tab="Docker" - # Dynamic Configuration - labels: - - "traefik.http.routers.api.rule=Host(`traefik.domain.com`)" - - "traefik.http.routers.api.service=api@internal" - - "traefik.http.routers.api.middlewares=auth" - - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" - ``` - - ```json tab="Marathon" - "labels": { - "traefik.http.routers.api.rule": "Host(`traefik.domain.com`)", - "traefik.http.routers.api.service": "api@internal", - "traefik.http.routers.api.middlewares": "auth", - "traefik.http.middlewares.auth.basicauth.users": "test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" - } - ``` - - ```yaml tab="Rancher" - # Dynamic Configuration - labels: - - "traefik.http.routers.api.rule=Host(`traefik.domain.com`)" - - "traefik.http.routers.api.service=api@internal" - - "traefik.http.routers.api.middlewares=auth" - - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" - ``` - - ```toml tab="File (TOML)" - # Dynamic Configuration - [http.routers.my-api] - rule = "Host(`traefik.domain.com`)" - service = "api@internal" - middlewares = ["auth"] - - [http.middlewares.auth.basicAuth] - users = [ - "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", - "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", - ] - ``` - - ```yaml tab="File (YAML)" - # Dynamic Configuration - http: - routers: - api: - rule: Host(`traefik.domain.com`) - service: api@internal - middlewares: - - auth - middlewares: - auth: - basicAuth: - users: - - "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/" - - "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0" - ``` + --8<-- "content/operations/include-api-examples.md" ### Dashboard Router Rule diff --git a/docs/content/operations/include-api-examples.md b/docs/content/operations/include-api-examples.md new file mode 100644 index 000000000..8992b5101 --- /dev/null +++ b/docs/content/operations/include-api-examples.md @@ -0,0 +1,69 @@ +```yaml tab="Docker" +# Dynamic Configuration +labels: + - "traefik.http.routers.api.rule=Host(`traefik.domain.com`)" + - "traefik.http.routers.api.service=api@internal" + - "traefik.http.routers.api.middlewares=auth" + - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" +``` + +```yaml tab="Docker (Swarm)" +# Dynamic Configuration +deploy: + labels: + - "traefik.http.routers.api.rule=Host(`traefik.domain.com`)" + - "traefik.http.routers.api.service=api@internal" + - "traefik.http.routers.api.middlewares=auth" + - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" + # Dummy service for Swarm port detection. The port can be any valid integer value. + - "traefik.http.services.dummy-svc.loadbalancer.server.port=9999" +``` + +```json tab="Marathon" +"labels": { + "traefik.http.routers.api.rule": "Host(`traefik.domain.com`)", + "traefik.http.routers.api.service": "api@internal", + "traefik.http.routers.api.middlewares": "auth", + "traefik.http.middlewares.auth.basicauth.users": "test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" +} +``` + +```yaml tab="Rancher" +# Dynamic Configuration +labels: + - "traefik.http.routers.api.rule=Host(`traefik.domain.com`)" + - "traefik.http.routers.api.service=api@internal" + - "traefik.http.routers.api.middlewares=auth" + - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" +``` + +```toml tab="File (TOML)" +# Dynamic Configuration +[http.routers.my-api] + rule = "Host(`traefik.domain.com`)" + service = "api@internal" + middlewares = ["auth"] + +[http.middlewares.auth.basicAuth] + users = [ + "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", + "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", + ] +``` + +```yaml tab="File (YAML)" +# Dynamic Configuration +http: + routers: + api: + rule: Host(`traefik.domain.com`) + service: api@internal + middlewares: + - auth + middlewares: + auth: + basicAuth: + users: + - "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/" + - "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0" +``` diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 3a111788c..45e82db52 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -42,6 +42,9 @@ extra_javascript: plugins: - search + - exclude: + glob: + - include-*.md # https://squidfunk.github.io/mkdocs-material/extensions/admonition/ # https://facelessuser.github.io/pymdown-extensions/ diff --git a/docs/requirements.txt b/docs/requirements.txt index e6552aa6a..c9f1cc3b3 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -3,3 +3,4 @@ pymdown-extensions==6.0 mkdocs-bootswatch==1.0 mkdocs-material==4.0.2 markdown-include==0.5.1 +mkdocs-exclude==1.0.2 From 587d3f9012b59f974d69a28da97b391ecea0f90e Mon Sep 17 00:00:00 2001 From: yacinelazaar Date: Tue, 12 Nov 2019 17:02:05 +0100 Subject: [PATCH 12/23] Wrong endpoint on the TLS secret example --- docs/content/routing/providers/kubernetes-crd.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/routing/providers/kubernetes-crd.md b/docs/content/routing/providers/kubernetes-crd.md index a8654701a..9ace387d9 100644 --- a/docs/content/routing/providers/kubernetes-crd.md +++ b/docs/content/routing/providers/kubernetes-crd.md @@ -189,7 +189,7 @@ metadata: spec: entryPoints: - - web + - websecure routes: - match: Host(`foo.com`) && PathPrefix(`/bar`) kind: Rule From dfca01e4693d316db9e4e210b401fa38add988cc Mon Sep 17 00:00:00 2001 From: Frieder Schlesier Date: Wed, 13 Nov 2019 00:34:04 +0100 Subject: [PATCH 13/23] fix typo in v1 to v2 migration guide --- docs/content/migration/v1-to-v2.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/content/migration/v1-to-v2.md b/docs/content/migration/v1-to-v2.md index d98318e51..c56056d41 100644 --- a/docs/content/migration/v1-to-v2.md +++ b/docs/content/migration/v1-to-v2.md @@ -519,7 +519,7 @@ Use Case: Incoming requests to `http://company.org/admin` are forwarded to the w with the path `/admin` stripped, e.g. to `http://:/`. In this case, you must: * First, configure a router named `admin` with a rule matching at least the path prefix with the `PathPrefix` keyword, -* Then, define a middlware of type [`stripprefix`](../../middlewares/stripprefix/), which remove the prefix `/admin`, associated to the router `admin`. +* Then, define a middleware of type [`stripprefix`](../../middlewares/stripprefix/), which remove the prefix `/admin`, associated to the router `admin`. !!! example "Strip Path Prefix When Forwarding to Backend" @@ -974,7 +974,7 @@ You need to activate the API to access the [dashboard](../operations/dashboard.m As the dashboard access is now secured by default you can either: * define a [specific router](../operations/api.md#configuration) with the `api@internal` service and one authentication middleware like the following example -* or use the [unsecure](../operations/api.md#insecure) option of the API +* or use the [insecure](../operations/api.md#insecure) option of the API !!! info "Dashboard with k8s and dedicated router" From ac8c9215cd880589f971353ea96fc76c5bfb5272 Mon Sep 17 00:00:00 2001 From: Damien Duportal Date: Thu, 14 Nov 2019 00:22:03 +0100 Subject: [PATCH 14/23] Update tooling used for documentation --- docs/.markdownlint.json | 3 ++- docs/check.Dockerfile | 19 ++++++------------- docs/docs.Dockerfile | 2 +- docs/requirements.txt | 4 ++-- 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/docs/.markdownlint.json b/docs/.markdownlint.json index c10d547d3..79b1ce5ec 100644 --- a/docs/.markdownlint.json +++ b/docs/.markdownlint.json @@ -7,5 +7,6 @@ "MD026": false, "MD033": false, "MD034": false, - "MD036": false + "MD036": false, + "MD046": false } diff --git a/docs/check.Dockerfile b/docs/check.Dockerfile index bbc51e6b4..79af5c3cf 100644 --- a/docs/check.Dockerfile +++ b/docs/check.Dockerfile @@ -1,9 +1,6 @@ -FROM alpine:3.9 as alpine +FROM alpine:3.10 as alpine -# The "build-dependencies" virtual package provides build tools for html-proofer installation. -# It compile ruby-nokogiri, because alpine native version is always out of date -# This virtual package is cleaned at the end. RUN apk --no-cache --no-progress add \ libcurl \ ruby \ @@ -11,21 +8,17 @@ RUN apk --no-cache --no-progress add \ ruby-etc \ ruby-ffi \ ruby-json \ - && apk add --no-cache --virtual build-dependencies \ - build-base \ - libcurl \ - libxml2-dev \ - libxslt-dev \ - ruby-dev \ - && gem install --no-document html-proofer -v 3.10.2 \ - && apk del build-dependencies + ruby-nokogiri +RUN gem install html-proofer --version 3.13.0 --no-document -- --use-system-libraries # After Ruby, some NodeJS YAY! RUN apk --no-cache --no-progress add \ git \ nodejs \ npm \ - && npm install markdownlint@0.12.0 markdownlint-cli@0.13.0 --global + && npm install --global \ + markdownlint@0.17.2 \ + markdownlint-cli@0.19.0 # Finally the shell tools we need for later # tini helps to terminate properly all the parallelized tasks when sending CTRL-C diff --git a/docs/docs.Dockerfile b/docs/docs.Dockerfile index 7c06736e7..024053ee9 100644 --- a/docs/docs.Dockerfile +++ b/docs/docs.Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.9 +FROM alpine:3.10 ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.local/bin diff --git a/docs/requirements.txt b/docs/requirements.txt index c9f1cc3b3..28465b6c2 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,6 +1,6 @@ mkdocs==1.0.4 -pymdown-extensions==6.0 +pymdown-extensions==6.1 mkdocs-bootswatch==1.0 -mkdocs-material==4.0.2 +mkdocs-material==4.4.3 markdown-include==0.5.1 mkdocs-exclude==1.0.2 From cdb2446e321cdf7b08bf0e6e40f23d6d6bb67657 Mon Sep 17 00:00:00 2001 From: Brad Jones Date: Thu, 14 Nov 2019 00:22:04 -0700 Subject: [PATCH 15/23] Update ACME storage docs to remove reference to KV store in CE --- docs/content/https/acme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/https/acme.md b/docs/content/https/acme.md index 039c54dd2..68d65ebab 100644 --- a/docs/content/https/acme.md +++ b/docs/content/https/acme.md @@ -390,7 +390,7 @@ docker run -v "/my/host/acme:/etc/traefik/acme" traefik ``` !!! warning - For concurrency reason, this file cannot be shared across multiple instances of Traefik. Use a key value store entry instead. + For concurrency reason, this file cannot be shared across multiple instances of Traefik. ## Fallback From 7afd2dbd20969acc7addc012478d5e6c5caebe55 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Thu, 14 Nov 2019 10:32:05 +0100 Subject: [PATCH 16/23] fix: stripPrefix middleware with empty resulting path. --- docs/content/middlewares/addprefix.md | 3 +- docs/content/middlewares/stripprefix.md | 82 +++++++ .../dynamic-configuration/docker-labels.yml | 55 ++--- .../reference/dynamic-configuration/file.toml | 27 +- .../reference/dynamic-configuration/file.yaml | 230 +++++++++--------- .../marathon-labels.json | 58 ++--- pkg/config/dynamic/middlewares.go | 8 +- pkg/config/label/label_test.go | 3 + pkg/middlewares/addprefix/add_prefix.go | 26 +- pkg/middlewares/addprefix/add_prefix_test.go | 8 +- pkg/middlewares/stripprefix/strip_prefix.go | 39 ++- .../stripprefix/strip_prefix_test.go | 61 ++++- .../stripprefixregex/strip_prefix_regex.go | 16 +- .../strip_prefix_regex_test.go | 55 ++++- 14 files changed, 426 insertions(+), 245 deletions(-) diff --git a/docs/content/middlewares/addprefix.md b/docs/content/middlewares/addprefix.md index c41cd1d40..61afb60b0 100644 --- a/docs/content/middlewares/addprefix.md +++ b/docs/content/middlewares/addprefix.md @@ -58,4 +58,5 @@ http: ### `prefix` -`prefix` is the string to add before the current path in the requested URL. It should include the leading slash (`/`). +`prefix` is the string to add before the current path in the requested URL. +It should include the leading slash (`/`). diff --git a/docs/content/middlewares/stripprefix.md b/docs/content/middlewares/stripprefix.md index f34ab014c..8a7f7f194 100644 --- a/docs/content/middlewares/stripprefix.md +++ b/docs/content/middlewares/stripprefix.md @@ -85,3 +85,85 @@ If your backend is serving assets (e.g., images or Javascript files), chances ar Continuing on the example, the backend should return `/products/shoes/image.png` (and not `/images.png` which Traefik would likely not be able to associate with the same backend). The `X-Forwarded-Prefix` header can be queried to build such URLs dynamically. + +### `forceSlash` + +_Optional, Default=true_ + +```yaml tab="Docker" +labels: + - "traefik.http.middlewares.example.stripprefix.prefixes=/foobar" + - "traefik.http.middlewares.example.stripprefix.forceslash=false" +``` + +```yaml tab="Kubernetes" +apiVersion: traefik.containo.us/v1alpha1 +kind: Middleware +metadata: + name: example +spec: + stripPrefix: + prefixes: + - "/foobar" + forceSlash: false +``` + +```json tab="Marathon" +"labels": { + "traefik.http.middlewares.example.stripprefix.prefixes": "/foobar", + "traefik.http.middlewares.example.stripprefix.forceslash": "false" +} +``` + +```yaml tab="Rancher" +labels: + - "traefik.http.middlewares.example.stripprefix.prefixes=/foobar" + - "traefik.http.middlewares.example.stripprefix.forceSlash=false" +``` + +```toml tab="File (TOML)" +[http.middlewares] + [http.middlewares.example.stripPrefix] + prefixes = ["/foobar"] + forceSlash = false +``` + +```yaml tab="File (YAML)" +http: + middlewares: + example: + stripPrefix: + prefixes: + - "/foobar" + forceSlash: false +``` + +The `forceSlash` option makes sure that the resulting stripped path is not the empty string, by replacing it with `/` when necessary. + +This option was added to keep the initial (non-intuitive) behavior of this middleware, in order to avoid introducing a breaking change. + +It's recommended to explicitly set `forceSlash` to `false`. + +??? info "Behavior examples" + + - `forceSlash=true` + + | Path | Prefix to strip | Result | + |------------|-----------------|--------| + | `/` | `/` | `/` | + | `/foo` | `/foo` | `/` | + | `/foo/` | `/foo` | `/` | + | `/foo/` | `/foo/` | `/` | + | `/bar` | `/foo` | `/bar` | + | `/foo/bar` | `/foo` | `/bar` | + + - `forceSlash=false` + + | Path | Prefix to strip | Result | + |------------|-----------------|--------| + | `/` | `/` | empty | + | `/foo` | `/foo` | empty | + | `/foo/` | `/foo` | `/` | + | `/foo/` | `/foo/` | empty | + | `/bar` | `/foo` | `/bar` | + | `/foo/bar` | `/foo` | `/bar` | diff --git a/docs/content/reference/dynamic-configuration/docker-labels.yml b/docs/content/reference/dynamic-configuration/docker-labels.yml index 722a4fdf4..19ce7cef5 100644 --- a/docs/content/reference/dynamic-configuration/docker-labels.yml +++ b/docs/content/reference/dynamic-configuration/docker-labels.yml @@ -103,6 +103,7 @@ - "traefik.http.middlewares.middleware17.replacepathregex.regex=foobar" - "traefik.http.middlewares.middleware17.replacepathregex.replacement=foobar" - "traefik.http.middlewares.middleware18.retry.attempts=42" +- "traefik.http.middlewares.middleware19.stripprefix.forceslash=true" - "traefik.http.middlewares.middleware19.stripprefix.prefixes=foobar, foobar" - "traefik.http.middlewares.middleware20.stripprefixregex.regex=foobar, foobar" - "traefik.http.routers.router0.entrypoints=foobar, foobar" @@ -129,38 +130,22 @@ - "traefik.http.routers.router1.tls.domains[1].main=foobar" - "traefik.http.routers.router1.tls.domains[1].sans=foobar, foobar" - "traefik.http.routers.router1.tls.options=foobar" -- "traefik.http.services.service0.loadbalancer.healthcheck.headers.name0=foobar" -- "traefik.http.services.service0.loadbalancer.healthcheck.headers.name1=foobar" -- "traefik.http.services.service0.loadbalancer.healthcheck.hostname=foobar" -- "traefik.http.services.service0.loadbalancer.healthcheck.interval=foobar" -- "traefik.http.services.service0.loadbalancer.healthcheck.path=foobar" -- "traefik.http.services.service0.loadbalancer.healthcheck.port=42" -- "traefik.http.services.service0.loadbalancer.healthcheck.scheme=foobar" -- "traefik.http.services.service0.loadbalancer.healthcheck.timeout=foobar" -- "traefik.http.services.service0.loadbalancer.passhostheader=true" -- "traefik.http.services.service0.loadbalancer.responseforwarding.flushinterval=foobar" -- "traefik.http.services.service0.loadbalancer.sticky=true" -- "traefik.http.services.service0.loadbalancer.sticky.cookie.httponly=true" -- "traefik.http.services.service0.loadbalancer.sticky.cookie.name=foobar" -- "traefik.http.services.service0.loadbalancer.sticky.cookie.secure=true" -- "traefik.http.services.service0.loadbalancer.server.port=foobar" -- "traefik.http.services.service0.loadbalancer.server.scheme=foobar" -- "traefik.http.services.service1.loadbalancer.healthcheck.headers.name0=foobar" -- "traefik.http.services.service1.loadbalancer.healthcheck.headers.name1=foobar" -- "traefik.http.services.service1.loadbalancer.healthcheck.hostname=foobar" -- "traefik.http.services.service1.loadbalancer.healthcheck.interval=foobar" -- "traefik.http.services.service1.loadbalancer.healthcheck.path=foobar" -- "traefik.http.services.service1.loadbalancer.healthcheck.port=42" -- "traefik.http.services.service1.loadbalancer.healthcheck.scheme=foobar" -- "traefik.http.services.service1.loadbalancer.healthcheck.timeout=foobar" -- "traefik.http.services.service1.loadbalancer.passhostheader=true" -- "traefik.http.services.service1.loadbalancer.responseforwarding.flushinterval=foobar" -- "traefik.http.services.service1.loadbalancer.sticky=true" -- "traefik.http.services.service1.loadbalancer.sticky.cookie.httponly=true" -- "traefik.http.services.service1.loadbalancer.sticky.cookie.name=foobar" -- "traefik.http.services.service1.loadbalancer.sticky.cookie.secure=true" -- "traefik.http.services.service1.loadbalancer.server.port=foobar" -- "traefik.http.services.service1.loadbalancer.server.scheme=foobar" +- "traefik.http.services.service01.loadbalancer.healthcheck.headers.name0=foobar" +- "traefik.http.services.service01.loadbalancer.healthcheck.headers.name1=foobar" +- "traefik.http.services.service01.loadbalancer.healthcheck.hostname=foobar" +- "traefik.http.services.service01.loadbalancer.healthcheck.interval=foobar" +- "traefik.http.services.service01.loadbalancer.healthcheck.path=foobar" +- "traefik.http.services.service01.loadbalancer.healthcheck.port=42" +- "traefik.http.services.service01.loadbalancer.healthcheck.scheme=foobar" +- "traefik.http.services.service01.loadbalancer.healthcheck.timeout=foobar" +- "traefik.http.services.service01.loadbalancer.passhostheader=true" +- "traefik.http.services.service01.loadbalancer.responseforwarding.flushinterval=foobar" +- "traefik.http.services.service01.loadbalancer.sticky=true" +- "traefik.http.services.service01.loadbalancer.sticky.cookie.httponly=true" +- "traefik.http.services.service01.loadbalancer.sticky.cookie.name=foobar" +- "traefik.http.services.service01.loadbalancer.sticky.cookie.secure=true" +- "traefik.http.services.service01.loadbalancer.server.port=foobar" +- "traefik.http.services.service01.loadbalancer.server.scheme=foobar" - "traefik.tcp.routers.tcprouter0.entrypoints=foobar, foobar" - "traefik.tcp.routers.tcprouter0.rule=foobar" - "traefik.tcp.routers.tcprouter0.service=foobar" @@ -183,7 +168,5 @@ - "traefik.tcp.routers.tcprouter1.tls.domains[1].sans=foobar, foobar" - "traefik.tcp.routers.tcprouter1.tls.options=foobar" - "traefik.tcp.routers.tcprouter1.tls.passthrough=true" -- "traefik.tcp.services.tcpservice0.loadbalancer.server.port=foobar" -- "traefik.tcp.services.tcpservice0.loadbalancer.terminationdelay=100" -- "traefik.tcp.services.tcpservice1.loadbalancer.server.port=foobar" -- "traefik.tcp.services.tcpservice1.loadbalancer.terminationdelay=100" +- "traefik.tcp.services.tcpservice01.loadbalancer.terminationdelay=42" +- "traefik.tcp.services.tcpservice01.loadbalancer.server.port=foobar" diff --git a/docs/content/reference/dynamic-configuration/file.toml b/docs/content/reference/dynamic-configuration/file.toml index 8027a082b..326212b6b 100644 --- a/docs/content/reference/dynamic-configuration/file.toml +++ b/docs/content/reference/dynamic-configuration/file.toml @@ -245,6 +245,7 @@ [http.middlewares.Middleware19] [http.middlewares.Middleware19.stripPrefix] prefixes = ["foobar", "foobar"] + forceSlash = true [http.middlewares.Middleware20] [http.middlewares.Middleware20.stripPrefixRegex] regex = ["foobar", "foobar"] @@ -284,25 +285,25 @@ main = "foobar" sans = ["foobar", "foobar"] [tcp.services] - [tcp.services.TCPService0] - [tcp.services.TCPService0.loadBalancer] - terminationDelay = 100 + [tcp.services.TCPService01] + [tcp.services.TCPService01.loadBalancer] + terminationDelay = 42 - [[tcp.services.TCPService0.loadBalancer.servers]] + [[tcp.services.TCPService01.loadBalancer.servers]] address = "foobar" - [[tcp.services.TCPService0.loadBalancer.servers]] + [[tcp.services.TCPService01.loadBalancer.servers]] address = "foobar" + [tcp.services.TCPService02] + [tcp.services.TCPService02.weighted] - [tcp.services.TCPService1] - [tcp.services.TCPService1.loadBalancer] - terminationDelay = 100 + [[tcp.services.TCPService02.weighted.services]] + name = "foobar" + weight = 42 - [[tcp.services.TCPService1.loadBalancer.servers]] - address = "foobar" - - [[tcp.services.TCPService1.loadBalancer.servers]] - address = "foobar" + [[tcp.services.TCPService02.weighted.services]] + name = "foobar" + weight = 42 [tls] diff --git a/docs/content/reference/dynamic-configuration/file.yaml b/docs/content/reference/dynamic-configuration/file.yaml index e8ab878b7..fe5d8c252 100644 --- a/docs/content/reference/dynamic-configuration/file.yaml +++ b/docs/content/reference/dynamic-configuration/file.yaml @@ -2,11 +2,11 @@ http: routers: Router0: entryPoints: - - foobar - - foobar + - foobar + - foobar middlewares: - - foobar - - foobar + - foobar + - foobar service: foobar rule: foobar priority: 42 @@ -14,21 +14,21 @@ http: options: foobar certResolver: foobar domains: - - main: foobar - sans: - - foobar - - foobar - - main: foobar - sans: - - foobar - - foobar + - main: foobar + sans: + - foobar + - foobar + - main: foobar + sans: + - foobar + - foobar Router1: entryPoints: - - foobar - - foobar + - foobar + - foobar middlewares: - - foobar - - foobar + - foobar + - foobar service: foobar rule: foobar priority: 42 @@ -36,14 +36,14 @@ http: options: foobar certResolver: foobar domains: - - main: foobar - sans: - - foobar - - foobar - - main: foobar - sans: - - foobar - - foobar + - main: foobar + sans: + - foobar + - foobar + - main: foobar + sans: + - foobar + - foobar services: Service01: loadBalancer: @@ -53,8 +53,8 @@ http: secure: true httpOnly: true servers: - - url: foobar - - url: foobar + - url: foobar + - url: foobar healthCheck: scheme: foobar path: foobar @@ -72,17 +72,17 @@ http: mirroring: service: foobar mirrors: - - name: foobar - percent: 42 - - name: foobar - percent: 42 + - name: foobar + percent: 42 + - name: foobar + percent: 42 Service03: weighted: services: - - name: foobar - weight: 42 - - name: foobar - weight: 42 + - name: foobar + weight: 42 + - name: foobar + weight: 42 sticky: cookie: name: foobar @@ -95,8 +95,8 @@ http: Middleware01: basicAuth: users: - - foobar - - foobar + - foobar + - foobar usersFile: foobar realm: foobar removeHeader: true @@ -111,8 +111,8 @@ http: Middleware03: chain: middlewares: - - foobar - - foobar + - foobar + - foobar Middleware04: circuitBreaker: expression: foobar @@ -121,8 +121,8 @@ http: Middleware06: digestAuth: users: - - foobar - - foobar + - foobar + - foobar usersFile: foobar removeHeader: true realm: foobar @@ -130,8 +130,8 @@ http: Middleware07: errors: status: - - foobar - - foobar + - foobar + - foobar service: foobar query: foobar Middleware08: @@ -145,8 +145,8 @@ http: insecureSkipVerify: true trustForwardHeader: true authResponseHeaders: - - foobar - - foobar + - foobar + - foobar Middleware09: headers: customRequestHeaders: @@ -157,23 +157,23 @@ http: name1: foobar accessControlAllowCredentials: true accessControlAllowHeaders: - - foobar - - foobar + - foobar + - foobar accessControlAllowMethods: - - foobar - - foobar + - foobar + - foobar accessControlAllowOrigin: foobar accessControlExposeHeaders: - - foobar - - foobar + - foobar + - foobar accessControlMaxAge: 42 addVaryHeader: true allowedHosts: - - foobar - - foobar + - foobar + - foobar hostsProxyHeaders: - - foobar - - foobar + - foobar + - foobar sslRedirect: true sslTemporaryRedirect: true sslHost: foobar @@ -198,13 +198,13 @@ http: Middleware10: ipWhiteList: sourceRange: - - foobar - - foobar + - foobar + - foobar ipStrategy: depth: 42 excludedIPs: - - foobar - - foobar + - foobar + - foobar Middleware11: inFlightReq: amount: 42 @@ -212,8 +212,8 @@ http: ipstrategy: depth: 42 excludedIPs: - - foobar - - foobar + - foobar + - foobar requestHeaderName: foobar requestHost: true Middleware12: @@ -247,8 +247,8 @@ http: ipstrategy: depth: 42 excludedIPs: - - foobar - - foobar + - foobar + - foobar requestHeaderName: foobar requestHost: true Middleware14: @@ -274,19 +274,20 @@ http: Middleware19: stripPrefix: prefixes: - - foobar - - foobar + - foobar + - foobar + forceSlash: true Middleware20: stripPrefixRegex: regex: - - foobar - - foobar + - foobar + - foobar tcp: routers: TCPRouter0: entryPoints: - - foobar - - foobar + - foobar + - foobar service: foobar rule: foobar tls: @@ -294,18 +295,18 @@ tcp: options: foobar certResolver: foobar domains: - - main: foobar - sans: - - foobar - - foobar - - main: foobar - sans: - - foobar - - foobar + - main: foobar + sans: + - foobar + - foobar + - main: foobar + sans: + - foobar + - foobar TCPRouter1: entryPoints: - - foobar - - foobar + - foobar + - foobar service: foobar rule: foobar tls: @@ -313,60 +314,61 @@ tcp: options: foobar certResolver: foobar domains: - - main: foobar - sans: - - foobar - - foobar - - main: foobar - sans: - - foobar - - foobar + - main: foobar + sans: + - foobar + - foobar + - main: foobar + sans: + - foobar + - foobar services: - TCPService0: + TCPService01: loadBalancer: - terminationDelay: 100 + terminationDelay: 42 servers: - - address: foobar - - address: foobar - TCPService1: - loadBalancer: - terminationDelay: 100 - servers: - - address: foobar - - address: foobar + - address: foobar + - address: foobar + TCPService02: + weighted: + services: + - name: foobar + weight: 42 + - name: foobar + weight: 42 tls: certificates: - - certFile: foobar - keyFile: foobar - stores: - - foobar - - foobar - - certFile: foobar - keyFile: foobar - stores: - - foobar - - foobar + - certFile: foobar + keyFile: foobar + stores: + - foobar + - foobar + - certFile: foobar + keyFile: foobar + stores: + - foobar + - foobar options: Options0: minVersion: foobar cipherSuites: - - foobar - - foobar + - foobar + - foobar clientAuth: caFiles: - - foobar - - foobar + - foobar + - foobar clientAuthType: foobar sniStrict: true Options1: minVersion: foobar cipherSuites: - - foobar - - foobar + - foobar + - foobar clientAuth: caFiles: - - foobar - - foobar + - foobar + - foobar clientAuthType: foobar sniStrict: true stores: diff --git a/docs/content/reference/dynamic-configuration/marathon-labels.json b/docs/content/reference/dynamic-configuration/marathon-labels.json index 695e8c6bd..4294c82e0 100644 --- a/docs/content/reference/dynamic-configuration/marathon-labels.json +++ b/docs/content/reference/dynamic-configuration/marathon-labels.json @@ -103,6 +103,7 @@ "traefik.http.middlewares.middleware17.replacepathregex.regex": "foobar", "traefik.http.middlewares.middleware17.replacepathregex.replacement": "foobar", "traefik.http.middlewares.middleware18.retry.attempts": "42", +"traefik.http.middlewares.middleware19.stripprefix.forceslash": "true", "traefik.http.middlewares.middleware19.stripprefix.prefixes": "foobar, foobar", "traefik.http.middlewares.middleware20.stripprefixregex.regex": "foobar, foobar", "traefik.http.routers.router0.entrypoints": "foobar, foobar", @@ -110,7 +111,6 @@ "traefik.http.routers.router0.priority": "42", "traefik.http.routers.router0.rule": "foobar", "traefik.http.routers.router0.service": "foobar", -"traefik.http.routers.router0.tls": "true", "traefik.http.routers.router0.tls.certresolver": "foobar", "traefik.http.routers.router0.tls.domains[0].main": "foobar", "traefik.http.routers.router0.tls.domains[0].sans": "foobar, foobar", @@ -122,49 +122,30 @@ "traefik.http.routers.router1.priority": "42", "traefik.http.routers.router1.rule": "foobar", "traefik.http.routers.router1.service": "foobar", -"traefik.http.routers.router1.tls": "true", "traefik.http.routers.router1.tls.certresolver": "foobar", "traefik.http.routers.router1.tls.domains[0].main": "foobar", "traefik.http.routers.router1.tls.domains[0].sans": "foobar, foobar", "traefik.http.routers.router1.tls.domains[1].main": "foobar", "traefik.http.routers.router1.tls.domains[1].sans": "foobar, foobar", "traefik.http.routers.router1.tls.options": "foobar", -"traefik.http.services.service0.loadbalancer.healthcheck.headers.name0": "foobar", -"traefik.http.services.service0.loadbalancer.healthcheck.headers.name1": "foobar", -"traefik.http.services.service0.loadbalancer.healthcheck.hostname": "foobar", -"traefik.http.services.service0.loadbalancer.healthcheck.interval": "foobar", -"traefik.http.services.service0.loadbalancer.healthcheck.path": "foobar", -"traefik.http.services.service0.loadbalancer.healthcheck.port": "42", -"traefik.http.services.service0.loadbalancer.healthcheck.scheme": "foobar", -"traefik.http.services.service0.loadbalancer.healthcheck.timeout": "foobar", -"traefik.http.services.service0.loadbalancer.passhostheader": "true", -"traefik.http.services.service0.loadbalancer.responseforwarding.flushinterval": "foobar", -"traefik.http.services.service0.loadbalancer.sticky": "true", -"traefik.http.services.service0.loadbalancer.sticky.cookie.httponly": "true", -"traefik.http.services.service0.loadbalancer.sticky.cookie.name": "foobar", -"traefik.http.services.service0.loadbalancer.sticky.cookie.secure": "true", -"traefik.http.services.service0.loadbalancer.server.port": "foobar", -"traefik.http.services.service0.loadbalancer.server.scheme": "foobar", -"traefik.http.services.service1.loadbalancer.healthcheck.headers.name0": "foobar", -"traefik.http.services.service1.loadbalancer.healthcheck.headers.name1": "foobar", -"traefik.http.services.service1.loadbalancer.healthcheck.hostname": "foobar", -"traefik.http.services.service1.loadbalancer.healthcheck.interval": "foobar", -"traefik.http.services.service1.loadbalancer.healthcheck.path": "foobar", -"traefik.http.services.service1.loadbalancer.healthcheck.port": "42", -"traefik.http.services.service1.loadbalancer.healthcheck.scheme": "foobar", -"traefik.http.services.service1.loadbalancer.healthcheck.timeout": "foobar", -"traefik.http.services.service1.loadbalancer.passhostheader": "true", -"traefik.http.services.service1.loadbalancer.responseforwarding.flushinterval": "foobar", -"traefik.http.services.service1.loadbalancer.sticky": "true", -"traefik.http.services.service1.loadbalancer.sticky.cookie.httponly": "true", -"traefik.http.services.service1.loadbalancer.sticky.cookie.name": "foobar", -"traefik.http.services.service1.loadbalancer.sticky.cookie.secure": "true", -"traefik.http.services.service1.loadbalancer.server.port": "foobar", -"traefik.http.services.service1.loadbalancer.server.scheme": "foobar", +"traefik.http.services.service01.loadbalancer.healthcheck.headers.name0": "foobar", +"traefik.http.services.service01.loadbalancer.healthcheck.headers.name1": "foobar", +"traefik.http.services.service01.loadbalancer.healthcheck.hostname": "foobar", +"traefik.http.services.service01.loadbalancer.healthcheck.interval": "foobar", +"traefik.http.services.service01.loadbalancer.healthcheck.path": "foobar", +"traefik.http.services.service01.loadbalancer.healthcheck.port": "42", +"traefik.http.services.service01.loadbalancer.healthcheck.scheme": "foobar", +"traefik.http.services.service01.loadbalancer.healthcheck.timeout": "foobar", +"traefik.http.services.service01.loadbalancer.passhostheader": "true", +"traefik.http.services.service01.loadbalancer.responseforwarding.flushinterval": "foobar", +"traefik.http.services.service01.loadbalancer.sticky.cookie.httponly": "true", +"traefik.http.services.service01.loadbalancer.sticky.cookie.name": "foobar", +"traefik.http.services.service01.loadbalancer.sticky.cookie.secure": "true", +"traefik.http.services.service01.loadbalancer.server.port": "foobar", +"traefik.http.services.service01.loadbalancer.server.scheme": "foobar", "traefik.tcp.routers.tcprouter0.entrypoints": "foobar, foobar", "traefik.tcp.routers.tcprouter0.rule": "foobar", "traefik.tcp.routers.tcprouter0.service": "foobar", -"traefik.tcp.routers.tcprouter0.tls": "true", "traefik.tcp.routers.tcprouter0.tls.certresolver": "foobar", "traefik.tcp.routers.tcprouter0.tls.domains[0].main": "foobar", "traefik.tcp.routers.tcprouter0.tls.domains[0].sans": "foobar, foobar", @@ -175,7 +156,6 @@ "traefik.tcp.routers.tcprouter1.entrypoints": "foobar, foobar", "traefik.tcp.routers.tcprouter1.rule": "foobar", "traefik.tcp.routers.tcprouter1.service": "foobar", -"traefik.tcp.routers.tcprouter1.tls": "true", "traefik.tcp.routers.tcprouter1.tls.certresolver": "foobar", "traefik.tcp.routers.tcprouter1.tls.domains[0].main": "foobar", "traefik.tcp.routers.tcprouter1.tls.domains[0].sans": "foobar, foobar", @@ -183,7 +163,5 @@ "traefik.tcp.routers.tcprouter1.tls.domains[1].sans": "foobar, foobar", "traefik.tcp.routers.tcprouter1.tls.options": "foobar", "traefik.tcp.routers.tcprouter1.tls.passthrough": "true", -"traefik.tcp.services.tcpservice0.loadbalancer.server.port": "foobar", -"traefik.tcp.services.tcpservice0.loadbalancer.terminationDelay": "100", -"traefik.tcp.services.tcpservice1.loadbalancer.server.port": "foobar" -"traefik.tcp.services.tcpservice1.loadbalancer.terminationDelay": "100", +"traefik.tcp.services.tcpservice01.loadbalancer.terminationdelay": "42", +"traefik.tcp.services.tcpservice01.loadbalancer.server.port": "foobar", diff --git a/pkg/config/dynamic/middlewares.go b/pkg/config/dynamic/middlewares.go index a627ce621..aa63da133 100644 --- a/pkg/config/dynamic/middlewares.go +++ b/pkg/config/dynamic/middlewares.go @@ -355,7 +355,13 @@ type Retry struct { // StripPrefix holds the StripPrefix configuration. type StripPrefix struct { - Prefixes []string `json:"prefixes,omitempty" toml:"prefixes,omitempty" yaml:"prefixes,omitempty"` + Prefixes []string `json:"prefixes,omitempty" toml:"prefixes,omitempty" yaml:"prefixes,omitempty"` + ForceSlash bool `json:"forceSlash,omitempty" toml:"forceSlash,omitempty" yaml:"forceSlash,omitempty"` // Deprecated +} + +// SetDefaults Default values for a StripPrefix. +func (s *StripPrefix) SetDefaults() { + s.ForceSlash = true } // +k8s:deepcopy-gen=true diff --git a/pkg/config/label/label_test.go b/pkg/config/label/label_test.go index 63ac2f48f..fbfece11e 100644 --- a/pkg/config/label/label_test.go +++ b/pkg/config/label/label_test.go @@ -368,6 +368,7 @@ func TestDecodeConfiguration(t *testing.T) { "foobar", "fiibar", }, + ForceSlash: true, }, }, "Middleware18": { @@ -771,6 +772,7 @@ func TestEncodeConfiguration(t *testing.T) { "foobar", "fiibar", }, + ForceSlash: true, }, }, "Middleware18": { @@ -1091,6 +1093,7 @@ func TestEncodeConfiguration(t *testing.T) { "traefik.HTTP.Middlewares.Middleware15.ReplacePathRegex.Replacement": "foobar", "traefik.HTTP.Middlewares.Middleware16.Retry.Attempts": "42", "traefik.HTTP.Middlewares.Middleware17.StripPrefix.Prefixes": "foobar, fiibar", + "traefik.HTTP.Middlewares.Middleware17.StripPrefix.ForceSlash": "true", "traefik.HTTP.Middlewares.Middleware18.StripPrefixRegex.Regex": "foobar, fiibar", "traefik.HTTP.Middlewares.Middleware19.Compress": "true", diff --git a/pkg/middlewares/addprefix/add_prefix.go b/pkg/middlewares/addprefix/add_prefix.go index 655e874a8..934b7e0ae 100644 --- a/pkg/middlewares/addprefix/add_prefix.go +++ b/pkg/middlewares/addprefix/add_prefix.go @@ -41,23 +41,35 @@ func New(ctx context.Context, next http.Handler, config dynamic.AddPrefix, name return result, nil } -func (ap *addPrefix) GetTracingInformation() (string, ext.SpanKindEnum) { - return ap.name, tracing.SpanKindNoneEnum +func (a *addPrefix) GetTracingInformation() (string, ext.SpanKindEnum) { + return a.name, tracing.SpanKindNoneEnum } -func (ap *addPrefix) ServeHTTP(rw http.ResponseWriter, req *http.Request) { - logger := log.FromContext(middlewares.GetLoggerCtx(req.Context(), ap.name, typeName)) +func (a *addPrefix) ServeHTTP(rw http.ResponseWriter, req *http.Request) { + logger := log.FromContext(middlewares.GetLoggerCtx(req.Context(), a.name, typeName)) oldURLPath := req.URL.Path - req.URL.Path = ap.prefix + req.URL.Path + req.URL.Path = ensureLeadingSlash(a.prefix + req.URL.Path) logger.Debugf("URL.Path is now %s (was %s).", req.URL.Path, oldURLPath) if req.URL.RawPath != "" { oldURLRawPath := req.URL.RawPath - req.URL.RawPath = ap.prefix + req.URL.RawPath + req.URL.RawPath = ensureLeadingSlash(a.prefix + req.URL.RawPath) logger.Debugf("URL.RawPath is now %s (was %s).", req.URL.RawPath, oldURLRawPath) } req.RequestURI = req.URL.RequestURI() - ap.next.ServeHTTP(rw, req) + a.next.ServeHTTP(rw, req) +} + +func ensureLeadingSlash(str string) string { + if str == "" { + return str + } + + if str[0] == '/' { + return str + } + + return "/" + str } diff --git a/pkg/middlewares/addprefix/add_prefix_test.go b/pkg/middlewares/addprefix/add_prefix_test.go index 1d130a083..cca4bd19e 100644 --- a/pkg/middlewares/addprefix/add_prefix_test.go +++ b/pkg/middlewares/addprefix/add_prefix_test.go @@ -7,7 +7,6 @@ import ( "github.com/containous/traefik/v2/pkg/config/dynamic" "github.com/containous/traefik/v2/pkg/testhelpers" - "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -47,7 +46,6 @@ func TestNewAddPrefix(t *testing.T) { } func TestAddPrefix(t *testing.T) { - logrus.SetLevel(logrus.DebugLevel) testCases := []struct { desc string prefix dynamic.AddPrefix @@ -61,6 +59,12 @@ func TestAddPrefix(t *testing.T) { path: "/b", expectedPath: "/a/b", }, + { + desc: "Works with missing leading slash", + prefix: dynamic.AddPrefix{Prefix: "a"}, + path: "/", + expectedPath: "/a/", + }, { desc: "Works with a raw path", prefix: dynamic.AddPrefix{Prefix: "/a"}, diff --git a/pkg/middlewares/stripprefix/strip_prefix.go b/pkg/middlewares/stripprefix/strip_prefix.go index b8008689a..1a0fd8bbc 100644 --- a/pkg/middlewares/stripprefix/strip_prefix.go +++ b/pkg/middlewares/stripprefix/strip_prefix.go @@ -20,18 +20,20 @@ const ( // stripPrefix is a middleware used to strip prefix from an URL request. type stripPrefix struct { - next http.Handler - prefixes []string - name string + next http.Handler + prefixes []string + forceSlash bool // TODO Must be removed (breaking), the default behavior must be forceSlash=false + name string } // New creates a new strip prefix middleware. func New(ctx context.Context, next http.Handler, config dynamic.StripPrefix, name string) (http.Handler, error) { log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware") return &stripPrefix{ - prefixes: config.Prefixes, - next: next, - name: name, + prefixes: config.Prefixes, + forceSlash: config.ForceSlash, + next: next, + name: name, }, nil } @@ -42,9 +44,9 @@ func (s *stripPrefix) GetTracingInformation() (string, ext.SpanKindEnum) { func (s *stripPrefix) ServeHTTP(rw http.ResponseWriter, req *http.Request) { for _, prefix := range s.prefixes { if strings.HasPrefix(req.URL.Path, prefix) { - req.URL.Path = getPrefixStripped(req.URL.Path, prefix) + req.URL.Path = s.getPrefixStripped(req.URL.Path, prefix) if req.URL.RawPath != "" { - req.URL.RawPath = getPrefixStripped(req.URL.RawPath, prefix) + req.URL.RawPath = s.getPrefixStripped(req.URL.RawPath, prefix) } s.serveRequest(rw, req, strings.TrimSpace(prefix)) return @@ -59,10 +61,25 @@ func (s *stripPrefix) serveRequest(rw http.ResponseWriter, req *http.Request, pr s.next.ServeHTTP(rw, req) } -func getPrefixStripped(s, prefix string) string { - return ensureLeadingSlash(strings.TrimPrefix(s, prefix)) +func (s *stripPrefix) getPrefixStripped(urlPath, prefix string) string { + if s.forceSlash { + // Only for compatibility reason with the previous behavior, + // but the previous behavior is wrong. + // This needs to be removed in the next breaking version. + return "/" + strings.TrimPrefix(strings.TrimPrefix(urlPath, prefix), "/") + } + + return ensureLeadingSlash(strings.TrimPrefix(urlPath, prefix)) } func ensureLeadingSlash(str string) string { - return "/" + strings.TrimPrefix(str, "/") + if str == "" { + return str + } + + if str[0] == '/' { + return str + } + + return "/" + str } diff --git a/pkg/middlewares/stripprefix/strip_prefix_test.go b/pkg/middlewares/stripprefix/strip_prefix_test.go index 449720902..13a5aedb2 100644 --- a/pkg/middlewares/stripprefix/strip_prefix_test.go +++ b/pkg/middlewares/stripprefix/strip_prefix_test.go @@ -31,6 +31,17 @@ func TestStripPrefix(t *testing.T) { expectedStatusCode: http.StatusOK, expectedPath: "/noprefixes", }, + { + desc: "wildcard (.*) requests (ForceSlash)", + config: dynamic.StripPrefix{ + Prefixes: []string{"/"}, + ForceSlash: true, + }, + path: "/", + expectedStatusCode: http.StatusOK, + expectedPath: "/", + expectedHeader: "/", + }, { desc: "wildcard (.*) requests", config: dynamic.StripPrefix{ @@ -38,9 +49,20 @@ func TestStripPrefix(t *testing.T) { }, path: "/", expectedStatusCode: http.StatusOK, - expectedPath: "/", + expectedPath: "", expectedHeader: "/", }, + { + desc: "prefix and path matching (ForceSlash)", + config: dynamic.StripPrefix{ + Prefixes: []string{"/stat"}, + ForceSlash: true, + }, + path: "/stat", + expectedStatusCode: http.StatusOK, + expectedPath: "/", + expectedHeader: "/stat", + }, { desc: "prefix and path matching", config: dynamic.StripPrefix{ @@ -48,9 +70,20 @@ func TestStripPrefix(t *testing.T) { }, path: "/stat", expectedStatusCode: http.StatusOK, - expectedPath: "/", + expectedPath: "", expectedHeader: "/stat", }, + { + desc: "path prefix on exactly matching path (ForceSlash)", + config: dynamic.StripPrefix{ + Prefixes: []string{"/stat/"}, + ForceSlash: true, + }, + path: "/stat/", + expectedStatusCode: http.StatusOK, + expectedPath: "/", + expectedHeader: "/stat/", + }, { desc: "path prefix on exactly matching path", config: dynamic.StripPrefix{ @@ -58,7 +91,7 @@ func TestStripPrefix(t *testing.T) { }, path: "/stat/", expectedStatusCode: http.StatusOK, - expectedPath: "/", + expectedPath: "", expectedHeader: "/stat/", }, { @@ -101,6 +134,17 @@ func TestStripPrefix(t *testing.T) { expectedPath: "/us", expectedHeader: "/stat", }, + { + desc: "later prefix matching (ForceSlash)", + config: dynamic.StripPrefix{ + Prefixes: []string{"/mismatch", "/stat"}, + ForceSlash: true, + }, + path: "/stat", + expectedStatusCode: http.StatusOK, + expectedPath: "/", + expectedHeader: "/stat", + }, { desc: "later prefix matching", config: dynamic.StripPrefix{ @@ -108,7 +152,7 @@ func TestStripPrefix(t *testing.T) { }, path: "/stat", expectedStatusCode: http.StatusOK, - expectedPath: "/", + expectedPath: "", expectedHeader: "/stat", }, { @@ -162,12 +206,15 @@ func TestStripPrefix(t *testing.T) { assert.Equal(t, test.expectedRawPath, actualRawPath, "Unexpected raw path.") assert.Equal(t, test.expectedHeader, actualHeader, "Unexpected '%s' header.", ForwardedPrefixHeader) - expectedURI := test.expectedPath + expectedRequestURI := test.expectedPath if test.expectedRawPath != "" { // go HTTP uses the raw path when existent in the RequestURI - expectedURI = test.expectedRawPath + expectedRequestURI = test.expectedRawPath } - assert.Equal(t, expectedURI, requestURI, "Unexpected request URI.") + if test.expectedPath == "" { + expectedRequestURI = "/" + } + assert.Equal(t, expectedRequestURI, requestURI, "Unexpected request URI.") }) } } diff --git a/pkg/middlewares/stripprefixregex/strip_prefix_regex.go b/pkg/middlewares/stripprefixregex/strip_prefix_regex.go index 4024ac4f0..4feda225e 100644 --- a/pkg/middlewares/stripprefixregex/strip_prefix_regex.go +++ b/pkg/middlewares/stripprefixregex/strip_prefix_regex.go @@ -60,12 +60,12 @@ func (s *stripPrefixRegex) ServeHTTP(rw http.ResponseWriter, req *http.Request) req.Header.Add(stripprefix.ForwardedPrefixHeader, prefix) - req.URL.Path = strings.Replace(req.URL.Path, prefix, "", 1) + req.URL.Path = ensureLeadingSlash(strings.Replace(req.URL.Path, prefix, "", 1)) if req.URL.RawPath != "" { - req.URL.RawPath = req.URL.RawPath[len(prefix):] + req.URL.RawPath = ensureLeadingSlash(req.URL.RawPath[len(prefix):]) } - req.RequestURI = ensureLeadingSlash(req.URL.RequestURI()) + req.RequestURI = req.URL.RequestURI() s.next.ServeHTTP(rw, req) return } @@ -75,5 +75,13 @@ func (s *stripPrefixRegex) ServeHTTP(rw http.ResponseWriter, req *http.Request) } func ensureLeadingSlash(str string) string { - return "/" + strings.TrimPrefix(str, "/") + if str == "" { + return str + } + + if str[0] == '/' { + return str + } + + return "/" + str } diff --git a/pkg/middlewares/stripprefixregex/strip_prefix_regex_test.go b/pkg/middlewares/stripprefixregex/strip_prefix_regex_test.go index 297fa7192..6e0293083 100644 --- a/pkg/middlewares/stripprefixregex/strip_prefix_regex_test.go +++ b/pkg/middlewares/stripprefixregex/strip_prefix_regex_test.go @@ -31,42 +31,66 @@ func TestStripPrefixRegex(t *testing.T) { expectedPath: "/a/test", }, { - path: "/a/test", + path: "/a/test/", expectedStatusCode: http.StatusOK, - expectedPath: "/a/test", + expectedPath: "/a/test/", + }, + { + path: "/a/api/", + expectedStatusCode: http.StatusOK, + expectedPath: "", + expectedHeader: "/a/api/", }, { path: "/a/api/test", expectedStatusCode: http.StatusOK, - expectedPath: "test", + expectedPath: "/test", + expectedHeader: "/a/api/", + }, + { + path: "/a/api/test/", + expectedStatusCode: http.StatusOK, + expectedPath: "/test/", expectedHeader: "/a/api/", }, { path: "/b/api/", expectedStatusCode: http.StatusOK, + expectedPath: "", expectedHeader: "/b/api/", }, + { + path: "/b/api", + expectedStatusCode: http.StatusOK, + expectedPath: "/b/api", + }, { path: "/b/api/test1", expectedStatusCode: http.StatusOK, - expectedPath: "test1", + expectedPath: "/test1", expectedHeader: "/b/api/", }, { path: "/b/api2/test2", expectedStatusCode: http.StatusOK, - expectedPath: "test2", + expectedPath: "/test2", expectedHeader: "/b/api2/", }, { path: "/c/api/123/", expectedStatusCode: http.StatusOK, + expectedPath: "", expectedHeader: "/c/api/123/", }, + { + path: "/c/api/123", + expectedStatusCode: http.StatusOK, + expectedPath: "/c/api/123", + }, { path: "/c/api/123/test3", expectedStatusCode: http.StatusOK, - expectedPath: "test3", + expectedPath: "/test3", expectedHeader: "/c/api/123/", }, { @@ -77,8 +101,8 @@ func TestStripPrefixRegex(t *testing.T) { { path: "/a/api/a%2Fb", expectedStatusCode: http.StatusOK, - expectedPath: "a/b", - expectedRawPath: "a%2Fb", + expectedPath: "/a/b", + expectedRawPath: "/a%2Fb", expectedHeader: "/a/api/", }, } @@ -88,11 +112,12 @@ func TestStripPrefixRegex(t *testing.T) { t.Run(test.path, func(t *testing.T) { t.Parallel() - var actualPath, actualRawPath, actualHeader string + var actualPath, actualRawPath, actualHeader, requestURI string handlerPath := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { actualPath = r.URL.Path actualRawPath = r.URL.RawPath actualHeader = r.Header.Get(stripprefix.ForwardedPrefixHeader) + requestURI = r.RequestURI }) handler, err := New(context.Background(), handlerPath, testPrefixRegex, "foo-strip-prefix-regex") require.NoError(t, err) @@ -106,6 +131,18 @@ func TestStripPrefixRegex(t *testing.T) { assert.Equal(t, test.expectedPath, actualPath, "Unexpected path.") assert.Equal(t, test.expectedRawPath, actualRawPath, "Unexpected raw path.") assert.Equal(t, test.expectedHeader, actualHeader, "Unexpected '%s' header.", stripprefix.ForwardedPrefixHeader) + + if test.expectedPath != test.path { + expectedRequestURI := test.expectedPath + if test.expectedRawPath != "" { + // go HTTP uses the raw path when existent in the RequestURI + expectedRequestURI = test.expectedRawPath + } + if test.expectedPath == "" { + expectedRequestURI = "/" + } + assert.Equal(t, expectedRequestURI, requestURI, "Unexpected request URI.") + } }) } } From 1db22f4a1b416543f895abbfbef94bbc14fc6e91 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Doumenjou Date: Thu, 14 Nov 2019 18:22:04 +0100 Subject: [PATCH 17/23] Prepare release v2.0.5 --- CHANGELOG.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96f36e5ed..7cec1b7d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,24 @@ +## [v2.0.5](https://github.com/containous/traefik/tree/v2.0.5) (2019-11-14) +[All Commits](https://github.com/containous/traefik/compare/v2.0.4...v2.0.5) + +**Bug fixes:** +- **[metrics]** fix: metric with services LB. ([#5759](https://github.com/containous/traefik/pull/5759) by [ldez](https://github.com/ldez)) +- **[middleware]** fix: stripPrefix middleware with empty resulting path. ([#5806](https://github.com/containous/traefik/pull/5806) by [ldez](https://github.com/ldez)) +- **[middleware]** Fix rate limiting and SSE ([#5737](https://github.com/containous/traefik/pull/5737) by [sylr](https://github.com/sylr)) +- **[tracing]** Upgrades zipkin library to avoid errors when using textMap. ([#5754](https://github.com/containous/traefik/pull/5754) by [jcchavezs](https://github.com/jcchavezs)) + +**Documentation:** +- **[acme,cluster]** Update ACME storage docs to remove reference to KV store in CE ([#5433](https://github.com/containous/traefik/pull/5433) by [bradjones1](https://github.com/bradjones1)) +- **[api]** docs: remove field api.entryPoint ([#5776](https://github.com/containous/traefik/pull/5776) by [waitingsong](https://github.com/waitingsong)) +- **[api]** Adds missed quotes in api.md ([#5787](https://github.com/containous/traefik/pull/5787) by [woto](https://github.com/woto)) +- **[docker/swarm]** Dashboard example with swarm ([#5795](https://github.com/containous/traefik/pull/5795) by [dduportal](https://github.com/dduportal)) +- **[docker]** Fix error in link description for priority ([#5746](https://github.com/containous/traefik/pull/5746) by [ASDFGamer](https://github.com/ASDFGamer)) +- **[k8s]** Wrong endpoint on the TLS secret example ([#5817](https://github.com/containous/traefik/pull/5817) by [yacinelazaar](https://github.com/yacinelazaar)) +- **[middleware,docker]** Double dollar on docker-compose config ([#5775](https://github.com/containous/traefik/pull/5775) by [clery](https://github.com/clery)) +- Fix quickstart link in README ([#5794](https://github.com/containous/traefik/pull/5794) by [mcky](https://github.com/mcky)) +- fix typo in v1 to v2 migration guide ([#5820](https://github.com/containous/traefik/pull/5820) by [fschl](https://github.com/fschl)) +- slashes ended up in bad place. ([#5798](https://github.com/containous/traefik/pull/5798) by [icepic](https://github.com/icepic)) + ## [v2.0.4](https://github.com/containous/traefik/tree/v2.0.4) (2019-10-28) [All Commits](https://github.com/containous/traefik/compare/v2.0.3...v2.0.4) From 424b97994ed61b41ec14253862577f05cd27dae3 Mon Sep 17 00:00:00 2001 From: Blake Buthod Date: Thu, 14 Nov 2019 17:42:04 -0600 Subject: [PATCH 18/23] Fixed spelling error --- docs/content/routing/routers/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/routing/routers/index.md b/docs/content/routing/routers/index.md index 4e00a72cc..de2fae0d9 100644 --- a/docs/content/routing/routers/index.md +++ b/docs/content/routing/routers/index.md @@ -300,7 +300,7 @@ A value of `0` for the priority is ignored: `priority = 0` means that the defaul The previous table shows that `Router-1` has a higher priority than `Router-2`. - To solve this issue, the priority must be setted. + To solve this issue, the priority must be set. ??? example "Set priorities -- using the [File Provider](../../providers/file.md)" From b3078b75cda2898aba023fe73ed7930074ea866b Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Fri, 15 Nov 2019 07:50:04 +0100 Subject: [PATCH 19/23] fix: location header rewrite. Co-authored-by: Daniel Tomcej --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e171de90c..cdb7c05b2 100644 --- a/go.mod +++ b/go.mod @@ -82,7 +82,7 @@ require ( github.com/uber/jaeger-client-go v2.16.0+incompatible github.com/uber/jaeger-lib v2.0.0+incompatible github.com/unrolled/render v1.0.1 - github.com/unrolled/secure v1.0.4 + github.com/unrolled/secure v1.0.5 github.com/vdemeester/shakers v0.1.0 github.com/vulcand/oxy v1.0.0 github.com/vulcand/predicate v1.1.0 diff --git a/go.sum b/go.sum index 94dd6bd84..b37fd872b 100644 --- a/go.sum +++ b/go.sum @@ -488,8 +488,8 @@ github.com/uber/jaeger-lib v2.0.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6 github.com/ugorji/go v0.0.0-20171019201919-bdcc60b419d1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= github.com/unrolled/render v1.0.1 h1:VDDnQQVfBMsOsp3VaCJszSO0nkBIVEYoPWeRThk9spY= github.com/unrolled/render v1.0.1/go.mod h1:gN9T0NhL4Bfbwu8ann7Ry/TGHYfosul+J0obPf6NBdM= -github.com/unrolled/secure v1.0.4 h1:DksfKsRTyXP2R8quDdOOuRpRO45VprFL0X9t9+JX1PU= -github.com/unrolled/secure v1.0.4/go.mod h1:R6rugAuzh4TQpbFAq69oqZggyBQxFRFQIewtz5z7Jsc= +github.com/unrolled/secure v1.0.5 h1:KRGJ8DQC3jKpERjBKF3H6b3HcAsM/SRTVwfNJnWs25E= +github.com/unrolled/secure v1.0.5/go.mod h1:R6rugAuzh4TQpbFAq69oqZggyBQxFRFQIewtz5z7Jsc= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/vdemeester/shakers v0.1.0 h1:K+n9sSyUCg2ywmZkv+3c7vsYZfivcfKhMh8kRxCrONM= github.com/vdemeester/shakers v0.1.0/go.mod h1:IZ1HHynUOQt32iQ3rvAeVddXLd19h/6LWiKsh9RZtAQ= From e28d9426b9e2ce6d4aaebbbf706d2706e8d3a76f Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Fri, 15 Nov 2019 10:08:05 +0100 Subject: [PATCH 20/23] doc: fix wrong acme information --- docs/content/routing/routers/index.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docs/content/routing/routers/index.md b/docs/content/routing/routers/index.md index de2fae0d9..773b71d16 100644 --- a/docs/content/routing/routers/index.md +++ b/docs/content/routing/routers/index.md @@ -419,10 +419,6 @@ Traefik will terminate the SSL connections (meaning that it will send decrypted tls: {} ``` -!!! info "HTTPS & ACME" - - In the current version, with [ACME](../../https/acme.md) enabled, automatic certificate generation will apply to every router declaring a TLS section. - !!! important "Routers for HTTP & HTTPS" If you need to define the same route for both HTTP and HTTPS requests, you will need to define two different routers: @@ -846,10 +842,6 @@ Services are the target for the router. passthrough: true ``` -!!! info "TLS & ACME" - - In the current version, with [ACME](../../https/acme.md) enabled, automatic certificate generation will apply to every router declaring a TLS section. - #### `options` The `options` field enables fine-grained control of the TLS parameters. From 8f340afca1fad06f7f47bdc33ff23863098b01b8 Mon Sep 17 00:00:00 2001 From: Pascal Andy Date: Fri, 15 Nov 2019 04:48:05 -0500 Subject: [PATCH 21/23] Add back the security section from v1 --- .../contributing/submitting-security-issues.md | 16 ++++++++++++++++ docs/mkdocs.yml | 1 + 2 files changed, 17 insertions(+) create mode 100644 docs/content/contributing/submitting-security-issues.md diff --git a/docs/content/contributing/submitting-security-issues.md b/docs/content/contributing/submitting-security-issues.md new file mode 100644 index 000000000..be3187ed1 --- /dev/null +++ b/docs/content/contributing/submitting-security-issues.md @@ -0,0 +1,16 @@ +# Security + +## Security Advisories + +We strongly advise you to join our mailing list to be aware of the latest announcements from our security team. +You can subscribe sending a mail to security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security). + +## CVE + +Reported vulnerabilities can be found on +[cve.mitre.org](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=traefik). + +## Report a Vulnerability + +We want to keep Traefik safe for everyone. +If you've discovered a security vulnerability in Traefik, we appreciate your help in disclosing it to us in a responsible manner, using [this form](https://security.traefik.io). diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 45e82db52..6e30c5f20 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -157,6 +157,7 @@ nav: - 'Thank You!': 'contributing/thank-you.md' - 'Submitting Issues': 'contributing/submitting-issues.md' - 'Submitting PRs': 'contributing/submitting-pull-requests.md' + - 'Security': 'contributing/submitting-security-issues.md' - 'Building and Testing': 'contributing/building-testing.md' - 'Documentation': 'contributing/documentation.md' - 'Data Collection': 'contributing/data-collection.md' From 3fd330c2fb70f6f3e277101c5653ba6ef90aa93e Mon Sep 17 00:00:00 2001 From: kolaente Date: Fri, 15 Nov 2019 12:06:05 +0100 Subject: [PATCH 22/23] Update go-acme/lego to 3.2.0 --- docs/content/https/acme.md | 1 + go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/content/https/acme.md b/docs/content/https/acme.md index 68d65ebab..80e48d1d6 100644 --- a/docs/content/https/acme.md +++ b/docs/content/https/acme.md @@ -215,6 +215,7 @@ For example, `CF_API_EMAIL_FILE=/run/secrets/traefik_cf-api-email` could be used | [ACME DNS](https://github.com/joohoi/acme-dns) | `acme-dns` | `ACME_DNS_API_BASE`, `ACME_DNS_STORAGE_PATH` | [Additional configuration](https://go-acme.github.io/lego/dns/acme-dns) | | [Alibaba Cloud](https://www.alibabacloud.com) | `alidns` | `ALICLOUD_ACCESS_KEY`, `ALICLOUD_SECRET_KEY`, `ALICLOUD_REGION_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/alidns) | | [Auroradns](https://www.pcextreme.com/aurora/dns) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` | [Additional configuration](https://go-acme.github.io/lego/dns/auroradns) | +| [Autodns](https://www.internetx.com/domains/autodns/) | `autodns` | `AUTODNS_API_USER`, `AUTODNS_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/autodns) | | [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP`, `[AZURE_METADATA_ENDPOINT]` | [Additional configuration](https://go-acme.github.io/lego/dns/azure) | | [Bindman](https://github.com/labbsr0x/bindman-dns-webhook) | `bindman` | `BINDMAN_MANAGER_ADDRESS` | [Additional configuration](https://go-acme.github.io/lego/dns/bindman) | | [Blue Cat](https://www.bluecatnetworks.com/) | `bluecat` | `BLUECAT_SERVER_URL`, `BLUECAT_USER_NAME`, `BLUECAT_PASSWORD`, `BLUECAT_CONFIG_NAME`, `BLUECAT_DNS_VIEW` | [Additional configuration](https://go-acme.github.io/lego/dns/bluecat) | diff --git a/go.mod b/go.mod index cdb7c05b2..f19f21bb0 100644 --- a/go.mod +++ b/go.mod @@ -39,7 +39,7 @@ require ( github.com/felixge/httpsnoop v1.0.0 // indirect github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2 - github.com/go-acme/lego/v3 v3.1.0 + github.com/go-acme/lego/v3 v3.2.0 github.com/go-check/check v0.0.0-00010101000000-000000000000 github.com/go-kit/kit v0.9.0 github.com/golang/protobuf v1.3.2 diff --git a/go.sum b/go.sum index b37fd872b..8a01a5fe3 100644 --- a/go.sum +++ b/go.sum @@ -181,8 +181,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2 h1:df6OFl8WNXk82xxP3R9ZPZ5seOA8XZkwLdbEzZF1/xI= github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2/go.mod h1:GLyXJD41gBO/NPKVPGQbhyyC06eugGy15QEZyUkE2/s= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-acme/lego/v3 v3.1.0 h1:yanYFoYW8azFkCvJfIk7edWWfjkYkhDxe45ZsxoW4Xk= -github.com/go-acme/lego/v3 v3.1.0/go.mod h1:074uqt+JS6plx+c9Xaiz6+L+GBb+7itGtzfcDM2AhEE= +github.com/go-acme/lego/v3 v3.2.0 h1:z0zvNlL1niv/1qA06V5X1BRC5PeLoGKAlVaWthXQz9c= +github.com/go-acme/lego/v3 v3.2.0/go.mod h1:074uqt+JS6plx+c9Xaiz6+L+GBb+7itGtzfcDM2AhEE= github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= From 42a8d84a1f13bc468e077478e51a60c99a520446 Mon Sep 17 00:00:00 2001 From: Ludovic Fernandez Date: Fri, 15 Nov 2019 12:36:04 +0100 Subject: [PATCH 23/23] X-Forwarded-Proto must not skip the redirection. --- pkg/middlewares/redirect/redirect.go | 8 +---- .../redirect/redirect_regex_test.go | 28 +++++++++++++++--- .../redirect/redirect_scheme_test.go | 29 +++++++++++++++---- 3 files changed, 48 insertions(+), 17 deletions(-) diff --git a/pkg/middlewares/redirect/redirect.go b/pkg/middlewares/redirect/redirect.go index 0e87013c3..9224a3252 100644 --- a/pkg/middlewares/redirect/redirect.go +++ b/pkg/middlewares/redirect/redirect.go @@ -132,19 +132,13 @@ func rawURL(req *http.Request) string { uri = match[4] } - if req.TLS != nil || isXForwardedHTTPS(req) { + if req.TLS != nil { scheme = "https" } return strings.Join([]string{scheme, "://", host, port, uri}, "") } -func isXForwardedHTTPS(request *http.Request) bool { - xForwardedProto := request.Header.Get("X-Forwarded-Proto") - - return len(xForwardedProto) > 0 && xForwardedProto == "https" -} - func applyString(in string, out io.Writer, req *http.Request) error { t, err := template.New("t").Parse(in) if err != nil { diff --git a/pkg/middlewares/redirect/redirect_regex_test.go b/pkg/middlewares/redirect/redirect_regex_test.go index ca4edbb79..fd4d71640 100644 --- a/pkg/middlewares/redirect/redirect_regex_test.go +++ b/pkg/middlewares/redirect/redirect_regex_test.go @@ -19,6 +19,7 @@ func TestRedirectRegexHandler(t *testing.T) { config dynamic.RedirectRegex method string url string + headers map[string]string secured bool expectedURL string expectedStatus int @@ -104,6 +105,19 @@ func TestRedirectRegexHandler(t *testing.T) { expectedURL: "https://foo:443", expectedStatus: http.StatusFound, }, + { + desc: "HTTP to HTTPS, with X-Forwarded-Proto", + config: dynamic.RedirectRegex{ + Regex: `http://foo:80`, + Replacement: "https://foo:443", + }, + url: "http://foo:80", + headers: map[string]string{ + "X-Forwarded-Proto": "https", + }, + expectedURL: "https://foo:443", + expectedStatus: http.StatusFound, + }, { desc: "HTTPS to HTTP", config: dynamic.RedirectRegex{ @@ -171,12 +185,18 @@ func TestRedirectRegexHandler(t *testing.T) { if test.method != "" { method = test.method } - r := testhelpers.MustNewRequest(method, test.url, nil) + + req := testhelpers.MustNewRequest(method, test.url, nil) if test.secured { - r.TLS = &tls.ConnectionState{} + req.TLS = &tls.ConnectionState{} } - r.Header.Set("X-Foo", "bar") - handler.ServeHTTP(recorder, r) + + for k, v := range test.headers { + req.Header.Set(k, v) + } + + req.Header.Set("X-Foo", "bar") + handler.ServeHTTP(recorder, req) assert.Equal(t, test.expectedStatus, recorder.Code) switch test.expectedStatus { diff --git a/pkg/middlewares/redirect/redirect_scheme_test.go b/pkg/middlewares/redirect/redirect_scheme_test.go index 384780d53..b0694cd57 100644 --- a/pkg/middlewares/redirect/redirect_scheme_test.go +++ b/pkg/middlewares/redirect/redirect_scheme_test.go @@ -19,6 +19,7 @@ func TestRedirectSchemeHandler(t *testing.T) { config dynamic.RedirectScheme method string url string + headers map[string]string secured bool expectedURL string expectedStatus int @@ -39,6 +40,18 @@ func TestRedirectSchemeHandler(t *testing.T) { expectedURL: "https://foo", expectedStatus: http.StatusFound, }, + { + desc: "HTTP to HTTPS, with X-Forwarded-Proto", + config: dynamic.RedirectScheme{ + Scheme: "https", + }, + url: "http://foo", + headers: map[string]string{ + "X-Forwarded-Proto": "https", + }, + expectedURL: "https://foo", + expectedStatus: http.StatusFound, + }, { desc: "HTTP with port to HTTPS without port", config: dynamic.RedirectScheme{ @@ -197,13 +210,17 @@ func TestRedirectSchemeHandler(t *testing.T) { if test.method != "" { method = test.method } - r := httptest.NewRequest(method, test.url, nil) + req := httptest.NewRequest(method, test.url, nil) + + for k, v := range test.headers { + req.Header.Set(k, v) + } if test.secured { - r.TLS = &tls.ConnectionState{} + req.TLS = &tls.ConnectionState{} } - r.Header.Set("X-Foo", "bar") - handler.ServeHTTP(recorder, r) + req.Header.Set("X-Foo", "bar") + handler.ServeHTTP(recorder, req) assert.Equal(t, test.expectedStatus, recorder.Code) @@ -223,9 +240,9 @@ func TestRedirectSchemeHandler(t *testing.T) { if re.Match([]byte(test.url)) { match := re.FindStringSubmatch(test.url) - r.RequestURI = match[4] + req.RequestURI = match[4] - handler.ServeHTTP(recorder, r) + handler.ServeHTTP(recorder, req) assert.Equal(t, test.expectedStatus, recorder.Code) if test.expectedStatus == http.StatusMovedPermanently ||