Merge branch 'v2.0' into master

This commit is contained in:
Fernandez Ludovic 2019-11-15 13:34:41 +01:00
commit ca9eaf383a
42 changed files with 645 additions and 444 deletions

View file

@ -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) ## [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) [All Commits](https://github.com/containous/traefik/compare/v2.0.3...v2.0.4)

View file

@ -77,7 +77,7 @@ _(But if you'd rather configure some of your routes manually, Traefik supports t
## Quickstart ## 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 ## Web UI

View file

@ -7,5 +7,6 @@
"MD026": false, "MD026": false,
"MD033": false, "MD033": false,
"MD034": false, "MD034": false,
"MD036": false "MD036": false,
"MD046": false
} }

View file

@ -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 \ RUN apk --no-cache --no-progress add \
libcurl \ libcurl \
ruby \ ruby \
@ -11,21 +8,17 @@ RUN apk --no-cache --no-progress add \
ruby-etc \ ruby-etc \
ruby-ffi \ ruby-ffi \
ruby-json \ ruby-json \
&& apk add --no-cache --virtual build-dependencies \ ruby-nokogiri
build-base \ RUN gem install html-proofer --version 3.13.0 --no-document -- --use-system-libraries
libcurl \
libxml2-dev \
libxslt-dev \
ruby-dev \
&& gem install --no-document html-proofer -v 3.10.2 \
&& apk del build-dependencies
# After Ruby, some NodeJS YAY! # After Ruby, some NodeJS YAY!
RUN apk --no-cache --no-progress add \ RUN apk --no-cache --no-progress add \
git \ git \
nodejs \ nodejs \
npm \ 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 # Finally the shell tools we need for later
# tini helps to terminate properly all the parallelized tasks when sending CTRL-C # tini helps to terminate properly all the parallelized tasks when sending CTRL-C

View file

@ -62,6 +62,7 @@ Requirements:
- `go` v1.13+ - `go` v1.13+
- environment variable `GO111MODULE=on` - environment variable `GO111MODULE=on`
- go-bindata `GO111MODULE=off go get -u github.com/containous/go-bindata/...`
!!! tip "Source Directory" !!! tip "Source Directory"

View file

@ -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).

View file

@ -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) | | [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) | | [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) | | [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) | | [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) | | [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) | | [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) |
@ -390,7 +391,7 @@ docker run -v "/my/host/acme:/etc/traefik/acme" traefik
``` ```
!!! warning !!! 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 ## Fallback

View file

@ -63,4 +63,5 @@ http:
### `prefix` ### `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 (`/`).

View file

@ -15,7 +15,7 @@ The ReplaceRegex replace a path from an url to another with regex matching and r
# Replace path with regex # Replace path with regex
labels: labels:
- "traefik.http.middlewares.test-replacepathregex.replacepathregex.regex=^/foo/(.*)" - "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" ```yaml tab="Kubernetes"

View file

@ -90,3 +90,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). 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. 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` |

View file

@ -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://<IP>:<port>/`. In this case, you must: with the path `/admin` stripped, e.g. to `http://<IP>:<port>/`. In this case, you must:
* First, configure a router named `admin` with a rule matching at least the path prefix with the `PathPrefix` keyword, * 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" !!! 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: 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 * 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" !!! info "Dashboard with k8s and dedicated router"

View file

@ -1,4 +1,5 @@
{ {
"extends": "../../.markdownlint.json", "extends": "../../.markdownlint.json",
"MD041": false,
"MD046": false "MD046": false
} }

View file

