1
0
mirror of https://github.com/containous/traefik.git synced 2025-09-19 01:44:23 +03:00

Compare commits

..

33 Commits

Author SHA1 Message Date
Fernandez Ludovic
683d5d5a48 chore: skip openbsd/freebsd arm64 2020-03-18 17:21:20 +01:00
Ludovic Fernandez
4f92ef5fa9 Prepare release v2.1.7 2020-03-18 15:50:05 +01:00
Ludovic Fernandez
62c3025a76 Access log field quotes. 2020-03-17 12:36:04 +01:00
Traefiker Bot
b5d205b78c fix statsd scale for duration based metrics 2020-03-05 15:10:07 +01:00
Ludovic Fernandez
dccc075f2c Add some missing doc. 2020-03-04 16:48:05 +01:00
Ole Rößner
5fdec48854 Added wildcard ACME example 2020-03-04 13:24:05 +01:00
robotte
353bd3d06f Added support for replacement containing escaped characters
Co-authored-by: Ludovic Fernandez <ldez@users.noreply.github.com>
2020-03-03 16:20:05 +01:00
Hamilton Turner
a7495f711b fix typo 2020-02-29 18:48:04 +01:00
Ludovic Fernandez
5072735866 Prepare release v2.1.6 2020-02-28 18:30:05 +01:00
Ludovic Fernandez
1746ed6e1c Prepare release v2.1.5 2020-02-28 18:02:05 +01:00
Ludovic Fernandez
664cd940c5 fix: YML example of template for the file provider. 2020-02-28 14:52:05 +01:00
Daniel Tomcej
f6c6c2b2c0 Allow fsnotify to reload config files on k8s (or symlinks) 2020-02-26 17:50:07 +01:00
Ludovic Fernandez
70fdfeb926 Use explicitly the word Kubernetes in the migration guide. 2020-02-26 16:38:05 +01:00
Evan Lurvey
f4d62d3342 Fix docs and code to match in haystack tracing. 2020-02-26 11:10:06 +01:00
Ludovic Fernandez
54df7b0a3c Update go-acme/lego to v3.4.0 2020-02-26 10:36:05 +01:00
Ludovic Fernandez
9795a7c4a9 fix: consul-catalog use port from label instead of item port. 2020-02-25 23:00:04 +01:00
Julien Salleyron
1557fda588 Consider SSLv2 as TLS in order to close the handshake correctly 2020-02-25 17:50:05 +01:00
Julien Salleyron
1e7f34c271 Launch healhcheck only one time instead of two 2020-02-25 16:30:05 +01:00
Michael
d71e8ab7c9 Fix secret informer load 2020-02-25 15:14:04 +01:00
Daniel Tomcej
3b4c8ba439 Use consistent protocol determination 2020-02-25 10:12:04 +01:00
Patrick Schaub
e04ebaa364 Fix typo in the godoc of TLS option MaxVersion 2020-02-21 17:48:05 +01:00
Ludovic Fernandez
aab7043d45 Add information about filename and directory options. 2020-02-18 17:30:05 +01:00
rYR79435
ef504f3eba Remove TLS cipher suites for TLS minVersion 1.3 2020-02-17 17:38:05 +01:00
Bret Fisher
86407871e6 Docs: Clarifying format of ingress endpoint service name 2020-02-17 17:30:06 +01:00
Ludovic Fernandez
76bb2ef60c fix: dashboard example with k8s CRD. 2020-02-17 17:20:05 +01:00
Ludovic Fernandez
beec65938e Improve documentation. 2020-02-17 11:04:04 +01:00
Ludovic Fernandez
d501c0786f Early filter of the catalog services. 2020-02-13 10:26:04 +01:00
Jean-Baptiste Doumenjou
322c329c6f fix: use the right error in the log 2020-02-12 18:28:05 +01:00
Vyacheslav Matyukhin
4a1d20e8a3 Fix formatting in "Kubernetes Namespace" block 2020-02-12 14:26:05 +01:00
Ludovic Fernandez
c33348e80c fix: return an error when ping is not enabled. 2020-02-11 16:06:06 +01:00
Andrew Parker
6e43ab5897 Don't throw away valid configuration updates 2020-02-10 21:40:06 +01:00
Rowayda Khayri
8bb625adb7 Minor readme improvements 2020-02-10 14:54:05 +01:00
Isaac Newton K
cf2b97b656 Added link to community forum 2020-02-07 17:36:05 +01:00
84 changed files with 1220 additions and 441 deletions

View File

@@ -34,8 +34,10 @@ builds:
goarch: 386
- goos: openbsd
goarch: arm
- goos: openbsd
goarch: arm64
- goos: freebsd
goarch: arm
goarch: arm64
changelog:
skip: true

View File

