1
0
mirror of https://github.com/containous/traefik.git synced 2024-12-22 13:34:03 +03:00

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)
[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
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

View File

@ -7,5 +7,6 @@
"MD026": false,
"MD033": 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 \
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

View File

@ -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"

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) |
| [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) |
@ -390,7 +391,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

View File

@ -63,4 +63,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 (`/`).

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
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"

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).
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:
* 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"

View File

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

View File

@ -43,71 +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"
```
```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"
```
--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.

View File

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

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.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"

View File

@ -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]

View File

@ -276,6 +276,7 @@ http:
prefixes:
- foobar
- foobar
forceSlash: true
Middleware20:
stripPrefixRegex:
regex:
@ -322,18 +323,19 @@ tcp:
- foobar
- foobar
services:
TCPService0:
TCPService01:
loadBalancer:
terminationDelay: 100
servers:
- address: foobar
- address: foobar
TCPService1:
loadBalancer:
terminationDelay: 100
terminationDelay: 42
servers:
- address: foobar
- address: foobar
TCPService02:
weighted:
services:
- name: foobar
weight: 42
- name: foobar
weight: 42
tls:
certificates:
- certFile: foobar

View File

@ -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",

View File

@ -267,7 +267,7 @@ metadata:
spec:
entryPoints:
- web
- websecure
routes:
- match: Host(`foo.com`) && PathPrefix(`/bar`)
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`.
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)"
@ -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.

View File

@ -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"

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

View File

@ -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/
@ -156,6 +159,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'

View File

@ -1,5 +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

View File

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

6
go.mod
View File

@ -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
@ -67,7 +67,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
@ -83,7 +83,7 @@ require (
github.com/uber/jaeger-client-go v2.19.0+incompatible
github.com/uber/jaeger-lib v2.2.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

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/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=
@ -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/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=
@ -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/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=

View File

@ -358,6 +358,12 @@ type Retry struct {
// StripPrefix holds the StripPrefix configuration.
type StripPrefix struct {
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

View File

@ -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",

View File

@ -290,6 +290,11 @@ func TestPrometheusMetricRemoval(t *testing.T) {
th.WithLoadBalancerServices(th.WithService("bar@providerName",
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
}
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
}

View File

@ -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"},

View File

@ -5,7 +5,6 @@ import (
"context"
"fmt"
"net/http"
"sync"
"time"
"github.com/containous/traefik/v2/pkg/config/dynamic"
@ -35,7 +34,6 @@ type rateLimiter struct {
sourceMatcher utils.SourceExtractor
next http.Handler
bucketsMu sync.Mutex
buckets *ttlmap.TtlMap // actual buckets, keyed by source.
}
@ -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)

View File

@ -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 {

View File

@ -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 {

View File

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

View File

@ -22,6 +22,7 @@ const (
type stripPrefix struct {
next http.Handler
prefixes []string
forceSlash bool // TODO Must be removed (breaking), the default behavior must be forceSlash=false
name string
}
@ -30,6 +31,7 @@ func New(ctx context.Context, next http.Handler, config dynamic.StripPrefix, nam
log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware")
return &stripPrefix{
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
}

View File

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

View File

@ -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
}

View File

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

View File

@ -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
#