@ -43,71 +43,7 @@ api: {}
And then define a routing configuration on Traefik itself with the And then define a routing configuration on Traefik itself with the
[dynamic configuration](../getting-started/configuration-overview.md#the-dynamic-configuration): [dynamic configuration](../getting-started/configuration-overview.md#the-dynamic-configuration):
```yaml tab="Docker" --8<-- "content/operations/include-api-examples.md"
# 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="Consul Catalog"
# Declaring the user list
- "traefik.http.routers.api.rule=PathPrefix(`/api`) || PathPrefix(`/dashboard`)"
- "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"
```
??? warning "The router's [rule](../../routing/routers#rule) must catch requests for the URI path `/api`" ??? 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. Using an "Host" rule is recommended, by catching all the incoming traffic on this host domain to the API.

View file

@ -60,8 +60,8 @@ api:
--api.dashboard=true --api.dashboard=true
``` ```
Then define a routing configuration on Traefik itself, Then define a routing configuration on Traefik itself,
with a router attached to the service `api@internal` in the with a router attached to the service `api@internal` in the
[dynamic configuration](../getting-started/configuration-overview.md#the-dynamic-configuration), [dynamic configuration](../getting-started/configuration-overview.md#the-dynamic-configuration),
to allow defining: to allow defining:
@ -73,64 +73,7 @@ to allow defining:
through Traefik itself (sometimes referred as "Traefik-ception"). through Traefik itself (sometimes referred as "Traefik-ception").
??? example "Dashboard Dynamic Configuration Examples" ??? example "Dashboard Dynamic Configuration Examples"
--8<-- "content/operations/include-api-examples.md"
```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"
```
### Dashboard Router Rule ### Dashboard Router Rule

View file

@ -0,0 +1,77 @@
```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"
```
```yaml tab="Consul Catalog"
# Dynamic Configuration
- "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"
```

View file

@ -103,6 +103,7 @@
- "traefik.http.middlewares.middleware17.replacepathregex.regex=foobar" - "traefik.http.middlewares.middleware17.replacepathregex.regex=foobar"
- "traefik.http.middlewares.middleware17.replacepathregex.replacement=foobar" - "traefik.http.middlewares.middleware17.replacepathregex.replacement=foobar"
- "traefik.http.middlewares.middleware18.retry.attempts=42" - "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.middleware19.stripprefix.prefixes=foobar, foobar"
- "traefik.http.middlewares.middleware20.stripprefixregex.regex=foobar, foobar" - "traefik.http.middlewares.middleware20.stripprefixregex.regex=foobar, foobar"
- "traefik.http.routers.router0.entrypoints=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].main=foobar"
- "traefik.http.routers.router1.tls.domains[1].sans=foobar, foobar" - "traefik.http.routers.router1.tls.domains[1].sans=foobar, foobar"
- "traefik.http.routers.router1.tls.options=foobar" - "traefik.http.routers.router1.tls.options=foobar"
- "traefik.http.services.service0.loadbalancer.healthcheck.headers.name0=foobar" - "traefik.http.services.service01.loadbalancer.healthcheck.headers.name0=foobar"
- "traefik.http.services.service0.loadbalancer.healthcheck.headers.name1=foobar" - "traefik.http.services.service01.loadbalancer.healthcheck.headers.name1=foobar"
- "traefik.http.services.service0.loadbalancer.healthcheck.hostname=foobar" - "traefik.http.services.service01.loadbalancer.healthcheck.hostname=foobar"
- "traefik.http.services.service0.loadbalancer.healthcheck.interval=foobar" - "traefik.http.services.service01.loadbalancer.healthcheck.interval=foobar"
- "traefik.http.services.service0.loadbalancer.healthcheck.path=foobar" - "traefik.http.services.service01.loadbalancer.healthcheck.path=foobar"
- "traefik.http.services.service0.loadbalancer.healthcheck.port=42" - "traefik.http.services.service01.loadbalancer.healthcheck.port=42"
- "traefik.http.services.service0.loadbalancer.healthcheck.scheme=foobar" - "traefik.http.services.service01.loadbalancer.healthcheck.scheme=foobar"
- "traefik.http.services.service0.loadbalancer.healthcheck.timeout=foobar" - "traefik.http.services.service01.loadbalancer.healthcheck.timeout=foobar"
- "traefik.http.services.service0.loadbalancer.passhostheader=true" - "traefik.http.services.service01.loadbalancer.passhostheader=true"
- "traefik.http.services.service0.loadbalancer.responseforwarding.flushinterval=foobar" - "traefik.http.services.service01.loadbalancer.responseforwarding.flushinterval=foobar"
- "traefik.http.services.service0.loadbalancer.sticky=true" - "traefik.http.services.service01.loadbalancer.sticky=true"
- "traefik.http.services.service0.loadbalancer.sticky.cookie.httponly=true" - "traefik.http.services.service01.loadbalancer.sticky.cookie.httponly=true"
- "traefik.http.services.service0.loadbalancer.sticky.cookie.name=foobar" - "traefik.http.services.service01.loadbalancer.sticky.cookie.name=foobar"
- "traefik.http.services.service0.loadbalancer.sticky.cookie.secure=true" - "traefik.http.services.service01.loadbalancer.sticky.cookie.secure=true"
- "traefik.http.services.service0.loadbalancer.server.port=foobar" - "traefik.http.services.service01.loadbalancer.server.port=foobar"
- "traefik.http.services.service0.loadbalancer.server.scheme=foobar" - "traefik.http.services.service01.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.tcp.routers.tcprouter0.entrypoints=foobar, foobar" - "traefik.tcp.routers.tcprouter0.entrypoints=foobar, foobar"
- "traefik.tcp.routers.tcprouter0.rule=foobar" - "traefik.tcp.routers.tcprouter0.rule=foobar"
- "traefik.tcp.routers.tcprouter0.service=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.domains[1].sans=foobar, foobar"
- "traefik.tcp.routers.tcprouter1.tls.options=foobar" - "traefik.tcp.routers.tcprouter1.tls.options=foobar"
- "traefik.tcp.routers.tcprouter1.tls.passthrough=true" - "traefik.tcp.routers.tcprouter1.tls.passthrough=true"
- "traefik.tcp.services.tcpservice0.loadbalancer.server.port=foobar" - "traefik.tcp.services.tcpservice01.loadbalancer.terminationdelay=42"
- "traefik.tcp.services.tcpservice0.loadbalancer.terminationdelay=100" - "traefik.tcp.services.tcpservice01.loadbalancer.server.port=foobar"
- "traefik.tcp.services.tcpservice1.loadbalancer.server.port=foobar"
- "traefik.tcp.services.tcpservice1.loadbalancer.terminationdelay=100"

View file

@ -245,6 +245,7 @@
[http.middlewares.Middleware19] [http.middlewares.Middleware19]
[http.middlewares.Middleware19.stripPrefix] [http.middlewares.Middleware19.stripPrefix]
prefixes = ["foobar", "foobar"] prefixes = ["foobar", "foobar"]
forceSlash = true
[http.middlewares.Middleware20] [http.middlewares.Middleware20]
[http.middlewares.Middleware20.stripPrefixRegex] [http.middlewares.Middleware20.stripPrefixRegex]
regex = ["foobar", "foobar"] regex = ["foobar", "foobar"]
@ -284,25 +285,25 @@
main = "foobar" main = "foobar"
sans = ["foobar", "foobar"] sans = ["foobar", "foobar"]
[tcp.services] [tcp.services]
[tcp.services.TCPService0] [tcp.services.TCPService01]
[tcp.services.TCPService0.loadBalancer] [tcp.services.TCPService01.loadBalancer]
terminationDelay = 100 terminationDelay = 42
[[tcp.services.TCPService0.loadBalancer.servers]] [[tcp.services.TCPService01.loadBalancer.servers]]
address = "foobar" address = "foobar"
[[tcp.services.TCPService0.loadBalancer.servers]] [[tcp.services.TCPService01.loadBalancer.servers]]
address = "foobar" address = "foobar"
[tcp.services.TCPService02]
[tcp.services.TCPService02.weighted]
[tcp.services.TCPService1] [[tcp.services.TCPService02.weighted.services]]
[tcp.services.TCPService1.loadBalancer] name = "foobar"
terminationDelay = 100 weight = 42
[[tcp.services.TCPService1.loadBalancer.servers]] [[tcp.services.TCPService02.weighted.services]]
address = "foobar" name = "foobar"
weight = 42
[[tcp.services.TCPService1.loadBalancer.servers]]
address = "foobar"
[tls] [tls]

View file

@ -2,11 +2,11 @@ http:
routers: routers:
Router0: Router0:
entryPoints: entryPoints:
- foobar - foobar
- foobar - foobar
middlewares: middlewares:
- foobar - foobar
- foobar - foobar
service: foobar service: foobar
rule: foobar rule: foobar
priority: 42 priority: 42
@ -14,21 +14,21 @@ http:
options: foobar options: foobar
certResolver: foobar certResolver: foobar
domains: domains:
- main: foobar - main: foobar
sans: sans:
- foobar - foobar
- foobar - foobar
- main: foobar - main: foobar
sans: sans:
- foobar - foobar
- foobar - foobar
Router1: Router1:
entryPoints: entryPoints:
- foobar - foobar
- foobar - foobar
middlewares: middlewares:
- foobar - foobar
- foobar - foobar
service: foobar service: foobar
rule: foobar rule: foobar
priority: 42 priority: 42
@ -36,14 +36,14 @@ http:
options: foobar options: foobar
certResolver: foobar certResolver: foobar
domains: domains:
- main: foobar - main: foobar
sans: sans:
- foobar - foobar
- foobar - foobar
- main: foobar - main: foobar
sans: sans:
- foobar - foobar
- foobar - foobar
services: services:
Service01: Service01:
loadBalancer: loadBalancer:
@ -53,8 +53,8 @@ http:
secure: true secure: true
httpOnly: true httpOnly: true
servers: servers:
- url: foobar - url: foobar
- url: foobar - url: foobar
healthCheck: healthCheck:
scheme: foobar scheme: foobar
path: foobar path: foobar
@ -72,17 +72,17 @@ http:
mirroring: mirroring:
service: foobar service: foobar
mirrors: mirrors:
- name: foobar - name: foobar
percent: 42 percent: 42
- name: foobar - name: foobar
percent: 42 percent: 42
Service03: Service03:
weighted: weighted:
services: services:
- name: foobar - name: foobar
weight: 42 weight: 42
- name: foobar - name: foobar
weight: 42 weight: 42
sticky: sticky:
cookie: cookie:
name: foobar name: foobar
@ -95,8 +95,8 @@ http:
Middleware01: Middleware01:
basicAuth: basicAuth:
users: users:
- foobar - foobar
- foobar - foobar
usersFile: foobar usersFile: foobar
realm: foobar realm: foobar
removeHeader: true removeHeader: true
@ -111,8 +111,8 @@ http:
Middleware03: Middleware03:
chain: chain:
middlewares: middlewares:
- foobar - foobar
- foobar - foobar
Middleware04: Middleware04:
circuitBreaker: circuitBreaker:
expression: foobar expression: foobar
@ -121,8 +121,8 @@ http:
Middleware06: Middleware06:
digestAuth: digestAuth:
users: users:
- foobar - foobar
- foobar - foobar
usersFile: foobar usersFile: foobar
removeHeader: true removeHeader: true
realm: foobar realm: foobar
@ -130,8 +130,8 @@ http:
Middleware07: Middleware07:
errors: errors:
status: status:
- foobar - foobar
- foobar - foobar
service: foobar service: foobar
query: foobar query: foobar
Middleware08: Middleware08:
@ -145,8 +145,8 @@ http:
insecureSkipVerify: true insecureSkipVerify: true
trustForwardHeader: true trustForwardHeader: true
authResponseHeaders: authResponseHeaders:
- foobar - foobar
- foobar - foobar
Middleware09: Middleware09:
headers: headers:
customRequestHeaders: customRequestHeaders:
@ -157,23 +157,23 @@ http:
name1: foobar name1: foobar
accessControlAllowCredentials: true accessControlAllowCredentials: true
accessControlAllowHeaders: accessControlAllowHeaders:
- foobar - foobar
- foobar - foobar
accessControlAllowMethods: accessControlAllowMethods:
- foobar - foobar
- foobar - foobar
accessControlAllowOrigin: foobar accessControlAllowOrigin: foobar
accessControlExposeHeaders: accessControlExposeHeaders:
- foobar - foobar
- foobar - foobar
accessControlMaxAge: 42 accessControlMaxAge: 42
addVaryHeader: true addVaryHeader: true
allowedHosts: allowedHosts:
- foobar - foobar
- foobar - foobar
hostsProxyHeaders: hostsProxyHeaders:
- foobar - foobar
- foobar - foobar
sslRedirect: true sslRedirect: true
sslTemporaryRedirect: true sslTemporaryRedirect: true
sslHost: foobar sslHost: foobar
@ -198,13 +198,13 @@ http:
Middleware10: Middleware10:
ipWhiteList: ipWhiteList:
sourceRange: sourceRange:
- foobar - foobar
- foobar - foobar
ipStrategy: ipStrategy:
depth: 42 depth: 42
excludedIPs: excludedIPs:
- foobar - foobar
- foobar - foobar
Middleware11: Middleware11:
inFlightReq: inFlightReq:
amount: 42 amount: 42
@ -212,8 +212,8 @@ http:
ipstrategy: ipstrategy:
depth: 42 depth: 42
excludedIPs: excludedIPs:
- foobar - foobar
- foobar - foobar
requestHeaderName: foobar requestHeaderName: foobar
requestHost: true requestHost: true
Middleware12: Middleware12:
@ -247,8 +247,8 @@ http:
ipstrategy: ipstrategy:
depth: 42 depth: 42
excludedIPs: excludedIPs:
- foobar - foobar
- foobar - foobar
requestHeaderName: foobar requestHeaderName: foobar
requestHost: true requestHost: true
Middleware14: Middleware14:
@ -274,19 +274,20 @@ http:
Middleware19: Middleware19:
stripPrefix: stripPrefix:
prefixes: prefixes:
- foobar - foobar
- foobar - foobar
forceSlash: true
Middleware20: Middleware20:
stripPrefixRegex: stripPrefixRegex:
regex: regex:
- foobar - foobar
- foobar - foobar
tcp: tcp:
routers: routers:
TCPRouter0: TCPRouter0:
entryPoints: entryPoints:
- foobar - foobar
- foobar - foobar
service: foobar service: foobar
rule: foobar rule: foobar
tls: tls:
@ -294,18 +295,18 @@ tcp:
options: foobar options: foobar
certResolver: foobar certResolver: foobar
domains: domains:
- main: foobar - main: foobar
sans: sans:
- foobar - foobar
- foobar - foobar
- main: foobar - main: foobar
sans: sans:
- foobar - foobar
- foobar - foobar
TCPRouter1: TCPRouter1:
entryPoints: entryPoints:
- foobar - foobar
- foobar - foobar
service: foobar service: foobar
rule: foobar rule: foobar
tls: tls:
@ -313,39 +314,40 @@ tcp:
options: foobar options: foobar
certResolver: foobar certResolver: foobar
domains: domains:
- main: foobar - main: foobar
sans: sans:
- foobar - foobar
- foobar - foobar
- main: foobar - main: foobar
sans: sans:
- foobar - foobar
- foobar - foobar
services: services:
TCPService0: TCPService01:
loadBalancer: loadBalancer:
terminationDelay: 100 terminationDelay: 42
servers: servers:
- address: foobar - address: foobar
- address: foobar - address: foobar
TCPService1: TCPService02:
loadBalancer: weighted:
terminationDelay: 100 services:
servers: - name: foobar
- address: foobar weight: 42
- address: foobar - name: foobar
weight: 42
tls: tls:
certificates: certificates:
- certFile: foobar - certFile: foobar
keyFile: foobar keyFile: foobar
stores: stores:
- foobar - foobar
- foobar - foobar
- certFile: foobar - certFile: foobar
keyFile: foobar keyFile: foobar
stores: stores:
- foobar - foobar
- foobar - foobar
options: options:
Options0: Options0:
minVersion: foobar minVersion: foobar
@ -354,12 +356,12 @@ tls:
- foobar - foobar
- foobar - foobar
curvePreferences: curvePreferences:
- foobar - foobar
- foobar - foobar
clientAuth: clientAuth:
caFiles: caFiles:
- foobar - foobar
- foobar - foobar
clientAuthType: foobar clientAuthType: foobar
sniStrict: true sniStrict: true
Options1: Options1:
@ -369,12 +371,12 @@ tls:
- foobar - foobar
- foobar - foobar
curvePreferences: curvePreferences:
- foobar - foobar
- foobar - foobar
clientAuth: clientAuth:
caFiles: caFiles:
- foobar - foobar
- foobar - foobar
clientAuthType: foobar clientAuthType: foobar
sniStrict: true sniStrict: true
stores: stores:

View file

@ -103,6 +103,7 @@
"traefik.http.middlewares.middleware17.replacepathregex.regex": "foobar", "traefik.http.middlewares.middleware17.replacepathregex.regex": "foobar",
"traefik.http.middlewares.middleware17.replacepathregex.replacement": "foobar", "traefik.http.middlewares.middleware17.replacepathregex.replacement": "foobar",
"traefik.http.middlewares.middleware18.retry.attempts": "42", "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.middleware19.stripprefix.prefixes": "foobar, foobar",
"traefik.http.middlewares.middleware20.stripprefixregex.regex": "foobar, foobar", "traefik.http.middlewares.middleware20.stripprefixregex.regex": "foobar, foobar",
"traefik.http.routers.router0.entrypoints": "foobar, foobar", "traefik.http.routers.router0.entrypoints": "foobar, foobar",
@ -110,7 +111,6 @@
"traefik.http.routers.router0.priority": "42", "traefik.http.routers.router0.priority": "42",
"traefik.http.routers.router0.rule": "foobar", "traefik.http.routers.router0.rule": "foobar",
"traefik.http.routers.router0.service": "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.certresolver": "foobar",
"traefik.http.routers.router0.tls.domains[0].main": "foobar", "traefik.http.routers.router0.tls.domains[0].main": "foobar",
"traefik.http.routers.router0.tls.domains[0].sans": "foobar, 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.priority": "42",
"traefik.http.routers.router1.rule": "foobar", "traefik.http.routers.router1.rule": "foobar",
"traefik.http.routers.router1.service": "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.certresolver": "foobar",
"traefik.http.routers.router1.tls.domains[0].main": "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[0].sans": "foobar, foobar",
"traefik.http.routers.router1.tls.domains[1].main": "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.domains[1].sans": "foobar, foobar",
"traefik.http.routers.router1.tls.options": "foobar", "traefik.http.routers.router1.tls.options": "foobar",
"traefik.http.services.service0.loadbalancer.healthcheck.headers.name0": "foobar", "traefik.http.services.service01.loadbalancer.healthcheck.headers.name0": "foobar",
"traefik.http.services.service0.loadbalancer.healthcheck.headers.name1": "foobar", "traefik.http.services.service01.loadbalancer.healthcheck.headers.name1": "foobar",
"traefik.http.services.service0.loadbalancer.healthcheck.hostname": "foobar", "traefik.http.services.service01.loadbalancer.healthcheck.hostname": "foobar",
"traefik.http.services.service0.loadbalancer.healthcheck.interval": "foobar", "traefik.http.services.service01.loadbalancer.healthcheck.interval": "foobar",
"traefik.http.services.service0.loadbalancer.healthcheck.path": "foobar", "traefik.http.services.service01.loadbalancer.healthcheck.path": "foobar",
"traefik.http.services.service0.loadbalancer.healthcheck.port": "42", "traefik.http.services.service01.loadbalancer.healthcheck.port": "42",
"traefik.http.services.service0.loadbalancer.healthcheck.scheme": "foobar", "traefik.http.services.service01.loadbalancer.healthcheck.scheme": "foobar",
"traefik.http.services.service0.loadbalancer.healthcheck.timeout": "foobar", "traefik.http.services.service01.loadbalancer.healthcheck.timeout": "foobar",
"traefik.http.services.service0.loadbalancer.passhostheader": "true", "traefik.http.services.service01.loadbalancer.passhostheader": "true",
"traefik.http.services.service0.loadbalancer.responseforwarding.flushinterval": "foobar", "traefik.http.services.service01.loadbalancer.responseforwarding.flushinterval": "foobar",
"traefik.http.services.service0.loadbalancer.sticky": "true", "traefik.http.services.service01.loadbalancer.sticky.cookie.httponly": "true",
"traefik.http.services.service0.loadbalancer.sticky.cookie.httponly": "true", "traefik.http.services.service01.loadbalancer.sticky.cookie.name": "foobar",
"traefik.http.services.service0.loadbalancer.sticky.cookie.name": "foobar", "traefik.http.services.service01.loadbalancer.sticky.cookie.secure": "true",
"traefik.http.services.service0.loadbalancer.sticky.cookie.secure": "true", "traefik.http.services.service01.loadbalancer.server.port": "foobar",
"traefik.http.services.service0.loadbalancer.server.port": "foobar", "traefik.http.services.service01.loadbalancer.server.scheme": "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.tcp.routers.tcprouter0.entrypoints": "foobar, foobar", "traefik.tcp.routers.tcprouter0.entrypoints": "foobar, foobar",
"traefik.tcp.routers.tcprouter0.rule": "foobar", "traefik.tcp.routers.tcprouter0.rule": "foobar",
"traefik.tcp.routers.tcprouter0.service": "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.certresolver": "foobar",
"traefik.tcp.routers.tcprouter0.tls.domains[0].main": "foobar", "traefik.tcp.routers.tcprouter0.tls.domains[0].main": "foobar",
"traefik.tcp.routers.tcprouter0.tls.domains[0].sans": "foobar, 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.entrypoints": "foobar, foobar",
"traefik.tcp.routers.tcprouter1.rule": "foobar", "traefik.tcp.routers.tcprouter1.rule": "foobar",
"traefik.tcp.routers.tcprouter1.service": "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.certresolver": "foobar",
"traefik.tcp.routers.tcprouter1.tls.domains[0].main": "foobar", "traefik.tcp.routers.tcprouter1.tls.domains[0].main": "foobar",
"traefik.tcp.routers.tcprouter1.tls.domains[0].sans": "foobar, 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.domains[1].sans": "foobar, foobar",
"traefik.tcp.routers.tcprouter1.tls.options": "foobar", "traefik.tcp.routers.tcprouter1.tls.options": "foobar",
"traefik.tcp.routers.tcprouter1.tls.passthrough": "true", "traefik.tcp.routers.tcprouter1.tls.passthrough": "true",
"traefik.tcp.services.tcpservice0.loadbalancer.server.port": "foobar", "traefik.tcp.services.tcpservice01.loadbalancer.terminationdelay": "42",
"traefik.tcp.services.tcpservice0.loadbalancer.terminationDelay": "100", "traefik.tcp.services.tcpservice01.loadbalancer.server.port": "foobar",
"traefik.tcp.services.tcpservice1.loadbalancer.server.port": "foobar"
"traefik.tcp.services.tcpservice1.loadbalancer.terminationDelay": "100",

View file

@ -267,7 +267,7 @@ metadata:
spec: spec:
entryPoints: entryPoints:
- web - websecure
routes: routes:
- match: Host(`foo.com`) && PathPrefix(`/bar`) - match: Host(`foo.com`) && PathPrefix(`/bar`)
kind: Rule kind: Rule

View file

@ -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`. 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)" ??? example "Set priorities -- using the [File Provider](../../providers/file.md)"
@ -419,10 +419,6 @@ Traefik will terminate the SSL connections (meaning that it will send decrypted
tls: {} 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" !!! 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: 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 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` #### `options`
The `options` field enables fine-grained control of the TLS parameters. The `options` field enables fine-grained control of the TLS parameters.

View file

@ -599,12 +599,12 @@ This strategy can only be defined with [File](../../providers/file.md).
[tcp.services.appv1] [tcp.services.appv1]
[tcp.services.appv1.loadBalancer] [tcp.services.appv1.loadBalancer]
[[tcp.services.appv1.loadBalancer.servers]] [[tcp.services.appv1.loadBalancer.servers]]
address = "private-ip-server-1/:8080" address = "private-ip-server-1:8080/"
[tcp.services.appv2] [tcp.services.appv2]
[tcp.services.appv2.loadBalancer] [tcp.services.appv2.loadBalancer]
[[tcp.services.appv2.loadBalancer.servers]] [[tcp.services.appv2.loadBalancer.servers]]
address = "private-ip-server-2/:8080" address = "private-ip-server-2:8080/"
``` ```
```yaml tab="YAML" ```yaml tab="YAML"

View file

@ -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 ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.local/bin

View file

@ -42,6 +42,9 @@ extra_javascript:
plugins: plugins:
- search - search
- exclude:
glob:
- include-*.md
# https://squidfunk.github.io/mkdocs-material/extensions/admonition/ # https://squidfunk.github.io/mkdocs-material/extensions/admonition/
# https://facelessuser.github.io/pymdown-extensions/ # https://facelessuser.github.io/pymdown-extensions/
@ -156,6 +159,7 @@ nav:
- 'Thank You!': 'contributing/thank-you.md' - 'Thank You!': 'contributing/thank-you.md'
- 'Submitting Issues': 'contributing/submitting-issues.md' - 'Submitting Issues': 'contributing/submitting-issues.md'
- 'Submitting PRs': 'contributing/submitting-pull-requests.md' - 'Submitting PRs': 'contributing/submitting-pull-requests.md'
- 'Security': 'contributing/submitting-security-issues.md'
- 'Building and Testing': 'contributing/building-testing.md' - 'Building and Testing': 'contributing/building-testing.md'
- 'Documentation': 'contributing/documentation.md' - 'Documentation': 'contributing/documentation.md'
- 'Data Collection': 'contributing/data-collection.md' - 'Data Collection': 'contributing/data-collection.md'

View file

@ -1,5 +1,6 @@
mkdocs==1.0.4 mkdocs==1.0.4
pymdown-extensions==6.0 pymdown-extensions==6.1
mkdocs-bootswatch==1.0 mkdocs-bootswatch==1.0
mkdocs-material==4.0.2 mkdocs-material==4.4.3
markdown-include==0.5.1 markdown-include==0.5.1
mkdocs-exclude==1.0.2

View file

@ -7,8 +7,8 @@ RUN mkdir -p $WEBUI_DIR
COPY ./webui/ $WEBUI_DIR/ COPY ./webui/ $WEBUI_DIR/
WORKDIR $WEBUI_DIR WORKDIR $WEBUI_DIR
RUN npm install
RUN npm install
RUN npm run build RUN npm run build
# BUILD # BUILD
@ -38,10 +38,12 @@ COPY --from=webui /src/static/ /go/src/github.com/containous/traefik/static/
RUN ./script/make.sh generate binary RUN ./script/make.sh generate binary
## IMAGE ## 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 / COPY --from=gobuild /go/src/github.com/containous/traefik/dist/traefik /
EXPOSE 80 EXPOSE 80

6
go.mod
View file

@ -39,7 +39,7 @@ require (
github.com/felixge/httpsnoop v1.0.0 // indirect github.com/felixge/httpsnoop v1.0.0 // indirect
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2 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-check/check v0.0.0-00010101000000-000000000000
github.com/go-kit/kit v0.9.0 github.com/go-kit/kit v0.9.0
github.com/golang/protobuf v1.3.2 github.com/golang/protobuf v1.3.2
@ -67,7 +67,7 @@ require (
github.com/opencontainers/runc v1.0.0-rc8 // indirect github.com/opencontainers/runc v1.0.0-rc8 // indirect
github.com/opentracing/basictracer-go v1.0.0 // indirect github.com/opentracing/basictracer-go v1.0.0 // indirect
github.com/opentracing/opentracing-go v1.1.0 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/openzipkin/zipkin-go v0.2.1
github.com/patrickmn/go-cache v2.1.0+incompatible github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/philhofer/fwd v1.0.0 // indirect github.com/philhofer/fwd v1.0.0 // indirect
@ -83,7 +83,7 @@ require (
github.com/uber/jaeger-client-go v2.19.0+incompatible github.com/uber/jaeger-client-go v2.19.0+incompatible
github.com/uber/jaeger-lib v2.2.0+incompatible github.com/uber/jaeger-lib v2.2.0+incompatible
github.com/unrolled/render v1.0.1 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/vdemeester/shakers v0.1.0
github.com/vulcand/oxy v1.0.0 github.com/vulcand/oxy v1.0.0
github.com/vulcand/predicate v1.1.0 github.com/vulcand/predicate v1.1.0

12
go.sum
View file

@ -193,8 +193,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 h1:df6OFl8WNXk82xxP3R9ZPZ5seOA8XZkwLdbEzZF1/xI=
github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2/go.mod h1:GLyXJD41gBO/NPKVPGQbhyyC06eugGy15QEZyUkE2/s= 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/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.2.0 h1:z0zvNlL1niv/1qA06V5X1BRC5PeLoGKAlVaWthXQz9c=
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/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-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 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
@ -454,8 +454,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/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 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= 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.4 h1:bzTJRoOZEN7uI1gq594S5HhMYNSud4FKUEwd4aFbsEI=
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/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.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 h1:noL5/5Uf1HpVl3wNsfkZhIKbSWCVi5jgqkONNx8PXcA=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
@ -560,8 +560,8 @@ github.com/uber/jaeger-lib v2.2.0+incompatible h1:MxZXOiR2JuoANZ3J6DE/U0kSFv/eJ/
github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/uber/jaeger-lib v2.2.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
github.com/unrolled/render v1.0.1 h1:VDDnQQVfBMsOsp3VaCJszSO0nkBIVEYoPWeRThk9spY= 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/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.5 h1:KRGJ8DQC3jKpERjBKF3H6b3HcAsM/SRTVwfNJnWs25E=
github.com/unrolled/secure v1.0.4/go.mod h1:R6rugAuzh4TQpbFAq69oqZggyBQxFRFQIewtz5z7Jsc= 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/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 h1:K+n9sSyUCg2ywmZkv+3c7vsYZfivcfKhMh8kRxCrONM=
github.com/vdemeester/shakers v0.1.0/go.mod h1:IZ1HHynUOQt32iQ3rvAeVddXLd19h/6LWiKsh9RZtAQ= github.com/vdemeester/shakers v0.1.0/go.mod h1:IZ1HHynUOQt32iQ3rvAeVddXLd19h/6LWiKsh9RZtAQ=

View file

@ -357,7 +357,13 @@ type Retry struct {
// StripPrefix holds the StripPrefix configuration. // StripPrefix holds the StripPrefix configuration.
type StripPrefix struct { 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 // +k8s:deepcopy-gen=true

View file

@ -368,6 +368,7 @@ func TestDecodeConfiguration(t *testing.T) {
"foobar", "foobar",
"fiibar", "fiibar",
}, },
ForceSlash: true,
}, },
}, },
"Middleware18": { "Middleware18": {
@ -771,6 +772,7 @@ func TestEncodeConfiguration(t *testing.T) {
"foobar", "foobar",
"fiibar", "fiibar",
}, },
ForceSlash: true,
}, },
}, },
"Middleware18": { "Middleware18": {
@ -1091,6 +1093,7 @@ func TestEncodeConfiguration(t *testing.T) {
"traefik.HTTP.Middlewares.Middleware15.ReplacePathRegex.Replacement": "foobar", "traefik.HTTP.Middlewares.Middleware15.ReplacePathRegex.Replacement": "foobar",
"traefik.HTTP.Middlewares.Middleware16.Retry.Attempts": "42", "traefik.HTTP.Middlewares.Middleware16.Retry.Attempts": "42",
"traefik.HTTP.Middlewares.Middleware17.StripPrefix.Prefixes": "foobar, fiibar", "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.Middleware18.StripPrefixRegex.Regex": "foobar, fiibar",
"traefik.HTTP.Middlewares.Middleware19.Compress": "true", "traefik.HTTP.Middlewares.Middleware19.Compress": "true",

View file

@ -290,6 +290,11 @@ func TestPrometheusMetricRemoval(t *testing.T) {
th.WithLoadBalancerServices(th.WithService("bar@providerName", th.WithLoadBalancerServices(th.WithService("bar@providerName",
th.WithServers(th.WithServer("http://localhost:9000"))), th.WithServers(th.WithServer("http://localhost:9000"))),
), ),
func(cfg *dynamic.HTTPConfiguration) {
cfg.Services["fii"] = &dynamic.Service{
Weighted: &dynamic.WeightedRoundRobin{},
}
},
), ),
} }