@@ -1,3 +1,50 @@
## [v2.1.7](https://github.com/containous/traefik/tree/v2.1.7) (2020-03-18)
[All Commits](https://github.com/containous/traefik/compare/v2.1.6...v2.1.7)
**Bug fixes:**
- **[logs,middleware]** Access log field quotes. ([#6484](https://github.com/containous/traefik/pull/6484) by [ldez](https://github.com/ldez))
- **[metrics]** fix statsd scale for duration based metrics ([#6054](https://github.com/containous/traefik/pull/6054) by [ddtmachado](https://github.com/ddtmachado))
- **[middleware]** Added support for replacement containing escaped characters ([#6413](https://github.com/containous/traefik/pull/6413) by [rtribotte](https://github.com/rtribotte))
**Documentation:**
- **[acme,docker]** Add some missing doc. ([#6422](https://github.com/containous/traefik/pull/6422) by [ldez](https://github.com/ldez))
- **[acme]** Added wildcard ACME example ([#6423](https://github.com/containous/traefik/pull/6423) by [Basster](https://github.com/Basster))
- **[acme]** fix typo ([#6408](https://github.com/containous/traefik/pull/6408) by [hamiltont](https://github.com/hamiltont))
## [v2.1.6](https://github.com/containous/traefik/tree/v2.1.6) (2020-02-28)
[All Commits](https://github.com/containous/traefik/compare/v2.1.4...v2.1.6)
**Bug fixes:**
- **[acme]** Update go-acme/lego to v3.4.0 ([#6376](https://github.com/containous/traefik/pull/6376) by [ldez](https://github.com/ldez))
- **[api]** Return an error when ping is not enabled. ([#6304](https://github.com/containous/traefik/pull/6304) by [ldez](https://github.com/ldez))
- **[consulcatalog]** Early filter of the catalog services. ([#6307](https://github.com/containous/traefik/pull/6307) by [ldez](https://github.com/ldez))
- **[consulcatalog]** fix: consul-catalog uses port from label instead of item port. ([#6345](https://github.com/containous/traefik/pull/6345) by [ldez](https://github.com/ldez))
- **[file]** fix: YML example of template for the file provider. ([#6402](https://github.com/containous/traefik/pull/6402) by [ldez](https://github.com/ldez))
- **[file]** Allow fsnotify to reload config files on k8s (or symlinks) ([#5037](https://github.com/containous/traefik/pull/5037) by [dtomcej](https://github.com/dtomcej))
- **[healthcheck]** Launch healthcheck only one time instead of two ([#6372](https://github.com/containous/traefik/pull/6372) by [juliens](https://github.com/juliens))
- **[k8s,k8s/crd,k8s/ingress]** Fix secret informer load ([#6364](https://github.com/containous/traefik/pull/6364) by [mmatur](https://github.com/mmatur))
- **[k8s,k8s/crd]** Use consistent protocol determination ([#6365](https://github.com/containous/traefik/pull/6365) by [dtomcej](https://github.com/dtomcej))
- **[k8s,k8s/crd]** fix: use the right error in the log ([#6311](https://github.com/containous/traefik/pull/6311) by [jbdoumenjou](https://github.com/jbdoumenjou))
- **[provider]** Don&#39;t throw away valid configuration updates ([#5952](https://github.com/containous/traefik/pull/5952) by [zaphod42](https://github.com/zaphod42))
- **[tls]** Consider SSLv2 as TLS in order to close the handshake correctly ([#6371](https://github.com/containous/traefik/pull/6371) by [juliens](https://github.com/juliens))
- **[tracing]** Fix docs and code to match in haystack tracing. ([#6352](https://github.com/containous/traefik/pull/6352) by [evanlurvey](https://github.com/evanlurvey))
**Documentation:**
- **[acme]** Improve documentation. ([#6324](https://github.com/containous/traefik/pull/6324) by [ldez](https://github.com/ldez))
- **[file]** Add information about filename and directory options. ([#6333](https://github.com/containous/traefik/pull/6333) by [ldez](https://github.com/ldez))
- **[k8s,k8s/ingress]** Docs: Clarifying format of ingress endpoint service name ([#6306](https://github.com/containous/traefik/pull/6306) by [BretFisher](https://github.com/BretFisher))
- **[k8s/crd]** fix: dashboard example with k8s CRD. ([#6330](https://github.com/containous/traefik/pull/6330) by [ldez](https://github.com/ldez))
- **[middleware,k8s]** Fix formatting in &#34;Kubernetes Namespace&#34; block ([#6305](https://github.com/containous/traefik/pull/6305) by [berekuk](https://github.com/berekuk))
- **[tls]** Remove TLS cipher suites for TLS minVersion 1.3 ([#6328](https://github.com/containous/traefik/pull/6328) by [rYR79435](https://github.com/rYR79435))
- **[tls]** Fix typo in the godoc of TLS option MaxVersion ([#6347](https://github.com/containous/traefik/pull/6347) by [pschaub](https://github.com/pschaub))
- Use explicitly the word Kubernetes in the migration guide. ([#6380](https://github.com/containous/traefik/pull/6380) by [ldez](https://github.com/ldez))
- Minor readme improvements ([#6293](https://github.com/containous/traefik/pull/6293) by [Rowayda-Khayri](https://github.com/Rowayda-Khayri))
- Added link to community forum ([#6283](https://github.com/containous/traefik/pull/6283) by [isaacnewtonfx](https://github.com/isaacnewtonfx))
## [v2.1.5](https://github.com/containous/traefik/tree/v2.1.5) (2020-02-28)
Skipped.
## [v2.1.4](https://github.com/containous/traefik/tree/v2.1.4) (2020-02-06)
[All Commits](https://github.com/containous/traefik/compare/v2.1.3...v2.1.4)

View File

@@ -89,7 +89,7 @@ You can access the simple HTML frontend of Traefik.
You can find the complete documentation of Traefik v2 at [https://docs.traefik.io](https://docs.traefik.io).
If you are using Traefik v1, you can find the complete documentation at [https://docs.traefik.io/v1.7/](https://docs.traefik.io/v1.7/)
If you are using Traefik v1, you can find the complete documentation at [https://docs.traefik.io/v1.7/](https://docs.traefik.io/v1.7/).
A collection of contributions around Traefik can be found at [https://awesome.traefik.io](https://awesome.traefik.io).
@@ -122,7 +122,7 @@ git clone https://github.com/containous/traefik
## Introductory Videos
You can find high level and deep dive videos on [videos.containo.us](https://videos.containo.us)
You can find high level and deep dive videos on [videos.containo.us](https://videos.containo.us).
## Maintainers
@@ -138,16 +138,16 @@ By participating in this project, you agree to abide by its terms.
## Release Cycle
- We release a new version (e.g. 1.1.0, 1.2.0, 1.3.0) every other month.
- Release Candidates are available before the release (e.g. 1.1.0-rc1, 1.1.0-rc2, 1.1.0-rc3, 1.1.0-rc4, before 1.1.0)
- Bug-fixes (e.g. 1.1.1, 1.1.2, 1.2.1, 1.2.3) are released as needed (no additional features are delivered in those versions, bug-fixes only)
- Release Candidates are available before the release (e.g. 1.1.0-rc1, 1.1.0-rc2, 1.1.0-rc3, 1.1.0-rc4, before 1.1.0).
- Bug-fixes (e.g. 1.1.1, 1.1.2, 1.2.1, 1.2.3) are released as needed (no additional features are delivered in those versions, bug-fixes only).
Each version is supported until the next one is released (e.g. 1.1.x will be supported until 1.2.0 is out)
Each version is supported until the next one is released (e.g. 1.1.x will be supported until 1.2.0 is out).
We use [Semantic Versioning](https://semver.org/)
We use [Semantic Versioning](https://semver.org/).
## Mailing lists
## Mailing Lists
- General announcements, new releases: mail at news+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/news)
- General announcements, new releases: mail at news+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/news).
- Security announcements: mail at security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
## Credits
@@ -156,5 +156,5 @@ Kudos to [Peka](http://peka.byethost11.com/photoblog/) for his awesome work on t
Traefik's logo is licensed under the Creative Commons 3.0 Attributions license.
Traefik's logo was inspired by the gopher stickers made by Takuya Ueda (https://twitter.com/tenntenn).
The original Go gopher was designed by Renee French (https://reneefrench.blogspot.com/).
Traefik's logo was inspired by the gopher stickers made by [Takuya Ueda](https://twitter.com/tenntenn).
The original Go gopher was designed by [Renee French](https://reneefrench.blogspot.com/).

View File

@@ -408,13 +408,13 @@ func stats(staticConfiguration *static.Configuration) {
logger.Info(`Stats collection is enabled.`)
logger.Info(`Many thanks for contributing to Traefik's improvement by allowing us to receive anonymous information from your configuration.`)
logger.Info(`Help us improve Traefik by leaving this feature on :)`)
logger.Info(`More details on: https://docs.traefik.io/v2.0/contributing/data-collection/`)
logger.Info(`More details on: https://docs.traefik.io/contributing/data-collection/`)
collect(staticConfiguration)
} else {
logger.Info(`
Stats collection is disabled.
Help us improve Traefik by turning this feature on :)
More details on: https://docs.traefik.io/v2.0/contributing/data-collection/
More details on: https://docs.traefik.io/contributing/data-collection/
`)
}
}

View File

@@ -3,11 +3,11 @@
A Quick Guide for Efficient Contributions
{: .subtitle }
So you've decide to improve Traefik?
So you've decided to improve Traefik?
Thank You!
Now the last step is to submit your Pull Request in a way that makes sure it gets the attention it deserves.
Let's go though the classic pitfalls to make sure everything is right.
Let's go through the classic pitfalls to make sure everything is right.
## Title
@@ -36,7 +36,7 @@ Help the readers focus on what matters, and help them understand the structure o
- Add tests.
- Address review comments in terms of additional commits (and don't amend/squash existing ones unless the PR is trivial).
!!! note "third-party dependencies"
!!! note "Third-Party Dependencies"
If a PR involves changes to third-party dependencies, the commits pertaining to the vendor folder and the manifest/lock file(s) should be committed separated.

View File

@@ -74,7 +74,7 @@ traefik --help
# or
docker run traefik[:version] --help
# ex: docker run traefik:2.0 --help
# ex: docker run traefik:2.1 --help
```
All available arguments can also be found [here](../reference/static-configuration/cli.md).

View File

@@ -9,11 +9,11 @@ You can install Traefik with the following flavors:
## Use the Official Docker Image
Choose one of the [official Docker images](https://hub.docker.com/_/traefik) and run it with the [sample configuration file](https://raw.githubusercontent.com/containous/traefik/v2.0/traefik.sample.toml):
Choose one of the [official Docker images](https://hub.docker.com/_/traefik) and run it with the [sample configuration file](https://raw.githubusercontent.com/containous/traefik/v2.1/traefik.sample.toml):
```bash
docker run -d -p 8080:8080 -p 80:80 \
-v $PWD/traefik.toml:/etc/traefik/traefik.toml traefik:v2.0
-v $PWD/traefik.toml:/etc/traefik/traefik.toml traefik:v2.1
```
For more details, go to the [Docker provider documentation](../providers/docker.md)
@@ -21,7 +21,7 @@ For more details, go to the [Docker provider documentation](../providers/docker.
!!! tip
* Prefer a fixed version than the latest that could be an unexpected version.
ex: `traefik:v2.0.0`
ex: `traefik:v2.1.4`
* Docker images are based from the [Alpine Linux Official image](https://hub.docker.com/_/alpine).
* Any orchestrator using docker images can fetch the official Traefik docker image.

View File

@@ -14,8 +14,8 @@ version: '3'
services:
reverse-proxy:
# The official v2.0 Traefik docker image
image: traefik:v2.0
# The official v2 Traefik docker image
image: traefik:v2.1
# Enables the web UI and tells Traefik to listen to docker
command: --api.insecure=true --providers.docker
ports:

View File

@@ -23,6 +23,25 @@ Certificates are requested for domain names retrieved from the router's [dynamic
You can read more about this retrieval mechanism in the following section: [ACME Domain Definition](#domain-definition).
!!! important "Defining a certificates resolver does not result in all routers automatically using it. Each router that is supposed to use the resolver must [reference](../routing/routers/index.md#certresolver) it."
??? note "Configuration Reference"
There are many available options for ACME.
For a quick glance at what's possible, browse the configuration reference:
```toml tab="File (TOML)"
--8<-- "content/https/ref-acme.toml"
```
```yaml tab="File (YAML)"
--8<-- "content/https/ref-acme.yaml"
```
```bash tab="CLI"
--8<-- "content/https/ref-acme.txt"
```
## Domain Definition
Certificate resolvers request certificates for a set of the domain names
@@ -59,10 +78,10 @@ Please check the [configuration examples below](#configuration-examples) for mor
[entryPoints.websecure]
address = ":443"
[certificatesResolvers.le.acme]
[certificatesResolvers.myresolver.acme]
email = "your-email@your-domain.org"
storage = "acme.json"
[certificatesResolvers.le.acme.httpChallenge]
[certificatesResolvers.myresolver.acme.httpChallenge]
# used during the challenge
entryPoint = "web"
```
@@ -76,7 +95,7 @@ Please check the [configuration examples below](#configuration-examples) for mor
address: ":443"
certificatesResolvers:
sample:
myresolver:
acme:
email: your-email@your-domain.org
storage: acme.json
@@ -89,31 +108,14 @@ Please check the [configuration examples below](#configuration-examples) for mor
--entryPoints.web.address=:80
--entryPoints.websecure.address=:443
# ...
--certificatesResolvers.le.acme.email=your-email@your-domain.org
--certificatesResolvers.le.acme.storage=acme.json
--certificatesResolvers.myresolver.acme.email=your-email@your-domain.org
--certificatesResolvers.myresolver.acme.storage=acme.json
# used during the challenge
--certificatesResolvers.le.acme.httpChallenge.entryPoint=web
--certificatesResolvers.myresolver.acme.httpChallenge.entryPoint=web
```
!!! important "Defining a certificates resolver does not result in all routers automatically using it. Each router that is supposed to use the resolver must [reference](../routing/routers/index.md#certresolver) it."
??? note "Configuration Reference"
There are many available options for ACME.
For a quick glance at what's possible, browse the configuration reference:
```toml tab="File (TOML)"
--8<-- "content/https/ref-acme.toml"
```
```yaml tab="File (YAML)"
--8<-- "content/https/ref-acme.yaml"
```
```bash tab="CLI"
--8<-- "content/https/ref-acme.txt"
```
??? example "Single Domain from Router's Rule Example"
* A certificate for the domain `company.com` is requested:
@@ -164,14 +166,14 @@ when using the `TLS-ALPN-01` challenge, Traefik must be reachable by Let's Encry
??? example "Configuring the `tlsChallenge`"
```toml tab="File (TOML)"
[certificatesResolvers.le.acme]
[certificatesResolvers.myresolver.acme]
# ...
[certificatesResolvers.le.acme.tlsChallenge]
[certificatesResolvers.myresolver.acme.tlsChallenge]
```
```yaml tab="File (YAML)"
certificatesResolvers:
sample:
myresolver:
acme:
# ...
tlsChallenge: {}
@@ -179,7 +181,7 @@ when using the `TLS-ALPN-01` challenge, Traefik must be reachable by Let's Encry
```bash tab="CLI"
# ...
--certificatesResolvers.le.acme.tlsChallenge=true
--certificatesResolvers.myresolver.acme.tlsChallenge=true
```
### `httpChallenge`
@@ -187,9 +189,9 @@ when using the `TLS-ALPN-01` challenge, Traefik must be reachable by Let's Encry
Use the `HTTP-01` challenge to generate and renew ACME certificates by provisioning an HTTP resource under a well-known URI.
As described on the Let's Encrypt [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72),
when using the `HTTP-01` challenge, `certificatesResolvers.le.acme.httpChallenge.entryPoint` must be reachable by Let's Encrypt through port 80.
when using the `HTTP-01` challenge, `certificatesResolvers.myresolver.acme.httpChallenge.entryPoint` must be reachable by Let's Encrypt through port 80.
??? example "Using an EntryPoint Called http for the `httpChallenge`"
??? example "Using an EntryPoint Called web for the `httpChallenge`"
```toml tab="File (TOML)"
[entryPoints]
@@ -199,9 +201,9 @@ when using the `HTTP-01` challenge, `certificatesResolvers.le.acme.httpChallenge
[entryPoints.websecure]
address = ":443"
[certificatesResolvers.le.acme]
[certificatesResolvers.myresolver.acme]
# ...
[certificatesResolvers.le.acme.httpChallenge]
[certificatesResolvers.myresolver.acme.httpChallenge]
entryPoint = "web"
```
@@ -214,7 +216,7 @@ when using the `HTTP-01` challenge, `certificatesResolvers.le.acme.httpChallenge
address: ":443"
certificatesResolvers:
sample:
myresolver:
acme:
# ...
httpChallenge:
@@ -225,7 +227,7 @@ when using the `HTTP-01` challenge, `certificatesResolvers.le.acme.httpChallenge
--entryPoints.web.address=:80
--entryPoints.websecure.address=:443
# ...
--certificatesResolvers.le.acme.httpChallenge.entryPoint=web
--certificatesResolvers.myresolver.acme.httpChallenge.entryPoint=web
```
!!! info ""
@@ -238,9 +240,9 @@ Use the `DNS-01` challenge to generate and renew ACME certificates by provisioni
??? example "Configuring a `dnsChallenge` with the DigitalOcean Provider"
```toml tab="File (TOML)"
[certificatesResolvers.le.acme]
[certificatesResolvers.myresolver.acme]
# ...
[certificatesResolvers.le.acme.dnsChallenge]
[certificatesResolvers.myresolver.acme.dnsChallenge]
provider = "digitalocean"
delayBeforeCheck = 0
# ...
@@ -248,7 +250,7 @@ Use the `DNS-01` challenge to generate and renew ACME certificates by provisioni
```yaml tab="File (YAML)"
certificatesResolvers:
sample:
myresolver:
acme:
# ...
dnsChallenge:
@@ -259,8 +261,8 @@ Use the `DNS-01` challenge to generate and renew ACME certificates by provisioni
```bash tab="CLI"
# ...
--certificatesResolvers.le.acme.dnsChallenge.provider=digitalocean
--certificatesResolvers.le.acme.dnsChallenge.delayBeforeCheck=0
--certificatesResolvers.myresolver.acme.dnsChallenge.provider=digitalocean
--certificatesResolvers.myresolver.acme.dnsChallenge.delayBeforeCheck=0
# ...
```
@@ -287,9 +289,10 @@ For example, `CF_API_EMAIL_FILE=/run/secrets/traefik_cf-api-email` could be used
| [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) |
| [Checkdomain](https://www.checkdomain.de/) | `checkdomain` | `CHECKDOMAIN_TOKEN`, | [Additional configuration](https://go-acme.github.io/lego/dns/checkdomain/) |
| [ClouDNS](https://www.cloudns.net/) | `cloudns` | `CLOUDNS_AUTH_ID`, `CLOUDNS_AUTH_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudns) |
| [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CF_API_EMAIL`, `CF_API_KEY` [^5] or `CF_DNS_API_TOKEN`, `[CF_ZONE_API_TOKEN]` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudflare) |
| [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CF_API_EMAIL`, `CF_API_KEY` [^5] or `CF_DNS_API_TOKEN`, `[CF_ZONE_API_TOKEN]` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudflare) |
| [CloudXNS](https://www.cloudxns.net) | `cloudxns` | `CLOUDXNS_API_KEY`, `CLOUDXNS_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudxns) |
| [ConoHa](https://www.conoha.jp) | `conoha` | `CONOHA_TENANT_ID`, `CONOHA_API_USERNAME`, `CONOHA_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/conoha) |
| [Constellix](https://constellix.com) | `constellix` | `CONSTELLIX_API_KEY`, `CONSTELLIX_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/constellix) |
| [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/digitalocean) |
| [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/dnsimple) |
| [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` | [Additional configuration](https://go-acme.github.io/lego/dns/dnsmadeeasy) |
@@ -333,7 +336,9 @@ For example, `CF_API_EMAIL_FILE=/run/secrets/traefik_cf-api-email` could be used
| [RFC2136](https://tools.ietf.org/html/rfc2136) | `rfc2136` | `RFC2136_TSIG_KEY`, `RFC2136_TSIG_SECRET`, `RFC2136_TSIG_ALGORITHM`, `RFC2136_NAMESERVER` | [Additional configuration](https://go-acme.github.io/lego/dns/rfc2136) |
| [Route 53](https://aws.amazon.com/route53/) | `route53` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `[AWS_REGION]`, `[AWS_HOSTED_ZONE_ID]` or a configured user/instance IAM profile. | [Additional configuration](https://go-acme.github.io/lego/dns/route53) |
| [Sakura Cloud](https://cloud.sakura.ad.jp/) | `sakuracloud` | `SAKURACLOUD_ACCESS_TOKEN`, `SAKURACLOUD_ACCESS_TOKEN_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/sakuracloud) |
| [Scaleway](https://www.scaleway.com) | `scaleway` | `SCALEWAY_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/scaleway) |
| [Selectel](https://selectel.ru/en/) | `selectel` | `SELECTEL_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/selectel) |
| [Servercow](https://servercow.de) | `servercow` | `SERVERCOW_USERNAME`, `SERVERCOW_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/servercow) |
| [Stackpath](https://www.stackpath.com/) | `stackpath` | `STACKPATH_CLIENT_ID`, `STACKPATH_CLIENT_SECRET`, `STACKPATH_STACK_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/stackpath) |
| [TransIP](https://www.transip.nl/) | `transip` | `TRANSIP_ACCOUNT_NAME`, `TRANSIP_PRIVATE_KEY_PATH` | [Additional configuration](https://go-acme.github.io/lego/dns/transip) |
| [VegaDNS](https://github.com/shupp/VegaDNS-API) | `vegadns` | `SECRET_VEGADNS_KEY`, `SECRET_VEGADNS_SECRET`, `VEGADNS_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/vegadns) |
@@ -358,16 +363,16 @@ For example, `CF_API_EMAIL_FILE=/run/secrets/traefik_cf-api-email` could be used
Use custom DNS servers to resolve the FQDN authority.
```toml tab="File (TOML)"
[certificatesResolvers.le.acme]
[certificatesResolvers.myresolver.acme]
# ...
[certificatesResolvers.le.acme.dnsChallenge]
[certificatesResolvers.myresolver.acme.dnsChallenge]
# ...
resolvers = ["1.1.1.1:53", "8.8.8.8:53"]
```
```yaml tab="File (YAML)"
certificatesResolvers:
sample:
myresolver:
acme:
# ...
dnsChallenge:
@@ -379,7 +384,7 @@ certificatesResolvers:
```bash tab="CLI"
# ...
--certificatesResolvers.le.acme.dnsChallenge.resolvers=1.1.1.1:53,8.8.8.8:53
--certificatesResolvers.myresolver.acme.dnsChallenge.resolvers=1.1.1.1:53,8.8.8.8:53
```
#### Wildcard Domains
@@ -391,10 +396,17 @@ As described in [Let's Encrypt's post](https://community.letsencrypt.org/t/stagi
### `caServer`
_Required, Default="https://acme-v02.api.letsencrypt.org/directory"_
The CA server to use:
- Let's Encrypt production server: https://acme-v02.api.letsencrypt.org/directory
- Let's Encrypt staging server: https://acme-staging-v02.api.letsencrypt.org/directory
??? example "Using the Let's Encrypt staging server"
```toml tab="File (TOML)"
[certificatesResolvers.le.acme]
[certificatesResolvers.myresolver.acme]
# ...
caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
# ...
@@ -402,7 +414,7 @@ As described in [Let's Encrypt's post](https://community.letsencrypt.org/t/stagi
```yaml tab="File (YAML)"
certificatesResolvers:
sample:
myresolver:
acme:
# ...
caServer: https://acme-staging-v02.api.letsencrypt.org/directory
@@ -411,16 +423,18 @@ As described in [Let's Encrypt's post](https://community.letsencrypt.org/t/stagi
```bash tab="CLI"
# ...
--certificatesResolvers.le.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory
--certificatesResolvers.myresolver.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory
# ...
```
### `storage`
_Required, Default="acme.json"_
The `storage` option sets the location where your ACME certificates are saved to.
```toml tab="File (TOML)"
[certificatesResolvers.le.acme]
[certificatesResolvers.myresolver.acme]
# ...
storage = "acme.json"
# ...
@@ -428,7 +442,7 @@ The `storage` option sets the location where your ACME certificates are saved to
```yaml tab="File (YAML)"
certificatesResolvers:
sample:
myresolver:
acme:
# ...
storage: acme.json
@@ -437,17 +451,11 @@ certificatesResolvers:
```bash tab="CLI"
# ...
--certificatesResolvers.le.acme.storage=acme.json
--certificatesResolvers.myresolver.acme.storage=acme.json
# ...
```
The value can refer to some kinds of storage:
- a JSON file
#### In a File
ACME certificates can be stored in a JSON file that needs to have a `600` file mode .
ACME certificates are stored in a JSON file that needs to have a `600` file mode.
In Docker you can mount either the JSON file, or the folder containing it:

View File

@@ -4,7 +4,7 @@
labels:
- traefik.http.routers.blog.rule=Host(`company.com`) && Path(`/blog`)
- traefik.http.routers.blog.tls=true
- traefik.http.routers.blog.tls.certresolver=le
- traefik.http.routers.blog.tls.certresolver=myresolver
- traefik.http.routers.blog.tls.domains[0].main=company.org
- traefik.http.routers.blog.tls.domains[0].sans=*.company.org
```
@@ -16,7 +16,7 @@ deploy:
- traefik.http.routers.blog.rule=Host(`company.com`) && Path(`/blog`)
- traefik.http.services.blog-svc.loadbalancer.server.port=8080"
- traefik.http.routers.blog.tls=true
- traefik.http.routers.blog.tls.certresolver=le
- traefik.http.routers.blog.tls.certresolver=myresolver
- traefik.http.routers.blog.tls.domains[0].main=company.org
- traefik.http.routers.blog.tls.domains[0].sans=*.company.org
```
@@ -36,14 +36,18 @@ spec:
- name: blog
port: 8080
tls:
certResolver: le
certResolver: myresolver
domains:
- main: company.org
sans:
- *.company.org
```
```json tab="Marathon"
labels: {
"traefik.http.routers.blog.rule": "Host(`company.com`) && Path(`/blog`)",
"traefik.http.routers.blog.tls": "true",
"traefik.http.routers.blog.tls.certresolver": "le",
"traefik.http.routers.blog.tls.certresolver": "myresolver",
"traefik.http.routers.blog.tls.domains[0].main": "company.com",
"traefik.http.routers.blog.tls.domains[0].sans": "*.company.com",
"traefik.http.services.blog-svc.loadbalancer.server.port": "8080"
@@ -55,7 +59,7 @@ labels: {
labels:
- traefik.http.routers.blog.rule=Host(`company.com`) && Path(`/blog`)
- traefik.http.routers.blog.tls=true
- traefik.http.routers.blog.tls.certresolver=le
- traefik.http.routers.blog.tls.certresolver=myresolver
- traefik.http.routers.blog.tls.domains[0].main=company.org
- traefik.http.routers.blog.tls.domains[0].sans=*.company.org
```
@@ -66,7 +70,7 @@ labels:
[http.routers.blog]
rule = "Host(`company.com`) && Path(`/blog`)"
[http.routers.blog.tls]
certResolver = "le" # From static configuration
certResolver = "myresolver" # From static configuration
[[http.routers.blog.tls.domains]]
main = "company.org"
sans = ["*.company.org"]
@@ -79,7 +83,7 @@ http:
blog:
rule: "Host(`company.com`) && Path(`/blog`)"
tls:
certResolver: le
certResolver: myresolver
domains:
- main: "company.org"
sans:

View File

@@ -4,7 +4,7 @@
labels:
- traefik.http.routers.blog.rule=(Host(`company.com`) && Path(`/blog`)) || Host(`blog.company.org`)
- traefik.http.routers.blog.tls=true
- traefik.http.routers.blog.tls.certresolver=le
- traefik.http.routers.blog.tls.certresolver=myresolver
```
```yaml tab="Docker (Swarm)"
@@ -13,7 +13,7 @@ deploy:
labels:
- traefik.http.routers.blog.rule=(Host(`company.com`) && Path(`/blog`)) || Host(`blog.company.org`)
- traefik.http.routers.blog.tls=true
- traefik.http.routers.blog.tls.certresolver=le
- traefik.http.routers.blog.tls.certresolver=myresolver
- traefik.http.services.blog-svc.loadbalancer.server.port=8080"
```
@@ -32,14 +32,14 @@ spec:
- name: blog
port: 8080
tls:
certresolver: le
certresolver: myresolver
```
```json tab="Marathon"
labels: {
"traefik.http.routers.blog.rule": "(Host(`company.com`) && Path(`/blog`)) || Host(`blog.company.org`)",
"traefik.http.routers.blog.tls": "true",
"traefik.http.routers.blog.tls.certresolver": "le",
"traefik.http.routers.blog.tls.certresolver": "myresolver",
"traefik.http.services.blog-svc.loadbalancer.server.port": "8080"
}
```
@@ -49,7 +49,7 @@ labels: {
labels:
- traefik.http.routers.blog.rule=(Host(`company.com`) && Path(`/blog`)) || Host(`blog.company.org`)
- traefik.http.routers.blog.tls=true
- traefik.http.routers.blog.tls.certresolver=le
- traefik.http.routers.blog.tls.certresolver=myresolver
```
```toml tab="File (TOML)"
@@ -58,7 +58,7 @@ labels:
[http.routers.blog]
rule = "(Host(`company.com`) && Path(`/blog`)) || Host(`blog.company.org`)"
[http.routers.blog.tls]
certResolver = "le"
certResolver = "myresolver"
```
```yaml tab="File (YAML)"
@@ -68,5 +68,5 @@ http:
blog:
rule: "(Host(`company.com`) && Path(`/blog`)) || Host(`blog.company.org`)"
tls:
certResolver: le
certResolver: myresolver
```

View File

@@ -4,7 +4,7 @@
labels:
- traefik.http.routers.blog.rule=Host(`company.com`) && Path(`/blog`)
- traefik.http.routers.blog.tls=true
- traefik.http.routers.blog.tls.certresolver=le
- traefik.http.routers.blog.tls.certresolver=myresolver
```
```yaml tab="Docker (Swarm)"
@@ -13,7 +13,7 @@ deploy:
labels:
- traefik.http.routers.blog.rule=Host(`company.com`) && Path(`/blog`)
- traefik.http.routers.blog.tls=true
- traefik.http.routers.blog.tls.certresolver=le
- traefik.http.routers.blog.tls.certresolver=myresolver
- traefik.http.services.blog-svc.loadbalancer.server.port=8080"
```
@@ -32,14 +32,14 @@ spec:
- name: blog
port: 8080
tls:
certresolver: le
certresolver: myresolver
```
```json tab="Marathon"
labels: {
"traefik.http.routers.blog.rule": "Host(`company.com`) && Path(`/blog`)",
"traefik.http.routers.blog.tls": "true",
"traefik.http.routers.blog.tls.certresolver": "le",
"traefik.http.routers.blog.tls.certresolver": "myresolver",
"traefik.http.services.blog-svc.loadbalancer.server.port": "8080"
}
```
@@ -49,7 +49,7 @@ labels: {
labels:
- traefik.http.routers.blog.rule=Host(`company.com`) && Path(`/blog`)
- traefik.http.routers.blog.tls=true
- traefik.http.routers.blog.tls.certresolver=le
- traefik.http.routers.blog.tls.certresolver=myresolver
```
```toml tab="Single Domain"
@@ -58,7 +58,7 @@ labels:
[http.routers.blog]
rule = "Host(`company.com`) && Path(`/blog`)"
[http.routers.blog.tls]
certResolver = "le"
certResolver = "myresolver"
```
```yaml tab="File (YAML)"
@@ -68,5 +68,5 @@ http:
blog:
rule: "Host(`company.com`) && Path(`/blog`)"
tls:
certResolver: le
certResolver: myresolver
```

View File

@@ -1,5 +1,5 @@
# Enable ACME (Let's Encrypt): automatic SSL.
[certificatesResolvers.sample.acme]
[certificatesResolvers.myresolver.acme]
# Email address used for registration.
#
@@ -35,13 +35,13 @@
#
# Optional (but recommended)
#
[certificatesResolvers.le.acme.tlsChallenge]
[certificatesResolvers.myresolver.acme.tlsChallenge]
# Use a HTTP-01 ACME challenge.
#
# Optional
#
# [certificatesResolvers.le.acme.httpChallenge]
# [certificatesResolvers.myresolver.acme.httpChallenge]
# EntryPoint to use for the HTTP-01 challenges.
#
@@ -54,7 +54,7 @@
#
# Optional
#
# [certificatesResolvers.le.acme.dnsChallenge]
# [certificatesResolvers.myresolver.acme.dnsChallenge]
# DNS provider used.
#

View File

@@ -4,13 +4,13 @@
#
# Required
#
--certificatesResolvers.le.acme.email=test@traefik.io
--certificatesResolvers.myresolver.acme.email=test@traefik.io
# File or key used for certificates storage.
#
# Required
#
--certificatesResolvers.le.acme.storage=acme.json
--certificatesResolvers.myresolver.acme.storage=acme.json
# CA server to use.
# Uncomment the line to use Let's Encrypt's staging server,
@@ -19,7 +19,7 @@
# Optional
# Default: "https://acme-v02.api.letsencrypt.org/directory"
#
--certificatesResolvers.le.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory
--certificatesResolvers.myresolver.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory
# KeyType to use.
#
@@ -28,38 +28,38 @@
#
# Available values : "EC256", "EC384", "RSA2048", "RSA4096", "RSA8192"
#
--certificatesResolvers.le.acme.keyType=RSA4096
--certificatesResolvers.myresolver.acme.keyType=RSA4096
# Use a TLS-ALPN-01 ACME challenge.
#
# Optional (but recommended)
#
--certificatesResolvers.le.acme.tlsChallenge=true
--certificatesResolvers.myresolver.acme.tlsChallenge=true
# Use a HTTP-01 ACME challenge.
#
# Optional
#
--certificatesResolvers.le.acme.httpChallenge=true
--certificatesResolvers.myresolver.acme.httpChallenge=true
# EntryPoint to use for the HTTP-01 challenges.
#
# Required
#
--certificatesResolvers.le.acme.httpChallenge.entryPoint=web
--certificatesResolvers.myresolver.acme.httpChallenge.entryPoint=web
# Use a DNS-01 ACME challenge rather than HTTP-01 challenge.
# Note: mandatory for wildcard certificate generation.
#
# Optional
#
--certificatesResolvers.le.acme.dnsChallenge=true
--certificatesResolvers.myresolver.acme.dnsChallenge=true
# DNS provider used.
#
# Required
#
--certificatesResolvers.le.acme.dnsChallenge.provider=digitalocean
--certificatesResolvers.myresolver.acme.dnsChallenge.provider=digitalocean
# By default, the provider will verify the TXT DNS challenge record before letting ACME verify.
# If delayBeforeCheck is greater than zero, this check is delayed for the configured duration in seconds.
@@ -68,14 +68,14 @@
# Optional
# Default: 0
#
--certificatesResolvers.le.acme.dnsChallenge.delayBeforeCheck=0
--certificatesResolvers.myresolver.acme.dnsChallenge.delayBeforeCheck=0
# Use following DNS servers to resolve the FQDN authority.
#
# Optional
# Default: empty
#
--certificatesResolvers.le.acme.dnsChallenge.resolvers=1.1.1.1:53,8.8.8.8:53
--certificatesResolvers.myresolver.acme.dnsChallenge.resolvers=1.1.1.1:53,8.8.8.8:53
# Disable the DNS propagation checks before notifying ACME that the DNS challenge is ready.
#
@@ -85,4 +85,4 @@
# Optional
# Default: false
#
--certificatesResolvers.le.acme.dnsChallenge.disablePropagationCheck=true
--certificatesResolvers.myresolver.acme.dnsChallenge.disablePropagationCheck=true

View File

@@ -1,5 +1,5 @@
certificatesResolvers:
le:
myresolver:
# Enable ACME (Let's Encrypt): automatic SSL.
acme:

View File

@@ -20,4 +20,9 @@ Developing Traefik, our main goal is to make it simple to use, and we're sure yo
!!! info
If you're a business running critical services behind Traefik, know that [Containous](https://containo.us), the company that sponsors Traefik's development, can provide [commercial support](https://info.containo.us/commercial-services) and develops an [Enterprise Edition](https://containo.us/traefikee/) of Traefik.
Join our user friendly and active [Community Forum](https://community.containo.us) to discuss, learn, and connect with the traefik community.
If you're a business running critical services behind Traefik,
know that [Containous](https://containo.us), the company that sponsors Traefik's development,
can provide [commercial support](https://info.containo.us/commercial-services)
and develops an [Enterprise Edition](https://containo.us/traefikee/) of Traefik.

View File

@@ -143,11 +143,11 @@ then you'll have to append to the middleware name, the `@` separator, followed b
!!! important "Kubernetes Namespace"
As Kubernetes also has its own notion of namespace, one should not confuse the "provider namespace"
with the "kubernetes namespace" of a resource when in the context of a cross-provider usage.
In this case, since the definition of the middleware is not in kubernetes,
specifying a "kubernetes namespace" when referring to the resource does not make any sense,
and therefore this specification would be ignored even if present.
As Kubernetes also has its own notion of namespace, one should not confuse the "provider namespace"
with the "kubernetes namespace" of a resource when in the context of a cross-provider usage.
In this case, since the definition of the middleware is not in kubernetes,
specifying a "kubernetes namespace" when referring to the resource does not make any sense,
and therefore this specification would be ignored even if present.
!!! abstract "Referencing a Middleware from Another Provider"

View File

@@ -11,6 +11,132 @@ RedirectScheme redirect request from a scheme to another.
## Configuration Examples
```yaml tab="Docker"
# Redirect to https
labels:
- "traefik.http.middlewares.test-redirectscheme.redirectscheme.scheme=https"
- "traefik.http.middlewares.test-redirectscheme.redirectscheme.permanent=true"
```
```yaml tab="Kubernetes"
# Redirect to https
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: test-redirectscheme
spec:
redirectScheme:
scheme: https
permanent: true
```
```yaml tab="Consul Catalog"
# Redirect to https
labels:
- "traefik.http.middlewares.test-redirectscheme.redirectscheme.scheme=https"
- "traefik.http.middlewares.test-redirectscheme.redirectscheme.permanent=true"
```
```json tab="Marathon"
"labels": {
"traefik.http.middlewares.test-redirectscheme.redirectscheme.scheme": "https"
"traefik.http.middlewares.test-redirectscheme.redirectscheme.permanent": "true"
}
```
```yaml tab="Rancher"
# Redirect to https
labels:
- "traefik.http.middlewares.test-redirectscheme.redirectscheme.scheme=https"
- "traefik.http.middlewares.test-redirectscheme.redirectscheme.permanent=true"
```
```toml tab="File (TOML)"
# Redirect to https
[http.middlewares]
[http.middlewares.test-redirectscheme.redirectScheme]
scheme = "https"
permanent = true
```
```yaml tab="File (YAML)"
# Redirect to https
http:
middlewares:
test-redirectscheme:
redirectScheme:
scheme: https
permanent: true
```
## Configuration Options
### `permanent`
Set the `permanent` option to `true` to apply a permanent redirection.
```yaml tab="Docker"
# Redirect to https
labels:
# ...
- "traefik.http.middlewares.test-redirectscheme.redirectscheme.permanent=true"
```
```yaml tab="Kubernetes"
# Redirect to https
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: test-redirectscheme
spec:
redirectScheme:
# ...
permanent: true
```
```yaml tab="Consul Catalog"
# Redirect to https
labels:
# ...
- "traefik.http.middlewares.test-redirectscheme.redirectscheme.permanent=true"
```
```json tab="Marathon"
"labels": {
"traefik.http.middlewares.test-redirectscheme.redirectscheme.permanent": "true"
}
```
```yaml tab="Rancher"
# Redirect to https
labels:
# ...
- "traefik.http.middlewares.test-redirectscheme.redirectscheme.permanent=true"
```
```toml tab="File (TOML)"
# Redirect to https
[http.middlewares]
[http.middlewares.test-redirectscheme.redirectScheme]
# ...
permanent = true
```
```yaml tab="File (YAML)"
# Redirect to https
http:
middlewares:
test-redirectscheme:
redirectScheme:
# ...
permanent: true
```
### `scheme`
The `scheme` option defines the scheme of the new url.
```yaml tab="Docker"
# Redirect to https
labels:
@@ -31,7 +157,7 @@ spec:
```yaml tab="Consul Catalog"
# Redirect to https
labels:
- "traefik.http.middlewares.test-redirectscheme.redirectscheme.scheme=https"
- "traefik.http.middlewares.test-redirectscheme.redirectscheme.scheme=https"
```
```json tab="Marathon"
@@ -62,16 +188,64 @@ http:
scheme: https
```
## Configuration Options
### `permanent`
Set the `permanent` option to `true` to apply a permanent redirection.
### `scheme`
The `scheme` option defines the scheme of the new url.
### `port`
The `port` option defines the port of the new url.
```yaml tab="Docker"
# Redirect to https
labels:
# ...
- "traefik.http.middlewares.test-redirectscheme.redirectscheme.port=443"
```
```yaml tab="Kubernetes"
# Redirect to https
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: test-redirectscheme
spec:
redirectScheme:
# ...
port: 443
```
```yaml tab="Consul Catalog"
# Redirect to https
labels:
# ...
- "traefik.http.middlewares.test-redirectscheme.redirectscheme.port=443"
```
```json tab="Marathon"
"labels": {
"traefik.http.middlewares.test-redirectscheme.redirectscheme.port": "443"
}
```
```yaml tab="Rancher"
# Redirect to https
labels:
# ...
- "traefik.http.middlewares.test-redirectscheme.redirectscheme.port=443"
```
```toml tab="File (TOML)"
# Redirect to https
[http.middlewares]
[http.middlewares.test-redirectscheme.redirectScheme]
# ...
port = 443
```
```yaml tab="File (YAML)"
# Redirect to https
http:
middlewares:
test-redirectscheme:
redirectScheme:
# ...
port: 443
```

View File

@@ -236,11 +236,8 @@ Then, a [router's TLS field](../routing/routers/index.md#tls) can refer to one o
keyFile = "/path/to/domain.key"
[tls.options]
[tls.options.default]
minVersion = "VersionTLS12"
[tls.options.myTLSOptions]
minVersion = "VersionTLS13"
minVersion = "VersionTLS12"
cipherSuites = [
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
@@ -267,7 +264,7 @@ Then, a [router's TLS field](../routing/routers/index.md#tls) can refer to one o
keyFile: /path/to/domain.key
options:
myTLSOptions:
minVersion: VersionTLS13
minVersion: VersionTLS12
cipherSuites:
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
@@ -286,7 +283,7 @@ Then, a [router's TLS field](../routing/routers/index.md#tls) can refer to one o
namespace: default
spec:
minVersion: VersionTLS13
minVersion: VersionTLS12
cipherSuites:
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
@@ -681,7 +678,7 @@ To apply a redirection, one of the redirect middlewares, [RedirectRegex](../midd
With the new core notions of v2 (introduced earlier in the section
["Frontends and Backends Are Dead... Long Live Routers, Middlewares, and Services"](#frontends-and-backends-are-dead-long-live-routers-middlewares-and-services)),
transforming the URL path prefix of incoming requests is configured with [middlewares](../middlewares/overview.md),
after the routing step with [router rule `PathPrefix`](https://docs.traefik.io/v2.0/routing/routers/#rule).
after the routing step with [router rule `PathPrefix`](../routing/routers/index.md#rule).
Use Case: Incoming requests to `http://company.org/admin` are forwarded to the webapplication "admin",
with the path `/admin` stripped, e.g. to `http://<IP>:<port>/`. In this case, you must:
@@ -826,7 +823,7 @@ with the path `/admin` stripped, e.g. to `http://<IP>:<port>/`. In this case, yo
entryPoint = "webs"
[entryPoints.websecure]
address = ":443"
[entryPoints.https.tls]
[entryPoints.websecure.tls]
[acme]
email = "your-email-here@my-awesome-app.org"
@@ -859,10 +856,10 @@ with the path `/admin` stripped, e.g. to `http://<IP>:<port>/`. In this case, yo
[entryPoints.websecure]
address = ":443"
[certificatesResolvers.sample.acme]
[certificatesResolvers.myresolver.acme]
email = "your-email@your-domain.org"
storage = "acme.json"
[certificatesResolvers.sample.acme.httpChallenge]
[certificatesResolvers.myresolver.acme.httpChallenge]
# used during the challenge
entryPoint = "web"
```
@@ -876,7 +873,7 @@ with the path `/admin` stripped, e.g. to `http://<IP>:<port>/`. In this case, yo
address: ":443"
certificatesResolvers:
sample:
myresolver:
acme:
email: your-email@your-domain.org
storage: acme.json
@@ -888,9 +885,9 @@ with the path `/admin` stripped, e.g. to `http://<IP>:<port>/`. In this case, yo
```bash tab="CLI"
--entryPoints.web.address=:80
--entryPoints.websecure.address=:443
--certificatesResolvers.sample.acme.email=your-email@your-domain.org
--certificatesResolvers.sample.acme.storage=acme.json
--certificatesResolvers.sample.acme.httpChallenge.entryPoint=web
--certificatesResolvers.myresolver.acme.email=your-email@your-domain.org
--certificatesResolvers.myresolver.acme.storage=acme.json
--certificatesResolvers.myresolver.acme.httpChallenge.entryPoint=web
```
## Traefik Logs

View File

@@ -2,8 +2,11 @@
## v2.0 to v2.1
In v2.1, a new CRD called `TraefikService` was added. While updating an installation to v2.1,
it is required to apply that CRD before as well as enhance the existing `ClusterRole` definition to allow Traefik to use that CRD.
### Kubernetes CRD
In v2.1, a new Kubernetes CRD called `TraefikService` was added.
While updating an installation to v2.1,
one should apply that CRD, and update the existing `ClusterRole` definition to allow Traefik to use that CRD.
To add that CRD and enhance the permissions, following definitions need to be applied to the cluster.

View File

@@ -35,7 +35,7 @@ If the given format is unsupported, the default (CLF) is used instead.
!!! info "Common Log Format"
```html
<remote_IP_address> - <client_user_name_if_available> [<timestamp>] "<request_method> <request_path> <request_protocol>" <origin_server_HTTP_status> <origin_server_content_size> "<request_referrer>" "<request_user_agent>" <number_of_requests_received_since_Traefik_started> "<Traefik_frontend_name>" "<Traefik_backend_URL>" <request_duration_in_ms>ms
<remote_IP_address> - <client_user_name_if_available> [<timestamp>] "<request_method> <request_path> <request_protocol>" <origin_server_HTTP_status> <origin_server_content_size> "<request_referrer>" "<request_user_agent>" <number_of_requests_received_since_Traefik_started> "<Traefik_router_name>" "<Traefik_server_URL>" <request_duration_in_ms>ms
```
### `bufferingSize`
@@ -195,6 +195,7 @@ accessLog:
| `RequestMethod` | The HTTP method. |
| `RequestPath` | The HTTP request URI, not including the scheme, host or port. |
| `RequestProtocol` | The version of HTTP requested. |
| `RequestScheme` | The HTTP scheme requested `http` or `https`. |
| `RequestLine` | `RequestMethod` + `RequestPath` + `RequestProtocol` |
| `RequestContentSize` | The number of bytes in the request entity (a.k.a. body) sent by the client. |
| `OriginDuration` | The time taken by the origin server ('upstream') to return its response. |

View File

@@ -40,24 +40,24 @@ tracing:
#### `localAgentPort`
_Require, Default=42699_
_Require, Default=35000_
Local Agent port instructs reporter to send spans to the haystack-agent at this port.
```toml tab="File (TOML)"
[tracing]
[tracing.haystack]
localAgentPort = 42699
localAgentPort = 35000
```
```yaml tab="File (YAML)"
tracing:
haystack:
localAgentPort: 42699
localAgentPort: 35000
```
```bash tab="CLI"
--tracing.haystack.localAgentPort=42699
--tracing.haystack.localAgentPort=35000
```
#### `globalTag`
@@ -91,61 +91,61 @@ Specifies the header name that will be used to store the trace ID.
```toml tab="File (TOML)"
[tracing]
[tracing.haystack]
traceIDHeaderName = "sample"
traceIDHeaderName = "Trace-ID"
```
```yaml tab="File (YAML)"
tracing:
haystack:
traceIDHeaderName: sample
traceIDHeaderName: Trace-ID
```
```bash tab="CLI"
--tracing.haystack.traceIDHeaderName=sample
--tracing.haystack.traceIDHeaderName=Trace-ID
```
#### `parentIDHeaderName`
_Optional, Default=empty_
Specifies the header name that will be used to store the parent ID.
```toml tab="File (TOML)"
[tracing]
[tracing.haystack]
parentIDHeaderName = "Parent-Message-ID"
```
```yaml tab="File (YAML)"
tracing:
haystack:
parentIDHeaderName: Parent-Message-ID
```
```bash tab="CLI"
--tracing.haystack.parentIDHeaderName=Parent-Message-ID
```
#### `spanIDHeaderName`
_Optional, Default=empty_
Specifies the header name that will be used to store the span ID.
```toml tab="File (TOML)"
[tracing]
[tracing.haystack]
parentIDHeaderName = "sample"
spanIDHeaderName = "Message-ID"
```
```yaml tab="File (YAML)"
tracing:
haystack:
parentIDHeaderName: "sample"
spanIDHeaderName: Message-ID
```
```bash tab="CLI"
--tracing.haystack.parentIDHeaderName=sample
```
#### `spanIDHeaderName`
_Optional, Default=empty_
Apply shared tag in a form of Key:Value to all the traces.
```toml tab="File (TOML)"
[tracing]
[tracing.haystack]
spanIDHeaderName = "sample:test"
```
```yaml tab="File (YAML)"
tracing:
haystack:
spanIDHeaderName: "sample:test"
```
```bash tab="CLI"
--tracing.haystack.spanIDHeaderName=sample:test
--tracing.haystack.spanIDHeaderName=Message-ID
```
#### `baggagePrefixHeaderName`

View File

@@ -31,6 +31,8 @@ spec:
services:
- name: api@internal
kind: TraefikService
middlewares:
- name: auth
---
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware

View File

@@ -565,7 +565,7 @@ Constraints is an expression that Traefik matches against the service's tags to
That is to say, if none of the service's tags match the expression, no route for that service is created.
If the expression is empty, all detected services are included.
The expression syntax is based on the `Tag("tag")`, and `TagRegex("tag")` functions,
The expression syntax is based on the ```Tag(`tag`)```, and ```TagRegex(`tag`)``` functions,
as well as the usual boolean logic, as shown in examples below.
??? example "Constraints Expression Examples"

View File

@@ -246,7 +246,7 @@ See the sections [Docker API Access](#docker-api-access) and [Docker Swarm API A
services:
traefik:
image: traefik:v2.0 # The official v2.0 Traefik docker image
image: traefik:v2.1 # The official v2 Traefik docker image
ports:
- "80:80"
volumes:
@@ -452,6 +452,30 @@ providers:
Defines the polling interval (in seconds) in Swarm Mode.
### `watch`
_Optional, Default=true_
```toml tab="File (TOML)"
[providers.docker]
watch = false
# ...
```
```yaml tab="File (YAML)"
providers:
docker:
watch: false
# ...
```
```bash tab="CLI"
--providers.docker.watch=false
# ...
```
Watch Docker Swarm events.
### `constraints`
_Optional, Default=""_

View File

@@ -118,27 +118,35 @@ If you're in a hurry, maybe you'd rather go through the [dynamic configuration](
### `filename`
Defines the path of the configuration file.
Defines the path to the configuration file.
!!! warning ""
`filename` and `directory` are mutually exclusive.
The recommendation is to use `directory`.
```toml tab="File (TOML)"
[providers]
[providers.file]
filename = "dynamic_conf.toml"
filename = "/path/to/config/dynamic_conf.toml"
```
```yaml tab="File (YAML)"
providers:
file:
filename: dynamic_conf.yml
filename: /path/to/config/dynamic_conf.yml
```
```bash tab="CLI"
--providers.file.filename=dynamic_conf.toml
--providers.file.filename=/path/to/config/dynamic_conf.toml
```
### `directory`
Defines the directory that contains the configuration files.
Defines the path to the directory that contains the configuration files.
!!! warning ""
`filename` and `directory` are mutually exclusive.
The recommendation is to use `directory`.
```toml tab="File (TOML)"
[providers]
@@ -186,8 +194,11 @@ providers:
Go Templating only works along with dedicated dynamic configuration files.
Templating does not work in the Traefik main static configuration file.
Traefik allows using Go templating.
Thus, it's possible to define easily lot of routers, services and TLS certificates as described in the file `template-rules.toml` :
Traefik allows using Go templating,
it must be a valid [Go template](https://golang.org/pkg/text/template/),
augmented with the [sprig template functions](http://masterminds.github.io/sprig/).
Thus, it's possible to define easily lot of routers, services and TLS certificates as described in the following examples:
??? example "Configuring Using Templating"
@@ -197,7 +208,7 @@ Thus, it's possible to define easily lot of routers, services and TLS certificat
[http.routers]
{{ range $i, $e := until 100 }}
[http.routers.router{{ $e }}]
[http.routers.router{{ $e }}-{{ env "MY_ENV_VAR" }}]
# ...
{{ end }}
@@ -239,40 +250,38 @@ Thus, it's possible to define easily lot of routers, services and TLS certificat
```yaml tab="YAML"
http:
{{range $i, $e := until 100 }}
routers:
router{{ $e }:
{{range $i, $e := until 100 }}
router{{ $e }}-{{ env "MY_ENV_VAR" }}:
# ...
{{end}}
{{end}}
{{range $i, $e := until 100 }}
services:
{{range $i, $e := until 100 }}
application{{ $e }}:
# ...
{{end}}
{{end}}
tcp:
{{range $i, $e := until 100 }}
routers:
router{{ $e }:
{{range $i, $e := until 100 }}
router{{ $e }}:
# ...
{{end}}
{{end}}
{{range $i, $e := until 100 }}
services:
{{range $i, $e := until 100 }}
service{{ $e }}:
# ...
{{end}}
{{end}}
{{ range $i, $e := until 10 }}
tls:
certificates:
{{ range $i, $e := until 10 }}
- certFile: "/etc/traefik/cert-{{ $e }}.pem"
keyFile: "/etc/traefik/cert-{{ $e }}.key"
store:
- "my-store-foo-{{ $e }}"
- "my-store-bar-{{ $e }}"
{{end}}
{{end}}
```

View File

@@ -23,7 +23,9 @@ providers:
--providers.kubernetesingress=true
```
The provider then watches for incoming ingresses events, such as the example below, and derives the corresponding dynamic configuration from it, which in turn will create the resulting routers, services, handlers, etc.
The provider then watches for incoming ingresses events, such as the example below,
and derives the corresponding dynamic configuration from it,
which in turn will create the resulting routers, services, handlers, etc.
```yaml tab="File (YAML)"
kind: Ingress
@@ -49,17 +51,26 @@ spec:
## LetsEncrypt Support with the Ingress Provider
By design, Traefik is a stateless application, meaning that it only derives its configuration from the environment it runs in, without additional configuration.
For this reason, users can run multiple instances of Traefik at the same time to achieve HA, as is a common pattern in the kubernetes ecosystem.
By design, Traefik is a stateless application,
meaning that it only derives its configuration from the environment it runs in,
without additional configuration.
For this reason, users can run multiple instances of Traefik at the same time to achieve HA,
as is a common pattern in the kubernetes ecosystem.
When using a single instance of Traefik with LetsEncrypt, no issues should be encountered, however this could be a single point of failure.
Unfortunately, it is not possible to run multiple instances of Traefik 2.0 with LetsEncrypt enabled, because there is no way to ensure that the correct instance of Traefik will receive the challenge request, and subsequent responses.
Previous versions of Traefik used a [KV store](https://docs.traefik.io/v1.7/configuration/acme/#storage) to attempt to achieve this, but due to sub-optimal performance was dropped as a feature in 2.0.
When using a single instance of Traefik with LetsEncrypt, no issues should be encountered,
however this could be a single point of failure.
Unfortunately, it is not possible to run multiple instances of Traefik 2.0 with LetsEncrypt enabled,
because there is no way to ensure that the correct instance of Traefik will receive the challenge request, and subsequent responses.
Previous versions of Traefik used a [KV store](https://docs.traefik.io/v1.7/configuration/acme/#storage) to attempt to achieve this,
but due to sub-optimal performance was dropped as a feature in 2.0.
If you require LetsEncrypt with HA in a kubernetes environment, we recommend using [TraefikEE](https://containo.us/traefikee/) where distributed LetsEncrypt is a supported feature.
If you require LetsEncrypt with HA in a kubernetes environment,
we recommend using [TraefikEE](https://containo.us/traefikee/) where distributed LetsEncrypt is a supported feature.
If you are wanting to continue to run Traefik Community Edition, LetsEncrypt HA can be achieved by using a Certificate Controller such as [Cert-Manager](https://docs.cert-manager.io/en/latest/index.html).
When using Cert-Manager to manage certificates, it will create secrets in your namespaces that can be referenced as TLS secrets in your [ingress objects](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls).
If you are wanting to continue to run Traefik Community Edition,
LetsEncrypt HA can be achieved by using a Certificate Controller such as [Cert-Manager](https://docs.cert-manager.io/en/latest/index.html).
When using Cert-Manager to manage certificates,
it will create secrets in your namespaces that can be referenced as TLS secrets in your [ingress objects](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls).
## Provider Configuration
@@ -93,7 +104,8 @@ They are both provided automatically as mounts in the pod where Traefik is deplo
When the environment variables are not found, Traefik tries to connect to the Kubernetes API server with an external-cluster client.
In which case, the endpoint is required.
Specifically, it may be set to the URL used by `kubectl proxy` to connect to a Kubernetes cluster using the granted authentication and authorization of the associated kubeconfig.
Specifically, it may be set to the URL used by `kubectl proxy` to connect to a Kubernetes cluster using the granted authentication
and authorization of the associated kubeconfig.
### `token`
@@ -298,7 +310,7 @@ _Optional, Default: empty_
```toml tab="File (TOML)"
[providers.kubernetesIngress.ingressEndpoint]
publishedService = "foo-service"
publishedService = "namespace/foo-service"
# ...
```
@@ -306,15 +318,16 @@ _Optional, Default: empty_
providers:
kubernetesIngress:
ingressEndpoint:
publishedService: "foo-service"
publishedService: "namespace/foo-service"
# ...
```
```bash tab="CLI"
--providers.kubernetesingress.ingressendpoint.publishedservice=foo-service
--providers.kubernetesingress.ingressendpoint.publishedservice=namespace/foo-service
```
Published Kubernetes Service to copy status from.
Format: `namespace/servicename`.
### `throttleDuration`
@@ -339,4 +352,5 @@ providers:
## Further
If one wants to know more about the various aspects of the Ingress spec that Traefik supports, many examples of Ingresses definitions are located in the tests [data](https://github.com/containous/traefik/tree/v2.0/pkg/provider/kubernetes/ingress/fixtures) of the Traefik repository.
If one wants to know more about the various aspects of the Ingress spec that Traefik supports,
many examples of Ingresses definitions are located in the tests [data](https://github.com/containous/traefik/tree/v2.1/pkg/provider/kubernetes/ingress/fixtures) of the Traefik repository.

View File

@@ -346,7 +346,7 @@ TLS key
Use the ip address from the bound port, rather than from the inner network. (Default: ```false```)
`--providers.docker.watch`:
Watch provider. (Default: ```true```)
Watch Docker Swarm events. (Default: ```true```)
`--providers.file.debugloggeneratedtemplate`:
Enable debug logging of generated configuration template. (Default: ```false```)
@@ -580,7 +580,7 @@ Specifies the header name prefix that will be used to store baggage items in a m
Key:Value tag to be set on all the spans.
`--tracing.haystack.localagenthost`:
Set haystack-agent's host that the reporter will used. (Default: ```LocalAgentHost```)
Set haystack-agent's host that the reporter will used. (Default: ```127.0.0.1```)
`--tracing.haystack.localagentport`:
Set haystack-agent's port that the reporter will used. (Default: ```35000```)

View File

@@ -346,7 +346,7 @@ TLS key
Use the ip address from the bound port, rather than from the inner network. (Default: ```false```)
`TRAEFIK_PROVIDERS_DOCKER_WATCH`:
Watch provider. (Default: ```true```)
Watch Docker Swarm events. (Default: ```true```)
`TRAEFIK_PROVIDERS_FILE_DEBUGLOGGENERATEDTEMPLATE`:
Enable debug logging of generated configuration template. (Default: ```false```)
@@ -580,7 +580,7 @@ Specifies the header name prefix that will be used to store baggage items in a m
Key:Value tag to be set on all the spans.
`TRAEFIK_TRACING_HAYSTACK_LOCALAGENTHOST`:
Set haystack-agent's host that the reporter will used. (Default: ```LocalAgentHost```)
Set haystack-agent's host that the reporter will used. (Default: ```127.0.0.1```)
`TRAEFIK_TRACING_HAYSTACK_LOCALAGENTPORT`:
Set haystack-agent's port that the reporter will used. (Default: ```35000```)

View File

@@ -313,6 +313,16 @@ Register the `IngressRoute` kind in the Kubernetes cluster before creating `Ingr
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0=
```
!!! important "Configuring Backend Protocol"
There are 3 ways to configure the backend protocol for communication between Traefik and your pods:
- Setting the scheme explicitly (http/https/h2c)
- Configuring the name of the kubernetes service port to start with https (https)
- Setting the kubernetes service port to use port 443 (https)
If you do not configure the above, Traefik will assume an http connection.
### Kind: `Middleware`
`Middleware` is the CRD implementation of a [Traefik middleware](../../middlewares/overview.md).

View File

@@ -26,19 +26,19 @@ spec:
serviceAccountName: traefik-ingress-controller
containers:
- name: traefik
image: traefik:v2.0
image: traefik:v2.1
args:
- --api.insecure
- --accesslog
- --entrypoints.web.Address=:8000
- --entrypoints.websecure.Address=:4443
- --providers.kubernetescrd
- --certificatesresolvers.default.acme.tlschallenge
- --certificatesresolvers.default.acme.email=foo@you.com
- --certificatesresolvers.default.acme.storage=acme.json
- --certificatesresolvers.myresolver.acme.tlschallenge
- --certificatesresolvers.myresolver.acme.email=foo@you.com
- --certificatesresolvers.myresolver.acme.storage=acme.json
# Please note that this is the staging Let's Encrypt server.
# Once you get things working, you should remove that whole line altogether.
- --certificatesresolvers.default.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
- --certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
ports:
- name: web
containerPort: 8000

View File

@@ -29,4 +29,4 @@ spec:
- name: whoami
port: 80
tls:
certResolver: default
certResolver: myresolver

View File

@@ -26,5 +26,5 @@ node:
- K3S_CLUSTER_SECRET=somethingtotallyrandom
volumes:
# this is where you would place a alternative traefik image (saved as a .tar file with
# 'docker save'), if you want to use it, instead of the traefik:v2.0 image.
# 'docker save'), if you want to use it, instead of the traefik:v2.1 image.
- /sowewhere/on/your/host/custom-image:/var/lib/rancher/k3s/agent/images

View File

@@ -3,7 +3,7 @@ version: "3.3"
services:
traefik:
image: "traefik:v2.0.0-rc3"
image: "traefik:v2.1"
container_name: "traefik"
command:
#- "--log.level=DEBUG"
@@ -12,11 +12,11 @@ services:
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.mydnschallenge.acme.dnschallenge=true"
- "--certificatesresolvers.mydnschallenge.acme.dnschallenge.provider=ovh"
#- "--certificatesresolvers.mydnschallenge.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
- "--certificatesresolvers.mydnschallenge.acme.email=postmaster@mydomain.com"
- "--certificatesresolvers.mydnschallenge.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.myresolver.acme.dnschallenge=true"
- "--certificatesresolvers.myresolver.acme.dnschallenge.provider=ovh"
#- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
- "--certificatesresolvers.myresolver.acme.email=postmaster@mydomain.com"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
@@ -37,4 +37,4 @@ services:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.mydomain.com`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.tls.certresolver=mydnschallenge"
- "traefik.http.routers.whoami.tls.certresolver=myresolver"

View File

@@ -13,7 +13,7 @@ secrets:
services:
traefik:
image: "traefik:v2.0.0-rc3"
image: "traefik:v2.1"
container_name: "traefik"
command:
#- "--log.level=DEBUG"
@@ -22,11 +22,11 @@ services:
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.mydnschallenge.acme.dnschallenge=true"
- "--certificatesresolvers.mydnschallenge.acme.dnschallenge.provider=ovh"
#- "--certificatesresolvers.mydnschallenge.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
- "--certificatesresolvers.mydnschallenge.acme.email=postmaster@mydomain.com"
- "--certificatesresolvers.mydnschallenge.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.myresolver.acme.dnschallenge=true"
- "--certificatesresolvers.myresolver.acme.dnschallenge.provider=ovh"
#- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
- "--certificatesresolvers.myresolver.acme.email=postmaster@mydomain.com"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
@@ -52,4 +52,4 @@ services:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.mydomain.com`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.tls.certresolver=mydnschallenge"
- "traefik.http.routers.whoami.tls.certresolver=myresolver"

View File

@@ -7,7 +7,7 @@ Please also read the [basic example](../basic-example) for details on how to exp
For the DNS challenge, you'll need:
- A working [provider](https://docs.traefik.io/v2.0/https/acme/#providers) along with the credentials allowing to create and remove DNS records.
- A working [provider](../../../https/acme.md#providers) along with the credentials allowing to create and remove DNS records.
!!! info "Variables may vary depending on the Provider."
Please note this guide may vary depending on the provider you use.
@@ -32,13 +32,13 @@ For the DNS challenge, you'll need:
- "OVH_CONSUMER_KEY=[YOUR_OWN_VALUE]"
```
- Replace `postmaster@mydomain.com` by your **own email** within the `certificatesresolvers.mydnschallenge.acme.email` command line argument of the `traefik` service.
- Replace `postmaster@mydomain.com` by your **own email** within the `certificatesresolvers.myresolver.acme.email` command line argument of the `traefik` service.
- Replace `whoami.mydomain.com` by your **own domain** within the `traefik.http.routers.whoami.rule` label of the `whoami` service.
- Optionally uncomment the following lines if you want to test/debug:
```yaml
#- "--log.level=DEBUG"
#- "--certificatesresolvers.mydnschallenge.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
#- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
```
- Run `docker-compose up -d` within the folder where you created the previous file.
@@ -68,12 +68,12 @@ ports:
```yaml
command:
# Enable a dns challenge named "mydnschallenge"
- "--certificatesresolvers.mydnschallenge.acme.dnschallenge=true"
# Enable a dns challenge named "myresolver"
- "--certificatesresolvers.myresolver.acme.dnschallenge=true"
# Tell which provider to use
- "--certificatesresolvers.mydnschallenge.acme.dnschallenge.provider=ovh"
- "--certificatesresolvers.myresolver.acme.dnschallenge.provider=ovh"
# The email to provide to let's encrypt
- "--certificatesresolvers.mydnschallenge.acme.email=postmaster@mydomain.com"
- "--certificatesresolvers.myresolver.acme.email=postmaster@mydomain.com"
```
- We provide the required configuration to our provider via environment variables:
@@ -101,14 +101,14 @@ volumes:
command:
# Tell to store the certificate on a path under our volume
- "--certificatesresolvers.mydnschallenge.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
```
- We configure the `whoami` service to tell Traefik to use the certificate resolver named `mydnschallenge` we just configured:
- We configure the `whoami` service to tell Traefik to use the certificate resolver named `myresolver` we just configured:
```yaml
labels:
- "traefik.http.routers.whoami.tls.certresolver=mydnschallenge" # Uses the Host rule to define which certificate to issue
- "traefik.http.routers.whoami.tls.certresolver=myresolver" # Uses the Host rule to define which certificate to issue
```
## Use Secrets

View File

@@ -3,7 +3,7 @@ version: "3.3"
services:
traefik:
image: "traefik:v2.0.0-rc3"
image: "traefik:v2.1"
container_name: "traefik"
command:
#- "--log.level=DEBUG"
@@ -12,11 +12,11 @@ services:
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.myhttpchallenge.acme.httpchallenge=true"
- "--certificatesresolvers.myhttpchallenge.acme.httpchallenge.entrypoint=web"
#- "--certificatesresolvers.myhttpchallenge.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
- "--certificatesresolvers.myhttpchallenge.acme.email=postmaster@mydomain.com"
- "--certificatesresolvers.myhttpchallenge.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.myresolver.acme.httpchallenge=true"
- "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
#- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
- "--certificatesresolvers.myresolver.acme.email=postmaster@mydomain.com"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
@@ -32,4 +32,4 @@ services:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.mydomain.com`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.tls.certresolver=myhttpchallenge"
- "traefik.http.routers.whoami.tls.certresolver=myresolver"

View File

@@ -18,13 +18,13 @@ For the HTTP challenge you will need:
--8<-- "content/user-guides/docker-compose/acme-http/docker-compose.yml"
```
- Replace `postmaster@mydomain.com` by your **own email** within the `certificatesresolvers.myhttpchallenge.acme.email` command line argument of the `traefik` service.
- Replace `postmaster@mydomain.com` by your **own email** within the `certificatesresolvers.myresolver.acme.email` command line argument of the `traefik` service.
- Replace `whoami.mydomain.com` by your **own domain** within the `traefik.http.routers.whoami.rule` label of the `whoami` service.
- Optionally uncomment the following lines if you want to test/debug:
```yaml
#- "--log.level=DEBUG"
#- "--certificatesresolvers.myhttpchallenge.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
#- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
```
- Run `docker-compose up -d` within the folder where you created the previous file.
@@ -54,12 +54,12 @@ ports:
```yaml
command:
# Enable a http challenge named "myhttpchallenge"
- "--certificatesresolvers.myhttpchallenge.acme.httpchallenge=true"
# Enable a http challenge named "myresolver"
- "--certificatesresolvers.myresolver.acme.httpchallenge=true"
# Tell it to use our predefined entrypoint named "web"
- "--certificatesresolvers.myhttpchallenge.acme.httpchallenge.entrypoint=web"
- "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
# The email to provide to let's encrypt
- "--certificatesresolvers.myhttpchallenge.acme.email=postmaster@mydomain.com"
- "--certificatesresolvers.myresolver.acme.email=postmaster@mydomain.com"
```
- We add a volume to store our certificates:
@@ -71,13 +71,13 @@ volumes:
command:
# Tell to store the certificate on a path under our volume
- "--certificatesresolvers.myhttpchallenge.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
```
- We configure the `whoami` service to tell Traefik to use the certificate resolver named `myhttpchallenge` we just configured:
- We configure the `whoami` service to tell Traefik to use the certificate resolver named `myresolver` we just configured:
```yaml
labels:
# Uses the Host rule to define which certificate to issue
- "traefik.http.routers.whoami.tls.certresolver=myhttpchallenge"
- "traefik.http.routers.whoami.tls.certresolver=myresolver"
```

View File

@@ -3,7 +3,7 @@ version: "3.3"
services:
traefik:
image: "traefik:v2.0.0-rc3"
image: "traefik:v2.1"
container_name: "traefik"
command:
#- "--log.level=DEBUG"
@@ -11,10 +11,10 @@ services:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.mytlschallenge.acme.tlschallenge=true"
#- "--certificatesresolvers.mytlschallenge.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
- "--certificatesresolvers.mytlschallenge.acme.email=postmaster@mydomain.com"
- "--certificatesresolvers.mytlschallenge.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.myresolver.acme.tlschallenge=true"
#- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
- "--certificatesresolvers.myresolver.acme.email=postmaster@mydomain.com"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
ports:
- "443:443"
- "8080:8080"
@@ -29,4 +29,4 @@ services:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.mydomain.com`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.tls.certresolver=mytlschallenge"
- "traefik.http.routers.whoami.tls.certresolver=myresolver"

View File

@@ -18,13 +18,13 @@ For the TLS challenge you will need:
--8<-- "content/user-guides/docker-compose/acme-tls/docker-compose.yml"
```
- Replace `postmaster@mydomain.com` by your **own email** within the `certificatesresolvers.mytlschallenge.acme.email` command line argument of the `traefik` service.
- Replace `postmaster@mydomain.com` by your **own email** within the `certificatesresolvers.myresolver.acme.email` command line argument of the `traefik` service.
- Replace `whoami.mydomain.com` by your **own domain** within the `traefik.http.routers.whoami.rule` label of the `whoami` service.
- Optionally uncomment the following lines if you want to test/debug:
```yaml
#- "--log.level=DEBUG"
#- "--certificatesresolvers.mytlschallenge.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
#- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
```
- Run `docker-compose up -d` within the folder where you created the previous file.
@@ -54,8 +54,8 @@ ports:
```yaml
command:
# Enable a tls challenge named "mytlschallenge"
- "--certificatesresolvers.mytlschallenge.acme.tlschallenge=true"
# Enable a tls challenge named "myresolver"
- "--certificatesresolvers.myresolver.acme.tlschallenge=true"
```
- We add a volume to store our certificates:
@@ -67,13 +67,13 @@ volumes:
command:
# Tell to store the certificate on a path under our volume
- "--certificatesresolvers.mytlschallenge.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
```
- We configure the `whoami` service to tell Traefik to use the certificate resolver named `mytlschallenge` we just configured:
- We configure the `whoami` service to tell Traefik to use the certificate resolver named `myresolver` we just configured:
```yaml
labels:
# Uses the Host rule to define which certificate to issue
- "traefik.http.routers.whoami.tls.certresolver=mytlschallenge"
- "traefik.http.routers.whoami.tls.certresolver=myresolver"
```

View File

@@ -3,7 +3,7 @@ version: "3.3"
services:
traefik:
image: "traefik:v2.0.0-rc3"
image: "traefik:v2.1"
container_name: "traefik"
command:
#- "--log.level=DEBUG"

4
go.mod
View File

@@ -17,7 +17,7 @@ require (
github.com/abbot/go-http-auth v0.0.0-00010101000000-000000000000
github.com/abronan/valkeyrie v0.0.0-20190822142731-f2e1850dc905
github.com/c0va23/go-proxyprotocol v0.9.1
github.com/cenkalti/backoff/v3 v3.0.0
github.com/cenkalti/backoff/v4 v4.0.0
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc // indirect
github.com/containous/alice v0.0.0-20181107144136-d83ebdd94cbd
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f
@@ -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.3.0
github.com/go-acme/lego/v3 v3.4.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

12
go.sum
View File

@@ -100,8 +100,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/c0va23/go-proxyprotocol v0.9.1 h1:5BCkp0fDJOhzzH1lhjUgHhmZz9VvRMMif1U2D31hb34=
github.com/c0va23/go-proxyprotocol v0.9.1/go.mod h1:TNjUV+llvk8TvWJxlPYAeAYZgSzT/iicNr3nWBWX320=
github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c=
github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
github.com/cenkalti/backoff/v4 v4.0.0 h1:6VeaLF9aI+MAUQ95106HwWzYZgJJpZ4stumjj6RFYAU=
github.com/cenkalti/backoff/v4 v4.0.0/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg=
github.com/census-instrumentation/opencensus-proto v0.2.0 h1:LzQXZOgg4CQfE6bFvXGM30YZL1WW/M337pXml+GrcZ4=
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
@@ -202,8 +202,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.3.0 h1:6BePZsOiYA4/w+M7QDytxQtMfCipMPGnWAHs9pWks98=
github.com/go-acme/lego/v3 v3.3.0/go.mod h1:iGSY2vQrvQs3WezicSB/oVbO2eCrD88dpWPwb1qLqu0=
github.com/go-acme/lego/v3 v3.4.0 h1:deB9NkelA+TfjGHVw8J7iKl/rMtffcGMWSMmptvMv0A=
github.com/go-acme/lego/v3 v3.4.0/go.mod h1:xYbLDuxq3Hy4bMUT1t9JIuz6GWIWb3m5X+TeTHYaT7M=
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=
@@ -443,8 +443,8 @@ github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
github.com/nrdcg/auroradns v1.0.0 h1:b+NpSqNG6HzMqX2ohGQe4Q/G0WQq8pduWCiZ19vdLY8=
github.com/nrdcg/auroradns v1.0.0/go.mod h1:6JPXKzIRzZzMqtTDgueIhTi6rFf1QvYE/HzqidhOhjw=
github.com/nrdcg/dnspod-go v0.3.0 h1:EbYggdEGFGq17Vp7sUwd9PyHZv5mMxJwX7nBPukKNoU=
github.com/nrdcg/dnspod-go v0.3.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ=
github.com/nrdcg/dnspod-go v0.4.0 h1:c/jn1mLZNKF3/osJ6mz3QPxTudvPArXTjpkmYj0uK6U=
github.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ=
github.com/nrdcg/goinwx v0.6.1 h1:AJnjoWPELyCtofhGcmzzcEMFd9YdF2JB/LgutWsWt/s=
github.com/nrdcg/goinwx v0.6.1/go.mod h1:XPiut7enlbEdntAqalBIqcYcTEVhpv/dKWgDCX2SwKQ=
github.com/nrdcg/namesilo v0.2.1 h1:kLjCjsufdW/IlC+iSfAqj0iQGgKjlbUUeDJio5Y6eMg=

View File

@@ -128,7 +128,18 @@ func (s *ConsulCatalogSuite) TestWithNotExposedByDefaultAndDefaultsSettings(c *c
c.Assert(err, checker.IsNil)
req.Host = "whoami"
err = try.Request(req, 2*time.Second, try.StatusCodeIs(200), try.BodyContainsOr("Hostname: whoami1", "Hostname: whoami2", "Hostname: whoami3"))
err = try.Request(req, 2*time.Second,
try.StatusCodeIs(200),
try.BodyContainsOr("Hostname: whoami1", "Hostname: whoami2", "Hostname: whoami3"))
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second,
try.StatusCodeIs(200),
try.BodyContains(
fmt.Sprintf(`"http://%s:80":"UP"`, reg1.Address),
fmt.Sprintf(`"http://%s:80":"UP"`, reg2.Address),
fmt.Sprintf(`"http://%s:80":"UP"`, reg3.Address),
))
c.Assert(err, checker.IsNil)
err = s.deregisterService("whoami1", false)

View File

@@ -3,7 +3,7 @@ package job
import (
"time"
"github.com/cenkalti/backoff/v3"
"github.com/cenkalti/backoff/v4"
)
var (

View File

@@ -4,7 +4,7 @@ import (
"testing"
"time"
"github.com/cenkalti/backoff/v3"
"github.com/cenkalti/backoff/v4"
)
func TestJobBackOff(t *testing.T) {

View File

@@ -50,14 +50,14 @@ func RegisterDatadog(ctx context.Context, config *types.Datadog) Registry {
if config.AddEntryPointsLabels {
registry.epEnabled = config.AddEntryPointsLabels
registry.entryPointReqsCounter = datadogClient.NewCounter(ddEntryPointReqsName, 1.0)
registry.entryPointReqDurationHistogram = datadogClient.NewHistogram(ddEntryPointReqDurationName, 1.0)
registry.entryPointReqDurationHistogram, _ = NewHistogramWithScale(datadogClient.NewHistogram(ddEntryPointReqDurationName, 1.0), time.Second)
registry.entryPointOpenConnsGauge = datadogClient.NewGauge(ddEntryPointOpenConnsName)
}
if config.AddServicesLabels {
registry.svcEnabled = config.AddServicesLabels
registry.serviceReqsCounter = datadogClient.NewCounter(ddMetricsServiceReqsName, 1.0)
registry.serviceReqDurationHistogram = datadogClient.NewHistogram(ddMetricsServiceLatencyName, 1.0)
registry.serviceReqDurationHistogram, _ = NewHistogramWithScale(datadogClient.NewHistogram(ddMetricsServiceLatencyName, 1.0), time.Second)
registry.serviceRetriesCounter = datadogClient.NewCounter(ddRetriesTotalName, 1.0)
registry.serviceOpenConnsGauge = datadogClient.NewGauge(ddOpenConnsName)
registry.serviceServerUpGauge = datadogClient.NewGauge(ddServerUpName)

View File

@@ -64,14 +64,14 @@ func RegisterInfluxDB(ctx context.Context, config *types.InfluxDB) Registry {
if config.AddEntryPointsLabels {
registry.epEnabled = config.AddEntryPointsLabels
registry.entryPointReqsCounter = influxDBClient.NewCounter(influxDBEntryPointReqsName)
registry.entryPointReqDurationHistogram = influxDBClient.NewHistogram(influxDBEntryPointReqDurationName)
registry.entryPointReqDurationHistogram, _ = NewHistogramWithScale(influxDBClient.NewHistogram(influxDBEntryPointReqDurationName), time.Second)
registry.entryPointOpenConnsGauge = influxDBClient.NewGauge(influxDBEntryPointOpenConnsName)
}
if config.AddServicesLabels {
registry.svcEnabled = config.AddServicesLabels
registry.serviceReqsCounter = influxDBClient.NewCounter(influxDBMetricsServiceReqsName)
registry.serviceReqDurationHistogram = influxDBClient.NewHistogram(influxDBMetricsServiceLatencyName)
registry.serviceReqDurationHistogram, _ = NewHistogramWithScale(influxDBClient.NewHistogram(influxDBMetricsServiceLatencyName), time.Second)
registry.serviceRetriesCounter = influxDBClient.NewCounter(influxDBRetriesTotalName)
registry.serviceOpenConnsGauge = influxDBClient.NewGauge(influxDBOpenConnsName)
registry.serviceServerUpGauge = influxDBClient.NewGauge(influxDBServerUpName)

View File

@@ -1,6 +1,9 @@
package metrics
import (
"errors"
"time"
"github.com/go-kit/kit/metrics"
"github.com/go-kit/kit/metrics/multi"
)
@@ -20,12 +23,12 @@ type Registry interface {
// entry point metrics
EntryPointReqsCounter() metrics.Counter
EntryPointReqDurationHistogram() metrics.Histogram
EntryPointReqDurationHistogram() ScalableHistogram
EntryPointOpenConnsGauge() metrics.Gauge
// service metrics
ServiceReqsCounter() metrics.Counter
ServiceReqDurationHistogram() metrics.Histogram
ServiceReqDurationHistogram() ScalableHistogram
ServiceOpenConnsGauge() metrics.Gauge
ServiceRetriesCounter() metrics.Counter
ServiceServerUpGauge() metrics.Gauge
@@ -46,10 +49,10 @@ func NewMultiRegistry(registries []Registry) Registry {
var lastConfigReloadSuccessGauge []metrics.Gauge
var lastConfigReloadFailureGauge []metrics.Gauge
var entryPointReqsCounter []metrics.Counter
var entryPointReqDurationHistogram []metrics.Histogram
var entryPointReqDurationHistogram []ScalableHistogram
var entryPointOpenConnsGauge []metrics.Gauge
var serviceReqsCounter []metrics.Counter
var serviceReqDurationHistogram []metrics.Histogram
var serviceReqDurationHistogram []ScalableHistogram
var serviceOpenConnsGauge []metrics.Gauge
var serviceRetriesCounter []metrics.Counter
var serviceServerUpGauge []metrics.Gauge
@@ -101,10 +104,10 @@ func NewMultiRegistry(registries []Registry) Registry {
lastConfigReloadSuccessGauge: multi.NewGauge(lastConfigReloadSuccessGauge...),
lastConfigReloadFailureGauge: multi.NewGauge(lastConfigReloadFailureGauge...),
entryPointReqsCounter: multi.NewCounter(entryPointReqsCounter...),
entryPointReqDurationHistogram: multi.NewHistogram(entryPointReqDurationHistogram...),
entryPointReqDurationHistogram: NewMultiHistogram(entryPointReqDurationHistogram...),
entryPointOpenConnsGauge: multi.NewGauge(entryPointOpenConnsGauge...),
serviceReqsCounter: multi.NewCounter(serviceReqsCounter...),
serviceReqDurationHistogram: multi.NewHistogram(serviceReqDurationHistogram...),
serviceReqDurationHistogram: NewMultiHistogram(serviceReqDurationHistogram...),
serviceOpenConnsGauge: multi.NewGauge(serviceOpenConnsGauge...),
serviceRetriesCounter: multi.NewCounter(serviceRetriesCounter...),
serviceServerUpGauge: multi.NewGauge(serviceServerUpGauge...),
@@ -119,10 +122,10 @@ type standardRegistry struct {
lastConfigReloadSuccessGauge metrics.Gauge
lastConfigReloadFailureGauge metrics.Gauge
entryPointReqsCounter metrics.Counter
entryPointReqDurationHistogram metrics.Histogram
entryPointReqDurationHistogram ScalableHistogram
entryPointOpenConnsGauge metrics.Gauge
serviceReqsCounter metrics.Counter
serviceReqDurationHistogram metrics.Histogram
serviceReqDurationHistogram ScalableHistogram
serviceOpenConnsGauge metrics.Gauge
serviceRetriesCounter metrics.Counter
serviceServerUpGauge metrics.Gauge
@@ -156,7 +159,7 @@ func (r *standardRegistry) EntryPointReqsCounter() metrics.Counter {
return r.entryPointReqsCounter
}
func (r *standardRegistry) EntryPointReqDurationHistogram() metrics.Histogram {
func (r *standardRegistry) EntryPointReqDurationHistogram() ScalableHistogram {
return r.entryPointReqDurationHistogram
}
@@ -168,7 +171,7 @@ func (r *standardRegistry) ServiceReqsCounter() metrics.Counter {
return r.serviceReqsCounter
}
func (r *standardRegistry) ServiceReqDurationHistogram() metrics.Histogram {
func (r *standardRegistry) ServiceReqDurationHistogram() ScalableHistogram {
return r.serviceReqDurationHistogram
}
@@ -183,3 +186,97 @@ func (r *standardRegistry) ServiceRetriesCounter() metrics.Counter {
func (r *standardRegistry) ServiceServerUpGauge() metrics.Gauge {
return r.serviceServerUpGauge
}
// ScalableHistogram is a Histogram with a predefined time unit,
// used when producing observations without explicitly setting the observed value.
type ScalableHistogram interface {
With(labelValues ...string) ScalableHistogram
StartAt(t time.Time)
Observe(v float64)
ObserveDuration()
}
// HistogramWithScale is a histogram that will convert its observed value to the specified unit.
type HistogramWithScale struct {
histogram metrics.Histogram
unit time.Duration
start time.Time
}
// With implements ScalableHistogram.
func (s *HistogramWithScale) With(labelValues ...string) ScalableHistogram {
s.histogram = s.histogram.With(labelValues...)
return s
}
// StartAt implements ScalableHistogram.
func (s *HistogramWithScale) StartAt(t time.Time) {
s.start = t
}
// ObserveDuration implements ScalableHistogram.
func (s *HistogramWithScale) ObserveDuration() {
if s.unit <= 0 {
return
}
d := float64(time.Since(s.start).Nanoseconds()) / float64(s.unit)
if d < 0 {
d = 0
}
s.histogram.Observe(d)
}
// Observe implements ScalableHistogram.
func (s *HistogramWithScale) Observe(v float64) {
s.histogram.Observe(v)
}
// NewHistogramWithScale returns a ScalableHistogram. It returns an error if the given unit is <= 0.
func NewHistogramWithScale(histogram metrics.Histogram, unit time.Duration) (ScalableHistogram, error) {
if unit <= 0 {
return nil, errors.New("invalid time unit")
}
return &HistogramWithScale{
histogram: histogram,
unit: unit,
}, nil
}
// MultiHistogram collects multiple individual histograms and treats them as a unit.
type MultiHistogram []ScalableHistogram
// NewMultiHistogram returns a multi-histogram, wrapping the passed histograms.
func NewMultiHistogram(h ...ScalableHistogram) MultiHistogram {
return MultiHistogram(h)
}
// StartAt implements ScalableHistogram.
func (h MultiHistogram) StartAt(t time.Time) {
for _, histogram := range h {
histogram.StartAt(t)
}
}
// ObserveDuration implements ScalableHistogram.
func (h MultiHistogram) ObserveDuration() {
for _, histogram := range h {
histogram.ObserveDuration()
}
}
// Observe implements ScalableHistogram.
func (h MultiHistogram) Observe(v float64) {
for _, histogram := range h {
histogram.Observe(v)
}
}
// With implements ScalableHistogram.
func (h MultiHistogram) With(labelValues ...string) ScalableHistogram {
next := make(MultiHistogram, len(h))
for i := range h {
next[i] = h[i].With(labelValues...)
}
return next
}

View File

@@ -1,18 +1,44 @@
package metrics
import (
"bytes"
"strings"
"testing"
"time"
"github.com/go-kit/kit/metrics"
"github.com/go-kit/kit/metrics/generic"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestScalableHistogram(t *testing.T) {
h := generic.NewHistogram("test", 1)
sh, err := NewHistogramWithScale(h, time.Millisecond)
require.NoError(t, err)
ticker := time.NewTicker(500 * time.Millisecond)
<-ticker.C
sh.StartAt(time.Now())
<-ticker.C
sh.ObserveDuration()
var b bytes.Buffer
h.Print(&b)
extractedDurationString := strings.Split(strings.Split(b.String(), "\n")[1], " ")
measuredDuration, err := time.ParseDuration(extractedDurationString[0] + "ms")
assert.NoError(t, err)
assert.InDelta(t, 500*time.Millisecond, measuredDuration, float64(1*time.Millisecond))
}
func TestNewMultiRegistry(t *testing.T) {
registries := []Registry{newCollectingRetryMetrics(), newCollectingRetryMetrics()}
registry := NewMultiRegistry(registries)
registry.ServiceReqsCounter().With("key", "requests").Add(1)
registry.ServiceReqDurationHistogram().With("key", "durations").Observe(2)
registry.ServiceReqDurationHistogram().With("key", "durations").Observe(float64(2))
registry.ServiceRetriesCounter().With("key", "retries").Add(3)
for _, collectingRegistry := range registries {
@@ -66,11 +92,17 @@ type histogramMock struct {
lastLabelValues []string
}
func (c *histogramMock) With(labelValues ...string) metrics.Histogram {
func (c *histogramMock) With(labelValues ...string) ScalableHistogram {
c.lastLabelValues = labelValues
return c
}
func (c *histogramMock) Observe(value float64) {
c.lastHistogramValue = value
func (c *histogramMock) Start() {}
func (c *histogramMock) StartAt(t time.Time) {}
func (c *histogramMock) ObserveDuration() {}
func (c *histogramMock) Observe(v float64) {
c.lastHistogramValue = v
}

View File

@@ -6,6 +6,7 @@ import (
"sort"
"strings"
"sync"
"time"
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/log"
@@ -152,7 +153,7 @@ func initStandardRegistry(config *types.Prometheus) Registry {
entryPointOpenConns.gv.Describe,
}...)
reg.entryPointReqsCounter = entryPointReqs
reg.entryPointReqDurationHistogram = entryPointReqDurations
reg.entryPointReqDurationHistogram, _ = NewHistogramWithScale(entryPointReqDurations, time.Second)
reg.entryPointOpenConnsGauge = entryPointOpenConns
}
if config.AddServicesLabels {
@@ -187,7 +188,7 @@ func initStandardRegistry(config *types.Prometheus) Registry {
}...)
reg.serviceReqsCounter = serviceReqs
reg.serviceReqDurationHistogram = serviceReqDurations
reg.serviceReqDurationHistogram, _ = NewHistogramWithScale(serviceReqDurations, time.Second)
reg.serviceOpenConnsGauge = serviceOpenConns
reg.serviceRetriesCounter = serviceRetries
reg.serviceServerUpGauge = serviceServerUp

View File

@@ -55,14 +55,14 @@ func RegisterStatsd(ctx context.Context, config *types.Statsd) Registry {
if config.AddEntryPointsLabels {
registry.epEnabled = config.AddEntryPointsLabels
registry.entryPointReqsCounter = statsdClient.NewCounter(statsdEntryPointReqsName, 1.0)
registry.entryPointReqDurationHistogram = statsdClient.NewTiming(statsdEntryPointReqDurationName, 1.0)
registry.entryPointReqDurationHistogram, _ = NewHistogramWithScale(statsdClient.NewTiming(statsdEntryPointReqDurationName, 1.0), time.Millisecond)
registry.entryPointOpenConnsGauge = statsdClient.NewGauge(statsdEntryPointOpenConnsName)
}
if config.AddServicesLabels {
registry.svcEnabled = config.AddServicesLabels
registry.serviceReqsCounter = statsdClient.NewCounter(statsdMetricsServiceReqsName, 1.0)
registry.serviceReqDurationHistogram = statsdClient.NewTiming(statsdMetricsServiceLatencyName, 1.0)
registry.serviceReqDurationHistogram, _ = NewHistogramWithScale(statsdClient.NewTiming(statsdMetricsServiceLatencyName, 1.0), time.Millisecond)
registry.serviceRetriesCounter = statsdClient.NewCounter(statsdRetriesTotalName, 1.0)
registry.serviceOpenConnsGauge = statsdClient.NewGauge(statsdOpenConnsName)
registry.serviceServerUpGauge = statsdClient.NewGauge(statsdServerUpName)

View File

@@ -45,8 +45,8 @@ func (f *CommonLogFormatter) Format(entry *logrus.Entry) ([]byte, error) {
toLog(entry.Data, "request_Referer", `"-"`, true),
toLog(entry.Data, "request_User-Agent", `"-"`, true),
toLog(entry.Data, RequestCount, defaultValue, true),
toLog(entry.Data, RouterName, defaultValue, true),
toLog(entry.Data, ServiceURL, defaultValue, true),
toLog(entry.Data, RouterName, `"-"`, true),
toLog(entry.Data, ServiceURL, `"-"`, true),
elapsedMillis)
return b.Bytes(), err

View File

@@ -36,7 +36,7 @@ func TestCommonLogFormatter_Format(t *testing.T) {
RouterName: "",
ServiceURL: "",
},
expectedLog: `10.0.0.1 - Client [10/Nov/2009:23:00:00 +0000] "GET /foo http" - - "-" "-" 0 - - 123000ms
expectedLog: `10.0.0.1 - Client [10/Nov/2009:23:00:00 +0000] "GET /foo http" - - "-" "-" 0 "-" "-" 123000ms
`,
},
{

View File

@@ -449,7 +449,7 @@ func TestNewLogHandlerOutputStdout(t *testing.T) {
DefaultMode: "drop",
},
},
expectedLog: `- - - [-] "- - -" - - "testReferer" "testUserAgent" - - - 0ms`,
expectedLog: `- - - [-] "- - -" - - "testReferer" "testUserAgent" - "-" "-" 0ms`,
},
{
desc: "Default mode drop with override",
@@ -464,7 +464,7 @@ func TestNewLogHandlerOutputStdout(t *testing.T) {
},
},
},
expectedLog: `- - TestUser [-] "- - -" - - "testReferer" "testUserAgent" - - - 0ms`,
expectedLog: `- - TestUser [-] "- - -" - - "testReferer" "testUserAgent" - "-" "-" 0ms`,
},
{
desc: "Default mode drop with header dropped",
@@ -482,7 +482,7 @@ func TestNewLogHandlerOutputStdout(t *testing.T) {
},
},
},
expectedLog: `- - TestUser [-] "- - -" - - "-" "-" - - - 0ms`,
expectedLog: `- - TestUser [-] "- - -" - - "-" "-" - "-" "-" 0ms`,
},
{
desc: "Default mode drop with header redacted",
@@ -500,7 +500,7 @@ func TestNewLogHandlerOutputStdout(t *testing.T) {
},
},
},
expectedLog: `- - TestUser [-] "- - -" - - "REDACTED" "REDACTED" - - - 0ms`,
expectedLog: `- - TestUser [-] "- - -" - - "REDACTED" "REDACTED" - "-" "-" 0ms`,
},
{
desc: "Default mode drop with header redacted",
@@ -521,7 +521,7 @@ func TestNewLogHandlerOutputStdout(t *testing.T) {
},
},
},
expectedLog: `- - TestUser [-] "- - -" - - "REDACTED" "testUserAgent" - - - 0ms`,
expectedLog: `- - TestUser [-] "- - -" - - "REDACTED" "testUserAgent" - "-" "-" 0ms`,
},
}

View File

@@ -32,7 +32,7 @@ type metricsMiddleware struct {
openConns int64
next http.Handler
reqsCounter gokitmetrics.Counter
reqDurationHistogram gokitmetrics.Histogram
reqDurationHistogram metrics.ScalableHistogram
openConnsGauge gokitmetrics.Gauge
baseLabels []string
}
@@ -88,13 +88,19 @@ func (m *metricsMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Request)
m.openConnsGauge.With(labelValues...).Set(float64(openConns))
}(labels)
start := time.Now()
recorder := newResponseRecorder(rw)
start := time.Now()
m.next.ServeHTTP(recorder, req)
labels = append(labels, "code", strconv.Itoa(recorder.getCode()))
histograms := m.reqDurationHistogram.With(labels...)
histograms.StartAt(start)
m.reqsCounter.With(labels...).Add(1)
m.reqDurationHistogram.With(labels...).Observe(time.Since(start).Seconds())
histograms.ObserveDuration()
}
func getRequestProtocol(req *http.Request) string {

View File

@@ -3,6 +3,7 @@ package replacepath
import (
"context"
"net/http"
"net/url"
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/log"
@@ -40,8 +41,22 @@ func (r *replacePath) GetTracingInformation() (string, ext.SpanKindEnum) {
}
func (r *replacePath) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
req.Header.Add(ReplacedPathHeader, req.URL.Path)
req.URL.Path = r.path
if req.URL.RawPath == "" {
req.Header.Add(ReplacedPathHeader, req.URL.Path)
} else {
req.Header.Add(ReplacedPathHeader, req.URL.RawPath)
}
req.URL.RawPath = r.path
var err error
req.URL.Path, err = url.PathUnescape(req.URL.RawPath)
if err != nil {
log.FromContext(middlewares.GetLoggerCtx(context.Background(), r.name, typeName)).Error(err)
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}
req.RequestURI = req.URL.RequestURI()
r.next.ServeHTTP(rw, req)

View File

@@ -3,43 +3,93 @@ package replacepath
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/testhelpers"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestReplacePath(t *testing.T) {
var replacementConfig = dynamic.ReplacePath{
Path: "/replacement-path",
testCases := []struct {
desc string
path string
config dynamic.ReplacePath
expectedPath string
expectedRawPath string
expectedHeader string
}{
{
desc: "simple path",
path: "/example",
config: dynamic.ReplacePath{
Path: "/replacement-path",
},
expectedPath: "/replacement-path",
expectedRawPath: "",
expectedHeader: "/example",
},
{
desc: "long path",
path: "/some/really/long/path",
config: dynamic.ReplacePath{
Path: "/replacement-path",
},
expectedPath: "/replacement-path",
expectedRawPath: "",
expectedHeader: "/some/really/long/path",
},
{
desc: "path with escaped value",
path: "/foo%2Fbar",
config: dynamic.ReplacePath{
Path: "/replacement-path",
},
expectedPath: "/replacement-path",
expectedRawPath: "",
expectedHeader: "/foo%2Fbar",
},
{
desc: "replacement with escaped value",
path: "/path",
config: dynamic.ReplacePath{
Path: "/foo%2Fbar",
},
expectedPath: "/foo/bar",
expectedRawPath: "/foo%2Fbar",
expectedHeader: "/path",
},
}
paths := []string{
"/example",
"/some/really/long/path",
}
for _, path := range paths {
t.Run(path, func(t *testing.T) {
var expectedPath, actualHeader, requestURI string
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
var actualPath, actualRawPath, actualHeader, requestURI string
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
expectedPath = r.URL.Path
actualPath = r.URL.Path
actualRawPath = r.URL.RawPath
actualHeader = r.Header.Get(ReplacedPathHeader)
requestURI = r.RequestURI
})
handler, err := New(context.Background(), next, replacementConfig, "foo-replace-path")
handler, err := New(context.Background(), next, test.config, "foo-replace-path")
require.NoError(t, err)
req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost"+path, nil)
server := httptest.NewServer(handler)
defer server.Close()
handler.ServeHTTP(nil, req)
resp, err := http.Get(server.URL + test.path)
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, expectedPath, replacementConfig.Path, "Unexpected path.")
assert.Equal(t, path, actualHeader, "Unexpected '%s' header.", ReplacedPathHeader)
assert.Equal(t, expectedPath, requestURI, "Unexpected request URI.")
assert.Equal(t, test.expectedPath, actualPath, "Unexpected path.")
assert.Equal(t, test.expectedHeader, actualHeader, "Unexpected '%s' header.", ReplacedPathHeader)
if actualRawPath == "" {
assert.Equal(t, actualPath, requestURI, "Unexpected request URI.")
} else {
assert.Equal(t, actualRawPath, requestURI, "Unexpected request URI.")
}
})
}
}

View File

@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"net/http"
"net/url"
"regexp"
"strings"
@@ -49,10 +50,31 @@ func (rp *replacePathRegex) GetTracingInformation() (string, ext.SpanKindEnum) {
}
func (rp *replacePathRegex) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
if rp.regexp != nil && len(rp.replacement) > 0 && rp.regexp.MatchString(req.URL.Path) {
req.Header.Add(replacepath.ReplacedPathHeader, req.URL.Path)
req.URL.Path = rp.regexp.ReplaceAllString(req.URL.Path, rp.replacement)
var currentPath string
if req.URL.RawPath == "" {
currentPath = req.URL.Path
} else {
currentPath = req.URL.RawPath
}
if rp.regexp != nil && len(rp.replacement) > 0 && rp.regexp.MatchString(currentPath) {
req.Header.Add(replacepath.ReplacedPathHeader, currentPath)
req.URL.RawPath = rp.regexp.ReplaceAllString(currentPath, rp.replacement)
// as replacement can introduce escaped characters
// Path must remain an unescaped version of RawPath
// Doesn't handle multiple times encoded replacement (`/` => `%2F` => `%252F` => ...)
var err error
req.URL.Path, err = url.PathUnescape(req.URL.RawPath)
if err != nil {
log.FromContext(middlewares.GetLoggerCtx(context.Background(), rp.name, typeName)).Error(err)
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}
req.RequestURI = req.URL.RequestURI()
}
rp.next.ServeHTTP(rw, req)
}

View File

@@ -3,23 +3,24 @@ package replacepathregex
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/middlewares/replacepath"
"github.com/containous/traefik/v2/pkg/testhelpers"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestReplacePathRegex(t *testing.T) {
testCases := []struct {
desc string
path string
config dynamic.ReplacePathRegex
expectedPath string
expectedHeader string
expectsError bool
desc string
path string
config dynamic.ReplacePathRegex
expectedPath string
expectedRawPath string
expectedHeader string
expectsError bool
}{
{
desc: "simple regex",
@@ -28,8 +29,9 @@ func TestReplacePathRegex(t *testing.T) {
Replacement: "/who-am-i/$1",
Regex: `^/whoami/(.*)`,
},
expectedPath: "/who-am-i/and/whoami",
expectedHeader: "/whoami/and/whoami",
expectedPath: "/who-am-i/and/whoami",
expectedRawPath: "/who-am-i/and/whoami",
expectedHeader: "/whoami/and/whoami",
},
{
desc: "simple replace (no regex)",
@@ -38,8 +40,9 @@ func TestReplacePathRegex(t *testing.T) {
Replacement: "/who-am-i",
Regex: `/whoami`,
},
expectedPath: "/who-am-i/and/who-am-i",
expectedHeader: "/whoami/and/whoami",
expectedPath: "/who-am-i/and/who-am-i",
expectedRawPath: "/who-am-i/and/who-am-i",
expectedHeader: "/whoami/and/whoami",
},
{
desc: "no match",
@@ -57,8 +60,9 @@ func TestReplacePathRegex(t *testing.T) {
Replacement: "/downloads/$1-$2",
Regex: `^(?i)/downloads/([^/]+)/([^/]+)$`,
},
expectedPath: "/downloads/src-source.go",
expectedHeader: "/downloads/src/source.go",
expectedPath: "/downloads/src-source.go",
expectedRawPath: "/downloads/src-source.go",
expectedHeader: "/downloads/src/source.go",
},
{
desc: "invalid regular expression",
@@ -70,13 +74,46 @@ func TestReplacePathRegex(t *testing.T) {
expectedPath: "/invalid/regexp/test",
expectsError: true,
},
{
desc: "replacement with escaped char",
path: "/aaa/bbb",
config: dynamic.ReplacePathRegex{
Replacement: "/foo%2Fbar",
Regex: `/aaa/bbb`,
},
expectedPath: "/foo/bar",
expectedRawPath: "/foo%2Fbar",
expectedHeader: "/aaa/bbb",
},
{
desc: "path and regex with escaped char",
path: "/aaa%2Fbbb",
config: dynamic.ReplacePathRegex{
Replacement: "/foo/bar",
Regex: `/aaa%2Fbbb`,
},
expectedPath: "/foo/bar",
expectedRawPath: "/foo/bar",
expectedHeader: "/aaa%2Fbbb",
},
{
desc: "path with escaped char (no match)",
path: "/aaa%2Fbbb",
config: dynamic.ReplacePathRegex{
Replacement: "/foo/bar",
Regex: `/aaa/bbb`,
},
expectedPath: "/aaa/bbb",
expectedRawPath: "/aaa%2Fbbb",
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
var actualPath, actualHeader, requestURI string
var actualPath, actualRawPath, actualHeader, requestURI string
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
actualPath = r.URL.Path
actualRawPath = r.URL.RawPath
actualHeader = r.Header.Get(replacepath.ReplacedPathHeader)
requestURI = r.RequestURI
})
@@ -84,19 +121,29 @@ func TestReplacePathRegex(t *testing.T) {
handler, err := New(context.Background(), next, test.config, "foo-replace-path-regexp")
if test.expectsError {
require.Error(t, err)
} else {
require.NoError(t, err)
return
}
req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost"+test.path, nil)
req.RequestURI = test.path
require.NoError(t, err)
handler.ServeHTTP(nil, req)
server := httptest.NewServer(handler)
defer server.Close()
assert.Equal(t, test.expectedPath, actualPath, "Unexpected path.")
resp, err := http.Get(server.URL + test.path)
require.NoError(t, err, "Unexpected error while making test request")
require.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, test.expectedPath, actualPath, "Unexpected path.")
assert.Equal(t, test.expectedRawPath, actualRawPath, "Unexpected raw path.")
if actualRawPath == "" {
assert.Equal(t, actualPath, requestURI, "Unexpected request URI.")
if test.expectedHeader != "" {
assert.Equal(t, test.expectedHeader, actualHeader, "Unexpected '%s' header.", replacepath.ReplacedPathHeader)
}
} else {
assert.Equal(t, actualRawPath, requestURI, "Unexpected request URI.")
}
if test.expectedHeader != "" {
assert.Equal(t, test.expectedHeader, actualHeader, "Unexpected '%s' header.", replacepath.ReplacedPathHeader)
}
})
}

View File

@@ -6,7 +6,7 @@ import (
"net/http"
"time"
"github.com/cenkalti/backoff/v3"
"github.com/cenkalti/backoff/v4"
"github.com/containous/traefik/v2/pkg/log"
"github.com/containous/traefik/v2/pkg/safe"
"github.com/go-acme/lego/v3/challenge"

View File

@@ -149,15 +149,19 @@ func (p *Provider) addServerTCP(ctx context.Context, item itemData, loadBalancer
return errors.New("load-balancer is not defined")
}
var port string
if len(loadBalancer.Servers) > 0 {
port = loadBalancer.Servers[0].Port
}
if len(loadBalancer.Servers) == 0 {
loadBalancer.Servers = []dynamic.TCPServer{{}}
}
var port string
if item.Port != "" {
if item.Port != "" && port == "" {
port = item.Port
loadBalancer.Servers[0].Port = ""
}
loadBalancer.Servers[0].Port = ""
if port == "" {
return errors.New("port is missing")
@@ -188,10 +192,10 @@ func (p *Provider) addServer(ctx context.Context, item itemData, loadBalancer *d
loadBalancer.Servers = []dynamic.Server{server}
}
if item.Port != "" {
if item.Port != "" && port == "" {
port = item.Port
loadBalancer.Servers[0].Port = ""
}
loadBalancer.Servers[0].Port = ""
if port == "" {
return errors.New("port is missing")

View File

@@ -1371,7 +1371,7 @@ func Test_buildConfiguration(t *testing.T) {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "h2c://127.0.0.1:80",
URL: "h2c://127.0.0.1:8080",
},
},
PassHostHeader: Bool(true),
@@ -1419,7 +1419,7 @@ func Test_buildConfiguration(t *testing.T) {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://127.0.0.1:80",
URL: "http://127.0.0.1:8080",
},
},
PassHostHeader: Bool(true),

View File

@@ -4,14 +4,16 @@ import (
"context"
"fmt"
"strconv"
"strings"
"text/template"
"time"
"github.com/cenkalti/backoff/v3"
"github.com/cenkalti/backoff/v4"
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/job"
"github.com/containous/traefik/v2/pkg/log"
"github.com/containous/traefik/v2/pkg/provider"
"github.com/containous/traefik/v2/pkg/provider/constraints"
"github.com/containous/traefik/v2/pkg/safe"
"github.com/containous/traefik/v2/pkg/types"
"github.com/hashicorp/consul/api"
@@ -151,7 +153,7 @@ func (p *Provider) getConsulServicesData(ctx context.Context) ([]itemData, error
}
var data []itemData
for name := range consulServiceNames {
for _, name := range consulServiceNames {
consulServices, healthServices, err := p.fetchService(ctx, name)
if err != nil {
return nil, err
@@ -204,10 +206,55 @@ func (p *Provider) fetchService(ctx context.Context, name string) ([]*api.Catalo
return consulServices, healthServices, err
}
func (p *Provider) fetchServices(ctx context.Context) (map[string][]string, error) {
func (p *Provider) fetchServices(ctx context.Context) ([]string, error) {
// The query option "Filter" is not supported by /catalog/services.
// https://www.consul.io/api/catalog.html#list-services
opts := &api.QueryOptions{AllowStale: p.Stale, RequireConsistent: p.RequireConsistent, UseCache: p.Cache}
serviceNames, _, err := p.client.Catalog().Services(opts)
return serviceNames, err
if err != nil {
return nil, err
}
// The keys are the service names, and the array values provide all known tags for a given service.
// https://www.consul.io/api/catalog.html#list-services
var filtered []string
for svcName, tags := range serviceNames {
logger := log.FromContext(log.With(ctx, log.Str("serviceName", svcName)))
if !p.ExposedByDefault && !contains(tags, p.Prefix+".enable=true") {
logger.Debug("Filtering disabled item")
continue
}
if contains(tags, p.Prefix+".enable=false") {
logger.Debug("Filtering disabled item")
continue
}
matches, err := constraints.MatchTags(tags, p.Constraints)
if err != nil {
logger.Errorf("Error matching constraints expression: %v", err)
continue
}
if !matches {
logger.Debugf("Container pruned by constraint expression: %q", p.Constraints)
continue
}
filtered = append(filtered, svcName)
}
return filtered, err
}
func contains(values []string, val string) bool {
for _, value := range values {
if strings.EqualFold(value, val) {
return true
}
}
return false
}
func createClient(cfg *EndpointConfig) (*api.Client, error) {

View File

@@ -11,7 +11,7 @@ import (
"text/template"
"time"
"github.com/cenkalti/backoff/v3"
"github.com/cenkalti/backoff/v4"
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/job"
"github.com/containous/traefik/v2/pkg/log"
@@ -46,7 +46,7 @@ var _ provider.Provider = (*Provider)(nil)
// Provider holds configurations of the provider.
type Provider struct {
Constraints string `description:"Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container." json:"constraints,omitempty" toml:"constraints,omitempty" yaml:"constraints,omitempty" export:"true"`
Watch bool `description:"Watch provider." json:"watch,omitempty" toml:"watch,omitempty" yaml:"watch,omitempty" export:"true"`
Watch bool `description:"Watch Docker Swarm events." json:"watch,omitempty" toml:"watch,omitempty" yaml:"watch,omitempty" export:"true"`
Endpoint string `description:"Docker server endpoint. Can be a tcp or a unix socket endpoint." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"`
DefaultRule string `description:"Default rule." json:"defaultRule,omitempty" toml:"defaultRule,omitempty" yaml:"defaultRule,omitempty"`
TLS *types.ClientTLS `description:"Enable Docker TLS support." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`

View File

@@ -110,6 +110,19 @@ func (p *Provider) addWatcher(pool *safe.Pool, directory string, configurationCh
case <-ctx.Done():
return
case evt := <-watcher.Events:
if evt.Op == fsnotify.Remove {
err = watcher.Remove(evt.Name)
if err != nil {
log.WithoutContext().WithField(log.ProviderName, providerName).
Errorf("Could not remove watcher for %s: %s", directory, err)
}
err = watcher.Add(directory)
if err != nil {
log.WithoutContext().WithField(log.ProviderName, providerName).
Errorf("Could not re-add watcher for %s: %s", directory, err)
}
}
if p.Directory == "" {
_, evtFileName := filepath.Split(evt.Name)
_, confFileName := filepath.Split(p.Filename)

View File

@@ -168,6 +168,7 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
factoryKube.Extensions().V1beta1().Ingresses().Informer().AddEventHandler(eventHandler)
factoryKube.Core().V1().Services().Informer().AddEventHandler(eventHandler)
factoryKube.Core().V1().Endpoints().Informer().AddEventHandler(eventHandler)
factoryKube.Core().V1().Secrets().Informer().AddEventHandler(eventHandler)
c.factoriesCrd[ns] = factoryCrd
c.factoriesKube[ns] = factoryKube
@@ -192,15 +193,6 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
}
}
// Do not wait for the Secrets store to get synced since we cannot rely on
// users having granted RBAC permissions for this object.
// https://github.com/containous/traefik/issues/1784 should improve the
// situation here in the future.
for _, ns := range namespaces {
c.factoriesKube[ns].Core().V1().Secrets().Informer().AddEventHandler(eventHandler)
c.factoriesKube[ns].Start(stopCh)
}
return eventCh, nil
}

View File

@@ -12,7 +12,7 @@ import (
"strings"
"time"
"github.com/cenkalti/backoff/v3"
"github.com/cenkalti/backoff/v4"
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/job"
"github.com/containous/traefik/v2/pkg/log"

View File

@@ -100,7 +100,7 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
errBuild := cb.buildServicesLB(ctx, ingressRoute.Namespace, spec, serviceName, conf.Services)
if errBuild != nil {
logger.Error(err)
logger.Error(errBuild)
continue
}
} else if len(route.Services) == 1 {
@@ -307,9 +307,9 @@ func (c configBuilder) loadServers(fallbackNamespace string, svc v1alpha1.LoadBa
var servers []dynamic.Server
if service.Spec.Type == corev1.ServiceTypeExternalName {
protocol := "http"
if portSpec.Port == 443 || strings.HasPrefix(portSpec.Name, "https") {
protocol = "https"
protocol, err := parseServiceProtocol(svc.Scheme, portSpec.Name, portSpec.Port)
if err != nil {
return nil, err
}
return append(servers, dynamic.Server{
@@ -341,17 +341,9 @@ func (c configBuilder) loadServers(fallbackNamespace string, svc v1alpha1.LoadBa
return nil, fmt.Errorf("cannot define a port for %s/%s", namespace, sanitizedName)
}
protocol := httpProtocol
scheme := svc.Scheme
switch scheme {
case httpProtocol, httpsProtocol, "h2c":
protocol = scheme
case "":
if portSpec.Port == 443 || strings.HasPrefix(portSpec.Name, httpsProtocol) {
protocol = httpsProtocol
}
default:
return nil, fmt.Errorf("invalid scheme %q specified", scheme)
protocol, err := parseServiceProtocol(svc.Scheme, portSpec.Name, portSpec.Port)
if err != nil {
return nil, err
}
for _, addr := range subset.Addresses {
@@ -448,3 +440,19 @@ func getTLSHTTP(ctx context.Context, ingressRoute *v1alpha1.IngressRoute, k8sCli
return nil
}
// parseServiceProtocol parses the scheme, port name, and number to determine the correct protocol.
// an error is returned if the scheme provided is invalid.
func parseServiceProtocol(providedScheme string, portName string, portNumber int32) (string, error) {
switch providedScheme {
case httpProtocol, httpsProtocol, "h2c":
return providedScheme, nil
case "":
if portNumber == 443 || strings.HasPrefix(portName, httpsProtocol) {
return httpsProtocol, nil
}
return httpProtocol, nil
}
return "", fmt.Errorf("invalid scheme %q specified", providedScheme)
}

View File

@@ -2311,3 +2311,72 @@ func TestLoadIngressRoutes(t *testing.T) {
})
}
}
func TestParseServiceProtocol(t *testing.T) {
testCases := []struct {
desc string
scheme string
portName string
portNumber int32
expected string
expectedError bool
}{
{
desc: "Empty scheme and name",
scheme: "",
portName: "",
portNumber: 1000,
expected: "http",
},
{
desc: "h2c scheme and emptyname",
scheme: "h2c",
portName: "",
portNumber: 1000,
expected: "h2c",
},
{
desc: "invalid scheme",
scheme: "foo",
portName: "",
portNumber: 1000,
expectedError: true,
},
{
desc: "Empty scheme and https name",
scheme: "",
portName: "https-secure",
portNumber: 1000,
expected: "https",
},
{
desc: "Empty scheme and port number",
scheme: "",
portName: "",
portNumber: 443,
expected: "https",
},
{
desc: "https scheme",
scheme: "https",
portName: "",
portNumber: 1000,
expected: "https",
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
protocol, err := parseServiceProtocol(test.scheme, test.portName, test.portNumber)
if test.expectedError {
assert.Error(t, err)
} else {
assert.Equal(t, test.expected, protocol)
}
})
}
}

View File

@@ -137,6 +137,7 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
factory.Extensions().V1beta1().Ingresses().Informer().AddEventHandler(eventHandler)
factory.Core().V1().Services().Informer().AddEventHandler(eventHandler)
factory.Core().V1().Endpoints().Informer().AddEventHandler(eventHandler)
factory.Core().V1().Secrets().Informer().AddEventHandler(eventHandler)
c.factories[ns] = factory
}
@@ -152,15 +153,6 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<
}
}
// Do not wait for the Secrets store to get synced since we cannot rely on
// users having granted RBAC permissions for this object.
// https://github.com/containous/traefik/issues/1784 should improve the
// situation here in the future.
for _, ns := range namespaces {
c.factories[ns].Core().V1().Secrets().Informer().AddEventHandler(eventHandler)
c.factories[ns].Start(stopCh)
}
return eventCh, nil
}

View File

@@ -11,7 +11,7 @@ import (
"strings"
"time"
"github.com/cenkalti/backoff/v3"
"github.com/cenkalti/backoff/v4"
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/job"
"github.com/containous/traefik/v2/pkg/log"

View File

@@ -9,7 +9,7 @@ import (
"text/template"
"time"
"github.com/cenkalti/backoff/v3"
"github.com/cenkalti/backoff/v4"
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/job"
"github.com/containous/traefik/v2/pkg/log"

View File

@@ -6,7 +6,7 @@ import (
"text/template"
"time"
"github.com/cenkalti/backoff/v3"
"github.com/cenkalti/backoff/v4"
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/job"
"github.com/containous/traefik/v2/pkg/log"

View File

@@ -6,7 +6,7 @@ import (
"runtime/debug"
"sync"
"github.com/cenkalti/backoff/v3"
"github.com/cenkalti/backoff/v4"
"github.com/containous/traefik/v2/pkg/log"
)

View File

@@ -7,7 +7,7 @@ import (
"testing"
"time"
"github.com/cenkalti/backoff/v3"
"github.com/cenkalti/backoff/v4"
)
func TestNewPoolContext(t *testing.T) {

View File

@@ -143,8 +143,6 @@ func (c *ConfigurationWatcher) loadMessage(configMsg dynamic.Message) {
}
func (c *ConfigurationWatcher) preLoadConfiguration(configMsg dynamic.Message) {
currentConfigurations := c.currentConfigurations.Get().(dynamic.Configurations)
logger := log.WithoutContext().WithField(log.ProviderName, configMsg.ProviderName)
if log.GetLevel() == logrus.DebugLevel {
copyConf := configMsg.Configuration.DeepCopy()
@@ -172,11 +170,6 @@ func (c *ConfigurationWatcher) preLoadConfiguration(configMsg dynamic.Message) {
return
}
if reflect.DeepEqual(currentConfigurations[configMsg.ProviderName], configMsg.Configuration) {
logger.Infof("Skipping same configuration for provider %s", configMsg.ProviderName)
return
}
providerConfigUpdateCh, ok := c.providerConfigUpdateMap[configMsg.ProviderName]
if !ok {
providerConfigUpdateCh = make(chan dynamic.Message)
@@ -211,11 +204,18 @@ func (c *ConfigurationWatcher) throttleProviderConfigReload(ctx context.Context,
}
})
var previousConfig dynamic.Message
for {
select {
case <-ctx.Done():
return
case nextConfig := <-in:
if reflect.DeepEqual(previousConfig, nextConfig) {
logger := log.WithoutContext().WithField(log.ProviderName, nextConfig.ProviderName)
logger.Info("Skipping same configuration")
continue
}
previousConfig = nextConfig
ring.In() <- nextConfig
}
}

View File

@@ -175,6 +175,66 @@ func TestListenProvidersSkipsSameConfigurationForProvider(t *testing.T) {
time.Sleep(100 * time.Millisecond)
}
func TestListenProvidersDoesNotSkipFlappingConfiguration(t *testing.T) {
routinesPool := safe.NewPool(context.Background())
configuration := &dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithRouters(th.WithRouter("foo")),
th.WithLoadBalancerServices(th.WithService("bar")),
),
}
transientConfiguration := &dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithRouters(th.WithRouter("bad")),
th.WithLoadBalancerServices(th.WithService("bad")),
),
}
pvd := &mockProvider{
wait: 5 * time.Millisecond, // The last message needs to be received before the second has been fully processed
messages: []dynamic.Message{
{ProviderName: "mock", Configuration: configuration},
{ProviderName: "mock", Configuration: transientConfiguration},
{ProviderName: "mock", Configuration: configuration},
},
}
watcher := NewConfigurationWatcher(routinesPool, pvd, 15*time.Millisecond)
var lastConfig dynamic.Configuration
watcher.AddListener(func(conf dynamic.Configuration) {
lastConfig = conf
})
watcher.Start()
defer watcher.Stop()
// give some time so that the configuration can be processed
time.Sleep(40 * time.Millisecond)
expected := dynamic.Configuration{
HTTP: th.BuildConfiguration(
th.WithRouters(th.WithRouter("foo@mock")),
th.WithLoadBalancerServices(th.WithService("bar@mock")),
th.WithMiddlewares(),
),
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Services: map[string]*dynamic.TCPService{},
},
TLS: &dynamic.TLSConfiguration{
Options: map[string]tls.Options{
"default": {},
},
Stores: map[string]tls.Store{},
},
}
assert.Equal(t, expected, lastConfig)
}
func TestListenProvidersPublishesConfigForEachProvider(t *testing.T) {
routinesPool := safe.NewPool(context.Background())

View File

@@ -109,8 +109,6 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string, t
entryPointHandlers[entryPointName] = handlerWithMiddlewares
}
m.serviceManager.LaunchHealthCheck()
return entryPointHandlers
}

View File

@@ -49,7 +49,12 @@ func NewManagerFactory(staticConfiguration static.Configuration, routinesPool *s
factory.metricsHandler = metrics.PrometheusHandler()
}
factory.pingHandler = staticConfiguration.Ping
// This check is necessary because even when staticConfiguration.Ping == nil ,
// the affectation would make factory.pingHandle become a typed nil, which does not pass the nil test,
// and would break things elsewhere.
if staticConfiguration.Ping != nil {
factory.pingHandler = staticConfiguration.Ping
}
return factory
}

View File

@@ -58,6 +58,8 @@ func (f *TCPRouterFactory) CreateTCPRouters(conf dynamic.Configuration) map[stri
handlersNonTLS := routerManager.BuildHandlers(ctx, f.entryPoints, false)
handlersTLS := routerManager.BuildHandlers(ctx, f.entryPoints, true)
serviceManager.LaunchHealthCheck()
// TCP
svcTCPManager := tcp.NewManager(rtConf)

View File

@@ -203,9 +203,17 @@ func clientHelloServerName(br *bufio.Reader) (string, bool, string, error) {
return "", false, "", err
}
// No valid TLS record has a type of 0x80, however SSLv2 handshakes
// start with a uint16 length where the MSB is set and the first record
// is always < 256 bytes long. Therefore typ == 0x80 strongly suggests
// an SSLv2 client.
const recordTypeSSLv2 = 0x80
const recordTypeHandshake = 0x16
if hdr[0] != recordTypeHandshake {
// log.Errorf("Error not tls")
if hdr[0] == recordTypeSSLv2 {
// we consider SSLv2 as TLS and it will be refuse by real TLS handshake.
return "", true, getPeeked(br), nil
}
return "", false, getPeeked(br), nil // Not TLS.
}

View File

@@ -22,7 +22,7 @@ var (
`VersionTLS13`: tls.VersionTLS13,
}
// MaxVersion Map of allowed TLS minimum versions
// MaxVersion Map of allowed TLS maximum versions
MaxVersion = map[string]uint16{
`VersionTLS10`: tls.VersionTLS10,
`VersionTLS11`: tls.VersionTLS11,

View File

@@ -26,7 +26,7 @@ type Config struct {
// SetDefaults sets the default values.
func (c *Config) SetDefaults() {
c.LocalAgentHost = "LocalAgentHost"
c.LocalAgentHost = "127.0.0.1"
c.LocalAgentPort = 35000
}