View file

@ -41,23 +41,35 @@ func New(ctx context.Context, next http.Handler, config dynamic.AddPrefix, name
return result, nil return result, nil
} }
func (ap *addPrefix) GetTracingInformation() (string, ext.SpanKindEnum) { func (a *addPrefix) GetTracingInformation() (string, ext.SpanKindEnum) {
return ap.name, tracing.SpanKindNoneEnum return a.name, tracing.SpanKindNoneEnum
} }
func (ap *addPrefix) ServeHTTP(rw http.ResponseWriter, req *http.Request) { func (a *addPrefix) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
logger := log.FromContext(middlewares.GetLoggerCtx(req.Context(), ap.name, typeName)) logger := log.FromContext(middlewares.GetLoggerCtx(req.Context(), a.name, typeName))
oldURLPath := req.URL.Path 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) logger.Debugf("URL.Path is now %s (was %s).", req.URL.Path, oldURLPath)
if req.URL.RawPath != "" { if req.URL.RawPath != "" {
oldURLRawPath := 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) logger.Debugf("URL.RawPath is now %s (was %s).", req.URL.RawPath, oldURLRawPath)
} }
req.RequestURI = req.URL.RequestURI() 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
} }

View file

@ -7,7 +7,6 @@ import (
"github.com/containous/traefik/v2/pkg/config/dynamic" "github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/testhelpers" "github.com/containous/traefik/v2/pkg/testhelpers"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -47,7 +46,6 @@ func TestNewAddPrefix(t *testing.T) {
} }
func TestAddPrefix(t *testing.T) { func TestAddPrefix(t *testing.T) {
logrus.SetLevel(logrus.DebugLevel)
testCases := []struct { testCases := []struct {
desc string desc string
prefix dynamic.AddPrefix prefix dynamic.AddPrefix
@ -61,6 +59,12 @@ func TestAddPrefix(t *testing.T) {
path: "/b", path: "/b",
expectedPath: "/a/b", expectedPath: "/a/b",
}, },
{
desc: "Works with missing leading slash",
prefix: dynamic.AddPrefix{Prefix: "a"},
path: "/",
expectedPath: "/a/",
},
{ {
desc: "Works with a raw path", desc: "Works with a raw path",
prefix: dynamic.AddPrefix{Prefix: "/a"}, prefix: dynamic.AddPrefix{Prefix: "/a"},

View file

@ -5,7 +5,6 @@ import (
"context" "context"
"fmt" "fmt"
"net/http" "net/http"
"sync"
"time" "time"
"github.com/containous/traefik/v2/pkg/config/dynamic" "github.com/containous/traefik/v2/pkg/config/dynamic"
@ -35,8 +34,7 @@ type rateLimiter struct {
sourceMatcher utils.SourceExtractor sourceMatcher utils.SourceExtractor
next http.Handler 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. // 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 return nil, err
} }
buckets, err := ttlmap.NewMap(maxSources) buckets, err := ttlmap.NewConcurrent(maxSources)
if err != nil { if err != nil {
return nil, err 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) logger.Infof("ignoring token bucket amount > 1: %d", amount)
} }
rl.bucketsMu.Lock()
defer rl.bucketsMu.Unlock()
var bucket *rate.Limiter var bucket *rate.Limiter
if rlSource, exists := rl.buckets.Get(source); exists { if rlSource, exists := rl.buckets.Get(source); exists {
bucket = rlSource.(*rate.Limiter) bucket = rlSource.(*rate.Limiter)

View file

@ -132,19 +132,13 @@ func rawURL(req *http.Request) string {
uri = match[4] uri = match[4]
} }
if req.TLS != nil || isXForwardedHTTPS(req) { if req.TLS != nil {
scheme = "https" scheme = "https"
} }
return strings.Join([]string{scheme, "://", host, port, uri}, "") 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 { func applyString(in string, out io.Writer, req *http.Request) error {
t, err := template.New("t").Parse(in) t, err := template.New("t").Parse(in)
if err != nil { if err != nil {

View file

@ -19,6 +19,7 @@ func TestRedirectRegexHandler(t *testing.T) {
config dynamic.RedirectRegex config dynamic.RedirectRegex
method string method string
url string url string
headers map[string]string
secured bool secured bool
expectedURL string expectedURL string
expectedStatus int expectedStatus int
@ -104,6 +105,19 @@ func TestRedirectRegexHandler(t *testing.T) {
expectedURL: "https://foo:443", expectedURL: "https://foo:443",
expectedStatus: http.StatusFound, 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", desc: "HTTPS to HTTP",
config: dynamic.RedirectRegex{ config: dynamic.RedirectRegex{
@ -171,12 +185,18 @@ func TestRedirectRegexHandler(t *testing.T) {
if test.method != "" { if test.method != "" {
method = test.method method = test.method
} }
r := testhelpers.MustNewRequest(method, test.url, nil)
req := testhelpers.MustNewRequest(method, test.url, nil)
if test.secured { 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) assert.Equal(t, test.expectedStatus, recorder.Code)
switch test.expectedStatus { switch test.expectedStatus {

View file

@ -19,6 +19,7 @@ func TestRedirectSchemeHandler(t *testing.T) {
config dynamic.RedirectScheme config dynamic.RedirectScheme
method string method string
url string url string
headers map[string]string
secured bool secured bool
expectedURL string expectedURL string
expectedStatus int expectedStatus int
@ -39,6 +40,18 @@ func TestRedirectSchemeHandler(t *testing.T) {
expectedURL: "https://foo", expectedURL: "https://foo",
expectedStatus: http.StatusFound, 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", desc: "HTTP with port to HTTPS without port",
config: dynamic.RedirectScheme{ config: dynamic.RedirectScheme{
@ -197,13 +210,17 @@ func TestRedirectSchemeHandler(t *testing.T) {
if test.method != "" { if test.method != "" {
method = 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 { if test.secured {
r.TLS = &tls.ConnectionState{} req.TLS = &tls.ConnectionState{}
} }
r.Header.Set("X-Foo", "bar") req.Header.Set("X-Foo", "bar")
handler.ServeHTTP(recorder, r) handler.ServeHTTP(recorder, req)
assert.Equal(t, test.expectedStatus, recorder.Code) assert.Equal(t, test.expectedStatus, recorder.Code)
@ -223,9 +240,9 @@ func TestRedirectSchemeHandler(t *testing.T) {
if re.Match([]byte(test.url)) { if re.Match([]byte(test.url)) {
match := re.FindStringSubmatch(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) assert.Equal(t, test.expectedStatus, recorder.Code)
if test.expectedStatus == http.StatusMovedPermanently || if test.expectedStatus == http.StatusMovedPermanently ||

View file

@ -20,18 +20,20 @@ const (
// stripPrefix is a middleware used to strip prefix from an URL request. // stripPrefix is a middleware used to strip prefix from an URL request.
type stripPrefix struct { type stripPrefix struct {
next http.Handler next http.Handler
prefixes []string prefixes []string
name string forceSlash bool // TODO Must be removed (breaking), the default behavior must be forceSlash=false
name string
} }
// New creates a new strip prefix middleware. // New creates a new strip prefix middleware.
func New(ctx context.Context, next http.Handler, config dynamic.StripPrefix, name string) (http.Handler, error) { 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") log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware")
return &stripPrefix{ return &stripPrefix{
prefixes: config.Prefixes, prefixes: config.Prefixes,
next: next, forceSlash: config.ForceSlash,
name: name, next: next,
name: name,
}, nil }, nil
} }
@ -42,9 +44,9 @@ func (s *stripPrefix) GetTracingInformation() (string, ext.SpanKindEnum) {
func (s *stripPrefix) ServeHTTP(rw http.ResponseWriter, req *http.Request) { func (s *stripPrefix) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
for _, prefix := range s.prefixes { for _, prefix := range s.prefixes {
if strings.HasPrefix(req.URL.Path, prefix) { 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 != "" { 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)) s.serveRequest(rw, req, strings.TrimSpace(prefix))
return return
@ -59,10 +61,25 @@ func (s *stripPrefix) serveRequest(rw http.ResponseWriter, req *http.Request, pr
s.next.ServeHTTP(rw, req) s.next.ServeHTTP(rw, req)
} }
func getPrefixStripped(s, prefix string) string { func (s *stripPrefix) getPrefixStripped(urlPath, prefix string) string {
return ensureLeadingSlash(strings.TrimPrefix(s, prefix)) 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 { func ensureLeadingSlash(str string) string {
return "/" + strings.TrimPrefix(str, "/") if str == "" {
return str
}
if str[0] == '/' {
return str
}
return "/" + str
} }

View file

@ -31,6 +31,17 @@ func TestStripPrefix(t *testing.T) {
expectedStatusCode: http.StatusOK, expectedStatusCode: http.StatusOK,
expectedPath: "/noprefixes", expectedPath: "/noprefixes",
}, },
{
desc: "wildcard (.*) requests (ForceSlash)",
config: dynamic.StripPrefix{
Prefixes: []string{"/"},
ForceSlash: true,
},
path: "/",
expectedStatusCode: http.StatusOK,
expectedPath: "/",
expectedHeader: "/",
},
{ {
desc: "wildcard (.*) requests", desc: "wildcard (.*) requests",
config: dynamic.StripPrefix{ config: dynamic.StripPrefix{
@ -38,9 +49,20 @@ func TestStripPrefix(t *testing.T) {
}, },
path: "/", path: "/",
expectedStatusCode: http.StatusOK, expectedStatusCode: http.StatusOK,
expectedPath: "/", expectedPath: "",
expectedHeader: "/", 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", desc: "prefix and path matching",
config: dynamic.StripPrefix{ config: dynamic.StripPrefix{
@ -48,9 +70,20 @@ func TestStripPrefix(t *testing.T) {
}, },
path: "/stat", path: "/stat",
expectedStatusCode: http.StatusOK, expectedStatusCode: http.StatusOK,
expectedPath: "/", expectedPath: "",
expectedHeader: "/stat", 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", desc: "path prefix on exactly matching path",
config: dynamic.StripPrefix{ config: dynamic.StripPrefix{
@ -58,7 +91,7 @@ func TestStripPrefix(t *testing.T) {
}, },
path: "/stat/", path: "/stat/",
expectedStatusCode: http.StatusOK, expectedStatusCode: http.StatusOK,
expectedPath: "/", expectedPath: "",
expectedHeader: "/stat/", expectedHeader: "/stat/",
}, },
{ {
@ -101,6 +134,17 @@ func TestStripPrefix(t *testing.T) {
expectedPath: "/us", expectedPath: "/us",
expectedHeader: "/stat", 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", desc: "later prefix matching",
config: dynamic.StripPrefix{ config: dynamic.StripPrefix{
@ -108,7 +152,7 @@ func TestStripPrefix(t *testing.T) {
}, },
path: "/stat", path: "/stat",
expectedStatusCode: http.StatusOK, expectedStatusCode: http.StatusOK,
expectedPath: "/", expectedPath: "",
expectedHeader: "/stat", expectedHeader: "/stat",
}, },
{ {
@ -162,12 +206,15 @@ func TestStripPrefix(t *testing.T) {
assert.Equal(t, test.expectedRawPath, actualRawPath, "Unexpected raw path.") assert.Equal(t, test.expectedRawPath, actualRawPath, "Unexpected raw path.")
assert.Equal(t, test.expectedHeader, actualHeader, "Unexpected '%s' header.", ForwardedPrefixHeader) assert.Equal(t, test.expectedHeader, actualHeader, "Unexpected '%s' header.", ForwardedPrefixHeader)
expectedURI := test.expectedPath expectedRequestURI := test.expectedPath
if test.expectedRawPath != "" { if test.expectedRawPath != "" {
// go HTTP uses the raw path when existent in the RequestURI // 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.")
}) })
} }
} }

View file

@ -60,12 +60,12 @@ func (s *stripPrefixRegex) ServeHTTP(rw http.ResponseWriter, req *http.Request)
req.Header.Add(stripprefix.ForwardedPrefixHeader, prefix) 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 != "" { 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) s.next.ServeHTTP(rw, req)
return return
} }
@ -75,5 +75,13 @@ func (s *stripPrefixRegex) ServeHTTP(rw http.ResponseWriter, req *http.Request)
} }
func ensureLeadingSlash(str string) string { func ensureLeadingSlash(str string) string {
return "/" + strings.TrimPrefix(str, "/") if str == "" {
return str
}
if str[0] == '/' {
return str
}
return "/" + str
} }

View file

@ -31,42 +31,66 @@ func TestStripPrefixRegex(t *testing.T) {
expectedPath: "/a/test", expectedPath: "/a/test",
}, },
{ {
path: "/a/test", path: "/a/test/",
expectedStatusCode: http.StatusOK, expectedStatusCode: http.StatusOK,
expectedPath: "/a/test", expectedPath: "/a/test/",
},
{
path: "/a/api/",
expectedStatusCode: http.StatusOK,
expectedPath: "",
expectedHeader: "/a/api/",
}, },
{ {
path: "/a/api/test", path: "/a/api/test",
expectedStatusCode: http.StatusOK, expectedStatusCode: http.StatusOK,
expectedPath: "test", expectedPath: "/test",
expectedHeader: "/a/api/",
},
{
path: "/a/api/test/",
expectedStatusCode: http.StatusOK,
expectedPath: "/test/",
expectedHeader: "/a/api/", expectedHeader: "/a/api/",
}, },
{ {
path: "/b/api/", path: "/b/api/",
expectedStatusCode: http.StatusOK, expectedStatusCode: http.StatusOK,
expectedPath: "",
expectedHeader: "/b/api/", expectedHeader: "/b/api/",
}, },
{
path: "/b/api",
expectedStatusCode: http.StatusOK,
expectedPath: "/b/api",
},
{ {
path: "/b/api/test1", path: "/b/api/test1",
expectedStatusCode: http.StatusOK, expectedStatusCode: http.StatusOK,
expectedPath: "test1", expectedPath: "/test1",
expectedHeader: "/b/api/", expectedHeader: "/b/api/",
}, },
{ {
path: "/b/api2/test2", path: "/b/api2/test2",
expectedStatusCode: http.StatusOK, expectedStatusCode: http.StatusOK,
expectedPath: "test2", expectedPath: "/test2",
expectedHeader: "/b/api2/", expectedHeader: "/b/api2/",
}, },
{ {
path: "/c/api/123/", path: "/c/api/123/",
expectedStatusCode: http.StatusOK, expectedStatusCode: http.StatusOK,
expectedPath: "",
expectedHeader: "/c/api/123/", expectedHeader: "/c/api/123/",
}, },
{
path: "/c/api/123",
expectedStatusCode: http.StatusOK,
expectedPath: "/c/api/123",
},
{ {
path: "/c/api/123/test3", path: "/c/api/123/test3",
expectedStatusCode: http.StatusOK, expectedStatusCode: http.StatusOK,
expectedPath: "test3", expectedPath: "/test3",
expectedHeader: "/c/api/123/", expectedHeader: "/c/api/123/",
}, },
{ {
@ -77,8 +101,8 @@ func TestStripPrefixRegex(t *testing.T) {
{ {
path: "/a/api/a%2Fb", path: "/a/api/a%2Fb",
expectedStatusCode: http.StatusOK, expectedStatusCode: http.StatusOK,
expectedPath: "a/b", expectedPath: "/a/b",
expectedRawPath: "a%2Fb", expectedRawPath: "/a%2Fb",
expectedHeader: "/a/api/", expectedHeader: "/a/api/",
}, },
} }
@ -88,11 +112,12 @@ func TestStripPrefixRegex(t *testing.T) {
t.Run(test.path, func(t *testing.T) { t.Run(test.path, func(t *testing.T) {
t.Parallel() t.Parallel()
var actualPath, actualRawPath, actualHeader string var actualPath, actualRawPath, actualHeader, requestURI string
handlerPath := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { handlerPath := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
actualPath = r.URL.Path actualPath = r.URL.Path
actualRawPath = r.URL.RawPath actualRawPath = r.URL.RawPath
actualHeader = r.Header.Get(stripprefix.ForwardedPrefixHeader) actualHeader = r.Header.Get(stripprefix.ForwardedPrefixHeader)
requestURI = r.RequestURI
}) })
handler, err := New(context.Background(), handlerPath, testPrefixRegex, "foo-strip-prefix-regex") handler, err := New(context.Background(), handlerPath, testPrefixRegex, "foo-strip-prefix-regex")
require.NoError(t, err) 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.expectedPath, actualPath, "Unexpected path.")
assert.Equal(t, test.expectedRawPath, actualRawPath, "Unexpected raw path.") assert.Equal(t, test.expectedRawPath, actualRawPath, "Unexpected raw path.")
assert.Equal(t, test.expectedHeader, actualHeader, "Unexpected '%s' header.", stripprefix.ForwardedPrefixHeader) 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.")
}
}) })
} }
} }

View file

@ -95,12 +95,12 @@
# Enable API and dashboard # Enable API and dashboard
[api] [api]
# Name of the related entry point # Enable the API in insecure mode
# #
# Optional # Optional
# Default: "traefik" # Default: true
# #
# entryPoint = "traefik" # insecure = false
# Enabled Dashboard # Enabled Dashboard
# #