Compare commits
364 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
bdc0e3bfcf | ||
|
f173ff02e3 | ||
|
bacd58ed7b | ||
|
f323df466d | ||
|
b1836587f2 | ||
|
dbc3b85cd0 | ||
|
5eda08e9b8 | ||
|
ec6e46e2cb | ||
|
aa705dd691 | ||
|
1c3e4124f8 | ||
|
c1757372d3 | ||
|
5b2b29043c | ||
|
bb3f28ffa7 | ||
|
6ceb2af4a7 | ||
|
b59276ff1c | ||
|
2e95832812 | ||
|
2240bf9430 | ||
|
db036edccd | ||
|
08e1f626c1 | ||
|
c0d08f5e3e | ||
|
dec3f0798a | ||
|
62ded580ce | ||
|
446d73fcf5 | ||
|
e299775d67 | ||
|
2c18750537 | ||
|
f317e50136 | ||
|
1d84bda7ca | ||
|
ae7c947ba5 | ||
|
6d07729c55 | ||
|
1d7bf200a8 | ||
|
6bc59f8b33 | ||
|
b2cf03fa5c | ||
|
36e273714d | ||
|
6be77b7fb9 | ||
|
6bcf45f136 | ||
|
8bca8236db | ||
|
fb5aa4c9c1 | ||
|
3f5772c62a | ||
|
2d946d7ee7 | ||
|
10ca35dccd | ||
|
e3671cbb04 | ||
|
a525d02cc5 | ||
|
1cc1a4e6e2 | ||
|
3f0af3fe09 | ||
|
bc26d9f0de | ||
|
0ba28bbc8b | ||
|
550184275a | ||
|
c376083ecb | ||
|
1db5fcf200 | ||
|
16b2555ab3 | ||
|
d9a5258f40 | ||
|
190ebbed27 | ||
|
a0872c9e31 | ||
|
f5b306e7ff | ||
|
7a1feb3c51 | ||
|
1e8df9f245 | ||
|
b72937e8fb | ||
|
67847c3117 | ||
|
a2a0c80acb | ||
|
b3fd06fb45 | ||
|
c5db8d903c | ||
|
8fcd242494 | ||
|
ebd9af900e | ||
|
b02381c2d5 | ||
|
9b199ea756 | ||
|
ec3b913ee4 | ||
|
c210ab31d9 | ||
|
6c1fa91c70 | ||
|
04bab185f6 | ||
|
2213b4cf37 | ||
|
1d770e5636 | ||
|
b7e15e0a2c | ||
|
9c651ae913 | ||
|
e09d5cb4ec | ||
|
cae353b9f6 | ||
|
edb5b3d711 | ||
|
667a0c41ed | ||
|
2975acdc82 | ||
|
76dcbe3429 | ||
|
d8e2d464ad | ||
|
5f8bcb0c26 | ||
|
7ef8d6fa10 | ||
|
5924a40222 | ||
|
95ce4f5c1e | ||
|
f258f20b04 | ||
|
7e2ad827aa | ||
|
3df588047d | ||
|
ac0e5cbb29 | ||
|
5ab584bc6a | ||
|
a2e03e3bd0 | ||
|
f0589b310f | ||
|
8519b0d353 | ||
|
0e3d1e1503 | ||
|
ebd77f314d | ||
|
749d833f65 | ||
|
0373cd6f97 | ||
|
1f3fc8a366 | ||
|
89c3930b28 | ||
|
85aa1a444a | ||
|
702876ae7f | ||
|
7109910f46 | ||
|
8168d2fdc1 | ||
|
edbcd01fbc | ||
|
c99266e961 | ||
|
f804053736 | ||
|
21f6f81914 | ||
|
2387010556 | ||
|
f35d574759 | ||
|
3be74bb275 | ||
|
b1be062437 | ||
|
2d0d320d05 | ||
|
1de5111ab5 | ||
|
3d530e4747 | ||
|
0ef1b7b683 | ||
|
66485e81b4 | ||
|
e74e7cf734 | ||
|
a19b93c966 | ||
|
f7fd1f2a63 | ||
|
88b71d23db | ||
|
6845068b82 | ||
|
5c0b18efe4 | ||
|
4b93d040b3 | ||
|
ff61cc971e | ||
|
46db91ce73 | ||
|
5921909ef5 | ||
|
1537861c61 | ||
|
197a5fbcf4 | ||
|
ff32529345 | ||
|
a179c3b399 | ||
|
a820585f56 | ||
|
bfb12f415c | ||
|
a731b43b52 | ||
|
118b4eb07a | ||
|
f1a05ab73c | ||
|
4c85a41bfb | ||
|
30e048d4ab | ||
|
aa0ab6d387 | ||
|
30b87985b7 | ||
|
df73211d56 | ||
|
e3a4ddcd08 | ||
|
0ea007b26f | ||
|
16bb9b6836 | ||
|
d2766b1b4f | ||
|
4802484729 | ||
|
c762b9bb2e | ||
|
5792a19b97 | ||
|
9699dc2a85 | ||
|
0fa0c2256a | ||
|
1b410980ca | ||
|
be0dbd62c1 | ||
|
1a411b658b | ||
|
d2e84a700f | ||
|
f94fa78565 | ||
|
007a1fc7f2 | ||
|
af7c9b520f | ||
|
329c576f44 | ||
|
526c19181e | ||
|
8d468925d3 | ||
|
f99363674b | ||
|
526a04d4c8 | ||
|
b91ae71241 | ||
|
0a41cd43a5 | ||
|
4831890232 | ||
|
b001b0da86 | ||
|
3a2b421566 | ||
|
c4529820f2 | ||
|
d3edccb839 | ||
|
8380de1bd9 | ||
|
bf43149d7e | ||
|
1f6f8d5e0f | ||
|
c77fe6b434 | ||
|
831a3e384b | ||
|
a2db3e0499 | ||
|
6f81e3479a | ||
|
0306b5e8f7 | ||
|
cb54e414ed | ||
|
bad71d1a36 | ||
|
088b8fb348 | ||
|
e28ebf1c62 | ||
|
39eeb67d91 | ||
|
0c0949679f | ||
|
58d4481118 | ||
|
21e28ae848 | ||
|
7c7ee2ca61 | ||
|
5ef55dd8b4 | ||
|
d47c1a7975 | ||
|
8068057040 | ||
|
fcdeec0bfa | ||
|
b9d8eff994 | ||
|
529e34d2ae | ||
|
26b3fe201b | ||
|
462dcbcf03 | ||
|
dafdaa4208 | ||
|
5212b7d3bd | ||
|
83a92596c3 | ||
|
029fa83690 | ||
|
abdcb9e332 | ||
|
17e85e31cd | ||
|
7d3dd5a0e4 | ||
|
dd873fbeee | ||
|
38a4c80995 | ||
|
91fa727c74 | ||
|
794c0206f3 | ||
|
6a92ac0b7b | ||
|
f5adea1061 | ||
|
c0563f1a39 | ||
|
ce3c72e9d9 | ||
|
dcba74deb9 | ||
|
04ebd9d46a | ||
|
52b4e93c38 | ||
|
58d6681824 | ||
|
c944d203fb | ||
|
62df067fac | ||
|
7c80b9a692 | ||
|
a4a8345a33 | ||
|
742dde72bb | ||
|
4497ddbb0e | ||
|
53388a3570 | ||
|
1c495d7ea4 | ||
|
4c0d6e211b | ||
|
5bfd6acd52 | ||
|
0b49de94c6 | ||
|
7c0e557f84 | ||
|
a81171d5f1 | ||
|
d426126a92 | ||
|
395b1702de | ||
|
ef4aa202d0 | ||
|
cc5ee00b89 | ||
|
fa1f4f761d | ||
|
b50aebd2ed | ||
|
9f741abd84 | ||
|
32ccc26712 | ||
|
563a0bd274 | ||
|
a91080b060 | ||
|
c878d262bf | ||
|
c8446c2dc8 | ||
|
fb4ba7af2b | ||
|
c134dcd6fe | ||
|
5140bbe99a | ||
|
0c33d110f4 | ||
|
5b37fb83fd | ||
|
bc6879ecc1 | ||
|
17137ba3e7 | ||
|
e9d2124885 | ||
|
f1f2e1bf64 | ||
|
72bc74001f | ||
|
89d90de7d8 | ||
|
2618aef008 | ||
|
709d50836b | ||
|
ee71b4bfef | ||
|
0d57e2aed9 | ||
|
30ffba78e6 | ||
|
8394549857 | ||
|
870c0b5cf4 | ||
|
b60edd9ee9 | ||
|
b1ea36793b | ||
|
750878d668 | ||
|
617b8b20f0 | ||
|
8327dd0c0b | ||
|
9b3750320b | ||
|
b9f1f7752d | ||
|
944008661f | ||
|
79ae52aca7 | ||
|
51390aa874 | ||
|
cfa1f47226 | ||
|
40b59da224 | ||
|
f7ed4a5805 | ||
|
3d47030349 | ||
|
34eb2e371e | ||
|
6573634012 | ||
|
61ecb4cd18 | ||
|
06d528a2bd | ||
|
1fe6a8b04d | ||
|
bd5cab6e87 | ||
|
238acd9330 | ||
|
8e7ac513b6 | ||
|
e56551d047 | ||
|
170fc13e02 | ||
|
97ce77169a | ||
|
c9b871a03a | ||
|
2fdefa258e | ||
|
f0a733d6d6 | ||
|
586b5714a7 | ||
|
6e23454202 | ||
|
de7dd068d9 | ||
|
a33476dea8 | ||
|
dceccbdb92 | ||
|
393651f5e2 | ||
|
5acee9e11d | ||
|
81626eef38 | ||
|
e60fbbbebe | ||
|
e45e63dc37 | ||
|
c3d5ad2eeb | ||
|
7c64f5d31e | ||
|
66f46c5b96 | ||
|
07a6d48a27 | ||
|
722ea28e3a | ||
|
f195ef27f3 | ||
|
7e5c258266 | ||
|
38b5aef208 | ||
|
a7e4ded722 | ||
|
22405a1259 | ||
|
d0a6689413 | ||
|
a1f47cb4db | ||
|
c884c7bb8a | ||
|
c042098889 | ||
|
571f41dcf0 | ||
|
cbd54470ba | ||
|
c84fb9895e | ||
|
5623a53464 | ||
|
c95393b238 | ||
|
be0dd71bb4 | ||
|
e3d1201b46 | ||
|
8f982ff1f2 | ||
|
0391e21c84 | ||
|
b8a1cb5c68 | ||
|
7a71cd3012 | ||
|
26bedced35 | ||
|
c1aefb8ad8 | ||
|
576e87f398 | ||
|
b4f6bf0f6a | ||
|
edc55aad3c | ||
|
38a3fe4316 | ||
|
81e3b2dd4c | ||
|
4524cdc151 | ||
|
aeffe1036d | ||
|
987e8a93bd | ||
|
2cb4acd6cc | ||
|
59549d5f39 | ||
|
4a7297d05c | ||
|
a5335667bb | ||
|
498b806ca9 | ||
|
dd7a8a9a87 | ||
|
133aa77c21 | ||
|
942614dd23 | ||
|
c30ebe5f90 | ||
|
50757b5e99 | ||
|
42b900b9b2 | ||
|
c26b9b1a5d | ||
|
9ee642a7db | ||
|
423385bca0 | ||
|
6e5f7650a5 | ||
|
705f3f1372 | ||
|
f6520727a3 | ||
|
5f6c5025d5 | ||
|
328be161d6 | ||
|
c446c291d9 | ||
|
c66d9de759 | ||
|
260ee980e0 | ||
|
6890dc1844 | ||
|
e2190bd9d5 | ||
|
0472d19bd4 | ||
|
07524f5c99 | ||
|
1710800cc0 | ||
|
c705d6f9b3 | ||
|
be718aea11 | ||
|
ca680710a2 | ||
|
5f71a43758 | ||
|
04dd63da1c | ||
|
cee022b935 | ||
|
ae2ae85070 | ||
|
ce6bbbaa33 | ||
|
dc74f76a03 | ||
|
e042ef3f27 |
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
# vendor/github.com/xenolf/lego/providers/dns/cloudxns/cloudxns.go eol=crlf
|
6
.github/ISSUE_TEMPLATE.md
vendored
@@ -46,6 +46,10 @@ HOW TO WRITE A GOOD ISSUE?
|
||||
For the Traefik Docker image:
|
||||
docker run [IMAGE] version
|
||||
ex: docker run traefik version
|
||||
|
||||
For the alpine Traefik Docker image:
|
||||
docker run [IMAGE] traefik version
|
||||
ex: docker run traefik traefik version
|
||||
-->
|
||||
|
||||
```
|
||||
@@ -62,7 +66,7 @@ Add more configuration information here.
|
||||
-->
|
||||
|
||||
|
||||
### If applicable, please paste the log output in debug mode (`--debug` switch)
|
||||
### If applicable, please paste the log output at DEBUG level (`--logLevel=DEBUG` switch)
|
||||
|
||||
```
|
||||
(paste your output here)
|
||||
|
6
.github/ISSUE_TEMPLATE/bugs.md
vendored
@@ -44,6 +44,10 @@ HOW TO WRITE A GOOD ISSUE?
|
||||
For the Traefik Docker image:
|
||||
docker run [IMAGE] version
|
||||
ex: docker run traefik version
|
||||
|
||||
For the alpine Traefik Docker image:
|
||||
docker run [IMAGE] traefik version
|
||||
ex: docker run traefik traefik version
|
||||
-->
|
||||
|
||||
```
|
||||
@@ -61,7 +65,7 @@ Add more configuration information here.
|
||||
-->
|
||||
|
||||
|
||||
### If applicable, please paste the log output in debug mode (`--debug` switch)
|
||||
### If applicable, please paste the log output in DEBUG level (`--logLevel=DEBUG` switch)
|
||||
|
||||
```
|
||||
(paste your output here)
|
||||
|
1
.gitignore
vendored
@@ -6,6 +6,7 @@
|
||||
/traefik
|
||||
/traefik.toml
|
||||
/static/
|
||||
/webui/.tmp/
|
||||
.vscode/
|
||||
/site/
|
||||
*.log
|
||||
|
42
.gometalinter.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"Vendor": true,
|
||||
"Sort": [
|
||||
"path",
|
||||
"line",
|
||||
"column",
|
||||
"severity",
|
||||
"linter"
|
||||
],
|
||||
"Test": true,
|
||||
"Cyclo": 15,
|
||||
"Enable": [
|
||||
"gotypex",
|
||||
"nakedret",
|
||||
"vet",
|
||||
"goimports",
|
||||
"golint",
|
||||
"ineffassign",
|
||||
"gotype",
|
||||
"misspell",
|
||||
"structcheck",
|
||||
"gosimple",
|
||||
"unconvert",
|
||||
"varcheck",
|
||||
"errcheck",
|
||||
"unused",
|
||||
"deadcode",
|
||||
"staticcheck"
|
||||
],
|
||||
"Disable": [
|
||||
"gas",
|
||||
"maligned",
|
||||
"interfacer",
|
||||
"goconst",
|
||||
"gocyclo",
|
||||
"vetshadow"
|
||||
],
|
||||
"Exclude": [
|
||||
"autogen/.*"
|
||||
],
|
||||
"Deadline": "5m"
|
||||
}
|
@@ -10,7 +10,7 @@ else
|
||||
export VERSION=''
|
||||
fi
|
||||
|
||||
export CODENAME=cancoillotte
|
||||
export CODENAME=tetedemoine
|
||||
|
||||
export N_MAKE_JOBS=2
|
||||
|
||||
|
@@ -11,7 +11,7 @@ env:
|
||||
global:
|
||||
- REPO: $TRAVIS_REPO_SLUG
|
||||
- VERSION: $TRAVIS_TAG
|
||||
- CODENAME: cancoillotte
|
||||
- CODENAME: tetedemoine
|
||||
- N_MAKE_JOBS: 2
|
||||
|
||||
script:
|
||||
|
472
CHANGELOG.md
@@ -1,5 +1,477 @@
|
||||
# Change Log
|
||||
|
||||
## [v1.6.5](https://github.com/containous/traefik/tree/v1.6.5) (2018-07-09)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.6.4...v1.6.5)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Add a mutex on local store for HTTPChallenges ([#3579](https://github.com/containous/traefik/pull/3579) by [Juliens](https://github.com/Juliens))
|
||||
- **[consulcatalog]** Split the error handling from Consul Catalog (deadlock) ([#3560](https://github.com/containous/traefik/pull/3560) by [ortz](https://github.com/ortz))
|
||||
- **[docker]** segment labels: multiple frontends for one backend. ([#3511](https://github.com/containous/traefik/pull/3511) by [ldez](https://github.com/ldez))
|
||||
- **[kv]** Better support on same prefix at the same level in the KV ([#3532](https://github.com/containous/traefik/pull/3532) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[logs]** Add logs when error is generated in error handler ([#3567](https://github.com/containous/traefik/pull/3567) by [Juliens](https://github.com/Juliens))
|
||||
- **[middleware]** Create middleware to be able to handle HTTP pipelining correctly ([#3513](https://github.com/containous/traefik/pull/3513) by [mmatur](https://github.com/mmatur))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** The gandiv5 provider works with wildcard ([#3506](https://github.com/containous/traefik/pull/3506) by [manu5801](https://github.com/manu5801))
|
||||
- **[kv]** Update keyFile first/last line comment in kv-config.md ([#3558](https://github.com/containous/traefik/pull/3558) by [madnight](https://github.com/madnight))
|
||||
- Minor formatting issue in user-guide ([#3546](https://github.com/containous/traefik/pull/3546) by [Vanuan](https://github.com/Vanuan))
|
||||
|
||||
## [v1.6.4](https://github.com/containous/traefik/tree/v1.6.4) (2018-06-15)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.6.3...v1.6.4)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Use logrus writer instead of os.Stderr ([#3498](https://github.com/containous/traefik/pull/3498) by [ldez](https://github.com/ldez))
|
||||
- **[consulcatalog]** Enclose IPv6 addresses in "[]" ([#3477](https://github.com/containous/traefik/pull/3477) by [herver](https://github.com/herver))
|
||||
- **[docker,ecs,marathon,mesos,rancher]** Use net.JoinHostPort for servers URL ([#3484](https://github.com/containous/traefik/pull/3484) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Backend name with docker-compose and segments. ([#3485](https://github.com/containous/traefik/pull/3485) by [ldez](https://github.com/ldez))
|
||||
- **[oxy]** Handle buffer pool for oxy ([#3450](https://github.com/containous/traefik/pull/3450) by [Juliens](https://github.com/Juliens))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** The exoscale provider works with wildcard ([#3479](https://github.com/containous/traefik/pull/3479) by [greut](https://github.com/greut))
|
||||
- **[consul,docker]** Edit wording ([#3438](https://github.com/containous/traefik/pull/3438) by [mayank23](https://github.com/mayank23))
|
||||
- **[k8s]** Add missing annotation documentation. ([#3454](https://github.com/containous/traefik/pull/3454) by [ldez](https://github.com/ldez))
|
||||
- **[kv]** Fix typo in kv user guide ([#3474](https://github.com/containous/traefik/pull/3474) by [shambarick](https://github.com/shambarick))
|
||||
- Clean metrics documentation. ([#3488](https://github.com/containous/traefik/pull/3488) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v1.6.3](https://github.com/containous/traefik/tree/v1.6.3) (2018-06-05)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.6.2...v1.6.3)
|
||||
|
||||
**Enhancements:**
|
||||
- **[acme]** Add user agent for ACME ([#3431](https://github.com/containous/traefik/pull/3431) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Use to the stable version of Lego ([#3418](https://github.com/containous/traefik/pull/3418) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme,cluster]** Improve ACME account registration URI management ([#3398](https://github.com/containous/traefik/pull/3398) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme,cluster]** Remove ACME empty certificates from KV store ([#3389](https://github.com/containous/traefik/pull/3389) by [nmengin](https://github.com/nmengin))
|
||||
- **[consulcatalog]** Reflect changes in catalog healthy nodes in healthCheck watch ([#3390](https://github.com/containous/traefik/pull/3390) by [thebinary](https://github.com/thebinary))
|
||||
- **[consulcatalog]** Detect change when service or node are in maintenance mode ([#3434](https://github.com/containous/traefik/pull/3434) by [mmatur](https://github.com/mmatur))
|
||||
- **[k8s]** Update Kubernetes provider to support IPv6 Backends ([#3432](https://github.com/containous/traefik/pull/3432) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[logs,middleware]** Add URL and Host for some access logs. ([#3430](https://github.com/containous/traefik/pull/3430) by [ldez](https://github.com/ldez))
|
||||
- **[metrics]** Improve Prometheus metrics removal ([#3287](https://github.com/containous/traefik/pull/3287) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[middleware]** Whitelist and XFF. ([#3411](https://github.com/containous/traefik/pull/3411) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Error pages and header merge ([#3394](https://github.com/containous/traefik/pull/3394) by [ldez](https://github.com/ldez))
|
||||
- **[websocket]** Includes the headers in the HTTP response of a websocket request ([#3425](https://github.com/containous/traefik/pull/3425) by [geraldcroes](https://github.com/geraldcroes))
|
||||
- **[webui]** Webui Whitelist overflow. ([#3412](https://github.com/containous/traefik/pull/3412) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** Docs: ACME Overhaul ([#3421](https://github.com/containous/traefik/pull/3421) by [Dargmuesli](https://github.com/Dargmuesli))
|
||||
- **[acme]** Minor documentation changes ([#3405](https://github.com/containous/traefik/pull/3405) by [amincheloh](https://github.com/amincheloh))
|
||||
- **[k8s]** Helm installation using values ([#3392](https://github.com/containous/traefik/pull/3392) by [erikaulin](https://github.com/erikaulin))
|
||||
- **[k8s]** Update Kubernetes Port Documentation ([#3368](https://github.com/containous/traefik/pull/3368) by [dtomcej](https://github.com/dtomcej))
|
||||
|
||||
## [v1.6.2](https://github.com/containous/traefik/tree/v1.6.2) (2018-05-22)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.6.1...v1.6.2)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** fix: acme errors management. ([#3329](https://github.com/containous/traefik/pull/3329) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Force to use ACME v02 endpoint. ([#3358](https://github.com/containous/traefik/pull/3358) by [ldez](https://github.com/ldez))
|
||||
- **[file]** No template parsing on traefik configuration file ([#3347](https://github.com/containous/traefik/pull/3347) by [Juliens](https://github.com/Juliens))
|
||||
- **[k8s]** Add redirect-permanent to kubernetes template ([#3332](https://github.com/containous/traefik/pull/3332) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[logs]** Enhance Load-balancing method validation log. ([#3361](https://github.com/containous/traefik/pull/3361) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Fix error pages content. ([#3337](https://github.com/containous/traefik/pull/3337) by [ldez](https://github.com/ldez))
|
||||
- **[webui]** Route rules overlaps in UI ([#3333](https://github.com/containous/traefik/pull/3333) by [ldez](https://github.com/ldez))
|
||||
- **[webui]** WebUI typo into the buffering section. ([#3363](https://github.com/containous/traefik/pull/3363) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** Update caServer to letsencrypt one in examples ([#3339](https://github.com/containous/traefik/pull/3339) by [woernfl](https://github.com/woernfl))
|
||||
- **[docker]** Add command for basic auth with Docker Compose ([#3346](https://github.com/containous/traefik/pull/3346) by [DeamonMV](https://github.com/DeamonMV))
|
||||
- **[docker]** Removes ambiguity with the word 'default' ([#3344](https://github.com/containous/traefik/pull/3344) by [ldez](https://github.com/ldez))
|
||||
- **[kv]** Add basicAuth example for KV ([#3274](https://github.com/containous/traefik/pull/3274) by [MichaelErmer](https://github.com/MichaelErmer))
|
||||
- **[provider]** Update docs to reflect Provider wording ([#3331](https://github.com/containous/traefik/pull/3331) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[servicefabric]** Update docs to match SF provider labels ([#3335](https://github.com/containous/traefik/pull/3335) by [jjcollinge](https://github.com/jjcollinge))
|
||||
|
||||
## [v1.6.1](https://github.com/containous/traefik/tree/v1.6.1) (2018-05-14)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.6.0...v1.6.1)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Add missing deprecation info in CLI help. ([#3291](https://github.com/containous/traefik/pull/3291) by [ldez](https://github.com/ldez))
|
||||
- **[docker,marathon,rancher]** Fix segment backend name ([#3317](https://github.com/containous/traefik/pull/3317) by [ldez](https://github.com/ldez))
|
||||
- **[logs,middleware]** Error when accesslog and error pages ([#3314](https://github.com/containous/traefik/pull/3314) by [ldez](https://github.com/ldez))
|
||||
- **[middleware,tracing]** Fix wrong tag in forward span in tracing middleware ([#3279](https://github.com/containous/traefik/pull/3279) by [mmatur](https://github.com/mmatur))
|
||||
- **[webui]** Fix webui ([#3299](https://github.com/containous/traefik/pull/3299) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- **[k8s]** Add Documentation update for Kubernetes Ingress ([#3294](https://github.com/containous/traefik/pull/3294) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[tls]** Enhance entry point TLS CLI reference. ([#3290](https://github.com/containous/traefik/pull/3290) by [ldez](https://github.com/ldez))
|
||||
- Typo in documentation ([#3261](https://github.com/containous/traefik/pull/3261) by [blakethepatton](https://github.com/blakethepatton))
|
||||
|
||||
## [v1.6.0](https://github.com/containous/traefik/tree/v1.6.0) (2018-04-30)
|
||||
[Commits](https://github.com/containous/traefik/compare/v1.5.0-rc1...v1.6.0)
|
||||
[Commits pre RC](https://github.com/containous/traefik/compare/v1.5.0-rc1...v1.6.0-rc1)
|
||||
|
||||
**Enhancements:**
|
||||
- **[acme]** Create ACME Provider ([#2889](https://github.com/containous/traefik/pull/2889) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Update Lego (Gandi API v5, cloudxns, ...) ([#2844](https://github.com/containous/traefik/pull/2844) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Simplify storing renewed acme certificate ([#2614](https://github.com/containous/traefik/pull/2614) by [ferhatelmas](https://github.com/ferhatelmas))
|
||||
- **[acme]** ACME V2 Integration ([#3063](https://github.com/containous/traefik/pull/3063) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Bump Lego Version for GoDaddy DNS Provider ([#2482](https://github.com/containous/traefik/pull/2482) by [sjawhar](https://github.com/sjawhar))
|
||||
- **[acme]** Delete TLS-SNI-01 challenge from ACME ([#2971](https://github.com/containous/traefik/pull/2971) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Create backup file during migration from ACME V1 to ACME V2 ([#3191](https://github.com/containous/traefik/pull/3191) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Generate wildcard certificate with SANs in ACME ([#3167](https://github.com/containous/traefik/pull/3167) by [nmengin](https://github.com/nmengin))
|
||||
- **[api,cluster]** Added cluster/leader endpoint ([#3009](https://github.com/containous/traefik/pull/3009) by [aantono](https://github.com/aantono))
|
||||
- **[authentication]** Forward Authentication: add X-Forwarded-Uri ([#2398](https://github.com/containous/traefik/pull/2398) by [sebastianbauer](https://github.com/sebastianbauer))
|
||||
- **[boltdb,consul,etcd,kv,zk]** Add all available configuration to KV Backend ([#2652](https://github.com/containous/traefik/pull/2652) by [ldez](https://github.com/ldez))
|
||||
- **[boltdb,consul,etcd,kv,zk]** homogenization of templates: KV ([#2661](https://github.com/containous/traefik/pull/2661) by [ldez](https://github.com/ldez))
|
||||
- **[boltdb,consul,etcd,kv,zk]** Homogenization of the providers (part 1): KV ([#2616](https://github.com/containous/traefik/pull/2616) by [ldez](https://github.com/ldez))
|
||||
- **[consul,consulcatalog]** Homogenization of templates: Consul Catalog ([#2668](https://github.com/containous/traefik/pull/2668) by [ldez](https://github.com/ldez))
|
||||
- **[consul,consulcatalog]** Split consul and consul catalog. ([#2655](https://github.com/containous/traefik/pull/2655) by [ldez](https://github.com/ldez))
|
||||
- **[consulcatalog,ecs,mesos]** Factorize labels managements. ([#3099](https://github.com/containous/traefik/pull/3099) by [ldez](https://github.com/ldez))
|
||||
- **[consulcatalog]** Check for endpoints while detecting Consul service changes ([#2882](https://github.com/containous/traefik/pull/2882) by [caseycs](https://github.com/caseycs))
|
||||
- **[consulcatalog]** TLS Support for ConsulCatalog ([#2900](https://github.com/containous/traefik/pull/2900) by [mmatur](https://github.com/mmatur))
|
||||
- **[consulcatalog]** Add all available tags to Consul Catalog Backend ([#2646](https://github.com/containous/traefik/pull/2646) by [ldez](https://github.com/ldez))
|
||||
- **[docker,docker/swarm]** Fix support for macvlan driver in docker provider ([#2827](https://github.com/containous/traefik/pull/2827) by [mmatur](https://github.com/mmatur))
|
||||
- **[docker,marathon,rancher]** Segments Labels: Rancher & Marathon ([#3073](https://github.com/containous/traefik/pull/3073) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Add all available labels to Docker Backend ([#2584](https://github.com/containous/traefik/pull/2584) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Homogenization of templates: Docker ([#2659](https://github.com/containous/traefik/pull/2659) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Custom headers by service labels for docker backends ([#2514](https://github.com/containous/traefik/pull/2514) by [Tiscs](https://github.com/Tiscs))
|
||||
- **[docker]** Segment labels: Docker ([#3055](https://github.com/containous/traefik/pull/3055) by [ldez](https://github.com/ldez))
|
||||
- **[dynamodb,ecs]** Upgrade AWS SKD to version v1.13.1 ([#2908](https://github.com/containous/traefik/pull/2908) by [mmatur](https://github.com/mmatur))
|
||||
- **[ecs]** Add all available labels to ECS Backend ([#2605](https://github.com/containous/traefik/pull/2605) by [ldez](https://github.com/ldez))
|
||||
- **[ecs]** Homogenization of templates: ECS ([#2663](https://github.com/containous/traefik/pull/2663) by [ldez](https://github.com/ldez))
|
||||
- **[ecs]** Factorize labels managements. ([#3159](https://github.com/containous/traefik/pull/3159) by [ldez](https://github.com/ldez))
|
||||
- **[eureka]** Homogenization of templates: Eureka ([#2846](https://github.com/containous/traefik/pull/2846) by [ldez](https://github.com/ldez))
|
||||
- **[eureka]** Replace Delay by RefreshSecond in Eureka ([#2972](https://github.com/containous/traefik/pull/2972) by [ldez](https://github.com/ldez))
|
||||
- **[file]** Added support for templates to file provider ([#2991](https://github.com/containous/traefik/pull/2991) by [aantono](https://github.com/aantono))
|
||||
- **[healthcheck]** Toggle /ping to artificially return unhealthy response on SIGTERM during requestAcceptGraceTimeout interval ([#3062](https://github.com/containous/traefik/pull/3062) by [ravilr](https://github.com/ravilr))
|
||||
- **[healthcheck]** Improve logging output for failing healthchecks ([#2443](https://github.com/containous/traefik/pull/2443) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[k8s,tls]** Add support for fetching k8s Ingress TLS data from secrets ([#2439](https://github.com/containous/traefik/pull/2439) by [gopenguin](https://github.com/gopenguin))
|
||||
- **[k8s]** Introduce k8s informer factory ([#2867](https://github.com/containous/traefik/pull/2867) by [yue9944882](https://github.com/yue9944882))
|
||||
- **[k8s]** Add all available annotations to k8s Backend ([#2612](https://github.com/containous/traefik/pull/2612) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Bump kubernetes/client-go ([#2848](https://github.com/containous/traefik/pull/2848) by [yue9944882](https://github.com/yue9944882))
|
||||
- **[k8s]** Add app-root annotation support for kubernetes ingress ([#2522](https://github.com/containous/traefik/pull/2522) by [yue9944882](https://github.com/yue9944882))
|
||||
- **[k8s]** Builders in k8s tests ([#2513](https://github.com/containous/traefik/pull/2513) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Allow custom value for kubernetes.io/ingress.class annotation ([#2222](https://github.com/containous/traefik/pull/2222) by [yuvipanda](https://github.com/yuvipanda))
|
||||
- **[logs,middleware]** Add access log filter for retry attempts ([#3042](https://github.com/containous/traefik/pull/3042) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[logs,middleware]** Add username in accesslog ([#2111](https://github.com/containous/traefik/pull/2111) by [bastiaanb](https://github.com/bastiaanb))
|
||||
- **[logs,middleware]** Ultimate Access log filter ([#2988](https://github.com/containous/traefik/pull/2988) by [mmatur](https://github.com/mmatur))
|
||||
- **[logs]** Allow overriding the log level in debug mode. ([#3050](https://github.com/containous/traefik/pull/3050) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[logs]** Display file log when test fails. ([#2801](https://github.com/containous/traefik/pull/2801) by [ldez](https://github.com/ldez))
|
||||
- **[marathon]** Remove health check filter from Marathon tasks. ([#2817](https://github.com/containous/traefik/pull/2817) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[marathon]** Add all available labels to Marathon Backend ([#2602](https://github.com/containous/traefik/pull/2602) by [ldez](https://github.com/ldez))
|
||||
- **[marathon]** homogenization of templates: Marathon ([#2665](https://github.com/containous/traefik/pull/2665) by [ldez](https://github.com/ldez))
|
||||
- **[mesos]** Add all available labels to Mesos Backend ([#2687](https://github.com/containous/traefik/pull/2687) by [ldez](https://github.com/ldez))
|
||||
- **[metrics]** Added entrypoint metrics to influxdb ([#2992](https://github.com/containous/traefik/pull/2992) by [adityacs](https://github.com/adityacs))
|
||||
- **[metrics]** Remove unnecessary conversion ([#2850](https://github.com/containous/traefik/pull/2850) by [ferhatelmas](https://github.com/ferhatelmas))
|
||||
- **[metrics]** Extend metrics and rebuild prometheus exporting logic ([#2567](https://github.com/containous/traefik/pull/2567) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[metrics]** Added missing metrics to registry for DataDog and StatsD ([#2890](https://github.com/containous/traefik/pull/2890) by [aantono](https://github.com/aantono))
|
||||
- **[middleware,consul,consulcatalog,docker,ecs,k8s,marathon,mesos,rancher]** New option in secure middleware ([#2958](https://github.com/containous/traefik/pull/2958) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware,consulcatalog,docker,ecs,k8s,kv,marathon,mesos,rancher]** Ability to use "X-Forwarded-For" as a source of IP for white list. ([#3070](https://github.com/containous/traefik/pull/3070) by [ldez](https://github.com/ldez))
|
||||
- **[middleware,docker]** Use pointer of error pages ([#2607](https://github.com/containous/traefik/pull/2607) by [ldez](https://github.com/ldez))
|
||||
- **[middleware,provider]** Redirection: permanent move option. ([#2774](https://github.com/containous/traefik/pull/2774) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Add tests on IPWhiteLister. ([#3106](https://github.com/containous/traefik/pull/3106) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Change port of traefik for error pages integration test ([#2907](https://github.com/containous/traefik/pull/2907) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware]** Remove unnecessary returns in tracing setup ([#2880](https://github.com/containous/traefik/pull/2880) by [ferhatelmas](https://github.com/ferhatelmas))
|
||||
- **[middleware]** Request buffering middleware ([#2217](https://github.com/containous/traefik/pull/2217) by [harnash](https://github.com/harnash))
|
||||
- **[middleware]** Add new options to the CLI entrypoint definition. ([#2799](https://github.com/containous/traefik/pull/2799) by [ldez](https://github.com/ldez))
|
||||
- **[provider]** No error pages must return nil. ([#2610](https://github.com/containous/traefik/pull/2610) by [ldez](https://github.com/ldez))
|
||||
- **[provider]** Homogenization of the providers (part 1) ([#2518](https://github.com/containous/traefik/pull/2518) by [ldez](https://github.com/ldez))
|
||||
- **[rancher]** Add all available labels to Rancher Backend ([#2601](https://github.com/containous/traefik/pull/2601) by [ldez](https://github.com/ldez))
|
||||
- **[rancher]** Homogenization of templates: Rancher ([#2662](https://github.com/containous/traefik/pull/2662) by [ldez](https://github.com/ldez))
|
||||
- **[rules]** Externalize Træfik rules in a dedicated package ([#2933](https://github.com/containous/traefik/pull/2933) by [nmengin](https://github.com/nmengin))
|
||||
- **[servicefabric]** Use shared label system ([#3197](https://github.com/containous/traefik/pull/3197) by [ldez](https://github.com/ldez))
|
||||
- **[servicefabric]** Update Service Fabric backend. ([#3064](https://github.com/containous/traefik/pull/3064) by [ldez](https://github.com/ldez))
|
||||
- **[servicefabric]** Add white list for Service Fabric ([#3079](https://github.com/containous/traefik/pull/3079) by [ldez](https://github.com/ldez))
|
||||
- **[tls]** Use default entryPoints when certificates are added with no entryPoints. ([#2534](https://github.com/containous/traefik/pull/2534) by [nmengin](https://github.com/nmengin))
|
||||
- **[tracing]** Handle zipkin collector creation ([#2860](https://github.com/containous/traefik/pull/2860) by [ferhatelmas](https://github.com/ferhatelmas))
|
||||
- **[tracing]** Opentracing support ([#2587](https://github.com/containous/traefik/pull/2587) by [tcolgate](https://github.com/tcolgate) and [mmatur](https://github.com/mmatur))
|
||||
- **[webui]** New web ui ([#2226](https://github.com/containous/traefik/pull/2226) by [jkuri](https://github.com/jkuri))
|
||||
- **[webui]** Add status code text to webui bar chart tooltip ([#2639](https://github.com/containous/traefik/pull/2639) by [wader](https://github.com/wader))
|
||||
- Logger and Leaks ([#2847](https://github.com/containous/traefik/pull/2847) by [ldez](https://github.com/ldez))
|
||||
- Separate command from the main package ([#2951](https://github.com/containous/traefik/pull/2951) by [Juliens](https://github.com/Juliens))
|
||||
- Use context in Server ([#3007](https://github.com/containous/traefik/pull/3007) by [Juliens](https://github.com/Juliens))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Check all the C/N and SANs of provided certificates before generating ACME certificates in ACME provider ([#2970](https://github.com/containous/traefik/pull/2970) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Update lego. ([#3158](https://github.com/containous/traefik/pull/3158) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Fix panic with wrong ACME configuration ([#3084](https://github.com/containous/traefik/pull/3084) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Minor updates to dumpcerts.sh ([#3116](https://github.com/containous/traefik/pull/3116) by [mathuin](https://github.com/mathuin))
|
||||
- **[acme]** Add ACME certificates only on ACME EntryPoint ([#3136](https://github.com/containous/traefik/pull/3136) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Add TTL and custom Timeout in DigitalOcean DNS provider ([#3143](https://github.com/containous/traefik/pull/3143) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Fix acme.json file automatic creation ([#3156](https://github.com/containous/traefik/pull/3156) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Fix wildcard match to ACME domains in cluster mode ([#3080](https://github.com/containous/traefik/pull/3080) by [oldmantaiter](https://github.com/oldmantaiter))
|
||||
- **[api,cluster]** Moved /api/cluster/leadership handler under public routes (requires no authentication) ([#3101](https://github.com/containous/traefik/pull/3101) by [aantono](https://github.com/aantono))
|
||||
- **[authentication,middleware]** Forward auth: copy response headers when auth failed. ([#3207](https://github.com/containous/traefik/pull/3207) by [ldez](https://github.com/ldez))
|
||||
- **[consul,docker,ecs,eureka,k8s,kv,marathon,mesos,rancher]** Server weight zero ([#3130](https://github.com/containous/traefik/pull/3130) by [ldez](https://github.com/ldez))
|
||||
- **[docker,k8s,marathon]** Fix custom headers template ([#2622](https://github.com/containous/traefik/pull/2622) by [ldez](https://github.com/ldez))
|
||||
- **[docker,marathon,mesos,rancher]** Fix: label 'traefik.domain' ([#3201](https://github.com/containous/traefik/pull/3201) by [ldez](https://github.com/ldez))
|
||||
- **[docker,rancher]** Frontend rule and segment labels. ([#3091](https://github.com/containous/traefik/pull/3091) by [ldez](https://github.com/ldez))
|
||||
- **[docker,rancher]** Ignore server for container with empty IP address. ([#3213](https://github.com/containous/traefik/pull/3213) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Fix multiple frontends with docker-compose --scale ([#3190](https://github.com/containous/traefik/pull/3190) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[healthcheck]** Remove unnecessary mutex usage in health checks ([#2726](https://github.com/containous/traefik/pull/2726) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[k8s]** Missing annotation prefix support. ([#2915](https://github.com/containous/traefik/pull/2915) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Remove hardcoded frontend prefix in Kubernetes template ([#2914](https://github.com/containous/traefik/pull/2914) by [psalaberria002](https://github.com/psalaberria002))
|
||||
- **[k8s]** Limit label selector to Ingress factory. ([#3137](https://github.com/containous/traefik/pull/3137) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[k8s]** Fixes prefixed annotations support. ([#3110](https://github.com/containous/traefik/pull/3110) by [ldez](https://github.com/ldez))
|
||||
- **[logs,middleware]** Fix bad access log ([#2682](https://github.com/containous/traefik/pull/2682) by [mmatur](https://github.com/mmatur))
|
||||
- **[logs]** Add missing argument in log. ([#3188](https://github.com/containous/traefik/pull/3188) by [chemidy](https://github.com/chemidy))
|
||||
- **[marathon]** Several apps with same backend name in Marathon. ([#3109](https://github.com/containous/traefik/pull/3109) by [ldez](https://github.com/ldez))
|
||||
- **[mesos]** fix: overflow on 32 bits arch. ([#3127](https://github.com/containous/traefik/pull/3127) by [ldez](https://github.com/ldez))
|
||||
- **[metrics]** Fix duplicated tags in InfluxDB ([#3189](https://github.com/containous/traefik/pull/3189) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware,consul,consulcatalog,docker,ecs,kv,marathon,mesos,rancher]** Fix: error pages ([#3138](https://github.com/containous/traefik/pull/3138) by [ldez](https://github.com/ldez))
|
||||
- **[middleware,tracing]** Fix <nil> tracer value in KV ([#2911](https://github.com/containous/traefik/pull/2911) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware,tracing]** Fix nil value when tracing is enabled ([#3192](https://github.com/containous/traefik/pull/3192) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware]** Use responseModifier to override secure headers ([#2946](https://github.com/containous/traefik/pull/2946) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware]** Correct conditional setting of buffering retry expression. ([#2865](https://github.com/containous/traefik/pull/2865) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Fix high memory usage in retry middleware ([#2740](https://github.com/containous/traefik/pull/2740) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[middleware]** Fix whitelist and XFF. ([#3211](https://github.com/containous/traefik/pull/3211) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Fix panic in atomic on ARM and x86-32 platforms ([#3195](https://github.com/containous/traefik/pull/3195) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware]** Redirect to HTTPS first before basic auth if header redirect (secure) is set ([#3187](https://github.com/containous/traefik/pull/3187) by [SantoDE](https://github.com/SantoDE))
|
||||
- **[middleware]** Fix error pages redirect and headers. ([#3217](https://github.com/containous/traefik/pull/3217) by [ldez](https://github.com/ldez))
|
||||
- **[provider]** Add some missing quotes in templates ([#2973](https://github.com/containous/traefik/pull/2973) by [ldez](https://github.com/ldez))
|
||||
- **[servicefabric]** Fix backend name for stateful service and more. ([#3183](https://github.com/containous/traefik/pull/3183) by [ldez](https://github.com/ldez))
|
||||
- **[tracing]** Fix missing configuration for jaeger reporter ([#2720](https://github.com/containous/traefik/pull/2720) by [mmatur](https://github.com/mmatur))
|
||||
- **[tracing]** Tracing statusCodeTracker need to implement CloseNotify ([#2733](https://github.com/containous/traefik/pull/2733) by [mmatur](https://github.com/mmatur))
|
||||
- **[tracing]** Fix integration tests in tracing ([#2759](https://github.com/containous/traefik/pull/2759) by [mmatur](https://github.com/mmatur))
|
||||
- **[webui]** Remove useless ACME tab from UI. ([#3154](https://github.com/containous/traefik/pull/3154) by [ldez](https://github.com/ldez))
|
||||
- **[webui]** Add redirect section. ([#3243](https://github.com/containous/traefik/pull/3243) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- **[docker]** Add default values for some Docker labels ([#2604](https://github.com/containous/traefik/pull/2604) by [ldez](https://github.com/ldez))
|
||||
- **[file]** Add documentation about Templating in backend file ([#3223](https://github.com/containous/traefik/pull/3223) by [nmengin](https://github.com/nmengin))
|
||||
- **[k8s]** Update traefik-ds.yaml with --api command line parameter ([#2803](https://github.com/containous/traefik/pull/2803) by [maniankara](https://github.com/maniankara))
|
||||
- **[k8s]** Remove web provider in example ([#2807](https://github.com/containous/traefik/pull/2807) by [pigletfly](https://github.com/pigletfly))
|
||||
- **[k8s]** Drop capabilities in Kubernetes DaemonSet example ([#3028](https://github.com/containous/traefik/pull/3028) by [nogoegst](https://github.com/nogoegst))
|
||||
- **[k8s]** Docs: Fix typos in k8s user-guide ([#2898](https://github.com/containous/traefik/pull/2898) by [cez81](https://github.com/cez81))
|
||||
- **[k8s]** Change boolean annotation values to string ([#2839](https://github.com/containous/traefik/pull/2839) by [hobti01](https://github.com/hobti01))
|
||||
- **[k8s]** Update kubernetes.md ([#3093](https://github.com/containous/traefik/pull/3093) by [rdrgporto](https://github.com/rdrgporto))
|
||||
- **[k8s]** Document custom k8s ingress class usage in guide. ([#3242](https://github.com/containous/traefik/pull/3242) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[k8s]** Update kubernetes.md ([#3171](https://github.com/containous/traefik/pull/3171) by [andreyfedoseev](https://github.com/andreyfedoseev))
|
||||
- **[provider]** Split security labels and custom labels documentation. ([#2872](https://github.com/containous/traefik/pull/2872) by [ldez](https://github.com/ldez))
|
||||
- **[provider]** Remove non-supported label. ([#3065](https://github.com/containous/traefik/pull/3065) by [ldez](https://github.com/ldez))
|
||||
- **[provider]** Remove obsolete paragraph about error pages. ([#2608](https://github.com/containous/traefik/pull/2608) by [ldez](https://github.com/ldez))
|
||||
- **[provider]** Cleaning labels/annotations documentation. ([#3245](https://github.com/containous/traefik/pull/3245) by [ldez](https://github.com/ldez))
|
||||
- **[provider]** Fix template version documentation. ([#3184](https://github.com/containous/traefik/pull/3184) by [ldez](https://github.com/ldez))
|
||||
- **[servicefabric]** Add SF to supported backends in docs ([#3033](https://github.com/containous/traefik/pull/3033) by [lawrencegripper](https://github.com/lawrencegripper))
|
||||
- **[servicefabric]** Update SF white list documentation section. ([#3082](https://github.com/containous/traefik/pull/3082) by [ldez](https://github.com/ldez))
|
||||
- **[tracing]** Fix typo in doc for rate limit label ([#2790](https://github.com/containous/traefik/pull/2790) by [mmatur](https://github.com/mmatur))
|
||||
- **[tracing]** Add Tracing entry in the documentation. ([#2713](https://github.com/containous/traefik/pull/2713) by [ldez](https://github.com/ldez))
|
||||
- **[tracing]** Fix documentation for tracing with Jaeger ([#3227](https://github.com/containous/traefik/pull/3227) by [mmatur](https://github.com/mmatur))
|
||||
- **[webui]** doc: update Traefik images. ([#3241](https://github.com/containous/traefik/pull/3241) by [ldez](https://github.com/ldez))
|
||||
- Fix typo in documentation ([#3215](https://github.com/containous/traefik/pull/3215) by [arnaslu](https://github.com/arnaslu))
|
||||
- Minor improvements to documentation ([#3221](https://github.com/containous/traefik/pull/3221) by [colincoller](https://github.com/colincoller))
|
||||
- Update some examples ([#3150](https://github.com/containous/traefik/pull/3150) by [zaporylie](https://github.com/zaporylie))
|
||||
- Normalize parameter names in configs ([#3132](https://github.com/containous/traefik/pull/3132) by [kachkaev](https://github.com/kachkaev))
|
||||
- Fixed documentation urls on README.md ([#3102](https://github.com/containous/traefik/pull/3102) by [emir](https://github.com/emir))
|
||||
- Fix typo and tweak formatting in quickstart ([#3250](https://github.com/containous/traefik/pull/3250) by [alexymik](https://github.com/alexymik))
|
||||
- Fix basic documentation ([#3086](https://github.com/containous/traefik/pull/3086) by [mmatur](https://github.com/mmatur))
|
||||
- Prepare release v1.6.0-rc6 ([#3199](https://github.com/containous/traefik/pull/3199) by [mmatur](https://github.com/mmatur))
|
||||
- Prepare release v1.6.0-rc5 ([#3179](https://github.com/containous/traefik/pull/3179) by [Juliens](https://github.com/Juliens))
|
||||
- Prepare release v1.6.0-rc4 ([#3126](https://github.com/containous/traefik/pull/3126) by [ldez](https://github.com/ldez))
|
||||
- Prepare release v1.6.0-rc3 ([#3096](https://github.com/containous/traefik/pull/3096) by [ldez](https://github.com/ldez))
|
||||
- Prepare release v1.6.0-rc2 ([#3087](https://github.com/containous/traefik/pull/3087) by [nmengin](https://github.com/nmengin))
|
||||
- Prepare release v1.6.0-rc1 ([#3078](https://github.com/containous/traefik/pull/3078) by [Juliens](https://github.com/Juliens))
|
||||
- Prepare release v1.6.0 ([#3251](https://github.com/containous/traefik/pull/3251) by [Juliens](https://github.com/Juliens))
|
||||
|
||||
**Misc:**
|
||||
- **[oxy]** Disable closeNotify when method GET for http pipelining ([#3108](https://github.com/containous/traefik/pull/3108) by [Juliens](https://github.com/Juliens))
|
||||
- **[boltdb,consul,etcd,kv,zk]** Migrate from libkv to valkeyrie library ([#2743](https://github.com/containous/traefik/pull/2743) by [nmengin](https://github.com/nmengin))
|
||||
- Drop unnecessary type conversions ([#2583](https://github.com/containous/traefik/pull/2583) by [ferhatelmas](https://github.com/ferhatelmas))
|
||||
- Code simplification ([#2516](https://github.com/containous/traefik/pull/2516) by [ferhatelmas](https://github.com/ferhatelmas))
|
||||
- Merge v1.5.4 into master ([#3024](https://github.com/containous/traefik/pull/3024) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.5.3 into master ([#2943](https://github.com/containous/traefik/pull/2943) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.5.2 into master ([#2843](https://github.com/containous/traefik/pull/2843) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.5.1 into master ([#2781](https://github.com/containous/traefik/pull/2781) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.5.0-rc5 into master ([#2708](https://github.com/containous/traefik/pull/2708) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.5.0-rc3 into master ([#2600](https://github.com/containous/traefik/pull/2600) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.5.0-rc2 into master ([#2536](https://github.com/containous/traefik/pull/2536) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v1.6.0-rc6](https://github.com/containous/traefik/tree/v1.6.0-rc6) (2018-04-17)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.6.0-rc5...v1.6.0-rc6)
|
||||
|
||||
**Enhancements:**
|
||||
- **[acme]** Create backup file during migration from ACME V1 to ACME V2 ([#3191](https://github.com/containous/traefik/pull/3191) by [nmengin](https://github.com/nmengin))
|
||||
- **[servicefabric]** Use shared label system ([#3197](https://github.com/containous/traefik/pull/3197) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[docker]** Fix multiple frontends with docker-compose --scale ([#3190](https://github.com/containous/traefik/pull/3190) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[metrics]** Fix duplicated tags in InfluxDB ([#3189](https://github.com/containous/traefik/pull/3189) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware,tracing]** Fix nil value when tracing is enabled ([#3192](https://github.com/containous/traefik/pull/3192) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware]** Fix panic in atomic on ARM and x86-32 platforms ([#3195](https://github.com/containous/traefik/pull/3195) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware]** Redirect to HTTPS first before basic auth if header redirect (secure) is set ([#3187](https://github.com/containous/traefik/pull/3187) by [SantoDE](https://github.com/SantoDE))
|
||||
- **[servicefabric]** Fix backend name for stateful service and more. ([#3183](https://github.com/containous/traefik/pull/3183) by [ldez](https://github.com/ldez))
|
||||
- Add missing argument in log. ([#3188](https://github.com/containous/traefik/pull/3188) by [chemidy](https://github.com/chemidy))
|
||||
|
||||
**Documentation:**
|
||||
- **[provider]** Fix template version documentation. ([#3184](https://github.com/containous/traefik/pull/3184) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v1.6.0-rc5](https://github.com/containous/traefik/tree/v1.6.0-rc5) (2018-04-12)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.6.0-rc4...v1.6.0-rc5)
|
||||
|
||||
**Enhancements:**
|
||||
- **[acme]** Generate wildcard certificate with SANs in ACME ([#3167](https://github.com/containous/traefik/pull/3167) by [nmengin](https://github.com/nmengin))
|
||||
- **[ecs]** Factorize labels managements. ([#3159](https://github.com/containous/traefik/pull/3159) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Update lego. ([#3158](https://github.com/containous/traefik/pull/3158) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Fix acme.json file automatic creation ([#3156](https://github.com/containous/traefik/pull/3156) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Minor updates to dumpcerts.sh ([#3116](https://github.com/containous/traefik/pull/3116) by [mathuin](https://github.com/mathuin))
|
||||
- **[acme]** Add TTL and custom Timeout in DigitalOcean DNS provider ([#3143](https://github.com/containous/traefik/pull/3143) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Add ACME certificates only on ACME EntryPoint ([#3136](https://github.com/containous/traefik/pull/3136) by [nmengin](https://github.com/nmengin))
|
||||
- **[consul,docker,ecs,eureka,k8s,kv,marathon,mesos,rancher]** Server weight zero ([#3130](https://github.com/containous/traefik/pull/3130) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Limit label selector to Ingress factory. ([#3137](https://github.com/containous/traefik/pull/3137) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[middleware,consul,consulcatalog,docker,ecs,kv,marathon,mesos,rancher]** Fix: error pages ([#3138](https://github.com/containous/traefik/pull/3138) by [ldez](https://github.com/ldez))
|
||||
- **[webui]** Remove useless ACME tab from UI. ([#3154](https://github.com/containous/traefik/pull/3154) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- **[k8s]** Update kubernetes.md ([#3171](https://github.com/containous/traefik/pull/3171) by [andreyfedoseev](https://github.com/andreyfedoseev))
|
||||
- Update some examples ([#3150](https://github.com/containous/traefik/pull/3150) by [zaporylie](https://github.com/zaporylie))
|
||||
- Normalize parameter names in configs ([#3132](https://github.com/containous/traefik/pull/3132) by [kachkaev](https://github.com/kachkaev))
|
||||
|
||||
**Misc:**
|
||||
- **[oxy]** Disable closeNotify when method GET for http pipelining ([#3108](https://github.com/containous/traefik/pull/3108) by [Juliens](https://github.com/Juliens))
|
||||
|
||||
## [v1.6.0-rc4](https://github.com/containous/traefik/tree/v1.6.0-rc4) (2018-04-04)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.6.0-rc3...v1.6.0-rc4)
|
||||
|
||||
**Enhancements:**
|
||||
- **[consulcatalog,ecs,mesos]** Factorize labels managements. ([#3099](https://github.com/containous/traefik/pull/3099) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Add tests on IPWhiteLister. ([#3106](https://github.com/containous/traefik/pull/3106) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[api,cluster]** Moved /api/cluster/leadership handler under public routes (requires no authentication) ([#3101](https://github.com/containous/traefik/pull/3101) by [aantono](https://github.com/aantono))
|
||||
- **[k8s]** Fixes prefixed annotations support. ([#3110](https://github.com/containous/traefik/pull/3110) by [ldez](https://github.com/ldez))
|
||||
- **[marathon]** Several apps with same backend name in Marathon. ([#3109](https://github.com/containous/traefik/pull/3109) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- **[k8s]** Update kubernetes.md ([#3093](https://github.com/containous/traefik/pull/3093) by [rdrgporto](https://github.com/rdrgporto))
|
||||
- Fixed documentation urls on README.md ([#3102](https://github.com/containous/traefik/pull/3102) by [emir](https://github.com/emir))
|
||||
|
||||
## [v1.6.0-rc3](https://github.com/containous/traefik/tree/v1.6.0-rc3) (2018-03-28)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.6.0-rc2...v1.6.0-rc3)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[docker,rancher]** Frontend rule and segment labels. ([#3091](https://github.com/containous/traefik/pull/3091) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v1.6.0-rc2](https://github.com/containous/traefik/tree/v1.6.0-rc2) (2018-03-27)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.6.0-rc1...v1.6.0-rc2)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Fix panic with wrong ACME configuration ([#3084](https://github.com/containous/traefik/pull/3084) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Fix wildcard match to ACME domains in cluster mode ([#3080](https://github.com/containous/traefik/pull/3080) by [oldmantaiter](https://github.com/oldmantaiter))
|
||||
|
||||
**Documentation:**
|
||||
- **[servicefabric]** Update SF white list documentation section. ([#3082](https://github.com/containous/traefik/pull/3082) by [ldez](https://github.com/ldez))
|
||||
- Fix basic documentation ([#3086](https://github.com/containous/traefik/pull/3086) by [mmatur](https://github.com/mmatur))
|
||||
|
||||
## [v1.6.0-rc1](https://github.com/containous/traefik/tree/v1.6.0-rc1) (2018-03-26)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.5.0-rc1...v1.6.0-rc1)
|
||||
|
||||
**Enhancements:**
|
||||
- **[acme]** Bump Lego Version for GoDaddy DNS Provider ([#2482](https://github.com/containous/traefik/pull/2482) by [sjawhar](https://github.com/sjawhar))
|
||||
- **[acme]** Simplify storing renewed acme certificate ([#2614](https://github.com/containous/traefik/pull/2614) by [ferhatelmas](https://github.com/ferhatelmas))
|
||||
- **[acme]** Delete TLS-SNI-01 challenge from ACME ([#2971](https://github.com/containous/traefik/pull/2971) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** ACME V2 Integration ([#3063](https://github.com/containous/traefik/pull/3063) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Update Lego (Gandi API v5, cloudxns, ...) ([#2844](https://github.com/containous/traefik/pull/2844) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Create ACME Provider ([#2889](https://github.com/containous/traefik/pull/2889) by [nmengin](https://github.com/nmengin))
|
||||
- **[api,cluster]** Added cluster/leader endpoint ([#3009](https://github.com/containous/traefik/pull/3009) by [aantono](https://github.com/aantono))
|
||||
- **[authentication]** Forward Authentication: add X-Forwarded-Uri ([#2398](https://github.com/containous/traefik/pull/2398) by [sebastianbauer](https://github.com/sebastianbauer))
|
||||
- **[boltdb,consul,etcd,kv,zk]** homogenization of templates: KV ([#2661](https://github.com/containous/traefik/pull/2661) by [ldez](https://github.com/ldez))
|
||||
- **[boltdb,consul,etcd,kv,zk]** Add all available configuration to KV Backend ([#2652](https://github.com/containous/traefik/pull/2652) by [ldez](https://github.com/ldez))
|
||||
- **[boltdb,consul,etcd,kv,zk]** Homogenization of the providers (part 1): KV ([#2616](https://github.com/containous/traefik/pull/2616) by [ldez](https://github.com/ldez))
|
||||
- **[consul,consulcatalog]** Homogenization of templates: Consul Catalog ([#2668](https://github.com/containous/traefik/pull/2668) by [ldez](https://github.com/ldez))
|
||||
- **[consul,consulcatalog]** Split consul and consul catalog. ([#2655](https://github.com/containous/traefik/pull/2655) by [ldez](https://github.com/ldez))
|
||||
- **[consulcatalog]** Add all available tags to Consul Catalog Backend ([#2646](https://github.com/containous/traefik/pull/2646) by [ldez](https://github.com/ldez))
|
||||
- **[consulcatalog]** Check for endpoints while detecting Consul service changes ([#2882](https://github.com/containous/traefik/pull/2882) by [caseycs](https://github.com/caseycs))
|
||||
- **[consulcatalog]** TLS Support for ConsulCatalog ([#2900](https://github.com/containous/traefik/pull/2900) by [mmatur](https://github.com/mmatur))
|
||||
- **[docker,docker/swarm]** Fix support for macvlan driver in docker provider ([#2827](https://github.com/containous/traefik/pull/2827) by [mmatur](https://github.com/mmatur))
|
||||
- **[docker,marathon,rancher]** Segments Labels: Rancher & Marathon ([#3073](https://github.com/containous/traefik/pull/3073) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Custom headers by service labels for docker backends ([#2514](https://github.com/containous/traefik/pull/2514) by [Tiscs](https://github.com/Tiscs))
|
||||
- **[docker]** Homogenization of templates: Docker ([#2659](https://github.com/containous/traefik/pull/2659) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Segment labels: Docker ([#3055](https://github.com/containous/traefik/pull/3055) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Add all available labels to Docker Backend ([#2584](https://github.com/containous/traefik/pull/2584) by [ldez](https://github.com/ldez))
|
||||
- **[dynamodb,ecs]** Upgrade AWS SKD to version v1.13.1 ([#2908](https://github.com/containous/traefik/pull/2908) by [mmatur](https://github.com/mmatur))
|
||||
- **[ecs]** Add all available labels to ECS Backend ([#2605](https://github.com/containous/traefik/pull/2605) by [ldez](https://github.com/ldez))
|
||||
- **[ecs]** Homogenization of templates: ECS ([#2663](https://github.com/containous/traefik/pull/2663) by [ldez](https://github.com/ldez))
|
||||
- **[eureka]** Replace Delay by RefreshSecond in Eureka ([#2972](https://github.com/containous/traefik/pull/2972) by [ldez](https://github.com/ldez))
|
||||
- **[eureka]** Homogenization of templates: Eureka ([#2846](https://github.com/containous/traefik/pull/2846) by [ldez](https://github.com/ldez))
|
||||
- **[file]** Added support for templates to file provider ([#2991](https://github.com/containous/traefik/pull/2991) by [aantono](https://github.com/aantono))
|
||||
- **[healthcheck]** Toggle /ping to artificially return unhealthy response on SIGTERM during requestAcceptGraceTimeout interval ([#3062](https://github.com/containous/traefik/pull/3062) by [ravilr](https://github.com/ravilr))
|
||||
- **[healthcheck]** Improve logging output for failing healthchecks ([#2443](https://github.com/containous/traefik/pull/2443) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[k8s,tls]** Add support for fetching k8s Ingress TLS data from secrets ([#2439](https://github.com/containous/traefik/pull/2439) by [gopenguin](https://github.com/gopenguin))
|
||||
- **[k8s]** Bump kubernetes/client-go ([#2848](https://github.com/containous/traefik/pull/2848) by [yue9944882](https://github.com/yue9944882))
|
||||
- **[k8s]** Allow custom value for kubernetes.io/ingress.class annotation ([#2222](https://github.com/containous/traefik/pull/2222) by [yuvipanda](https://github.com/yuvipanda))
|
||||
- **[k8s]** Add app-root annotation support for kubernetes ingress ([#2522](https://github.com/containous/traefik/pull/2522) by [yue9944882](https://github.com/yue9944882))
|
||||
- **[k8s]** Builders in k8s tests ([#2513](https://github.com/containous/traefik/pull/2513) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Add all available annotations to k8s Backend ([#2612](https://github.com/containous/traefik/pull/2612) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Introduce k8s informer factory ([#2867](https://github.com/containous/traefik/pull/2867) by [yue9944882](https://github.com/yue9944882))
|
||||
- **[logs,middleware]** Add access log filter for retry attempts ([#3042](https://github.com/containous/traefik/pull/3042) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[logs,middleware]** Ultimate Access log filter ([#2988](https://github.com/containous/traefik/pull/2988) by [mmatur](https://github.com/mmatur))
|
||||
- **[logs,middleware]** Add username in accesslog ([#2111](https://github.com/containous/traefik/pull/2111) by [bastiaanb](https://github.com/bastiaanb))
|
||||
- **[logs]** Allow overriding the log level in debug mode. ([#3050](https://github.com/containous/traefik/pull/3050) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[logs]** Display file log when test fails. ([#2801](https://github.com/containous/traefik/pull/2801) by [ldez](https://github.com/ldez))
|
||||
- **[marathon]** Remove health check filter from Marathon tasks. ([#2817](https://github.com/containous/traefik/pull/2817) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[marathon]** Add all available labels to Marathon Backend ([#2602](https://github.com/containous/traefik/pull/2602) by [ldez](https://github.com/ldez))
|
||||
- **[marathon]** homogenization of templates: Marathon ([#2665](https://github.com/containous/traefik/pull/2665) by [ldez](https://github.com/ldez))
|
||||
- **[mesos]** Add all available labels to Mesos Backend ([#2687](https://github.com/containous/traefik/pull/2687) by [ldez](https://github.com/ldez))
|
||||
- **[metrics]** Added entrypoint metrics to influxdb ([#2992](https://github.com/containous/traefik/pull/2992) by [adityacs](https://github.com/adityacs))
|
||||
- **[metrics]** Extend metrics and rebuild prometheus exporting logic ([#2567](https://github.com/containous/traefik/pull/2567) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[metrics]** Added missing metrics to registry for DataDog and StatsD ([#2890](https://github.com/containous/traefik/pull/2890) by [aantono](https://github.com/aantono))
|
||||
- **[metrics]** Remove unnecessary conversion ([#2850](https://github.com/containous/traefik/pull/2850) by [ferhatelmas](https://github.com/ferhatelmas))
|
||||
- **[middleware,consul,consulcatalog,docker,ecs,k8s,marathon,mesos,rancher]** New option in secure middleware ([#2958](https://github.com/containous/traefik/pull/2958) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware,consulcatalog,docker,ecs,k8s,kv,marathon,mesos,rancher]** Ability to use "X-Forwarded-For" as a source of IP for white list. ([#3070](https://github.com/containous/traefik/pull/3070) by [ldez](https://github.com/ldez))
|
||||
- **[middleware,docker]** Use pointer of error pages ([#2607](https://github.com/containous/traefik/pull/2607) by [ldez](https://github.com/ldez))
|
||||
- **[middleware,provider]** Redirection: permanent move option. ([#2774](https://github.com/containous/traefik/pull/2774) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Add new options to the CLI entrypoint definition. ([#2799](https://github.com/containous/traefik/pull/2799) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Change port of traefik for error pages integration test ([#2907](https://github.com/containous/traefik/pull/2907) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware]** Request buffering middleware ([#2217](https://github.com/containous/traefik/pull/2217) by [harnash](https://github.com/harnash))
|
||||
- **[middleware]** Remove unnecessary returns in tracing setup ([#2880](https://github.com/containous/traefik/pull/2880) by [ferhatelmas](https://github.com/ferhatelmas))
|
||||
- **[provider]** Homogenization of the providers (part 1) ([#2518](https://github.com/containous/traefik/pull/2518) by [ldez](https://github.com/ldez))
|
||||
- **[provider]** No error pages must return nil. ([#2610](https://github.com/containous/traefik/pull/2610) by [ldez](https://github.com/ldez))
|
||||
- **[rancher]** Homogenization of templates: Rancher ([#2662](https://github.com/containous/traefik/pull/2662) by [ldez](https://github.com/ldez))
|
||||
- **[rancher]** Add all available labels to Rancher Backend ([#2601](https://github.com/containous/traefik/pull/2601) by [ldez](https://github.com/ldez))
|
||||
- **[rules]** Externalize Træfik rules in a dedicated package ([#2933](https://github.com/containous/traefik/pull/2933) by [nmengin](https://github.com/nmengin))
|
||||
- **[servicefabric]** Update Service Fabric backend. ([#3064](https://github.com/containous/traefik/pull/3064) by [ldez](https://github.com/ldez))
|
||||
- **[servicefabric]** Add white list for Service Fabric. ([#3079](https://github.com/containous/traefik/pull/3079) by [ldez](https://github.com/ldez))
|
||||
- **[tls]** Use default entryPoints when certificates are added with no entryPoints. ([#2534](https://github.com/containous/traefik/pull/2534) by [nmengin](https://github.com/nmengin))
|
||||
- **[tracing]** Handle zipkin collector creation ([#2860](https://github.com/containous/traefik/pull/2860) by [ferhatelmas](https://github.com/ferhatelmas))
|
||||
- **[tracing]** Opentracing support ([#2587](https://github.com/containous/traefik/pull/2587) by [mmatur](https://github.com/mmatur))
|
||||
- **[webui]** Add status code text to webui bar chart tooltip ([#2639](https://github.com/containous/traefik/pull/2639) by [wader](https://github.com/wader))
|
||||
- Separate command from the main package ([#2951](https://github.com/containous/traefik/pull/2951) by [Juliens](https://github.com/Juliens))
|
||||
- Use context in Server ([#3007](https://github.com/containous/traefik/pull/3007) by [Juliens](https://github.com/Juliens))
|
||||
- Logger and Leaks ([#2847](https://github.com/containous/traefik/pull/2847) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Check all the C/N and SANs of provided certificates before generating ACME certificates in ACME provider ([#2970](https://github.com/containous/traefik/pull/2970) by [nmengin](https://github.com/nmengin))
|
||||
- **[docker,k8s,marathon]** Fix custom headers template ([#2622](https://github.com/containous/traefik/pull/2622) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Missing annotation prefix support. ([#2915](https://github.com/containous/traefik/pull/2915) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Remove hardcoded frontend prefix in Kubernetes template ([#2914](https://github.com/containous/traefik/pull/2914) by [psalaberria002](https://github.com/psalaberria002))
|
||||
- **[logs,middleware]** Fix bad access log ([#2682](https://github.com/containous/traefik/pull/2682) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware,tracing]** Fix <nil> tracer value in KV ([#2911](https://github.com/containous/traefik/pull/2911) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware]** Use responseModifier to override secure headers ([#2946](https://github.com/containous/traefik/pull/2946) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware]** Correct conditional setting of buffering retry expression. ([#2865](https://github.com/containous/traefik/pull/2865) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Fix high memory usage in retry middleware ([#2740](https://github.com/containous/traefik/pull/2740) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[provider]** Add some missing quotes in templates ([#2973](https://github.com/containous/traefik/pull/2973) by [ldez](https://github.com/ldez))
|
||||
- **[tracing]** Fix missing configuration for jaeger reporter ([#2720](https://github.com/containous/traefik/pull/2720) by [mmatur](https://github.com/mmatur))
|
||||
- **[tracing]** Tracing statusCodeTracker need to implement CloseNotify ([#2733](https://github.com/containous/traefik/pull/2733) by [mmatur](https://github.com/mmatur))
|
||||
- **[tracing]** Fix integration tests in tracing ([#2759](https://github.com/containous/traefik/pull/2759) by [mmatur](https://github.com/mmatur))
|
||||
- Remove unnecessary mutex usage in health checks ([#2726](https://github.com/containous/traefik/pull/2726) by [marco-jantke](https://github.com/marco-jantke))
|
||||
|
||||
**Documentation:**
|
||||
- **[docker]** Add default values for some Docker labels ([#2604](https://github.com/containous/traefik/pull/2604) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Remove web provider in example ([#2807](https://github.com/containous/traefik/pull/2807) by [pigletfly](https://github.com/pigletfly))
|
||||
- **[k8s]** Update traefik-ds.yaml with --api command line parameter ([#2803](https://github.com/containous/traefik/pull/2803) by [maniankara](https://github.com/maniankara))
|
||||
- **[k8s]** Drop capabilities in Kubernetes DaemonSet example ([#3028](https://github.com/containous/traefik/pull/3028) by [nogoegst](https://github.com/nogoegst))
|
||||
- **[k8s]** Docs: Fix typos in k8s user-guide ([#2898](https://github.com/containous/traefik/pull/2898) by [cez81](https://github.com/cez81))
|
||||
- **[k8s]** Change boolean annotation values to string ([#2839](https://github.com/containous/traefik/pull/2839) by [hobti01](https://github.com/hobti01))
|
||||
- **[provider]** Split security labels and custom labels documentation. ([#2872](https://github.com/containous/traefik/pull/2872) by [ldez](https://github.com/ldez))
|
||||
- **[provider]** Remove non-supported label. ([#3065](https://github.com/containous/traefik/pull/3065) by [ldez](https://github.com/ldez))
|
||||
- **[provider]** Remove obsolete paragraph about error pages. ([#2608](https://github.com/containous/traefik/pull/2608) by [ldez](https://github.com/ldez))
|
||||
- **[servicefabric]** Add SF to supported backends in docs ([#3033](https://github.com/containous/traefik/pull/3033) by [lawrencegripper](https://github.com/lawrencegripper))
|
||||
- Fix typo in doc for rate limit label ([#2790](https://github.com/containous/traefik/pull/2790) by [mmatur](https://github.com/mmatur))
|
||||
- Add Tracing entry in the documentation. ([#2713](https://github.com/containous/traefik/pull/2713) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Misc:**
|
||||
- **[boltdb,consul,etcd,kv,zk]** Migrate from libkv to valkeyrie library ([#2743](https://github.com/containous/traefik/pull/2743) by [nmengin](https://github.com/nmengin))
|
||||
- Merge v1.5.4 into master ([#3024](https://github.com/containous/traefik/pull/3024) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.5.3 into master ([#2943](https://github.com/containous/traefik/pull/2943) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.5.2 into master ([#2843](https://github.com/containous/traefik/pull/2843) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.5.1 into master ([#2781](https://github.com/containous/traefik/pull/2781) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.5.0-rc5 into master ([#2708](https://github.com/containous/traefik/pull/2708) by [ldez](https://github.com/ldez))
|
||||
- Merge 1.5.0-rc3 into master ([#2600](https://github.com/containous/traefik/pull/2600) by [ldez](https://github.com/ldez))
|
||||
- Drop unnecessary type conversions ([#2583](https://github.com/containous/traefik/pull/2583) by [ferhatelmas](https://github.com/ferhatelmas))
|
||||
- Merge 1.5.0-rc2 into master ([#2536](https://github.com/containous/traefik/pull/2536) by [ldez](https://github.com/ldez))
|
||||
- Code simplification ([#2516](https://github.com/containous/traefik/pull/2516) by [ferhatelmas](https://github.com/ferhatelmas))
|
||||
|
||||
## [v1.5.4](https://github.com/containous/traefik/tree/v1.5.4) (2018-03-15)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.5.3...v1.5.4)
|
||||
|
||||
|
@@ -13,7 +13,7 @@ You need to run the `binary` target. This will create binaries for Linux platfor
|
||||
$ make binary
|
||||
docker build -t "traefik-dev:no-more-godep-ever" -f build.Dockerfile .
|
||||
Sending build context to Docker daemon 295.3 MB
|
||||
Step 0 : FROM golang:1.9-alpine
|
||||
Step 0 : FROM golang:1.10-alpine
|
||||
---> 8c6473912976
|
||||
Step 1 : RUN go get github.com/golang/dep/cmd/dep
|
||||
[...]
|
||||
|
735
Gopkg.lock
generated
124
Gopkg.toml
@@ -19,8 +19,6 @@
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
|
||||
ignored = ["github.com/sirupsen/logrus"]
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/ArthurHlt/go-eureka-client"
|
||||
@@ -48,7 +46,7 @@ ignored = ["github.com/sirupsen/logrus"]
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/aws/aws-sdk-go"
|
||||
version = "1.6.18"
|
||||
version = "1.13.1"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
@@ -64,11 +62,11 @@ ignored = ["github.com/sirupsen/logrus"]
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/containous/staert"
|
||||
version = "2.1.0"
|
||||
version = "3.1.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/containous/traefik-extra-service-fabric"
|
||||
version = "1.0.6"
|
||||
version = "1.1.5"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/coreos/go-systemd"
|
||||
@@ -79,10 +77,6 @@ ignored = ["github.com/sirupsen/logrus"]
|
||||
name = "github.com/docker/leadership"
|
||||
source = "github.com/containous/leadership"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/docker/libkv"
|
||||
source = "github.com/abronan/libkv"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/eapache/channels"
|
||||
version = "1.1.0"
|
||||
@@ -92,12 +86,26 @@ ignored = ["github.com/sirupsen/logrus"]
|
||||
name = "github.com/elazarl/go-bindata-assetfs"
|
||||
|
||||
[[constraint]]
|
||||
branch = "fork-containous"
|
||||
name = "github.com/go-check/check"
|
||||
source = "github.com/containous/check"
|
||||
|
||||
[[override]]
|
||||
branch = "fork-containous"
|
||||
name = "github.com/go-check/check"
|
||||
source = "github.com/containous/check"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/go-kit/kit"
|
||||
version = "0.3.0"
|
||||
version = "0.7.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/gorilla/websocket"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/hashicorp/consul"
|
||||
version = "1.0.6"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/influxdata/influxdb"
|
||||
@@ -108,8 +116,8 @@ ignored = ["github.com/sirupsen/logrus"]
|
||||
name = "github.com/jjcollinge/servicefabric"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/mattn/go-shellwords"
|
||||
version = "1.0.3"
|
||||
branch = "master"
|
||||
name = "github.com/abronan/valkeyrie"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/mesosphere/mesos-dns"
|
||||
@@ -128,8 +136,13 @@ ignored = ["github.com/sirupsen/logrus"]
|
||||
name = "github.com/mitchellh/mapstructure"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/opentracing/opentracing-go"
|
||||
version = "1.0.2"
|
||||
|
||||
[[constraint]]
|
||||
branch = "containous-fork"
|
||||
name = "github.com/rancher/go-rancher-metadata"
|
||||
source = "github.com/containous/go-rancher-metadata"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
@@ -143,18 +156,34 @@ ignored = ["github.com/sirupsen/logrus"]
|
||||
branch = "master"
|
||||
name = "github.com/stvp/go-udp-testing"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/stretchr/testify"
|
||||
version = "1.2.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/uber/jaeger-client-go"
|
||||
version = "2.9.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/uber/jaeger-lib"
|
||||
version = "1.1.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "v1"
|
||||
name = "github.com/unrolled/secure"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/vdemeester/shakers"
|
||||
version = "0.1.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "containous-fork"
|
||||
branch = "master"
|
||||
name = "github.com/vulcand/oxy"
|
||||
source = "https://github.com/containous/oxy.git"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/xenolf/lego"
|
||||
version = "0.4.1"
|
||||
# version = "1.0.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "google.golang.org/grpc"
|
||||
@@ -167,39 +196,62 @@ ignored = ["github.com/sirupsen/logrus"]
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/client-go"
|
||||
version = "2.0.0"
|
||||
version = "6.0.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/api"
|
||||
version = "kubernetes-1.9.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/apimachinery"
|
||||
version = "kubernetes-1.9.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/libkermit/docker"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/libkermit/docker-check"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/libkermit/compose"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/docker/docker"
|
||||
revision = "7848b8beb9d38a98a78b75f78e05f8d2255f9dfe"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/docker/docker"
|
||||
revision = "7848b8beb9d38a98a78b75f78e05f8d2255f9dfe"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/docker/cli"
|
||||
revision = "6b63d7b96a41055baddc3fa71f381c7f60bd5d8e"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/docker/distribution"
|
||||
revision = "edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c"
|
||||
|
||||
[[override]]
|
||||
branch = "master"
|
||||
name = "github.com/docker/libcompose"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/Nvveen/Gotty"
|
||||
revision = "6018b68f96b839edfbe3fb48668853f5dbad88a3"
|
||||
revision = "a8b993ba6abdb0e0c12b0125c603323a71c7790c"
|
||||
source = "github.com/ijc25/Gotty"
|
||||
|
||||
[[override]]
|
||||
# always keep this override
|
||||
# ALWAYS keep this override
|
||||
name = "github.com/mailgun/timetools"
|
||||
revision = "7e6055773c5137efbeb3bd2410d705fe10ab6bfd"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/vulcand/predicate"
|
||||
revision = "19b9dde14240d94c804ae5736ad0e1de10bf8fe6"
|
||||
|
||||
[[override]]
|
||||
# remove override on master
|
||||
name = "github.com/coreos/bbolt"
|
||||
revision = "32c383e75ce054674c53b5a07e55de85332aee14"
|
||||
|
||||
[[override]]
|
||||
branch = "master"
|
||||
name = "github.com/miekg/dns"
|
||||
|
||||
[[override]]
|
||||
name = "golang.org/x/crypto"
|
||||
revision = "b080dc9a8c480b08e698fb1219160d598526310f"
|
||||
|
||||
[[override]]
|
||||
name = "golang.org/x/net"
|
||||
revision = "894f8ed5849b15b810ae41e9590a0d05395bba27"
|
||||
|
||||
[prune]
|
||||
non-go = true
|
||||
go-tests = true
|
||||
|
2
Makefile
@@ -73,7 +73,7 @@ test-integration: build ## run the integration tests
|
||||
$(DOCKER_RUN_TRAEFIK) ./script/make.sh generate binary test-integration
|
||||
TEST_HOST=1 ./script/make.sh test-integration
|
||||
|
||||
validate: build ## validate gofmt, golint and go vet
|
||||
validate: build ## validate code, vendor and autogen
|
||||
$(DOCKER_RUN_TRAEFIK) ./script/make.sh validate-gofmt validate-govet validate-golint validate-misspell validate-vendor validate-autogen
|
||||
|
||||
build: dist
|
||||
|
30
README.md
@@ -14,7 +14,7 @@
|
||||
|
||||
Træfik is a modern HTTP reverse proxy and load balancer that makes deploying microservices easy.
|
||||
Træfik integrates with your existing infrastructure components ([Docker](https://www.docker.com/), [Swarm mode](https://docs.docker.com/engine/swarm/), [Kubernetes](https://kubernetes.io), [Marathon](https://mesosphere.github.io/marathon/), [Consul](https://www.consul.io/), [Etcd](https://coreos.com/etcd/), [Rancher](https://rancher.com), [Amazon ECS](https://aws.amazon.com/ecs), ...) and configures itself automatically and dynamically.
|
||||
Telling Træfik where your orchestrator is could be the _only_ configuration step you need to do.
|
||||
Pointing Træfik at your orchestrator should be the _only_ configuration step you need.
|
||||
|
||||
---
|
||||
|
||||
@@ -56,32 +56,32 @@ _(But if you'd rather configure some of your routes manually, Træfik supports t
|
||||
|
||||
- Continuously updates its configuration (No restarts!)
|
||||
- Supports multiple load balancing algorithms
|
||||
- Provides HTTPS to your microservices by leveraging [Let's Encrypt](https://letsencrypt.org)
|
||||
- Provides HTTPS to your microservices by leveraging [Let's Encrypt](https://letsencrypt.org) (wildcard certificates support)
|
||||
- Circuit breakers, retry
|
||||
- High Availability with cluster mode (beta)
|
||||
- See the magic through its clean web UI
|
||||
- Websocket, HTTP/2, GRPC ready
|
||||
- Provides metrics (Rest, Prometheus, Datadog, Statsd, InfluxDB)
|
||||
- Keeps access logs (JSON, CLF)
|
||||
- [Fast](https://docs.traefik.io/benchmarks) ... which is nice
|
||||
- Fast
|
||||
- Exposes a Rest API
|
||||
- Packaged as a single binary file (made with :heart: with go) and available as a [tiny](https://microbadger.com/images/traefik) [official](https://hub.docker.com/r/_/traefik/) docker image
|
||||
|
||||
|
||||
## Supported Backends
|
||||
|
||||
- [Docker](docs/configuration/backends/docker/) / [Swarm mode](docs/configuration/backends/docker/#docker-swarm-mode)
|
||||
- [Kubernetes](docs/configuration/backends/kubernetes/)
|
||||
- [Mesos](docs/configuration/backends/mesos/) / [Marathon](docs/configuration/backends/marathon/)
|
||||
- [Rancher](docs/configuration/backends/rancher/) (API, Metadata)
|
||||
- [Azure Service Fabric](docs/configuration/backends/servicefabric/)
|
||||
- [Consul Catalog](docs/configuration/backends/consulcatalog/)
|
||||
- [Consul](docs/configuration/backends/consul/) / [Etcd](docs/configuration/backends/etcd/) / [Zookeeper](docs/configuration/backends/zookeeper/) / [BoltDB](docs/configuration/backends/boltdb/)
|
||||
- [Eureka](docs/configuration/backends/eureka/)
|
||||
- [Amazon ECS](docs/configuration/backends/ecs/)
|
||||
- [Amazon DynamoDB](docs/configuration/backends/dynamodb/)
|
||||
- [File](docs/configuration/backends/file/)
|
||||
- [Rest](docs/configuration/backends/rest/)
|
||||
- [Docker](https://docs.traefik.io/configuration/backends/docker) / [Swarm mode](https://docs.traefik.io/configuration/backends/docker#docker-swarm-mode)
|
||||
- [Kubernetes](https://docs.traefik.io/configuration/backends/kubernetes)
|
||||
- [Mesos](https://docs.traefik.io/configuration/backends/mesos) / [Marathon](https://docs.traefik.io/configuration/backends/marathon)
|
||||
- [Rancher](https://docs.traefik.io/configuration/backends/rancher) (API, Metadata)
|
||||
- [Azure Service Fabric](https://docs.traefik.io/configuration/backends/servicefabric)
|
||||
- [Consul Catalog](https://docs.traefik.io/configuration/backends/consulcatalog)
|
||||
- [Consul](https://docs.traefik.io/configuration/backends/consul) / [Etcd](https://docs.traefik.io/configuration/backends/etcd) / [Zookeeper](https://docs.traefik.io/configuration/backends/zookeeper) / [BoltDB](https://docs.traefik.io/configuration/backends/boltdb)
|
||||
- [Eureka](https://docs.traefik.io/configuration/backends/eureka)
|
||||
- [Amazon ECS](https://docs.traefik.io/configuration/backends/ecs)
|
||||
- [Amazon DynamoDB](https://docs.traefik.io/configuration/backends/dynamodb)
|
||||
- [File](https://docs.traefik.io/configuration/backends/file)
|
||||
- [Rest](https://docs.traefik.io/configuration/backends/rest)
|
||||
|
||||
## Quickstart
|
||||
|
||||
|
@@ -8,12 +8,15 @@ import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
acmeprovider "github.com/containous/traefik/provider/acme"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/xenolf/lego/acme"
|
||||
)
|
||||
|
||||
@@ -34,13 +37,18 @@ type ChallengeCert struct {
|
||||
certificate *tls.Certificate
|
||||
}
|
||||
|
||||
// Init inits account struct
|
||||
// Init account struct
|
||||
func (a *Account) Init() error {
|
||||
err := a.DomainsCertificate.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = a.RemoveAccountV1Values()
|
||||
if err != nil {
|
||||
log.Errorf("Unable to remove ACME Account V1 values during account initialization: %v", err)
|
||||
}
|
||||
|
||||
for _, cert := range a.ChallengeCerts {
|
||||
if cert.certificate == nil {
|
||||
certificate, err := tls.X509KeyPair(cert.Certificate, cert.PrivateKey)
|
||||
@@ -49,6 +57,7 @@ func (a *Account) Init() error {
|
||||
}
|
||||
cert.certificate = &certificate
|
||||
}
|
||||
|
||||
if cert.certificate.Leaf == nil {
|
||||
leaf, err := x509.ParseCertificate(cert.certificate.Certificate[0])
|
||||
if err != nil {
|
||||
@@ -61,14 +70,19 @@ func (a *Account) Init() error {
|
||||
}
|
||||
|
||||
// NewAccount creates an account
|
||||
func NewAccount(email string) (*Account, error) {
|
||||
func NewAccount(email string, certs []*DomainsCertificate) (*Account, error) {
|
||||
// Create a user. New accounts need an email and private key to start
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
domainsCerts := DomainsCertificates{Certs: []*DomainsCertificate{}}
|
||||
domainsCerts.Init()
|
||||
|
||||
domainsCerts := DomainsCertificates{Certs: certs}
|
||||
err = domainsCerts.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Account{
|
||||
Email: email,
|
||||
PrivateKey: x509.MarshalPKCS1PrivateKey(privateKey),
|
||||
@@ -91,10 +105,34 @@ func (a *Account) GetPrivateKey() crypto.PrivateKey {
|
||||
if privateKey, err := x509.ParsePKCS1PrivateKey(a.PrivateKey); err == nil {
|
||||
return privateKey
|
||||
}
|
||||
|
||||
log.Errorf("Cannot unmarshall private key %+v", a.PrivateKey)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveAccountV1Values removes ACME account V1 values
|
||||
func (a *Account) RemoveAccountV1Values() error {
|
||||
// Check if ACME Account is in ACME V1 format
|
||||
if a.Registration != nil {
|
||||
isOldRegistration, err := regexp.MatchString(acmeprovider.RegistrationURLPathV1Regexp, a.Registration.URI)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isOldRegistration {
|
||||
a.reset()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Account) reset() {
|
||||
log.Debug("Reset ACME account object.")
|
||||
a.Email = ""
|
||||
a.Registration = nil
|
||||
a.PrivateKey = nil
|
||||
}
|
||||
|
||||
// Certificate is used to store certificate info
|
||||
type Certificate struct {
|
||||
Domain string
|
||||
@@ -122,9 +160,11 @@ func (dc *DomainsCertificates) Less(i, j int) bool {
|
||||
if reflect.DeepEqual(dc.Certs[i].Domains, dc.Certs[j].Domains) {
|
||||
return dc.Certs[i].tlsCert.Leaf.NotAfter.After(dc.Certs[j].tlsCert.Leaf.NotAfter)
|
||||
}
|
||||
|
||||
if dc.Certs[i].Domains.Main == dc.Certs[j].Domains.Main {
|
||||
return strings.Join(dc.Certs[i].Domains.SANs, ",") < strings.Join(dc.Certs[j].Domains.SANs, ",")
|
||||
}
|
||||
|
||||
return dc.Certs[i].Domains.Main < dc.Certs[j].Domains.Main
|
||||
}
|
||||
|
||||
@@ -142,29 +182,46 @@ func (dc *DomainsCertificates) removeDuplicates() {
|
||||
}
|
||||
}
|
||||
|
||||
// Init inits DomainsCertificates
|
||||
func (dc *DomainsCertificates) removeEmpty() {
|
||||
certs := []*DomainsCertificate{}
|
||||
for _, cert := range dc.Certs {
|
||||
if cert.Certificate != nil && len(cert.Certificate.Certificate) > 0 && len(cert.Certificate.PrivateKey) > 0 {
|
||||
certs = append(certs, cert)
|
||||
}
|
||||
}
|
||||
dc.Certs = certs
|
||||
}
|
||||
|
||||
// Init DomainsCertificates
|
||||
func (dc *DomainsCertificates) Init() error {
|
||||
dc.lock.Lock()
|
||||
defer dc.lock.Unlock()
|
||||
|
||||
dc.removeEmpty()
|
||||
|
||||
for _, domainsCertificate := range dc.Certs {
|
||||
tlsCert, err := tls.X509KeyPair(domainsCertificate.Certificate.Certificate, domainsCertificate.Certificate.PrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
domainsCertificate.tlsCert = &tlsCert
|
||||
|
||||
if domainsCertificate.tlsCert.Leaf == nil {
|
||||
leaf, err := x509.ParseCertificate(domainsCertificate.tlsCert.Certificate[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
domainsCertificate.tlsCert.Leaf = leaf
|
||||
}
|
||||
}
|
||||
|
||||
dc.removeDuplicates()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) renewCertificates(acmeCert *Certificate, domain Domain) error {
|
||||
func (dc *DomainsCertificates) renewCertificates(acmeCert *Certificate, domain types.Domain) error {
|
||||
dc.lock.Lock()
|
||||
defer dc.lock.Unlock()
|
||||
|
||||
@@ -174,15 +231,17 @@ func (dc *DomainsCertificates) renewCertificates(acmeCert *Certificate, domain D
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
domainsCertificate.Certificate = acmeCert
|
||||
domainsCertificate.tlsCert = &tlsCert
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("certificate to renew not found for domain %s", domain.Main)
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) addCertificateForDomains(acmeCert *Certificate, domain Domain) (*DomainsCertificate, error) {
|
||||
func (dc *DomainsCertificates) addCertificateForDomains(acmeCert *Certificate, domain types.Domain) (*DomainsCertificate, error) {
|
||||
dc.lock.Lock()
|
||||
defer dc.lock.Unlock()
|
||||
|
||||
@@ -190,6 +249,7 @@ func (dc *DomainsCertificates) addCertificateForDomains(acmeCert *Certificate, d
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cert := DomainsCertificate{Domains: domain, Certificate: acmeCert, tlsCert: &tlsCert}
|
||||
dc.Certs = append(dc.Certs, &cert)
|
||||
return &cert, nil
|
||||
@@ -198,11 +258,12 @@ func (dc *DomainsCertificates) addCertificateForDomains(acmeCert *Certificate, d
|
||||
func (dc *DomainsCertificates) getCertificateForDomain(domainToFind string) (*DomainsCertificate, bool) {
|
||||
dc.lock.RLock()
|
||||
defer dc.lock.RUnlock()
|
||||
|
||||
for _, domainsCertificate := range dc.Certs {
|
||||
domains := []string{}
|
||||
domains = append(domains, domainsCertificate.Domains.Main)
|
||||
domains = append(domains, domainsCertificate.Domains.SANs...)
|
||||
for _, domain := range domains {
|
||||
for _, domain := range domainsCertificate.Domains.ToStrArray() {
|
||||
if strings.HasPrefix(domain, "*.") && types.MatchDomain(domainToFind, domain) {
|
||||
return domainsCertificate, true
|
||||
}
|
||||
if domain == domainToFind {
|
||||
return domainsCertificate, true
|
||||
}
|
||||
@@ -211,9 +272,10 @@ func (dc *DomainsCertificates) getCertificateForDomain(domainToFind string) (*Do
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) exists(domainToFind Domain) (*DomainsCertificate, bool) {
|
||||
func (dc *DomainsCertificates) exists(domainToFind types.Domain) (*DomainsCertificate, bool) {
|
||||
dc.lock.RLock()
|
||||
defer dc.lock.RUnlock()
|
||||
|
||||
for _, domainsCertificate := range dc.Certs {
|
||||
if reflect.DeepEqual(domainToFind, domainsCertificate.Domains) {
|
||||
return domainsCertificate, true
|
||||
@@ -224,16 +286,18 @@ func (dc *DomainsCertificates) exists(domainToFind Domain) (*DomainsCertificate,
|
||||
|
||||
func (dc *DomainsCertificates) toDomainsMap() map[string]*tls.Certificate {
|
||||
domainsCertificatesMap := make(map[string]*tls.Certificate)
|
||||
|
||||
for _, domainCertificate := range dc.Certs {
|
||||
certKey := domainCertificate.Domains.Main
|
||||
|
||||
if domainCertificate.Domains.SANs != nil {
|
||||
sort.Strings(domainCertificate.Domains.SANs)
|
||||
|
||||
for _, dnsName := range domainCertificate.Domains.SANs {
|
||||
if dnsName != domainCertificate.Domains.Main {
|
||||
certKey += fmt.Sprintf(",%s", dnsName)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
domainsCertificatesMap[certKey] = domainCertificate.tlsCert
|
||||
}
|
||||
@@ -242,7 +306,7 @@ func (dc *DomainsCertificates) toDomainsMap() map[string]*tls.Certificate {
|
||||
|
||||
// DomainsCertificate contains a certificate for multiple domains
|
||||
type DomainsCertificate struct {
|
||||
Domains Domain
|
||||
Domains types.Domain
|
||||
Certificate *Certificate
|
||||
tlsCert *tls.Certificate
|
||||
}
|
||||
@@ -254,8 +318,9 @@ func (dc *DomainsCertificate) needRenew() bool {
|
||||
// If there's an error, we assume the cert is broken, and needs update
|
||||
return true
|
||||
}
|
||||
|
||||
// <= 30 days left, renew certificate
|
||||
if crt.NotAfter.Before(time.Now().Add(time.Duration(24 * 30 * time.Hour))) {
|
||||
if crt.NotAfter.Before(time.Now().Add(24 * 30 * time.Hour)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
461
acme/acme.go
@@ -9,8 +9,7 @@ import (
|
||||
fmtlog "log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -21,12 +20,15 @@ import (
|
||||
"github.com/containous/staert"
|
||||
"github.com/containous/traefik/cluster"
|
||||
"github.com/containous/traefik/log"
|
||||
acmeprovider "github.com/containous/traefik/provider/acme"
|
||||
"github.com/containous/traefik/safe"
|
||||
traefikTls "github.com/containous/traefik/tls"
|
||||
"github.com/containous/traefik/tls/generate"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/containous/traefik/version"
|
||||
"github.com/eapache/channels"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/xenolf/lego/acme"
|
||||
legolog "github.com/xenolf/lego/log"
|
||||
"github.com/xenolf/lego/providers/dns"
|
||||
)
|
||||
|
||||
@@ -36,24 +38,24 @@ var (
|
||||
)
|
||||
|
||||
// ACME allows to connect to lets encrypt and retrieve certs
|
||||
// Deprecated Please use provider/acme/Provider
|
||||
type ACME struct {
|
||||
Email string `description:"Email address used for registration"`
|
||||
Domains []Domain `description:"SANs (alternative domains) to each main domain using format: --acme.domains='main.com,san1.com,san2.com' --acme.domains='main.net,san1.net,san2.net'"`
|
||||
Storage string `description:"File or key used for certificates storage."`
|
||||
StorageFile string // deprecated
|
||||
OnDemand bool `description:"Enable on demand certificate generation. This will request a certificate from Let's Encrypt during the first TLS handshake for a hostname that does not yet have a certificate."` //deprecated
|
||||
OnHostRule bool `description:"Enable certificate generation on frontends Host rules."`
|
||||
CAServer string `description:"CA server to use."`
|
||||
EntryPoint string `description:"Entrypoint to proxy acme challenge to."`
|
||||
DNSChallenge *DNSChallenge `description:"Activate DNS-01 Challenge"`
|
||||
HTTPChallenge *HTTPChallenge `description:"Activate HTTP-01 Challenge"`
|
||||
DNSProvider string `description:"Use a DNS-01 acme challenge rather than TLS-SNI-01 challenge."` // deprecated
|
||||
DelayDontCheckDNS flaeg.Duration `description:"Assume DNS propagates after a delay in seconds rather than finding and querying nameservers."` // deprecated
|
||||
ACMELogging bool `description:"Enable debug logging of ACME actions."`
|
||||
Email string `description:"Email address used for registration"`
|
||||
Domains []types.Domain `description:"SANs (alternative domains) to each main domain using format: --acme.domains='main.com,san1.com,san2.com' --acme.domains='main.net,san1.net,san2.net'"`
|
||||
Storage string `description:"File or key used for certificates storage."`
|
||||
StorageFile string // Deprecated
|
||||
OnDemand bool `description:"(Deprecated) Enable on demand certificate generation. This will request a certificate from Let's Encrypt during the first TLS handshake for a hostname that does not yet have a certificate."` // Deprecated
|
||||
OnHostRule bool `description:"Enable certificate generation on frontends Host rules."`
|
||||
CAServer string `description:"CA server to use."`
|
||||
EntryPoint string `description:"Entrypoint to proxy acme challenge to."`
|
||||
DNSChallenge *acmeprovider.DNSChallenge `description:"Activate DNS-01 Challenge"`
|
||||
HTTPChallenge *acmeprovider.HTTPChallenge `description:"Activate HTTP-01 Challenge"`
|
||||
DNSProvider string `description:"(Deprecated) Activate DNS-01 Challenge"` // Deprecated
|
||||
DelayDontCheckDNS flaeg.Duration `description:"(Deprecated) Assume DNS propagates after a delay in seconds rather than finding and querying nameservers."` // Deprecated
|
||||
ACMELogging bool `description:"Enable debug logging of ACME actions."`
|
||||
client *acme.Client
|
||||
defaultCertificate *tls.Certificate
|
||||
store cluster.Store
|
||||
challengeTLSProvider *challengeTLSProvider
|
||||
challengeHTTPProvider *challengeHTTPProvider
|
||||
checkOnDemandDomain func(domain string) bool
|
||||
jobs *channels.InfiniteChannel
|
||||
@@ -61,83 +63,21 @@ type ACME struct {
|
||||
dynamicCerts *safe.Safe
|
||||
}
|
||||
|
||||
// DNSChallenge contains DNS challenge Configuration
|
||||
type DNSChallenge struct {
|
||||
Provider string `description:"Use a DNS-01 based challenge provider rather than HTTPS."`
|
||||
DelayBeforeCheck flaeg.Duration `description:"Assume DNS propagates after a delay in seconds rather than finding and querying nameservers."`
|
||||
}
|
||||
|
||||
// HTTPChallenge contains HTTP challenge Configuration
|
||||
type HTTPChallenge struct {
|
||||
EntryPoint string `description:"HTTP challenge EntryPoint"`
|
||||
}
|
||||
|
||||
//Domains parse []Domain
|
||||
type Domains []Domain
|
||||
|
||||
//Set []Domain
|
||||
func (ds *Domains) Set(str string) error {
|
||||
fargs := func(c rune) bool {
|
||||
return c == ',' || c == ';'
|
||||
}
|
||||
// get function
|
||||
slice := strings.FieldsFunc(str, fargs)
|
||||
if len(slice) < 1 {
|
||||
return fmt.Errorf("Parse error ACME.Domain. Imposible to parse %s", str)
|
||||
}
|
||||
d := Domain{
|
||||
Main: slice[0],
|
||||
SANs: []string{},
|
||||
}
|
||||
if len(slice) > 1 {
|
||||
d.SANs = slice[1:]
|
||||
}
|
||||
*ds = append(*ds, d)
|
||||
return nil
|
||||
}
|
||||
|
||||
//Get []Domain
|
||||
func (ds *Domains) Get() interface{} { return []Domain(*ds) }
|
||||
|
||||
//String returns []Domain in string
|
||||
func (ds *Domains) String() string { return fmt.Sprintf("%+v", *ds) }
|
||||
|
||||
//SetValue sets []Domain into the parser
|
||||
func (ds *Domains) SetValue(val interface{}) {
|
||||
*ds = Domains(val.([]Domain))
|
||||
}
|
||||
|
||||
// Domain holds a domain name with SANs
|
||||
type Domain struct {
|
||||
Main string
|
||||
SANs []string
|
||||
}
|
||||
|
||||
func (a *ACME) init() error {
|
||||
// FIXME temporary fix, waiting for https://github.com/xenolf/lego/pull/478
|
||||
acme.HTTPClient = http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 15 * time.Second,
|
||||
ResponseHeaderTimeout: 15 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
},
|
||||
}
|
||||
acme.UserAgent = fmt.Sprintf("containous-traefik/%s", version.Version)
|
||||
|
||||
if a.ACMELogging {
|
||||
acme.Logger = fmtlog.New(os.Stderr, "legolog: ", fmtlog.LstdFlags)
|
||||
legolog.Logger = fmtlog.New(log.WriterLevel(logrus.DebugLevel), "legolog: ", 0)
|
||||
} else {
|
||||
acme.Logger = fmtlog.New(ioutil.Discard, "", 0)
|
||||
legolog.Logger = fmtlog.New(ioutil.Discard, "", 0)
|
||||
}
|
||||
|
||||
// no certificates in TLS config, so we add a default one
|
||||
cert, err := generate.DefaultCertificate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.defaultCertificate = cert
|
||||
|
||||
a.jobs = channels.NewInfiniteChannel()
|
||||
@@ -211,7 +151,6 @@ func (a *ACME) CreateClusterConfig(leadership *cluster.Leadership, tlsConfig *tl
|
||||
}
|
||||
|
||||
a.store = datastore
|
||||
a.challengeTLSProvider = &challengeTLSProvider{store: a.store}
|
||||
|
||||
ticker := time.NewTicker(24 * time.Hour)
|
||||
leadership.Pool.AddGoCtx(func(ctx context.Context) {
|
||||
@@ -237,20 +176,34 @@ func (a *ACME) leadershipListener(elected bool) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
transaction, object, err := a.store.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
account := object.(*Account)
|
||||
account.Init()
|
||||
// Reset Account values if caServer changed, thus registration URI can be updated
|
||||
if account != nil && account.Registration != nil && !strings.HasPrefix(account.Registration.URI, a.CAServer) {
|
||||
account.reset()
|
||||
}
|
||||
|
||||
var needRegister bool
|
||||
if account == nil || len(account.Email) == 0 {
|
||||
account, err = NewAccount(a.Email)
|
||||
domainsCerts := DomainsCertificates{Certs: []*DomainsCertificate{}}
|
||||
if account != nil {
|
||||
domainsCerts = account.DomainsCertificate
|
||||
}
|
||||
|
||||
account, err = NewAccount(a.Email, domainsCerts.Certs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
needRegister = true
|
||||
}
|
||||
|
||||
a.client, err = a.buildACMEClient(account)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -258,29 +211,15 @@ func (a *ACME) leadershipListener(elected bool) error {
|
||||
if needRegister {
|
||||
// New users will need to register; be sure to save it
|
||||
log.Debug("Register...")
|
||||
reg, err := a.client.Register()
|
||||
|
||||
reg, err := a.client.Register(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
account.Registration = reg
|
||||
}
|
||||
// The client has a URL to the current Let's Encrypt Subscriber
|
||||
// Agreement. The user will need to agree to it.
|
||||
log.Debug("AgreeToTOS...")
|
||||
err = a.client.AgreeToTOS()
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
// Let's Encrypt Subscriber Agreement renew ?
|
||||
reg, err := a.client.QueryRegistration()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
account.Registration = reg
|
||||
err = a.client.AgreeToTOS()
|
||||
if err != nil {
|
||||
log.Errorf("Error sending ACME agreement to TOS: %+v: %s", account, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
err = transaction.Commit(account)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -293,104 +232,6 @@ func (a *ACME) leadershipListener(elected bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateLocalConfig creates a tls.config using local ACME configuration
|
||||
func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, certs *safe.Safe, checkOnDemandDomain func(domain string) bool) error {
|
||||
defer a.runJobs()
|
||||
err := a.init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(a.Storage) == 0 {
|
||||
return errors.New("Empty Store, please provide a filename for certs storage")
|
||||
}
|
||||
a.checkOnDemandDomain = checkOnDemandDomain
|
||||
a.dynamicCerts = certs
|
||||
tlsConfig.Certificates = append(tlsConfig.Certificates, *a.defaultCertificate)
|
||||
tlsConfig.GetCertificate = a.getCertificate
|
||||
a.TLSConfig = tlsConfig
|
||||
localStore := NewLocalStore(a.Storage)
|
||||
a.store = localStore
|
||||
a.challengeTLSProvider = &challengeTLSProvider{store: a.store}
|
||||
|
||||
var needRegister bool
|
||||
var account *Account
|
||||
|
||||
if fileInfo, fileErr := os.Stat(a.Storage); fileErr == nil && fileInfo.Size() != 0 {
|
||||
log.Info("Loading ACME Account...")
|
||||
// load account
|
||||
object, err := localStore.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
account = object.(*Account)
|
||||
} else {
|
||||
log.Info("Generating ACME Account...")
|
||||
account, err = NewAccount(a.Email)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
needRegister = true
|
||||
}
|
||||
|
||||
a.client, err = a.buildACMEClient(account)
|
||||
if err != nil {
|
||||
log.Errorf(`Failed to build ACME client: %s
|
||||
Let's Encrypt functionality will be limited until Traefik is restarted.`, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if needRegister {
|
||||
// New users will need to register; be sure to save it
|
||||
log.Info("Register...")
|
||||
reg, err := a.client.Register()
|
||||
if err != nil {
|
||||
log.Errorf(`Failed to register user: %s
|
||||
Let's Encrypt functionality will be limited until Traefik is restarted.`, err)
|
||||
return nil
|
||||
}
|
||||
account.Registration = reg
|
||||
}
|
||||
|
||||
// The client has a URL to the current Let's Encrypt Subscriber
|
||||
// Agreement. The user will need to agree to it.
|
||||
log.Debug("AgreeToTOS...")
|
||||
err = a.client.AgreeToTOS()
|
||||
if err != nil {
|
||||
// Let's Encrypt Subscriber Agreement renew ?
|
||||
reg, err := a.client.QueryRegistration()
|
||||
if err != nil {
|
||||
log.Errorf(`Failed to renew subscriber agreement: %s
|
||||
Let's Encrypt functionality will be limited until Traefik is restarted.`, err)
|
||||
return nil
|
||||
}
|
||||
account.Registration = reg
|
||||
err = a.client.AgreeToTOS()
|
||||
if err != nil {
|
||||
log.Errorf("Error sending ACME agreement to TOS: %+v: %s", account, err.Error())
|
||||
}
|
||||
}
|
||||
// save account
|
||||
transaction, _, err := a.store.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = transaction.Commit(account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.retrieveCertificates()
|
||||
a.renewCertificates()
|
||||
|
||||
ticker := time.NewTicker(24 * time.Hour)
|
||||
safe.Go(func() {
|
||||
for range ticker.C {
|
||||
a.renewCertificates()
|
||||
}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *ACME) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
domain := types.CanonicalDomain(clientHello.ServerName)
|
||||
account := a.store.Get().(*Account)
|
||||
@@ -399,10 +240,6 @@ func (a *ACME) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificat
|
||||
return providedCertificate, nil
|
||||
}
|
||||
|
||||
if challengeCert, ok := a.challengeTLSProvider.getCertificate(domain); ok {
|
||||
log.Debugf("ACME got challenge %s", domain)
|
||||
return challengeCert, nil
|
||||
}
|
||||
if domainCert, ok := account.DomainsCertificate.getCertificateForDomain(domain); ok {
|
||||
log.Debugf("ACME got domain cert %s", domain)
|
||||
return domainCert.tlsCert, nil
|
||||
@@ -420,36 +257,50 @@ func (a *ACME) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificat
|
||||
func (a *ACME) retrieveCertificates() {
|
||||
a.jobs.In() <- func() {
|
||||
log.Info("Retrieving ACME certificates...")
|
||||
for _, domain := range a.Domains {
|
||||
|
||||
a.deleteUnnecessaryDomains()
|
||||
|
||||
for i := 0; i < len(a.Domains); i++ {
|
||||
domain := a.Domains[i]
|
||||
|
||||
// check if cert isn't already loaded
|
||||
account := a.store.Get().(*Account)
|
||||
if _, exists := account.DomainsCertificate.exists(domain); !exists {
|
||||
domains := []string{}
|
||||
var domains []string
|
||||
domains = append(domains, domain.Main)
|
||||
domains = append(domains, domain.SANs...)
|
||||
domains, err := a.getValidDomains(domains, true)
|
||||
if err != nil {
|
||||
log.Errorf("Error validating ACME certificate for domain %q: %s", domains, err)
|
||||
continue
|
||||
}
|
||||
|
||||
certificateResource, err := a.getDomainsCertificates(domains)
|
||||
if err != nil {
|
||||
log.Errorf("Error getting ACME certificate for domain %s: %s", domains, err.Error())
|
||||
log.Errorf("Error getting ACME certificate for domain %q: %s", domains, err)
|
||||
continue
|
||||
}
|
||||
|
||||
transaction, object, err := a.store.Begin()
|
||||
if err != nil {
|
||||
log.Errorf("Error creating ACME store transaction from domain %s: %s", domain, err.Error())
|
||||
log.Errorf("Error creating ACME store transaction from domain %q: %s", domain, err)
|
||||
continue
|
||||
}
|
||||
|
||||
account = object.(*Account)
|
||||
_, err = account.DomainsCertificate.addCertificateForDomains(certificateResource, domain)
|
||||
if err != nil {
|
||||
log.Errorf("Error adding ACME certificate for domain %s: %s", domains, err.Error())
|
||||
log.Errorf("Error adding ACME certificate for domain %q: %s", domains, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if err = transaction.Commit(account); err != nil {
|
||||
log.Errorf("Error Saving ACME account %+v: %s", account, err.Error())
|
||||
log.Errorf("Error Saving ACME account %+v: %s", account, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("Retrieved ACME certificates")
|
||||
}
|
||||
}
|
||||
@@ -467,7 +318,7 @@ func (a *ACME) renewCertificates() {
|
||||
continue
|
||||
}
|
||||
operation := func() error {
|
||||
return a.storeRenewedCertificate(account, certificateResource, renewedACMECert)
|
||||
return a.storeRenewedCertificate(certificateResource, renewedACMECert)
|
||||
}
|
||||
notify := func(err error, time time.Duration) {
|
||||
log.Warnf("Renewed certificate storage error: %v, retrying in %s", err, time)
|
||||
@@ -505,14 +356,14 @@ func (a *ACME) renewACMECertificate(certificateResource *DomainsCertificate) (*C
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *ACME) storeRenewedCertificate(account *Account, certificateResource *DomainsCertificate, renewedACMECert *Certificate) error {
|
||||
func (a *ACME) storeRenewedCertificate(certificateResource *DomainsCertificate, renewedACMECert *Certificate) error {
|
||||
transaction, object, err := a.store.Begin()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error during transaction initialization for renewing certificate: %v", err)
|
||||
}
|
||||
|
||||
log.Infof("Renewing certificate in data store : %+v ", certificateResource.Domains)
|
||||
account = object.(*Account)
|
||||
account := object.(*Account)
|
||||
err = account.DomainsCertificate.renewCertificates(renewedACMECert, certificateResource.Domains)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error renewing certificate in datastore: %v ", err)
|
||||
@@ -550,7 +401,7 @@ func dnsOverrideDelay(delay flaeg.Duration) error {
|
||||
|
||||
func (a *ACME) buildACMEClient(account *Account) (*acme.Client, error) {
|
||||
log.Debug("Building ACME client...")
|
||||
caServer := "https://acme-v01.api.letsencrypt.org/directory"
|
||||
caServer := "https://acme-v02.api.letsencrypt.org/directory"
|
||||
if len(a.CAServer) > 0 {
|
||||
caServer = a.CAServer
|
||||
}
|
||||
@@ -573,15 +424,15 @@ func (a *ACME) buildACMEClient(account *Account) (*acme.Client, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.TLSSNI01})
|
||||
client.ExcludeChallenges([]acme.Challenge{acme.HTTP01})
|
||||
err = client.SetChallengeProvider(acme.DNS01, provider)
|
||||
} else if a.HTTPChallenge != nil && len(a.HTTPChallenge.EntryPoint) > 0 {
|
||||
client.ExcludeChallenges([]acme.Challenge{acme.DNS01, acme.TLSSNI01})
|
||||
log.Debug("Using HTTP Challenge provider.")
|
||||
client.ExcludeChallenges([]acme.Challenge{acme.DNS01})
|
||||
a.challengeHTTPProvider = &challengeHTTPProvider{store: a.store}
|
||||
err = client.SetChallengeProvider(acme.HTTP01, a.challengeHTTPProvider)
|
||||
} else {
|
||||
client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.DNS01})
|
||||
err = client.SetChallengeProvider(acme.TLSSNI01, a.challengeTLSProvider)
|
||||
return nil, errors.New("ACME challenge not specified, please select HTTP or DNS Challenge")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -607,7 +458,7 @@ func (a *ACME) loadCertificateOnDemand(clientHello *tls.ClientHelloInfo) (*tls.C
|
||||
return nil, err
|
||||
}
|
||||
account = object.(*Account)
|
||||
cert, err := account.DomainsCertificate.addCertificateForDomains(certificate, Domain{Main: domain})
|
||||
cert, err := account.DomainsCertificate.addCertificateForDomains(certificate, types.Domain{Main: domain})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -622,13 +473,12 @@ func (a *ACME) LoadCertificateForDomains(domains []string) {
|
||||
a.jobs.In() <- func() {
|
||||
log.Debugf("LoadCertificateForDomains %v...", domains)
|
||||
|
||||
if len(domains) == 0 {
|
||||
// no domain
|
||||
domains, err := a.getValidDomains(domains, false)
|
||||
if err != nil {
|
||||
log.Errorf("Error getting valid domain: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
domains = fun.Map(types.CanonicalDomain, domains).([]string)
|
||||
|
||||
operation := func() error {
|
||||
if a.client == nil {
|
||||
return errors.New("ACME client still not built")
|
||||
@@ -640,7 +490,7 @@ func (a *ACME) LoadCertificateForDomains(domains []string) {
|
||||
}
|
||||
ebo := backoff.NewExponentialBackOff()
|
||||
ebo.MaxElapsedTime = 30 * time.Second
|
||||
err := backoff.RetryNotify(safe.OperationWithRecover(operation), ebo, notify)
|
||||
err = backoff.RetryNotify(safe.OperationWithRecover(operation), ebo, notify)
|
||||
if err != nil {
|
||||
log.Errorf("Error getting ACME client: %v", err)
|
||||
return
|
||||
@@ -664,11 +514,11 @@ func (a *ACME) LoadCertificateForDomains(domains []string) {
|
||||
log.Errorf("Error creating transaction %+v : %v", uncheckedDomains, err)
|
||||
return
|
||||
}
|
||||
var domain Domain
|
||||
var domain types.Domain
|
||||
if len(uncheckedDomains) > 1 {
|
||||
domain = Domain{Main: uncheckedDomains[0], SANs: uncheckedDomains[1:]}
|
||||
domain = types.Domain{Main: uncheckedDomains[0], SANs: uncheckedDomains[1:]}
|
||||
} else {
|
||||
domain = Domain{Main: uncheckedDomains[0]}
|
||||
domain = types.Domain{Main: uncheckedDomains[0]}
|
||||
}
|
||||
account = object.(*Account)
|
||||
_, err = account.DomainsCertificate.addCertificateForDomains(certificate, domain)
|
||||
@@ -689,7 +539,7 @@ func (a *ACME) getProvidedCertificate(domains string) *tls.Certificate {
|
||||
log.Debugf("Looking for provided certificate to validate %s...", domains)
|
||||
cert := searchProvidedCertificateForDomains(domains, a.TLSConfig.NameToCertificate)
|
||||
if cert == nil && a.dynamicCerts != nil && a.dynamicCerts.Get() != nil {
|
||||
cert = searchProvidedCertificateForDomains(domains, a.dynamicCerts.Get().(*traefikTls.DomainsCertificates).Get().(map[string]*tls.Certificate))
|
||||
cert = searchProvidedCertificateForDomains(domains, a.dynamicCerts.Get().(map[string]*tls.Certificate))
|
||||
}
|
||||
if cert == nil {
|
||||
log.Debugf("No provided certificate found for domains %s, get ACME certificate.", domains)
|
||||
@@ -700,15 +550,14 @@ func (a *ACME) getProvidedCertificate(domains string) *tls.Certificate {
|
||||
func searchProvidedCertificateForDomains(domain string, certs map[string]*tls.Certificate) *tls.Certificate {
|
||||
// Use regex to test for provided certs that might have been added into TLSConfig
|
||||
for certDomains := range certs {
|
||||
domainCheck := false
|
||||
domainChecked := false
|
||||
for _, certDomain := range strings.Split(certDomains, ",") {
|
||||
selector := "^" + strings.Replace(certDomain, "*.", "[^\\.]*\\.?", -1) + "$"
|
||||
domainCheck, _ = regexp.MatchString(selector, domain)
|
||||
if domainCheck {
|
||||
domainChecked = types.MatchDomain(domain, certDomain)
|
||||
if domainChecked {
|
||||
break
|
||||
}
|
||||
}
|
||||
if domainCheck {
|
||||
if domainChecked {
|
||||
log.Debugf("Domain %q checked by provided certificate %q", domain, certDomains)
|
||||
return certs[certDomains]
|
||||
}
|
||||
@@ -729,7 +578,7 @@ func (a *ACME) getUncheckedDomains(domains []string, account *Account) []string
|
||||
|
||||
// Get dynamic certificates
|
||||
if a.dynamicCerts != nil && a.dynamicCerts.Get() != nil {
|
||||
for domains, certificate := range a.dynamicCerts.Get().(*traefikTls.DomainsCertificates).Get().(map[string]*tls.Certificate) {
|
||||
for domains, certificate := range a.dynamicCerts.Get().(map[string]*tls.Certificate) {
|
||||
allCerts[domains] = certificate
|
||||
}
|
||||
}
|
||||
@@ -741,31 +590,25 @@ func (a *ACME) getUncheckedDomains(domains []string, account *Account) []string
|
||||
}
|
||||
}
|
||||
|
||||
// Get Configuration Domains
|
||||
for i := 0; i < len(a.Domains); i++ {
|
||||
allCerts[a.Domains[i].Main] = &tls.Certificate{}
|
||||
for _, san := range a.Domains[i].SANs {
|
||||
allCerts[san] = &tls.Certificate{}
|
||||
}
|
||||
}
|
||||
|
||||
return searchUncheckedDomains(domains, allCerts)
|
||||
}
|
||||
|
||||
func searchUncheckedDomains(domains []string, certs map[string]*tls.Certificate) []string {
|
||||
uncheckedDomains := []string{}
|
||||
var uncheckedDomains []string
|
||||
for _, domainToCheck := range domains {
|
||||
domainCheck := false
|
||||
for certDomains := range certs {
|
||||
domainCheck = false
|
||||
for _, certDomain := range strings.Split(certDomains, ",") {
|
||||
// Use regex to test for provided certs that might have been added into TLSConfig
|
||||
selector := "^" + strings.Replace(certDomain, "*.", "[^\\.]*\\.?", -1) + "$"
|
||||
domainCheck, _ = regexp.MatchString(selector, domainToCheck)
|
||||
if domainCheck {
|
||||
break
|
||||
}
|
||||
}
|
||||
if domainCheck {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !domainCheck {
|
||||
if !isDomainAlreadyChecked(domainToCheck, certs) {
|
||||
uncheckedDomains = append(uncheckedDomains, domainToCheck)
|
||||
}
|
||||
}
|
||||
|
||||
if len(uncheckedDomains) == 0 {
|
||||
log.Debugf("No ACME certificate to generate for domains %q.", domains)
|
||||
} else {
|
||||
@@ -778,11 +621,13 @@ func (a *ACME) getDomainsCertificates(domains []string) (*Certificate, error) {
|
||||
domains = fun.Map(types.CanonicalDomain, domains).([]string)
|
||||
log.Debugf("Loading ACME certificates %s...", domains)
|
||||
bundle := true
|
||||
certificate, failures := a.client.ObtainCertificate(domains, bundle, nil, OSCPMustStaple)
|
||||
if len(failures) > 0 {
|
||||
log.Error(failures)
|
||||
return nil, fmt.Errorf("cannot obtain certificates %+v", failures)
|
||||
|
||||
certificate, err := a.client.ObtainCertificate(domains, bundle, nil, OSCPMustStaple)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return nil, fmt.Errorf("cannot obtain certificates: %+v", err)
|
||||
}
|
||||
|
||||
log.Debugf("Loaded ACME certificates %s", domains)
|
||||
return &Certificate{
|
||||
Domain: certificate.Domain,
|
||||
@@ -801,3 +646,105 @@ func (a *ACME) runJobs() {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// getValidDomains checks if given domain is allowed to generate a ACME certificate and return it
|
||||
func (a *ACME) getValidDomains(domains []string, wildcardAllowed bool) ([]string, error) {
|
||||
// Check if the domains array is empty or contains only one empty value
|
||||
if len(domains) == 0 || (len(domains) == 1 && len(domains[0]) == 0) {
|
||||
return nil, errors.New("unable to generate a certificate when no domain is given")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(domains[0], "*") {
|
||||
if !wildcardAllowed {
|
||||
return nil, fmt.Errorf("unable to generate a wildcard certificate for domain %q from a 'Host' rule", strings.Join(domains, ","))
|
||||
}
|
||||
|
||||
if a.DNSChallenge == nil && len(a.DNSProvider) == 0 {
|
||||
return nil, fmt.Errorf("unable to generate a wildcard certificate for domain %q : ACME needs a DNSChallenge", strings.Join(domains, ","))
|
||||
}
|
||||
if strings.HasPrefix(domains[0], "*.*") {
|
||||
return nil, fmt.Errorf("unable to generate a wildcard certificate for domain %q : ACME does not allow '*.*' wildcard domain", strings.Join(domains, ","))
|
||||
}
|
||||
}
|
||||
for _, san := range domains[1:] {
|
||||
if strings.HasPrefix(san, "*") {
|
||||
return nil, fmt.Errorf("unable to generate a certificate for domains %q: SANs can not be a wildcard domain", strings.Join(domains, ","))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
domains = fun.Map(types.CanonicalDomain, domains).([]string)
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
func isDomainAlreadyChecked(domainToCheck string, existentDomains map[string]*tls.Certificate) bool {
|
||||
for certDomains := range existentDomains {
|
||||
for _, certDomain := range strings.Split(certDomains, ",") {
|
||||
if types.MatchDomain(domainToCheck, certDomain) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// deleteUnnecessaryDomains deletes from the configuration :
|
||||
// - Duplicated domains
|
||||
// - Domains which are checked by wildcard domain
|
||||
func (a *ACME) deleteUnnecessaryDomains() {
|
||||
var newDomains []types.Domain
|
||||
|
||||
for idxDomainToCheck, domainToCheck := range a.Domains {
|
||||
keepDomain := true
|
||||
|
||||
for idxDomain, domain := range a.Domains {
|
||||
if idxDomainToCheck == idxDomain {
|
||||
continue
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(domain, domainToCheck) {
|
||||
if idxDomainToCheck > idxDomain {
|
||||
log.Warnf("The domain %v is duplicated in the configuration but will be process by ACME only once.", domainToCheck)
|
||||
keepDomain = false
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
var newDomainsToCheck []string
|
||||
|
||||
// Check if domains can be validated by the wildcard domain
|
||||
domainsMap := make(map[string]*tls.Certificate)
|
||||
domainsMap[domain.Main] = &tls.Certificate{}
|
||||
if len(domain.SANs) > 0 {
|
||||
domainsMap[strings.Join(domain.SANs, ",")] = &tls.Certificate{}
|
||||
}
|
||||
|
||||
for _, domainProcessed := range domainToCheck.ToStrArray() {
|
||||
if idxDomain < idxDomainToCheck && isDomainAlreadyChecked(domainProcessed, domainsMap) {
|
||||
// The domain is duplicated in a CN
|
||||
log.Warnf("Domain %q is duplicated in the configuration or validated by the domain %v. It will be processed once.", domainProcessed, domain)
|
||||
continue
|
||||
} else if domain.Main != domainProcessed && strings.HasPrefix(domain.Main, "*") && types.MatchDomain(domainProcessed, domain.Main) {
|
||||
// Check if a wildcard can validate the domain
|
||||
log.Warnf("Domain %q will not be processed by ACME provider because it is validated by the wildcard %q", domainProcessed, domain.Main)
|
||||
continue
|
||||
}
|
||||
newDomainsToCheck = append(newDomainsToCheck, domainProcessed)
|
||||
}
|
||||
|
||||
// Delete the domain if both Main and SANs can be validated by the wildcard domain
|
||||
// otherwise keep the unchecked values
|
||||
if newDomainsToCheck == nil {
|
||||
keepDomain = false
|
||||
break
|
||||
}
|
||||
domainToCheck.Set(newDomainsToCheck)
|
||||
}
|
||||
|
||||
if keepDomain {
|
||||
newDomains = append(newDomains, domainToCheck)
|
||||
}
|
||||
}
|
||||
|
||||
a.Domains = newDomains
|
||||
}
|
||||
|
@@ -6,80 +6,127 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"sort"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
acmeprovider "github.com/containous/traefik/provider/acme"
|
||||
"github.com/containous/traefik/tls/generate"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/xenolf/lego/acme"
|
||||
)
|
||||
|
||||
func TestDomainsSet(t *testing.T) {
|
||||
checkMap := map[string]Domains{
|
||||
"": {},
|
||||
"foo.com": {Domain{Main: "foo.com", SANs: []string{}}},
|
||||
"foo.com,bar.net": {Domain{Main: "foo.com", SANs: []string{"bar.net"}}},
|
||||
"foo.com,bar1.net,bar2.net,bar3.net": {Domain{Main: "foo.com", SANs: []string{"bar1.net", "bar2.net", "bar3.net"}}},
|
||||
testCases := []struct {
|
||||
input string
|
||||
expected types.Domains
|
||||
}{
|
||||
{
|
||||
input: "",
|
||||
expected: types.Domains{},
|
||||
},
|
||||
{
|
||||
input: "foo1.com",
|
||||
expected: types.Domains{
|
||||
types.Domain{Main: "foo1.com"},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "foo2.com,bar.net",
|
||||
expected: types.Domains{
|
||||
types.Domain{
|
||||
Main: "foo2.com",
|
||||
SANs: []string{"bar.net"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "foo3.com,bar1.net,bar2.net,bar3.net",
|
||||
expected: types.Domains{
|
||||
types.Domain{
|
||||
Main: "foo3.com",
|
||||
SANs: []string{"bar1.net", "bar2.net", "bar3.net"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for in, check := range checkMap {
|
||||
ds := Domains{}
|
||||
ds.Set(in)
|
||||
if !reflect.DeepEqual(check, ds) {
|
||||
t.Errorf("Expected %+v\nGot %+v", check, ds)
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.input, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
domains := types.Domains{}
|
||||
domains.Set(test.input)
|
||||
assert.Exactly(t, test.expected, domains)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDomainsSetAppend(t *testing.T) {
|
||||
inSlice := []string{
|
||||
"",
|
||||
"foo1.com",
|
||||
"foo2.com,bar.net",
|
||||
"foo3.com,bar1.net,bar2.net,bar3.net",
|
||||
testCases := []struct {
|
||||
input string
|
||||
expected types.Domains
|
||||
}{
|
||||
{
|
||||
input: "",
|
||||
expected: types.Domains{},
|
||||
},
|
||||
{
|
||||
input: "foo1.com",
|
||||
expected: types.Domains{
|
||||
types.Domain{Main: "foo1.com"},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "foo2.com,bar.net",
|
||||
expected: types.Domains{
|
||||
types.Domain{Main: "foo1.com"},
|
||||
types.Domain{
|
||||
Main: "foo2.com",
|
||||
SANs: []string{"bar.net"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "foo3.com,bar1.net,bar2.net,bar3.net",
|
||||
expected: types.Domains{
|
||||
types.Domain{Main: "foo1.com"},
|
||||
types.Domain{
|
||||
Main: "foo2.com",
|
||||
SANs: []string{"bar.net"},
|
||||
},
|
||||
types.Domain{
|
||||
Main: "foo3.com",
|
||||
SANs: []string{"bar1.net", "bar2.net", "bar3.net"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
checkSlice := []Domains{
|
||||
{},
|
||||
{
|
||||
Domain{
|
||||
Main: "foo1.com",
|
||||
SANs: []string{}}},
|
||||
{
|
||||
Domain{
|
||||
Main: "foo1.com",
|
||||
SANs: []string{}},
|
||||
Domain{
|
||||
Main: "foo2.com",
|
||||
SANs: []string{"bar.net"}}},
|
||||
{
|
||||
Domain{
|
||||
Main: "foo1.com",
|
||||
SANs: []string{}},
|
||||
Domain{
|
||||
Main: "foo2.com",
|
||||
SANs: []string{"bar.net"}},
|
||||
Domain{Main: "foo3.com",
|
||||
SANs: []string{"bar1.net", "bar2.net", "bar3.net"}}},
|
||||
}
|
||||
ds := Domains{}
|
||||
for i, in := range inSlice {
|
||||
ds.Set(in)
|
||||
if !reflect.DeepEqual(checkSlice[i], ds) {
|
||||
t.Errorf("Expected %s %+v\nGot %+v", in, checkSlice[i], ds)
|
||||
}
|
||||
|
||||
// append to
|
||||
domains := types.Domains{}
|
||||
for _, test := range testCases {
|
||||
t.Run(test.input, func(t *testing.T) {
|
||||
|
||||
domains.Set(test.input)
|
||||
assert.Exactly(t, test.expected, domains)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCertificatesRenew(t *testing.T) {
|
||||
foo1Cert, foo1Key, _ := generate.KeyPair("foo1.com", time.Now())
|
||||
foo2Cert, foo2Key, _ := generate.KeyPair("foo2.com", time.Now())
|
||||
|
||||
domainsCertificates := DomainsCertificates{
|
||||
lock: sync.RWMutex{},
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Domains: Domain{
|
||||
Main: "foo1.com",
|
||||
SANs: []string{}},
|
||||
Domains: types.Domain{
|
||||
Main: "foo1.com"},
|
||||
Certificate: &Certificate{
|
||||
Domain: "foo1.com",
|
||||
CertURL: "url",
|
||||
@@ -89,9 +136,8 @@ func TestCertificatesRenew(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
Domains: Domain{
|
||||
Main: "foo2.com",
|
||||
SANs: []string{}},
|
||||
Domains: types.Domain{
|
||||
Main: "foo2.com"},
|
||||
Certificate: &Certificate{
|
||||
Domain: "foo2.com",
|
||||
CertURL: "url",
|
||||
@@ -102,6 +148,7 @@ func TestCertificatesRenew(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
foo1Cert, foo1Key, _ = generate.KeyPair("foo1.com", time.Now())
|
||||
newCertificate := &Certificate{
|
||||
Domain: "foo1.com",
|
||||
@@ -111,17 +158,15 @@ func TestCertificatesRenew(t *testing.T) {
|
||||
Certificate: foo1Cert,
|
||||
}
|
||||
|
||||
err := domainsCertificates.renewCertificates(
|
||||
newCertificate,
|
||||
Domain{
|
||||
Main: "foo1.com",
|
||||
SANs: []string{}})
|
||||
err := domainsCertificates.renewCertificates(newCertificate, types.Domain{Main: "foo1.com"})
|
||||
if err != nil {
|
||||
t.Errorf("Error in renewCertificates :%v", err)
|
||||
}
|
||||
|
||||
if len(domainsCertificates.Certs) != 2 {
|
||||
t.Errorf("Expected domainsCertificates length %d %+v\nGot %+v", 2, domainsCertificates.Certs, len(domainsCertificates.Certs))
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(domainsCertificates.Certs[0].Certificate, newCertificate) {
|
||||
t.Errorf("Expected new certificate %+v \nGot %+v", newCertificate, domainsCertificates.Certs[0].Certificate)
|
||||
}
|
||||
@@ -137,9 +182,8 @@ func TestRemoveDuplicates(t *testing.T) {
|
||||
lock: sync.RWMutex{},
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Domains: Domain{
|
||||
Main: "foo.com",
|
||||
SANs: []string{}},
|
||||
Domains: types.Domain{
|
||||
Main: "foo.com"},
|
||||
Certificate: &Certificate{
|
||||
Domain: "foo.com",
|
||||
CertURL: "url",
|
||||
@@ -149,9 +193,8 @@ func TestRemoveDuplicates(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
Domains: Domain{
|
||||
Main: "foo.com",
|
||||
SANs: []string{}},
|
||||
Domains: types.Domain{
|
||||
Main: "foo.com"},
|
||||
Certificate: &Certificate{
|
||||
Domain: "foo.com",
|
||||
CertURL: "url",
|
||||
@@ -161,9 +204,8 @@ func TestRemoveDuplicates(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
Domains: Domain{
|
||||
Main: "foo.com",
|
||||
SANs: []string{}},
|
||||
Domains: types.Domain{
|
||||
Main: "foo.com"},
|
||||
Certificate: &Certificate{
|
||||
Domain: "foo.com",
|
||||
CertURL: "url",
|
||||
@@ -173,9 +215,8 @@ func TestRemoveDuplicates(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
Domains: Domain{
|
||||
Main: "bar.com",
|
||||
SANs: []string{}},
|
||||
Domains: types.Domain{
|
||||
Main: "bar.com"},
|
||||
Certificate: &Certificate{
|
||||
Domain: "bar.com",
|
||||
CertURL: "url",
|
||||
@@ -185,9 +226,8 @@ func TestRemoveDuplicates(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
Domains: Domain{
|
||||
Main: "foo.com",
|
||||
SANs: []string{}},
|
||||
Domains: types.Domain{
|
||||
Main: "foo.com"},
|
||||
Certificate: &Certificate{
|
||||
Domain: "foo.com",
|
||||
CertURL: "url",
|
||||
@@ -260,14 +300,19 @@ llJh9MC0svjevGtNlxJoE3lmEQIhAKXy1wfZ32/XtcrnENPvi6lzxI0T94X7s5pP3aCoPPoJAiEAl
|
||||
cijFkALeQp/qyeXdFld2v9gUN3eCgljgcl0QweRoIc=---`)
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(`{
|
||||
"new-authz": "https://foo/acme/new-authz",
|
||||
"new-cert": "https://foo/acme/new-cert",
|
||||
"new-reg": "https://foo/acme/new-reg",
|
||||
"revoke-cert": "https://foo/acme/revoke-cert"
|
||||
"GPHhmRVEDas": "https://community.letsencrypt.org/t/adding-random-entries-to-the-directory/33417",
|
||||
"keyChange": "https://foo/acme/key-change",
|
||||
"meta": {
|
||||
"termsOfService": "https://boulder:4431/terms/v7"
|
||||
},
|
||||
"newAccount": "https://foo/acme/new-acct",
|
||||
"newNonce": "https://foo/acme/new-nonce",
|
||||
"newOrder": "https://foo/acme/new-order",
|
||||
"revokeCert": "https://foo/acme/revoke-cert"
|
||||
}`))
|
||||
}))
|
||||
defer ts.Close()
|
||||
a := ACME{DNSChallenge: &DNSChallenge{Provider: "manual", DelayBeforeCheck: 10}, CAServer: ts.URL}
|
||||
a := ACME{DNSChallenge: &acmeprovider.DNSChallenge{Provider: "manual", DelayBeforeCheck: 10}, CAServer: ts.URL}
|
||||
|
||||
client, err := a.buildACMEClient(account)
|
||||
if err != nil {
|
||||
@@ -297,7 +342,7 @@ func TestAcme_getUncheckedCertificates(t *testing.T) {
|
||||
domainsCertificates := DomainsCertificates{Certs: []*DomainsCertificate{
|
||||
{
|
||||
tlsCert: &tls.Certificate{},
|
||||
Domains: Domain{
|
||||
Domains: types.Domain{
|
||||
Main: "*.acme.wtf",
|
||||
SANs: []string{"trae.acme.io"},
|
||||
},
|
||||
@@ -322,3 +367,452 @@ func TestAcme_getProvidedCertificate(t *testing.T) {
|
||||
certificate = a.getProvidedCertificate(domain)
|
||||
assert.Nil(t, certificate)
|
||||
}
|
||||
|
||||
func TestAcme_getValidDomain(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
domains []string
|
||||
wildcardAllowed bool
|
||||
dnsChallenge *acmeprovider.DNSChallenge
|
||||
expectedErr string
|
||||
expectedDomains []string
|
||||
}{
|
||||
{
|
||||
desc: "valid wildcard",
|
||||
domains: []string{"*.traefik.wtf"},
|
||||
dnsChallenge: &acmeprovider.DNSChallenge{},
|
||||
wildcardAllowed: true,
|
||||
expectedErr: "",
|
||||
expectedDomains: []string{"*.traefik.wtf"},
|
||||
},
|
||||
{
|
||||
desc: "no wildcard",
|
||||
domains: []string{"traefik.wtf", "foo.traefik.wtf"},
|
||||
dnsChallenge: &acmeprovider.DNSChallenge{},
|
||||
expectedErr: "",
|
||||
wildcardAllowed: true,
|
||||
expectedDomains: []string{"traefik.wtf", "foo.traefik.wtf"},
|
||||
},
|
||||
{
|
||||
desc: "unauthorized wildcard",
|
||||
domains: []string{"*.traefik.wtf"},
|
||||
dnsChallenge: &acmeprovider.DNSChallenge{},
|
||||
wildcardAllowed: false,
|
||||
expectedErr: "unable to generate a wildcard certificate for domain \"*.traefik.wtf\" from a 'Host' rule",
|
||||
expectedDomains: nil,
|
||||
},
|
||||
{
|
||||
desc: "no domain",
|
||||
domains: []string{},
|
||||
dnsChallenge: nil,
|
||||
wildcardAllowed: true,
|
||||
expectedErr: "unable to generate a certificate when no domain is given",
|
||||
expectedDomains: nil,
|
||||
},
|
||||
{
|
||||
desc: "no DNSChallenge",
|
||||
domains: []string{"*.traefik.wtf", "foo.traefik.wtf"},
|
||||
dnsChallenge: nil,
|
||||
wildcardAllowed: true,
|
||||
expectedErr: "unable to generate a wildcard certificate for domain \"*.traefik.wtf,foo.traefik.wtf\" : ACME needs a DNSChallenge",
|
||||
expectedDomains: nil,
|
||||
},
|
||||
{
|
||||
desc: "unauthorized wildcard with SAN",
|
||||
domains: []string{"*.*.traefik.wtf", "foo.traefik.wtf"},
|
||||
dnsChallenge: &acmeprovider.DNSChallenge{},
|
||||
wildcardAllowed: true,
|
||||
expectedErr: "unable to generate a wildcard certificate for domain \"*.*.traefik.wtf,foo.traefik.wtf\" : ACME does not allow '*.*' wildcard domain",
|
||||
expectedDomains: nil,
|
||||
},
|
||||
{
|
||||
desc: "wildcard with SANs",
|
||||
domains: []string{"*.traefik.wtf", "traefik.wtf"},
|
||||
dnsChallenge: &acmeprovider.DNSChallenge{},
|
||||
wildcardAllowed: true,
|
||||
expectedErr: "",
|
||||
expectedDomains: []string{"*.traefik.wtf", "traefik.wtf"},
|
||||
},
|
||||
{
|
||||
desc: "unexpected SANs",
|
||||
domains: []string{"*.traefik.wtf", "*.acme.wtf"},
|
||||
dnsChallenge: &acmeprovider.DNSChallenge{},
|
||||
wildcardAllowed: true,
|
||||
expectedErr: "unable to generate a certificate for domains \"*.traefik.wtf,*.acme.wtf\": SANs can not be a wildcard domain",
|
||||
expectedDomains: nil,
|
||||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
a := ACME{}
|
||||
if test.dnsChallenge != nil {
|
||||
a.DNSChallenge = test.dnsChallenge
|
||||
}
|
||||
domains, err := a.getValidDomains(test.domains, test.wildcardAllowed)
|
||||
|
||||
if len(test.expectedErr) > 0 {
|
||||
assert.EqualError(t, err, test.expectedErr, "Unexpected error.")
|
||||
} else {
|
||||
assert.Equal(t, len(test.expectedDomains), len(domains), "Unexpected domains.")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAcme_getCertificateForDomain(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
domain string
|
||||
dc *DomainsCertificates
|
||||
expected *DomainsCertificate
|
||||
expectedFound bool
|
||||
}{
|
||||
{
|
||||
desc: "non-wildcard exact match",
|
||||
domain: "foo.traefik.wtf",
|
||||
dc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Domains: types.Domain{
|
||||
Main: "foo.traefik.wtf",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &DomainsCertificate{
|
||||
Domains: types.Domain{
|
||||
Main: "foo.traefik.wtf",
|
||||
},
|
||||
},
|
||||
expectedFound: true,
|
||||
},
|
||||
{
|
||||
desc: "non-wildcard no match",
|
||||
domain: "bar.traefik.wtf",
|
||||
dc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Domains: types.Domain{
|
||||
Main: "foo.traefik.wtf",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
expectedFound: false,
|
||||
},
|
||||
{
|
||||
desc: "wildcard match",
|
||||
domain: "foo.traefik.wtf",
|
||||
dc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Domains: types.Domain{
|
||||
Main: "*.traefik.wtf",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &DomainsCertificate{
|
||||
Domains: types.Domain{
|
||||
Main: "*.traefik.wtf",
|
||||
},
|
||||
},
|
||||
expectedFound: true,
|
||||
},
|
||||
{
|
||||
desc: "wildcard no match",
|
||||
domain: "foo.traefik.wtf",
|
||||
dc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Domains: types.Domain{
|
||||
Main: "*.bar.traefik.wtf",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
expectedFound: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, found := test.dc.getCertificateForDomain(test.domain)
|
||||
assert.Equal(t, test.expectedFound, found)
|
||||
assert.Equal(t, test.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveEmptyCertificates(t *testing.T) {
|
||||
now := time.Now()
|
||||
fooCert, fooKey, _ := generate.KeyPair("foo.com", now)
|
||||
acmeCert, acmeKey, _ := generate.KeyPair("acme.wtf", now.Add(24*time.Hour))
|
||||
barCert, barKey, _ := generate.KeyPair("bar.com", now)
|
||||
testCases := []struct {
|
||||
desc string
|
||||
dc *DomainsCertificates
|
||||
expectedDc *DomainsCertificates
|
||||
}{
|
||||
{
|
||||
desc: "No empty certificate",
|
||||
dc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: fooCert,
|
||||
PrivateKey: fooKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "foo.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: acmeCert,
|
||||
PrivateKey: acmeKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "acme.wtf",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: barCert,
|
||||
PrivateKey: barKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "bar.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedDc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: fooCert,
|
||||
PrivateKey: fooKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "foo.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: acmeCert,
|
||||
PrivateKey: acmeKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "acme.wtf",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: barCert,
|
||||
PrivateKey: barKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "bar.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "First certificate is nil",
|
||||
dc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Domains: types.Domain{
|
||||
Main: "foo.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: acmeCert,
|
||||
PrivateKey: acmeKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "acme.wtf",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: barCert,
|
||||
PrivateKey: barKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "bar.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedDc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: acmeCert,
|
||||
PrivateKey: acmeKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "acme.wtf",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: nil,
|
||||
PrivateKey: barKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "bar.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Last certificate is empty",
|
||||
dc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: fooCert,
|
||||
PrivateKey: fooKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "foo.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: acmeCert,
|
||||
PrivateKey: acmeKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "acme.wtf",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{},
|
||||
Domains: types.Domain{
|
||||
Main: "bar.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedDc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: fooCert,
|
||||
PrivateKey: fooKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "foo.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: acmeCert,
|
||||
PrivateKey: acmeKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "acme.wtf",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "First and last certificates are nil or empty",
|
||||
dc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Domains: types.Domain{
|
||||
Main: "foo.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: acmeCert,
|
||||
PrivateKey: acmeKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "acme.wtf",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{},
|
||||
Domains: types.Domain{
|
||||
Main: "bar.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedDc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: acmeCert,
|
||||
PrivateKey: acmeKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "acme.wtf",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "All certificates are nil or empty",
|
||||
dc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Domains: types.Domain{
|
||||
Main: "foo.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
Domains: types.Domain{
|
||||
Main: "foo24.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{},
|
||||
Domains: types.Domain{
|
||||
Main: "bar.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedDc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
a := &Account{DomainsCertificate: *test.dc}
|
||||
a.Init()
|
||||
|
||||
assert.Equal(t, len(test.expectedDc.Certs), len(a.DomainsCertificate.Certs))
|
||||
sort.Sort(&a.DomainsCertificate)
|
||||
sort.Sort(test.expectedDc)
|
||||
for key, value := range test.expectedDc.Certs {
|
||||
assert.Equal(t, value.Domains.Main, a.DomainsCertificate.Certs[key].Domains.Main)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -1,150 +0,0 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/cenk/backoff"
|
||||
"github.com/containous/traefik/cluster"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/containous/traefik/tls/generate"
|
||||
"github.com/xenolf/lego/acme"
|
||||
)
|
||||
|
||||
var _ acme.ChallengeProviderTimeout = (*challengeTLSProvider)(nil)
|
||||
|
||||
type challengeTLSProvider struct {
|
||||
store cluster.Store
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func (c *challengeTLSProvider) getCertificate(domain string) (cert *tls.Certificate, exists bool) {
|
||||
log.Debugf("Looking for an existing ACME challenge for %s...", domain)
|
||||
if !strings.HasSuffix(domain, ".acme.invalid") {
|
||||
return nil, false
|
||||
}
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
account := c.store.Get().(*Account)
|
||||
if account.ChallengeCerts == nil {
|
||||
return nil, false
|
||||
}
|
||||
account.Init()
|
||||
var result *tls.Certificate
|
||||
operation := func() error {
|
||||
for _, cert := range account.ChallengeCerts {
|
||||
for _, dns := range cert.certificate.Leaf.DNSNames {
|
||||
if domain == dns {
|
||||
result = cert.certificate
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("cannot find challenge cert for domain %s", domain)
|
||||
}
|
||||
notify := func(err error, time time.Duration) {
|
||||
log.Errorf("Error getting cert: %v, retrying in %s", err, time)
|
||||
}
|
||||
ebo := backoff.NewExponentialBackOff()
|
||||
ebo.MaxElapsedTime = 60 * time.Second
|
||||
err := backoff.RetryNotify(safe.OperationWithRecover(operation), ebo, notify)
|
||||
if err != nil {
|
||||
log.Errorf("Error getting cert: %v", err)
|
||||
return nil, false
|
||||
}
|
||||
return result, true
|
||||
}
|
||||
|
||||
func (c *challengeTLSProvider) Present(domain, token, keyAuth string) error {
|
||||
log.Debugf("Challenge Present %s", domain)
|
||||
cert, _, err := tlsSNI01ChallengeCert(keyAuth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
transaction, object, err := c.store.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
account := object.(*Account)
|
||||
if account.ChallengeCerts == nil {
|
||||
account.ChallengeCerts = map[string]*ChallengeCert{}
|
||||
}
|
||||
account.ChallengeCerts[domain] = &cert
|
||||
return transaction.Commit(account)
|
||||
}
|
||||
|
||||
func (c *challengeTLSProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
log.Debugf("Challenge CleanUp %s", domain)
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
transaction, object, err := c.store.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
account := object.(*Account)
|
||||
delete(account.ChallengeCerts, domain)
|
||||
return transaction.Commit(account)
|
||||
}
|
||||
|
||||
func (c *challengeTLSProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return 60 * time.Second, 5 * time.Second
|
||||
}
|
||||
|
||||
// tlsSNI01ChallengeCert returns a certificate and target domain for the `tls-sni-01` challenge
|
||||
func tlsSNI01ChallengeCert(keyAuth string) (ChallengeCert, string, error) {
|
||||
// generate a new RSA key for the certificates
|
||||
var tempPrivKey crypto.PrivateKey
|
||||
tempPrivKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return ChallengeCert{}, "", err
|
||||
}
|
||||
rsaPrivKey := tempPrivKey.(*rsa.PrivateKey)
|
||||
rsaPrivPEM := pemEncode(rsaPrivKey)
|
||||
|
||||
zBytes := sha256.Sum256([]byte(keyAuth))
|
||||
z := hex.EncodeToString(zBytes[:sha256.Size])
|
||||
domain := fmt.Sprintf("%s.%s.acme.invalid", z[:32], z[32:])
|
||||
tempCertPEM, err := generate.PemCert(rsaPrivKey, domain, time.Time{})
|
||||
if err != nil {
|
||||
return ChallengeCert{}, "", err
|
||||
}
|
||||
|
||||
certificate, err := tls.X509KeyPair(tempCertPEM, rsaPrivPEM)
|
||||
if err != nil {
|
||||
return ChallengeCert{}, "", err
|
||||
}
|
||||
|
||||
return ChallengeCert{Certificate: tempCertPEM, PrivateKey: rsaPrivPEM, certificate: &certificate}, domain, nil
|
||||
}
|
||||
|
||||
func pemEncode(data interface{}) []byte {
|
||||
var pemBlock *pem.Block
|
||||
switch key := data.(type) {
|
||||
case *ecdsa.PrivateKey:
|
||||
keyBytes, _ := x509.MarshalECPrivateKey(key)
|
||||
pemBlock = &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes}
|
||||
case *rsa.PrivateKey:
|
||||
pemBlock = &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}
|
||||
case *x509.CertificateRequest:
|
||||
pemBlock = &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: key.Raw}
|
||||
case []byte:
|
||||
pemBlock = &pem.Block{Type: "CERTIFICATE", Bytes: []byte(data.([]byte))}
|
||||
}
|
||||
|
||||
return pem.EncodeToMemory(pemBlock)
|
||||
}
|
@@ -2,22 +2,16 @@ package acme
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/containous/traefik/cluster"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/provider/acme"
|
||||
)
|
||||
|
||||
var _ cluster.Store = (*LocalStore)(nil)
|
||||
|
||||
// LocalStore is a store using a file as storage
|
||||
type LocalStore struct {
|
||||
file string
|
||||
storageLock sync.RWMutex
|
||||
account *Account
|
||||
file string
|
||||
}
|
||||
|
||||
// NewLocalStore create a LocalStore
|
||||
@@ -27,71 +21,155 @@ func NewLocalStore(file string) *LocalStore {
|
||||
}
|
||||
}
|
||||
|
||||
// Get atomically a struct from the file storage
|
||||
func (s *LocalStore) Get() cluster.Object {
|
||||
s.storageLock.RLock()
|
||||
defer s.storageLock.RUnlock()
|
||||
return s.account
|
||||
}
|
||||
|
||||
// Load loads file into store
|
||||
func (s *LocalStore) Load() (cluster.Object, error) {
|
||||
s.storageLock.Lock()
|
||||
defer s.storageLock.Unlock()
|
||||
// Get loads file into store and returns the Account
|
||||
func (s *LocalStore) Get() (*Account, error) {
|
||||
account := &Account{}
|
||||
|
||||
err := checkPermissions(s.file)
|
||||
hasData, err := acme.CheckFile(s.file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, err := os.Open(s.file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
if hasData {
|
||||
f, err := os.Open(s.file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
file, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(file, &account); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
defer f.Close()
|
||||
file, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := json.Unmarshal(file, &account); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
account.Init()
|
||||
s.account = account
|
||||
log.Infof("Loaded ACME config from store %s", s.file)
|
||||
|
||||
return account, nil
|
||||
}
|
||||
|
||||
// Begin creates a transaction with the KV store.
|
||||
func (s *LocalStore) Begin() (cluster.Transaction, cluster.Object, error) {
|
||||
s.storageLock.Lock()
|
||||
return &localTransaction{LocalStore: s}, s.account, nil
|
||||
}
|
||||
// ConvertToNewFormat converts old acme.json format to the new one and store the result into the file (used for the backward compatibility)
|
||||
func ConvertToNewFormat(fileName string) {
|
||||
localStore := acme.NewLocalStore(fileName)
|
||||
|
||||
var _ cluster.Transaction = (*localTransaction)(nil)
|
||||
|
||||
type localTransaction struct {
|
||||
*LocalStore
|
||||
dirty bool
|
||||
}
|
||||
|
||||
// Commit allows to set an object in the file storage
|
||||
func (t *localTransaction) Commit(object cluster.Object) error {
|
||||
t.LocalStore.account = object.(*Account)
|
||||
defer t.storageLock.Unlock()
|
||||
if t.dirty {
|
||||
return fmt.Errorf("transaction already used, please begin a new one")
|
||||
storeAccount, err := localStore.GetAccount()
|
||||
if err != nil {
|
||||
log.Errorf("Failed to read new account, ACME data conversion is not available : %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
storeCertificates, err := localStore.GetCertificates()
|
||||
if err != nil {
|
||||
log.Errorf("Failed to read new certificates, ACME data conversion is not available : %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if storeAccount == nil {
|
||||
localStore := NewLocalStore(fileName)
|
||||
|
||||
account, err := localStore.Get()
|
||||
if err != nil {
|
||||
log.Errorf("Failed to read old account, ACME data conversion is not available : %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Convert ACME data from old to new format
|
||||
newAccount := &acme.Account{}
|
||||
if account != nil && len(account.Email) > 0 {
|
||||
err = backupACMEFile(fileName, account)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to create a backup for the V1 formatted ACME file: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = account.RemoveAccountV1Values()
|
||||
if err != nil {
|
||||
log.Errorf("Unable to remove ACME Account V1 values during format conversion: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
newAccount = &acme.Account{
|
||||
PrivateKey: account.PrivateKey,
|
||||
Registration: account.Registration,
|
||||
Email: account.Email,
|
||||
}
|
||||
|
||||
var newCertificates []*acme.Certificate
|
||||
for _, cert := range account.DomainsCertificate.Certs {
|
||||
newCertificates = append(newCertificates, &acme.Certificate{
|
||||
Certificate: cert.Certificate.Certificate,
|
||||
Key: cert.Certificate.PrivateKey,
|
||||
Domain: cert.Domains,
|
||||
})
|
||||
}
|
||||
|
||||
// If account is in the old format, storeCertificates is nil or empty and has to be initialized
|
||||
storeCertificates = newCertificates
|
||||
}
|
||||
|
||||
// Store the data in new format into the file even if account is nil
|
||||
// to delete Account in ACME v1 format and keeping the certificates
|
||||
newLocalStore := acme.NewLocalStore(fileName)
|
||||
newLocalStore.SaveDataChan <- &acme.StoredData{Account: newAccount, Certificates: storeCertificates}
|
||||
}
|
||||
}
|
||||
|
||||
func backupACMEFile(originalFileName string, account interface{}) error {
|
||||
// write account to file
|
||||
data, err := json.MarshalIndent(object, "", " ")
|
||||
data, err := json.MarshalIndent(account, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(t.file, data, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.dirty = true
|
||||
return nil
|
||||
return ioutil.WriteFile(originalFileName+".bak", data, 0600)
|
||||
}
|
||||
|
||||
// FromNewToOldFormat converts new acme account to the old one (used for the backward compatibility)
|
||||
func FromNewToOldFormat(fileName string) (*Account, error) {
|
||||
localStore := acme.NewLocalStore(fileName)
|
||||
|
||||
storeAccount, err := localStore.GetAccount()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
storeCertificates, err := localStore.GetCertificates()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert ACME Account from new to old format
|
||||
// (Needed by the KV stores)
|
||||
var account *Account
|
||||
if storeAccount != nil {
|
||||
account = &Account{
|
||||
Email: storeAccount.Email,
|
||||
PrivateKey: storeAccount.PrivateKey,
|
||||
Registration: storeAccount.Registration,
|
||||
DomainsCertificate: DomainsCertificates{},
|
||||
}
|
||||
}
|
||||
|
||||
// Convert ACME Certificates from new to old format
|
||||
// (Needed by the KV stores)
|
||||
if len(storeCertificates) > 0 {
|
||||
// Account can be nil if data are migrated from new format
|
||||
// with a ACME V1 Account
|
||||
if account == nil {
|
||||
account = &Account{}
|
||||
}
|
||||
for _, cert := range storeCertificates {
|
||||
_, err := account.DomainsCertificate.addCertificateForDomains(&Certificate{
|
||||
Domain: cert.Domain.Main,
|
||||
Certificate: cert.Certificate,
|
||||
PrivateKey: cert.Key,
|
||||
}, cert.Domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return account, nil
|
||||
}
|
||||
|
@@ -5,37 +5,27 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLoad(t *testing.T) {
|
||||
func TestGet(t *testing.T) {
|
||||
acmeFile := "./acme_example.json"
|
||||
|
||||
folder, prefix := filepath.Split(acmeFile)
|
||||
tmpFile, err := ioutil.TempFile(folder, prefix)
|
||||
defer os.Remove(tmpFile.Name())
|
||||
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
|
||||
fileContent, err := ioutil.ReadFile(acmeFile)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
|
||||
tmpFile.Write(fileContent)
|
||||
|
||||
localStore := NewLocalStore(tmpFile.Name())
|
||||
obj, err := localStore.Load()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
account, ok := obj.(*Account)
|
||||
if !ok {
|
||||
t.Error("Object is not an ACME Account")
|
||||
}
|
||||
account, err := localStore.Get()
|
||||
assert.NoError(t, err)
|
||||
|
||||
if len(account.DomainsCertificate.Certs) != 1 {
|
||||
t.Errorf("Must found %d and found %d certificates in Account", 3, len(account.DomainsCertificate.Certs))
|
||||
}
|
||||
assert.Len(t, account.DomainsCertificate.Certs, 1)
|
||||
}
|
||||
|
@@ -1,25 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package acme
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Check file permissions
|
||||
func checkPermissions(name string) error {
|
||||
f, err := os.Open(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
fi, err := f.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if fi.Mode().Perm()&0077 != 0 {
|
||||
return fmt.Errorf("permissions %o for %s are too open, please use 600", fi.Mode().Perm(), name)
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -1,6 +0,0 @@
|
||||
package acme
|
||||
|
||||
// Do not check file permissions on Windows right now
|
||||
func checkPermissions(name string) error {
|
||||
return nil
|
||||
}
|
@@ -9,8 +9,10 @@ import (
|
||||
"github.com/containous/traefik/acme"
|
||||
"github.com/containous/traefik/configuration"
|
||||
"github.com/containous/traefik/provider"
|
||||
acmeprovider "github.com/containous/traefik/provider/acme"
|
||||
"github.com/containous/traefik/provider/boltdb"
|
||||
"github.com/containous/traefik/provider/consul"
|
||||
"github.com/containous/traefik/provider/consulcatalog"
|
||||
"github.com/containous/traefik/provider/docker"
|
||||
"github.com/containous/traefik/provider/dynamodb"
|
||||
"github.com/containous/traefik/provider/ecs"
|
||||
@@ -23,7 +25,7 @@ import (
|
||||
"github.com/containous/traefik/provider/mesos"
|
||||
"github.com/containous/traefik/provider/rancher"
|
||||
"github.com/containous/traefik/provider/zk"
|
||||
traefikTls "github.com/containous/traefik/tls"
|
||||
traefiktls "github.com/containous/traefik/tls"
|
||||
"github.com/containous/traefik/types"
|
||||
)
|
||||
|
||||
@@ -43,16 +45,15 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
config.LogLevel = "LogLevel"
|
||||
config.EntryPoints = configuration.EntryPoints{
|
||||
"foo": {
|
||||
Network: "foo Network",
|
||||
Address: "foo Address",
|
||||
TLS: &traefikTls.TLS{
|
||||
TLS: &traefiktls.TLS{
|
||||
MinVersion: "foo MinVersion",
|
||||
CipherSuites: []string{"foo CipherSuites 1", "foo CipherSuites 2", "foo CipherSuites 3"},
|
||||
Certificates: traefikTls.Certificates{
|
||||
Certificates: traefiktls.Certificates{
|
||||
{CertFile: "CertFile 1", KeyFile: "KeyFile 1"},
|
||||
{CertFile: "CertFile 2", KeyFile: "KeyFile 2"},
|
||||
},
|
||||
ClientCA: traefikTls.ClientCA{
|
||||
ClientCA: traefiktls.ClientCA{
|
||||
Files: []string{"foo ClientCAFiles 1", "foo ClientCAFiles 2", "foo ClientCAFiles 3"},
|
||||
Optional: false,
|
||||
},
|
||||
@@ -89,16 +90,15 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
},
|
||||
},
|
||||
"fii": {
|
||||
Network: "fii Network",
|
||||
Address: "fii Address",
|
||||
TLS: &traefikTls.TLS{
|
||||
TLS: &traefiktls.TLS{
|
||||
MinVersion: "fii MinVersion",
|
||||
CipherSuites: []string{"fii CipherSuites 1", "fii CipherSuites 2", "fii CipherSuites 3"},
|
||||
Certificates: traefikTls.Certificates{
|
||||
Certificates: traefiktls.Certificates{
|
||||
{CertFile: "CertFile 1", KeyFile: "KeyFile 1"},
|
||||
{CertFile: "CertFile 2", KeyFile: "KeyFile 2"},
|
||||
},
|
||||
ClientCA: traefikTls.ClientCA{
|
||||
ClientCA: traefiktls.ClientCA{
|
||||
Files: []string{"fii ClientCAFiles 1", "fii ClientCAFiles 2", "fii ClientCAFiles 3"},
|
||||
Optional: false,
|
||||
},
|
||||
@@ -156,7 +156,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
}
|
||||
config.ACME = &acme.ACME{
|
||||
Email: "acme Email",
|
||||
Domains: []acme.Domain{
|
||||
Domains: []types.Domain{
|
||||
{
|
||||
Main: "Domains Main",
|
||||
SANs: []string{"Domains acme SANs 1", "Domains acme SANs 2", "Domains acme SANs 3"},
|
||||
@@ -168,7 +168,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
OnHostRule: true,
|
||||
CAServer: "CAServer",
|
||||
EntryPoint: "EntryPoint",
|
||||
DNSChallenge: &acme.DNSChallenge{Provider: "DNSProvider"},
|
||||
DNSChallenge: &acmeprovider.DNSChallenge{Provider: "DNSProvider"},
|
||||
DelayDontCheckDNS: 666,
|
||||
ACMELogging: true,
|
||||
TLSConfig: &tls.Config{
|
||||
@@ -181,7 +181,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
config.MaxIdleConnsPerHost = 666
|
||||
config.IdleTimeout = flaeg.Duration(666 * time.Second)
|
||||
config.InsecureSkipVerify = true
|
||||
config.RootCAs = traefikTls.RootCAs{"RootCAs 1", "RootCAs 2", "RootCAs 3"}
|
||||
config.RootCAs = traefiktls.RootCAs{"RootCAs 1", "RootCAs 2", "RootCAs 3"}
|
||||
config.Retry = &configuration.Retry{
|
||||
Attempts: 666,
|
||||
}
|
||||
@@ -333,7 +333,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
},
|
||||
RespectReadinessChecks: true,
|
||||
}
|
||||
config.ConsulCatalog = &consul.CatalogProvider{
|
||||
config.ConsulCatalog = &consulcatalog.Provider{
|
||||
BaseProvider: provider.BaseProvider{
|
||||
Watch: true,
|
||||
Filename: "ConsulCatalog Filename",
|
||||
@@ -432,8 +432,9 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
Trace: true,
|
||||
DebugLogGeneratedTemplate: true,
|
||||
},
|
||||
Endpoint: "eureka Endpoint",
|
||||
Delay: "eureka Delay",
|
||||
Endpoint: "eureka Endpoint",
|
||||
Delay: flaeg.Duration(30 * time.Second),
|
||||
RefreshSeconds: flaeg.Duration(30 * time.Second),
|
||||
}
|
||||
config.ECS = &ecs.Provider{
|
||||
BaseProvider: provider.BaseProvider{
|
@@ -29,7 +29,6 @@ func Test_doOnJSON(t *testing.T) {
|
||||
"Compress": false
|
||||
},
|
||||
"https": {
|
||||
"Network": "",
|
||||
"Address": ":443",
|
||||
"TLS": {
|
||||
"MinVersion": "",
|
||||
@@ -119,7 +118,6 @@ func Test_doOnJSON(t *testing.T) {
|
||||
"Compress": false
|
||||
},
|
||||
"https": {
|
||||
"Network": "",
|
||||
"Address": ":443",
|
||||
"TLS": {
|
||||
"MinVersion": "",
|
@@ -14,9 +14,19 @@ type DashboardHandler struct{}
|
||||
// AddRoutes add dashboard routes on a router
|
||||
func (g DashboardHandler) AddRoutes(router *mux.Router) {
|
||||
// Expose dashboard
|
||||
router.Methods(http.MethodGet).Path("/").HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
|
||||
http.Redirect(response, request, request.Header.Get("X-Forwarded-Prefix")+"/dashboard/", 302)
|
||||
})
|
||||
router.Methods(http.MethodGet).PathPrefix("/dashboard/").
|
||||
router.Methods(http.MethodGet).
|
||||
Path("/").
|
||||
HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
|
||||
http.Redirect(response, request, request.Header.Get("X-Forwarded-Prefix")+"/dashboard/", 302)
|
||||
})
|
||||
|
||||
router.Methods(http.MethodGet).
|
||||
Path("/dashboard/status").
|
||||
HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
|
||||
http.Redirect(response, request, "/dashboard/", 302)
|
||||
})
|
||||
|
||||
router.Methods(http.MethodGet).
|
||||
PathPrefix("/dashboard/").
|
||||
Handler(http.StripPrefix("/dashboard/", http.FileServer(&assetfs.AssetFS{Asset: genstatic.Asset, AssetInfo: genstatic.AssetInfo, AssetDir: genstatic.AssetDir, Prefix: "static"})))
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.9-alpine
|
||||
FROM golang:1.10-alpine
|
||||
|
||||
RUN apk --update upgrade \
|
||||
&& apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar \
|
||||
|
@@ -7,12 +7,12 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/abronan/valkeyrie/store"
|
||||
"github.com/cenk/backoff"
|
||||
"github.com/containous/staert"
|
||||
"github.com/containous/traefik/job"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/docker/libkv/store"
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
|
@@ -2,15 +2,22 @@ package cluster
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/cenk/backoff"
|
||||
"github.com/containous/mux"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/docker/leadership"
|
||||
"github.com/unrolled/render"
|
||||
)
|
||||
|
||||
var templatesRenderer = render.New(render.Options{
|
||||
Directory: "nowhere",
|
||||
})
|
||||
|
||||
// Leadership allows leadership election using a KV store
|
||||
type Leadership struct {
|
||||
*safe.Pool
|
||||
@@ -98,7 +105,32 @@ func (l *Leadership) onElection(elected bool) {
|
||||
}
|
||||
}
|
||||
|
||||
type leaderResponse struct {
|
||||
Leader bool `json:"leader"`
|
||||
}
|
||||
|
||||
func (l *Leadership) getLeaderHandler(response http.ResponseWriter, request *http.Request) {
|
||||
leader := &leaderResponse{Leader: l.IsLeader()}
|
||||
|
||||
status := http.StatusOK
|
||||
if !leader.Leader {
|
||||
// Set status to be `429`, as this will typically cause load balancers to stop sending requests to the instance without removing them from rotation.
|
||||
status = http.StatusTooManyRequests
|
||||
}
|
||||
|
||||
err := templatesRenderer.JSON(response, status, leader)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// IsLeader returns true if current node is leader
|
||||
func (l *Leadership) IsLeader() bool {
|
||||
return l.leader.Get().(bool)
|
||||
}
|
||||
|
||||
// AddRoutes add dashboard routes on a router
|
||||
func (l *Leadership) AddRoutes(router *mux.Router) {
|
||||
// Expose cluster leader
|
||||
router.Methods(http.MethodGet).Path("/api/cluster/leader").HandlerFunc(l.getLeaderHandler)
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package bug
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -9,7 +9,9 @@ import (
|
||||
"text/template"
|
||||
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/traefik/cmd/traefik/anonymize"
|
||||
"github.com/containous/traefik/anonymize"
|
||||
"github.com/containous/traefik/cmd"
|
||||
"github.com/containous/traefik/cmd/version"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -74,7 +76,7 @@ HOW TO WRITE A GOOD ISSUE?
|
||||
Add more configuration information here.
|
||||
-->
|
||||
|
||||
### If applicable, please paste the log output in debug mode (` + "`" + `--debug` + "`" + ` switch)
|
||||
### If applicable, please paste the log output at DEBUG level (` + "`" + `--logLevel=DEBUG` + "`" + ` switch)
|
||||
|
||||
` + "```" + `
|
||||
(paste your output here)
|
||||
@@ -83,8 +85,8 @@ Add more configuration information here.
|
||||
`
|
||||
)
|
||||
|
||||
// newBugCmd builds a new Bug command
|
||||
func newBugCmd(traefikConfiguration *TraefikConfiguration, traefikPointersConfiguration *TraefikConfiguration) *flaeg.Command {
|
||||
// NewCmd builds a new Bug command
|
||||
func NewCmd(traefikConfiguration *cmd.TraefikConfiguration, traefikPointersConfiguration *cmd.TraefikConfiguration) *flaeg.Command {
|
||||
|
||||
//version Command init
|
||||
return &flaeg.Command{
|
||||
@@ -92,30 +94,30 @@ func newBugCmd(traefikConfiguration *TraefikConfiguration, traefikPointersConfig
|
||||
Description: `Report an issue on Traefik bugtracker`,
|
||||
Config: traefikConfiguration,
|
||||
DefaultPointersConfig: traefikPointersConfiguration,
|
||||
Run: runBugCmd(traefikConfiguration),
|
||||
Run: runCmd(traefikConfiguration),
|
||||
Metadata: map[string]string{
|
||||
"parseAllSources": "true",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func runBugCmd(traefikConfiguration *TraefikConfiguration) func() error {
|
||||
func runCmd(traefikConfiguration *cmd.TraefikConfiguration) func() error {
|
||||
return func() error {
|
||||
|
||||
body, err := createBugReport(traefikConfiguration)
|
||||
body, err := createReport(traefikConfiguration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sendBugReport(body)
|
||||
sendReport(body)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func createBugReport(traefikConfiguration *TraefikConfiguration) (string, error) {
|
||||
var version bytes.Buffer
|
||||
if err := getVersionPrint(&version); err != nil {
|
||||
func createReport(traefikConfiguration *cmd.TraefikConfiguration) (string, error) {
|
||||
var versionPrint bytes.Buffer
|
||||
if err := version.GetPrint(&versionPrint); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -133,7 +135,7 @@ func createBugReport(traefikConfiguration *TraefikConfiguration) (string, error)
|
||||
Version string
|
||||
Configuration string
|
||||
}{
|
||||
Version: version.String(),
|
||||
Version: versionPrint.String(),
|
||||
Configuration: config,
|
||||
}
|
||||
|
||||
@@ -145,7 +147,7 @@ func createBugReport(traefikConfiguration *TraefikConfiguration) (string, error)
|
||||
return bug.String(), nil
|
||||
}
|
||||
|
||||
func sendBugReport(body string) {
|
||||
func sendReport(body string) {
|
||||
URL := bugTracker + "?body=" + url.QueryEscape(body)
|
||||
if err := openBrowser(URL); err != nil {
|
||||
fmt.Printf("Please file a new issue at %s using this template:\n\n", bugTracker)
|
@@ -1,9 +1,10 @@
|
||||
package main
|
||||
package bug
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/containous/traefik/cmd/traefik/anonymize"
|
||||
"github.com/containous/traefik/anonymize"
|
||||
"github.com/containous/traefik/cmd"
|
||||
"github.com/containous/traefik/configuration"
|
||||
"github.com/containous/traefik/provider/file"
|
||||
"github.com/containous/traefik/tls"
|
||||
@@ -11,8 +12,8 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_createBugReport(t *testing.T) {
|
||||
traefikConfiguration := &TraefikConfiguration{
|
||||
func Test_createReport(t *testing.T) {
|
||||
traefikConfiguration := &cmd.TraefikConfiguration{
|
||||
ConfigFile: "FOO",
|
||||
GlobalConfiguration: configuration.GlobalConfiguration{
|
||||
EntryPoints: configuration.EntryPoints{
|
||||
@@ -37,7 +38,7 @@ func Test_createBugReport(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
report, err := createBugReport(traefikConfiguration)
|
||||
report, err := createReport(traefikConfiguration)
|
||||
assert.NoError(t, err, report)
|
||||
|
||||
// exported anonymous configuration
|
||||
@@ -47,7 +48,7 @@ func Test_createBugReport(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_anonymize_traefikConfiguration(t *testing.T) {
|
||||
traefikConfiguration := &TraefikConfiguration{
|
||||
traefikConfiguration := &cmd.TraefikConfiguration{
|
||||
ConfigFile: "FOO",
|
||||
GlobalConfiguration: configuration.GlobalConfiguration{
|
||||
EntryPoints: configuration.EntryPoints{
|
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"time"
|
||||
@@ -8,9 +8,13 @@ import (
|
||||
"github.com/containous/traefik/api"
|
||||
"github.com/containous/traefik/configuration"
|
||||
"github.com/containous/traefik/middlewares/accesslog"
|
||||
"github.com/containous/traefik/middlewares/tracing"
|
||||
"github.com/containous/traefik/middlewares/tracing/jaeger"
|
||||
"github.com/containous/traefik/middlewares/tracing/zipkin"
|
||||
"github.com/containous/traefik/ping"
|
||||
"github.com/containous/traefik/provider/boltdb"
|
||||
"github.com/containous/traefik/provider/consul"
|
||||
"github.com/containous/traefik/provider/consulcatalog"
|
||||
"github.com/containous/traefik/provider/docker"
|
||||
"github.com/containous/traefik/provider/dynamodb"
|
||||
"github.com/containous/traefik/provider/ecs"
|
||||
@@ -35,7 +39,7 @@ type TraefikConfiguration struct {
|
||||
|
||||
// NewTraefikDefaultPointersConfiguration creates a TraefikConfiguration with pointers default values
|
||||
func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||
//default Docker
|
||||
// default Docker
|
||||
var defaultDocker docker.Provider
|
||||
defaultDocker.Watch = true
|
||||
defaultDocker.ExposedByDefault = true
|
||||
@@ -45,7 +49,7 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||
// default File
|
||||
var defaultFile file.Provider
|
||||
defaultFile.Watch = true
|
||||
defaultFile.Filename = "" //needs equivalent to viper.ConfigFileUsed()
|
||||
defaultFile.Filename = "" // needs equivalent to viper.ConfigFileUsed()
|
||||
|
||||
// default Rest
|
||||
var defaultRest rest.Provider
|
||||
@@ -95,7 +99,7 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||
defaultConsul.Constraints = types.Constraints{}
|
||||
|
||||
// default CatalogProvider
|
||||
var defaultConsulCatalog consul.CatalogProvider
|
||||
var defaultConsulCatalog consulcatalog.Provider
|
||||
defaultConsulCatalog.Endpoint = "127.0.0.1:8500"
|
||||
defaultConsulCatalog.ExposedByDefault = true
|
||||
defaultConsulCatalog.Constraints = types.Constraints{}
|
||||
@@ -109,25 +113,23 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||
defaultEtcd.Prefix = "/traefik"
|
||||
defaultEtcd.Constraints = types.Constraints{}
|
||||
|
||||
//default Zookeeper
|
||||
// default Zookeeper
|
||||
var defaultZookeeper zk.Provider
|
||||
defaultZookeeper.Watch = true
|
||||
defaultZookeeper.Endpoint = "127.0.0.1:2181"
|
||||
defaultZookeeper.Prefix = "traefik"
|
||||
defaultZookeeper.Constraints = types.Constraints{}
|
||||
|
||||
//default Boltdb
|
||||
// default Boltdb
|
||||
var defaultBoltDb boltdb.Provider
|
||||
defaultBoltDb.Watch = true
|
||||
defaultBoltDb.Endpoint = "127.0.0.1:4001"
|
||||
defaultBoltDb.Prefix = "/traefik"
|
||||
defaultBoltDb.Constraints = types.Constraints{}
|
||||
|
||||
//default Kubernetes
|
||||
// default Kubernetes
|
||||
var defaultKubernetes kubernetes.Provider
|
||||
defaultKubernetes.Watch = true
|
||||
defaultKubernetes.Endpoint = ""
|
||||
defaultKubernetes.LabelSelector = ""
|
||||
defaultKubernetes.Constraints = types.Constraints{}
|
||||
|
||||
// default Mesos
|
||||
@@ -140,7 +142,7 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||
defaultMesos.ZkDetectionTimeout = 30
|
||||
defaultMesos.StateTimeoutSecond = 30
|
||||
|
||||
//default ECS
|
||||
// default ECS
|
||||
var defaultECS ecs.Provider
|
||||
defaultECS.Watch = true
|
||||
defaultECS.ExposedByDefault = true
|
||||
@@ -149,7 +151,7 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||
defaultECS.RefreshSeconds = 15
|
||||
defaultECS.Constraints = types.Constraints{}
|
||||
|
||||
//default Rancher
|
||||
// default Rancher
|
||||
var defaultRancher rancher.Provider
|
||||
defaultRancher.Watch = true
|
||||
defaultRancher.ExposedByDefault = true
|
||||
@@ -164,7 +166,7 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||
|
||||
// default Eureka
|
||||
var defaultEureka eureka.Provider
|
||||
defaultEureka.Delay = "30s"
|
||||
defaultEureka.RefreshSeconds = flaeg.Duration(30 * time.Second)
|
||||
|
||||
// default ServiceFabric
|
||||
var defaultServiceFabric servicefabric.Provider
|
||||
@@ -186,6 +188,13 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||
defaultAccessLog := types.AccessLog{
|
||||
Format: accesslog.CommonFormat,
|
||||
FilePath: "",
|
||||
Filters: &types.AccessLogFilters{},
|
||||
Fields: &types.AccessLogFields{
|
||||
DefaultMode: types.AccessLogKeep,
|
||||
Headers: &types.FieldHeaders{
|
||||
DefaultMode: types.AccessLogKeep,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// default HealthCheckConfig
|
||||
@@ -203,6 +212,24 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||
DialTimeout: flaeg.Duration(configuration.DefaultDialTimeout),
|
||||
}
|
||||
|
||||
// default Tracing
|
||||
defaultTracing := tracing.Tracing{
|
||||
Backend: "jaeger",
|
||||
ServiceName: "traefik",
|
||||
Jaeger: &jaeger.Config{
|
||||
SamplingServerURL: "http://localhost:5778/sampling",
|
||||
SamplingType: "const",
|
||||
SamplingParam: 1.0,
|
||||
LocalAgentHostPort: "127.0.0.1:6831",
|
||||
},
|
||||
Zipkin: &zipkin.Config{
|
||||
HTTPEndpoint: "http://localhost:9411/api/v1/spans",
|
||||
SameSpan: false,
|
||||
ID128Bit: true,
|
||||
Debug: false,
|
||||
},
|
||||
}
|
||||
|
||||
// default LifeCycle
|
||||
defaultLifeCycle := configuration.LifeCycle{
|
||||
GraceTimeOut: flaeg.Duration(configuration.DefaultGraceTimeout),
|
||||
@@ -264,6 +291,7 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||
Ping: &defaultPing,
|
||||
API: &defaultAPI,
|
||||
Metrics: &defaultMetrics,
|
||||
Tracing: &defaultTracing,
|
||||
}
|
||||
|
||||
return &TraefikConfiguration{
|
||||
@@ -277,7 +305,6 @@ func NewTraefikConfiguration() *TraefikConfiguration {
|
||||
GlobalConfiguration: configuration.GlobalConfiguration{
|
||||
AccessLogsFile: "",
|
||||
TraefikLogsFile: "",
|
||||
LogLevel: "ERROR",
|
||||
EntryPoints: map[string]*configuration.EntryPoint{},
|
||||
Constraints: types.Constraints{},
|
||||
DefaultEntryPoints: []string{"http"},
|
22
cmd/context.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// ContextWithSignal create a context cancelled when SIGINT or SIGTERM are notified
|
||||
func ContextWithSignal(ctx context.Context) context.Context {
|
||||
newCtx, cancel := context.WithCancel(ctx)
|
||||
signals := make(chan os.Signal)
|
||||
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
|
||||
go func() {
|
||||
select {
|
||||
case <-signals:
|
||||
cancel()
|
||||
}
|
||||
}()
|
||||
return newCtx
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package healthcheck
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
@@ -9,27 +9,29 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/traefik/cmd"
|
||||
"github.com/containous/traefik/configuration"
|
||||
)
|
||||
|
||||
func newHealthCheckCmd(traefikConfiguration *TraefikConfiguration, traefikPointersConfiguration *TraefikConfiguration) *flaeg.Command {
|
||||
// NewCmd builds a new HealthCheck command
|
||||
func NewCmd(traefikConfiguration *cmd.TraefikConfiguration, traefikPointersConfiguration *cmd.TraefikConfiguration) *flaeg.Command {
|
||||
return &flaeg.Command{
|
||||
Name: "healthcheck",
|
||||
Description: `Calls traefik /ping to check health (web provider must be enabled)`,
|
||||
Config: traefikConfiguration,
|
||||
DefaultPointersConfig: traefikPointersConfiguration,
|
||||
Run: runHealthCheck(traefikConfiguration),
|
||||
Run: runCmd(traefikConfiguration),
|
||||
Metadata: map[string]string{
|
||||
"parseAllSources": "true",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func runHealthCheck(traefikConfiguration *TraefikConfiguration) func() error {
|
||||
func runCmd(traefikConfiguration *cmd.TraefikConfiguration) func() error {
|
||||
return func() error {
|
||||
traefikConfiguration.GlobalConfiguration.SetEffectiveConfiguration(traefikConfiguration.ConfigFile)
|
||||
|
||||
resp, errPing := healthCheck(traefikConfiguration.GlobalConfiguration)
|
||||
resp, errPing := Do(traefikConfiguration.GlobalConfiguration)
|
||||
if errPing != nil {
|
||||
fmt.Printf("Error calling healthcheck: %s\n", errPing)
|
||||
os.Exit(1)
|
||||
@@ -44,11 +46,11 @@ func runHealthCheck(traefikConfiguration *TraefikConfiguration) func() error {
|
||||
}
|
||||
}
|
||||
|
||||
func healthCheck(globalConfiguration configuration.GlobalConfiguration) (*http.Response, error) {
|
||||
// Do try to do a healthcheck
|
||||
func Do(globalConfiguration configuration.GlobalConfiguration) (*http.Response, error) {
|
||||
if globalConfiguration.Ping == nil {
|
||||
return nil, errors.New("please enable `ping` to use health check")
|
||||
}
|
||||
|
||||
pingEntryPoint, ok := globalConfiguration.EntryPoints[globalConfiguration.Ping.EntryPoint]
|
||||
if !ok {
|
||||
return nil, errors.New("missing `ping` entrypoint")
|
@@ -1,18 +1,23 @@
|
||||
package main
|
||||
package storeconfig
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
stdlog "log"
|
||||
"os"
|
||||
|
||||
"github.com/abronan/valkeyrie/store"
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/staert"
|
||||
"github.com/containous/traefik/acme"
|
||||
"github.com/containous/traefik/cluster"
|
||||
"github.com/docker/libkv/store"
|
||||
"github.com/containous/traefik/cmd"
|
||||
"github.com/containous/traefik/log"
|
||||
)
|
||||
|
||||
func newStoreConfigCmd(traefikConfiguration *TraefikConfiguration, traefikPointersConfiguration *TraefikConfiguration) *flaeg.Command {
|
||||
// NewCmd builds a new StoreConfig command
|
||||
func NewCmd(traefikConfiguration *cmd.TraefikConfiguration, traefikPointersConfiguration *cmd.TraefikConfiguration) *flaeg.Command {
|
||||
return &flaeg.Command{
|
||||
Name: "storeconfig",
|
||||
Description: `Store the static traefik configuration into a Key-value stores. Traefik will not start.`,
|
||||
@@ -24,7 +29,8 @@ func newStoreConfigCmd(traefikConfiguration *TraefikConfiguration, traefikPointe
|
||||
}
|
||||
}
|
||||
|
||||
func runStoreConfig(kv *staert.KvSource, traefikConfiguration *TraefikConfiguration) func() error {
|
||||
// Run store config in KV
|
||||
func Run(kv *staert.KvSource, traefikConfiguration *cmd.TraefikConfiguration) func() error {
|
||||
return func() error {
|
||||
if kv == nil {
|
||||
return fmt.Errorf("error using command storeconfig, no Key-value store defined")
|
||||
@@ -56,7 +62,7 @@ func runStoreConfig(kv *staert.KvSource, traefikConfiguration *TraefikConfigurat
|
||||
}
|
||||
|
||||
stdlog.Printf("Storing file configuration: %s\n", jsonConf)
|
||||
config, err := fileConfig.LoadConfig()
|
||||
config, err := fileConfig.BuildConfiguration()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -67,24 +73,20 @@ func runStoreConfig(kv *staert.KvSource, traefikConfiguration *TraefikConfigurat
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if traefikConfiguration.GlobalConfiguration.ACME != nil {
|
||||
var object cluster.Object
|
||||
account := &acme.Account{}
|
||||
|
||||
// Migrate ACME data from file to KV store if needed
|
||||
if len(traefikConfiguration.GlobalConfiguration.ACME.StorageFile) > 0 {
|
||||
// convert ACME json file to KV store
|
||||
localStore := acme.NewLocalStore(traefikConfiguration.GlobalConfiguration.ACME.StorageFile)
|
||||
object, err = localStore.Load()
|
||||
account, err = migrateACMEData(traefikConfiguration.GlobalConfiguration.ACME.StorageFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
} else {
|
||||
// Create an empty account to create all the keys into the KV store
|
||||
account := &acme.Account{}
|
||||
account.Init()
|
||||
object = account
|
||||
}
|
||||
|
||||
meta := cluster.NewMetadata(object)
|
||||
// Store the ACME Account into the KV Store
|
||||
meta := cluster.NewMetadata(account)
|
||||
err = meta.Marshall()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -94,23 +96,68 @@ func runStoreConfig(kv *staert.KvSource, traefikConfiguration *TraefikConfigurat
|
||||
Store: kv,
|
||||
Prefix: traefikConfiguration.GlobalConfiguration.ACME.Storage,
|
||||
}
|
||||
|
||||
err = source.StoreConfig(meta)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Force to delete storagefile
|
||||
err = kv.Delete(kv.Prefix + "/acme/storagefile")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return kv.Delete(kv.Prefix + "/acme/storagefile")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// createKvSource creates KvSource
|
||||
// migrateACMEData allows migrating data from acme.json file to KV store in function of the file format
|
||||
func migrateACMEData(fileName string) (*acme.Account, error) {
|
||||
|
||||
f, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
file, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check if the storage file is not empty before to get data
|
||||
account := &acme.Account{}
|
||||
if len(file) > 0 {
|
||||
accountFromNewFormat, err := acme.FromNewToOldFormat(fileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if accountFromNewFormat == nil {
|
||||
// convert ACME json file to KV store (used for backward compatibility)
|
||||
localStore := acme.NewLocalStore(fileName)
|
||||
|
||||
account, err = localStore.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = account.RemoveAccountV1Values()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
account = accountFromNewFormat
|
||||
}
|
||||
} else {
|
||||
log.Warnf("No data will be imported from the storageFile %q because it is empty.", fileName)
|
||||
}
|
||||
|
||||
err = account.Init()
|
||||
return account, err
|
||||
}
|
||||
|
||||
// CreateKvSource creates KvSource
|
||||
// TLS support is enable for Consul and Etcd backends
|
||||
func createKvSource(traefikConfiguration *TraefikConfiguration) (*staert.KvSource, error) {
|
||||
func CreateKvSource(traefikConfiguration *cmd.TraefikConfiguration) (*staert.KvSource, error) {
|
||||
var kv *staert.KvSource
|
||||
var kvStore store.Store
|
||||
var err error
|
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
fmtlog "log"
|
||||
"net/http"
|
||||
@@ -10,32 +11,39 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/cenk/backoff"
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/staert"
|
||||
"github.com/containous/traefik/acme"
|
||||
"github.com/containous/traefik/cmd"
|
||||
"github.com/containous/traefik/cmd/bug"
|
||||
"github.com/containous/traefik/cmd/healthcheck"
|
||||
"github.com/containous/traefik/cmd/storeconfig"
|
||||
cmdVersion "github.com/containous/traefik/cmd/version"
|
||||
"github.com/containous/traefik/collector"
|
||||
"github.com/containous/traefik/configuration"
|
||||
"github.com/containous/traefik/job"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/provider/acme"
|
||||
"github.com/containous/traefik/provider/ecs"
|
||||
"github.com/containous/traefik/provider/kubernetes"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/containous/traefik/server"
|
||||
"github.com/containous/traefik/server/uuid"
|
||||
traefikTls "github.com/containous/traefik/tls"
|
||||
traefiktls "github.com/containous/traefik/tls"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/containous/traefik/version"
|
||||
"github.com/coreos/go-systemd/daemon"
|
||||
"github.com/ogier/pflag"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/vulcand/oxy/roundrobin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
//traefik config inits
|
||||
traefikConfiguration := NewTraefikConfiguration()
|
||||
traefikPointersConfiguration := NewTraefikDefaultPointersConfiguration()
|
||||
//traefik Command init
|
||||
// traefik config inits
|
||||
traefikConfiguration := cmd.NewTraefikConfiguration()
|
||||
traefikPointersConfiguration := cmd.NewTraefikDefaultPointersConfiguration()
|
||||
|
||||
// traefik Command init
|
||||
traefikCmd := &flaeg.Command{
|
||||
Name: "traefik",
|
||||
Description: `traefik is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
|
||||
@@ -43,36 +51,39 @@ Complete documentation is available at https://traefik.io`,
|
||||
Config: traefikConfiguration,
|
||||
DefaultPointersConfig: traefikPointersConfiguration,
|
||||
Run: func() error {
|
||||
run(&traefikConfiguration.GlobalConfiguration, traefikConfiguration.ConfigFile)
|
||||
runCmd(&traefikConfiguration.GlobalConfiguration, traefikConfiguration.ConfigFile)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
//storeconfig Command init
|
||||
storeConfigCmd := newStoreConfigCmd(traefikConfiguration, traefikPointersConfiguration)
|
||||
// storeconfig Command init
|
||||
storeConfigCmd := storeconfig.NewCmd(traefikConfiguration, traefikPointersConfiguration)
|
||||
|
||||
//init flaeg source
|
||||
// init flaeg source
|
||||
f := flaeg.New(traefikCmd, os.Args[1:])
|
||||
//add custom parsers
|
||||
// add custom parsers
|
||||
f.AddParser(reflect.TypeOf(configuration.EntryPoints{}), &configuration.EntryPoints{})
|
||||
f.AddParser(reflect.TypeOf(configuration.DefaultEntryPoints{}), &configuration.DefaultEntryPoints{})
|
||||
f.AddParser(reflect.TypeOf(traefikTls.RootCAs{}), &traefikTls.RootCAs{})
|
||||
f.AddParser(reflect.TypeOf(traefiktls.RootCAs{}), &traefiktls.RootCAs{})
|
||||
f.AddParser(reflect.TypeOf(types.Constraints{}), &types.Constraints{})
|
||||
f.AddParser(reflect.TypeOf(kubernetes.Namespaces{}), &kubernetes.Namespaces{})
|
||||
f.AddParser(reflect.TypeOf(ecs.Clusters{}), &ecs.Clusters{})
|
||||
f.AddParser(reflect.TypeOf([]acme.Domain{}), &acme.Domains{})
|
||||
f.AddParser(reflect.TypeOf([]types.Domain{}), &types.Domains{})
|
||||
f.AddParser(reflect.TypeOf(types.Buckets{}), &types.Buckets{})
|
||||
f.AddParser(reflect.TypeOf(types.StatusCodes{}), &types.StatusCodes{})
|
||||
f.AddParser(reflect.TypeOf(types.FieldNames{}), &types.FieldNames{})
|
||||
f.AddParser(reflect.TypeOf(types.FieldHeaderNames{}), &types.FieldHeaderNames{})
|
||||
|
||||
//add commands
|
||||
f.AddCommand(newVersionCmd())
|
||||
f.AddCommand(newBugCmd(traefikConfiguration, traefikPointersConfiguration))
|
||||
// add commands
|
||||
f.AddCommand(cmdVersion.NewCmd())
|
||||
f.AddCommand(bug.NewCmd(traefikConfiguration, traefikPointersConfiguration))
|
||||
f.AddCommand(storeConfigCmd)
|
||||
f.AddCommand(newHealthCheckCmd(traefikConfiguration, traefikPointersConfiguration))
|
||||
f.AddCommand(healthcheck.NewCmd(traefikConfiguration, traefikPointersConfiguration))
|
||||
|
||||
usedCmd, err := f.GetCommand()
|
||||
if err != nil {
|
||||
fmtlog.Println(err)
|
||||
os.Exit(-1)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if _, err := f.Parse(usedCmd); err != nil {
|
||||
@@ -80,32 +91,32 @@ Complete documentation is available at https://traefik.io`,
|
||||
os.Exit(0)
|
||||
}
|
||||
fmtlog.Printf("Error parsing command: %s\n", err)
|
||||
os.Exit(-1)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
//staert init
|
||||
// staert init
|
||||
s := staert.NewStaert(traefikCmd)
|
||||
//init toml source
|
||||
// init TOML source
|
||||
toml := staert.NewTomlSource("traefik", []string{traefikConfiguration.ConfigFile, "/etc/traefik/", "$HOME/.traefik/", "."})
|
||||
|
||||
//add sources to staert
|
||||
// add sources to staert
|
||||
s.AddSource(toml)
|
||||
s.AddSource(f)
|
||||
if _, err := s.LoadConfig(); err != nil {
|
||||
fmtlog.Printf("Error reading TOML config file %s : %s\n", toml.ConfigFileUsed(), err)
|
||||
os.Exit(-1)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
traefikConfiguration.ConfigFile = toml.ConfigFileUsed()
|
||||
|
||||
kv, err := createKvSource(traefikConfiguration)
|
||||
kv, err := storeconfig.CreateKvSource(traefikConfiguration)
|
||||
if err != nil {
|
||||
fmtlog.Printf("Error creating kv store: %s\n", err)
|
||||
os.Exit(-1)
|
||||
os.Exit(1)
|
||||
}
|
||||
storeConfigCmd.Run = runStoreConfig(kv, traefikConfiguration)
|
||||
storeConfigCmd.Run = storeconfig.Run(kv, traefikConfiguration)
|
||||
|
||||
// IF a KV Store is enable and no sub-command called in args
|
||||
// if a KV Store is enable and no sub-command called in args
|
||||
if kv != nil && usedCmd == traefikCmd {
|
||||
if traefikConfiguration.Cluster == nil {
|
||||
traefikConfiguration.Cluster = &types.Cluster{Node: uuid.Get()}
|
||||
@@ -124,19 +135,19 @@ Complete documentation is available at https://traefik.io`,
|
||||
err := backoff.RetryNotify(safe.OperationWithRecover(operation), job.NewBackOff(backoff.NewExponentialBackOff()), notify)
|
||||
if err != nil {
|
||||
fmtlog.Printf("Error loading configuration: %s\n", err)
|
||||
os.Exit(-1)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.Run(); err != nil {
|
||||
fmtlog.Printf("Error running traefik: %s\n", err)
|
||||
os.Exit(-1)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func run(globalConfiguration *configuration.GlobalConfiguration, configFile string) {
|
||||
func runCmd(globalConfiguration *configuration.GlobalConfiguration, configFile string) {
|
||||
configureLogging(globalConfiguration)
|
||||
|
||||
if len(configFile) > 0 {
|
||||
@@ -145,6 +156,10 @@ func run(globalConfiguration *configuration.GlobalConfiguration, configFile stri
|
||||
|
||||
http.DefaultTransport.(*http.Transport).Proxy = http.ProxyFromEnvironment
|
||||
|
||||
if globalConfiguration.AllowMinWeightZero {
|
||||
roundrobin.SetDefaultWeight(0)
|
||||
}
|
||||
|
||||
globalConfiguration.SetEffectiveConfiguration(configFile)
|
||||
globalConfiguration.ValidateConfiguration()
|
||||
|
||||
@@ -158,8 +173,17 @@ func run(globalConfiguration *configuration.GlobalConfiguration, configFile stri
|
||||
stats(globalConfiguration)
|
||||
|
||||
log.Debugf("Global configuration loaded %s", string(jsonConf))
|
||||
svr := server.NewServer(*globalConfiguration)
|
||||
svr.Start()
|
||||
if acme.IsEnabled() {
|
||||
store := acme.NewLocalStore(acme.Get().Storage)
|
||||
acme.Get().Store = store
|
||||
}
|
||||
svr := server.NewServer(*globalConfiguration, configuration.NewProviderAggregator(globalConfiguration))
|
||||
if acme.IsEnabled() && acme.Get().OnHostRule {
|
||||
acme.Get().SetConfigListenerChan(make(chan types.Configuration))
|
||||
svr.AddListener(acme.Get().ListenConfiguration)
|
||||
}
|
||||
ctx := cmd.ContextWithSignal(context.Background())
|
||||
svr.StartWithContext(ctx)
|
||||
defer svr.Close()
|
||||
|
||||
sent, err := daemon.SdNotify(false, "READY=1")
|
||||
@@ -177,7 +201,7 @@ func run(globalConfiguration *configuration.GlobalConfiguration, configFile stri
|
||||
safe.Go(func() {
|
||||
tick := time.Tick(t)
|
||||
for range tick {
|
||||
_, errHealthCheck := healthCheck(*globalConfiguration)
|
||||
_, errHealthCheck := healthcheck.Do(*globalConfiguration)
|
||||
if globalConfiguration.Ping == nil || errHealthCheck == nil {
|
||||
if ok, _ := daemon.SdNotify(false, "WATCHDOG=1"); !ok {
|
||||
log.Error("Fail to tick watchdog")
|
||||
@@ -198,12 +222,18 @@ func configureLogging(globalConfiguration *configuration.GlobalConfiguration) {
|
||||
// configure default log flags
|
||||
fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags)
|
||||
|
||||
if globalConfiguration.Debug {
|
||||
globalConfiguration.LogLevel = "DEBUG"
|
||||
}
|
||||
|
||||
// configure log level
|
||||
level, err := logrus.ParseLevel(strings.ToLower(globalConfiguration.LogLevel))
|
||||
// an explicitly defined log level always has precedence. if none is
|
||||
// given and debug mode is disabled, the default is ERROR, and DEBUG
|
||||
// otherwise.
|
||||
levelStr := strings.ToLower(globalConfiguration.LogLevel)
|
||||
if levelStr == "" {
|
||||
levelStr = "error"
|
||||
if globalConfiguration.Debug {
|
||||
levelStr = "debug"
|
||||
}
|
||||
}
|
||||
level, err := logrus.ParseLevel(levelStr)
|
||||
if err != nil {
|
||||
log.Error("Error getting level", err)
|
||||
}
|
||||
@@ -223,10 +253,7 @@ func configureLogging(globalConfiguration *configuration.GlobalConfiguration) {
|
||||
if globalConfiguration.TraefikLog != nil && globalConfiguration.TraefikLog.Format == "json" {
|
||||
formatter = &logrus.JSONFormatter{}
|
||||
} else {
|
||||
disableColors := false
|
||||
if len(logFile) > 0 {
|
||||
disableColors = true
|
||||
}
|
||||
disableColors := len(logFile) > 0
|
||||
formatter = &logrus.TextFormatter{DisableColors: disableColors, FullTimestamp: true, DisableSorting: true}
|
||||
}
|
||||
log.SetFormatter(formatter)
|
||||
@@ -234,8 +261,7 @@ func configureLogging(globalConfiguration *configuration.GlobalConfiguration) {
|
||||
if len(logFile) > 0 {
|
||||
dir := filepath.Dir(logFile)
|
||||
|
||||
err := os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
log.Errorf("Failed to create log path %s: %s", dir, err)
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package version
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -17,17 +17,15 @@ Go version: {{.GoVersion}}
|
||||
Built: {{.BuildTime}}
|
||||
OS/Arch: {{.Os}}/{{.Arch}}`
|
||||
|
||||
// newVersionCmd builds a new Version command
|
||||
func newVersionCmd() *flaeg.Command {
|
||||
|
||||
//version Command init
|
||||
// NewCmd builds a new Version command
|
||||
func NewCmd() *flaeg.Command {
|
||||
return &flaeg.Command{
|
||||
Name: "version",
|
||||
Description: `Print version`,
|
||||
Config: struct{}{},
|
||||
DefaultPointersConfig: struct{}{},
|
||||
Run: func() error {
|
||||
if err := getVersionPrint(os.Stdout); err != nil {
|
||||
if err := GetPrint(os.Stdout); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Print("\n")
|
||||
@@ -37,7 +35,8 @@ func newVersionCmd() *flaeg.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func getVersionPrint(wr io.Writer) error {
|
||||
// GetPrint write Printable version
|
||||
func GetPrint(wr io.Writer) error {
|
||||
tmpl, err := template.New("").Parse(versionTemplate)
|
||||
if err != nil {
|
||||
return err
|
@@ -9,7 +9,7 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/cmd/traefik/anonymize"
|
||||
"github.com/containous/traefik/anonymize"
|
||||
"github.com/containous/traefik/configuration"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/version"
|
||||
|
@@ -10,9 +10,14 @@ import (
|
||||
"github.com/containous/traefik/acme"
|
||||
"github.com/containous/traefik/api"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/middlewares/tracing"
|
||||
"github.com/containous/traefik/middlewares/tracing/jaeger"
|
||||
"github.com/containous/traefik/middlewares/tracing/zipkin"
|
||||
"github.com/containous/traefik/ping"
|
||||
acmeprovider "github.com/containous/traefik/provider/acme"
|
||||
"github.com/containous/traefik/provider/boltdb"
|
||||
"github.com/containous/traefik/provider/consul"
|
||||
"github.com/containous/traefik/provider/consulcatalog"
|
||||
"github.com/containous/traefik/provider/docker"
|
||||
"github.com/containous/traefik/provider/dynamodb"
|
||||
"github.com/containous/traefik/provider/ecs"
|
||||
@@ -45,6 +50,9 @@ const (
|
||||
// DefaultGraceTimeout controls how long Traefik serves pending requests
|
||||
// prior to shutting down.
|
||||
DefaultGraceTimeout = 10 * time.Second
|
||||
|
||||
// DefaultAcmeCAServer is the default ACME API endpoint
|
||||
DefaultAcmeCAServer = "https://acme-v02.api.letsencrypt.org/directory"
|
||||
)
|
||||
|
||||
// GlobalConfiguration holds global configuration (with providers, etc.).
|
||||
@@ -59,6 +67,7 @@ type GlobalConfiguration struct {
|
||||
AccessLog *types.AccessLog `description:"Access log settings" export:"true"`
|
||||
TraefikLogsFile string `description:"(Deprecated) Traefik logs file. Stdout is used when omitted or empty" export:"true"` // Deprecated
|
||||
TraefikLog *types.TraefikLog `description:"Traefik log settings" export:"true"`
|
||||
Tracing *tracing.Tracing `description:"OpenTracing configuration" export:"true"`
|
||||
LogLevel string `short:"l" description:"Log level" export:"true"`
|
||||
EntryPoints EntryPoints `description:"Entrypoints definition using format: --entryPoints='Name:http Address::8000 Redirect.EntryPoint:https' --entryPoints='Name:https Address::4442 TLS:tests/traefik.crt,tests/traefik.key;prod/traefik.crt,prod/traefik.key'" export:"true"`
|
||||
Cluster *types.Cluster `description:"Enable clustering" export:"true"`
|
||||
@@ -74,12 +83,13 @@ type GlobalConfiguration struct {
|
||||
HealthCheck *HealthCheckConfig `description:"Health check parameters" export:"true"`
|
||||
RespondingTimeouts *RespondingTimeouts `description:"Timeouts for incoming requests to the Traefik instance" export:"true"`
|
||||
ForwardingTimeouts *ForwardingTimeouts `description:"Timeouts for requests forwarded to the backend servers" export:"true"`
|
||||
AllowMinWeightZero bool `description:"Allow weight to take 0 as minimum real value." export:"true"` // Deprecated
|
||||
Web *WebCompatibility `description:"(Deprecated) Enable Web backend with default settings" export:"true"` // Deprecated
|
||||
Docker *docker.Provider `description:"Enable Docker backend with default settings" export:"true"`
|
||||
File *file.Provider `description:"Enable File backend with default settings" export:"true"`
|
||||
Marathon *marathon.Provider `description:"Enable Marathon backend with default settings" export:"true"`
|
||||
Consul *consul.Provider `description:"Enable Consul backend with default settings" export:"true"`
|
||||
ConsulCatalog *consul.CatalogProvider `description:"Enable Consul catalog backend with default settings" export:"true"`
|
||||
ConsulCatalog *consulcatalog.Provider `description:"Enable Consul catalog backend with default settings" export:"true"`
|
||||
Etcd *etcd.Provider `description:"Enable Etcd backend with default settings" export:"true"`
|
||||
Zookeeper *zk.Provider `description:"Enable Zookeeper backend with default settings" export:"true"`
|
||||
Boltdb *boltdb.Provider `description:"Enable Boltdb backend with default settings" export:"true"`
|
||||
@@ -98,13 +108,13 @@ type GlobalConfiguration struct {
|
||||
|
||||
// WebCompatibility is a configuration to handle compatibility with deprecated web provider options
|
||||
type WebCompatibility struct {
|
||||
Address string `description:"Web administration port" export:"true"`
|
||||
CertFile string `description:"SSL certificate" export:"true"`
|
||||
KeyFile string `description:"SSL certificate" export:"true"`
|
||||
ReadOnly bool `description:"Enable read only API" export:"true"`
|
||||
Statistics *types.Statistics `description:"Enable more detailed statistics" export:"true"`
|
||||
Metrics *types.Metrics `description:"Enable a metrics exporter" export:"true"`
|
||||
Path string `description:"Root path for dashboard and API" export:"true"`
|
||||
Address string `description:"(Deprecated) Web administration port" export:"true"`
|
||||
CertFile string `description:"(Deprecated) SSL certificate" export:"true"`
|
||||
KeyFile string `description:"(Deprecated) SSL certificate" export:"true"`
|
||||
ReadOnly bool `description:"(Deprecated) Enable read only API" export:"true"`
|
||||
Statistics *types.Statistics `description:"(Deprecated) Enable more detailed statistics" export:"true"`
|
||||
Metrics *types.Metrics `description:"(Deprecated) Enable a metrics exporter" export:"true"`
|
||||
Path string `description:"(Deprecated) Root path for dashboard and API" export:"true"`
|
||||
Auth *types.Auth `export:"true"`
|
||||
Debug bool `export:"true"`
|
||||
}
|
||||
@@ -178,12 +188,23 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
|
||||
}
|
||||
}
|
||||
|
||||
// ForwardedHeaders must be remove in the next breaking version
|
||||
for entryPointName := range gc.EntryPoints {
|
||||
entryPoint := gc.EntryPoints[entryPointName]
|
||||
// ForwardedHeaders must be remove in the next breaking version
|
||||
if entryPoint.ForwardedHeaders == nil {
|
||||
entryPoint.ForwardedHeaders = &ForwardedHeaders{Insecure: true}
|
||||
}
|
||||
|
||||
if len(entryPoint.WhitelistSourceRange) > 0 {
|
||||
log.Warnf("Deprecated configuration found: %s. Please use %s.", "whiteListSourceRange", "whiteList.sourceRange")
|
||||
|
||||
if entryPoint.WhiteList == nil {
|
||||
entryPoint.WhiteList = &types.WhiteList{
|
||||
SourceRange: entryPoint.WhitelistSourceRange,
|
||||
}
|
||||
entryPoint.WhitelistSourceRange = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure LifeCycle isn't nil to spare nil checks elsewhere.
|
||||
@@ -197,7 +218,66 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
|
||||
gc.LifeCycle.GraceTimeOut = gc.GraceTimeOut
|
||||
}
|
||||
|
||||
if gc.Docker != nil {
|
||||
if len(gc.Docker.Filename) != 0 && gc.Docker.TemplateVersion != 2 {
|
||||
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
|
||||
gc.Docker.TemplateVersion = 1
|
||||
} else {
|
||||
gc.Docker.TemplateVersion = 2
|
||||
}
|
||||
}
|
||||
|
||||
if gc.Marathon != nil {
|
||||
if len(gc.Marathon.Filename) != 0 && gc.Marathon.TemplateVersion != 2 {
|
||||
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
|
||||
gc.Marathon.TemplateVersion = 1
|
||||
} else {
|
||||
gc.Marathon.TemplateVersion = 2
|
||||
}
|
||||
}
|
||||
|
||||
if gc.Mesos != nil {
|
||||
if len(gc.Mesos.Filename) != 0 && gc.Mesos.TemplateVersion != 2 {
|
||||
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
|
||||
gc.Mesos.TemplateVersion = 1
|
||||
} else {
|
||||
gc.Mesos.TemplateVersion = 2
|
||||
}
|
||||
}
|
||||
|
||||
if gc.Eureka != nil {
|
||||
if gc.Eureka.Delay != 0 {
|
||||
log.Warn("Delay has been deprecated -- please use RefreshSeconds")
|
||||
gc.Eureka.RefreshSeconds = gc.Eureka.Delay
|
||||
}
|
||||
}
|
||||
|
||||
if gc.ECS != nil {
|
||||
if len(gc.ECS.Filename) != 0 && gc.ECS.TemplateVersion != 2 {
|
||||
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
|
||||
gc.ECS.TemplateVersion = 1
|
||||
} else {
|
||||
gc.ECS.TemplateVersion = 2
|
||||
}
|
||||
}
|
||||
|
||||
if gc.ConsulCatalog != nil {
|
||||
if len(gc.ConsulCatalog.Filename) != 0 && gc.ConsulCatalog.TemplateVersion != 2 {
|
||||
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
|
||||
gc.ConsulCatalog.TemplateVersion = 1
|
||||
} else {
|
||||
gc.ConsulCatalog.TemplateVersion = 2
|
||||
}
|
||||
}
|
||||
|
||||
if gc.Rancher != nil {
|
||||
if len(gc.Rancher.Filename) != 0 && gc.Rancher.TemplateVersion != 2 {
|
||||
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
|
||||
gc.Rancher.TemplateVersion = 1
|
||||
} else {
|
||||
gc.Rancher.TemplateVersion = 2
|
||||
}
|
||||
|
||||
// Ensure backwards compatibility for now
|
||||
if len(gc.Rancher.AccessKey) > 0 ||
|
||||
len(gc.Rancher.Endpoint) > 0 ||
|
||||
@@ -223,26 +303,64 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
|
||||
gc.API.Debug = gc.Debug
|
||||
}
|
||||
|
||||
if gc.Debug {
|
||||
gc.LogLevel = "DEBUG"
|
||||
}
|
||||
|
||||
if gc.Web != nil && (gc.Web.Path == "" || !strings.HasSuffix(gc.Web.Path, "/")) {
|
||||
gc.Web.Path += "/"
|
||||
}
|
||||
|
||||
// Try to fallback to traefik config file in case the file provider is enabled
|
||||
// but has no file name configured.
|
||||
if gc.File != nil && len(gc.File.Filename) == 0 {
|
||||
if len(configFile) > 0 {
|
||||
gc.File.Filename = configFile
|
||||
} else {
|
||||
log.Errorln("Error using file configuration backend, no filename defined")
|
||||
}
|
||||
if gc.File != nil {
|
||||
gc.File.TraefikFile = configFile
|
||||
}
|
||||
|
||||
gc.initACMEProvider()
|
||||
gc.initTracing()
|
||||
}
|
||||
|
||||
func (gc *GlobalConfiguration) initTracing() {
|
||||
if gc.Tracing != nil {
|
||||
switch gc.Tracing.Backend {
|
||||
case jaeger.Name:
|
||||
if gc.Tracing.Jaeger == nil {
|
||||
gc.Tracing.Jaeger = &jaeger.Config{
|
||||
SamplingServerURL: "http://localhost:5778/sampling",
|
||||
SamplingType: "const",
|
||||
SamplingParam: 1.0,
|
||||
LocalAgentHostPort: "127.0.0.1:6831",
|
||||
}
|
||||
}
|
||||
if gc.Tracing.Zipkin != nil {
|
||||
log.Warn("Zipkin configuration will be ignored")
|
||||
gc.Tracing.Zipkin = nil
|
||||
}
|
||||
case zipkin.Name:
|
||||
if gc.Tracing.Zipkin == nil {
|
||||
gc.Tracing.Zipkin = &zipkin.Config{
|
||||
HTTPEndpoint: "http://localhost:9411/api/v1/spans",
|
||||
SameSpan: false,
|
||||
ID128Bit: true,
|
||||
Debug: false,
|
||||
}
|
||||
}
|
||||
if gc.Tracing.Jaeger != nil {
|
||||
log.Warn("Jaeger configuration will be ignored")
|
||||
gc.Tracing.Jaeger = nil
|
||||
}
|
||||
default:
|
||||
log.Warnf("Unknown tracer %q", gc.Tracing.Backend)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (gc *GlobalConfiguration) initACMEProvider() {
|
||||
if gc.ACME != nil {
|
||||
// TODO: to remove in the futurs
|
||||
gc.ACME.CAServer = getSafeACMECAServer(gc.ACME.CAServer)
|
||||
|
||||
if gc.ACME.DNSChallenge != nil && gc.ACME.HTTPChallenge != nil {
|
||||
log.Warn("Unable to use DNS challenge and HTTP challenge at the same time. Fallback to DNS challenge.")
|
||||
gc.ACME.HTTPChallenge = nil
|
||||
}
|
||||
|
||||
// TODO: to remove in the future
|
||||
if len(gc.ACME.StorageFile) > 0 && len(gc.ACME.Storage) == 0 {
|
||||
log.Warn("ACME.StorageFile is deprecated, use ACME.Storage instead")
|
||||
gc.ACME.Storage = gc.ACME.StorageFile
|
||||
@@ -250,15 +368,53 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
|
||||
|
||||
if len(gc.ACME.DNSProvider) > 0 {
|
||||
log.Warn("ACME.DNSProvider is deprecated, use ACME.DNSChallenge instead")
|
||||
gc.ACME.DNSChallenge = &acme.DNSChallenge{Provider: gc.ACME.DNSProvider, DelayBeforeCheck: gc.ACME.DelayDontCheckDNS}
|
||||
gc.ACME.DNSChallenge = &acmeprovider.DNSChallenge{Provider: gc.ACME.DNSProvider, DelayBeforeCheck: gc.ACME.DelayDontCheckDNS}
|
||||
}
|
||||
|
||||
if gc.ACME.OnDemand {
|
||||
log.Warn("ACME.OnDemand is deprecated")
|
||||
}
|
||||
|
||||
// TODO: Remove when Provider ACME will replace totally ACME
|
||||
// If provider file, use Provider ACME instead of ACME
|
||||
if gc.Cluster == nil {
|
||||
acmeprovider.Get().Configuration = &acmeprovider.Configuration{
|
||||
OnHostRule: gc.ACME.OnHostRule,
|
||||
OnDemand: gc.ACME.OnDemand,
|
||||
Email: gc.ACME.Email,
|
||||
Storage: gc.ACME.Storage,
|
||||
HTTPChallenge: gc.ACME.HTTPChallenge,
|
||||
DNSChallenge: gc.ACME.DNSChallenge,
|
||||
Domains: gc.ACME.Domains,
|
||||
ACMELogging: gc.ACME.ACMELogging,
|
||||
CAServer: gc.ACME.CAServer,
|
||||
EntryPoint: gc.ACME.EntryPoint,
|
||||
}
|
||||
gc.ACME = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getSafeACMECAServer(caServerSrc string) string {
|
||||
if len(caServerSrc) == 0 {
|
||||
return DefaultAcmeCAServer
|
||||
}
|
||||
|
||||
if strings.HasPrefix(caServerSrc, "https://acme-v01.api.letsencrypt.org") {
|
||||
caServer := strings.Replace(caServerSrc, "v01", "v02", 1)
|
||||
log.Warnf("The CA server %[1]q refers to a v01 endpoint of the ACME API, please change to %[2]q. Fallback to %[2]q.", caServerSrc, caServer)
|
||||
return caServer
|
||||
}
|
||||
|
||||
if strings.HasPrefix(caServerSrc, "https://acme-staging.api.letsencrypt.org") {
|
||||
caServer := strings.Replace(caServerSrc, "https://acme-staging.api.letsencrypt.org", "https://acme-staging-v02.api.letsencrypt.org", 1)
|
||||
log.Warnf("The CA server %[1]q refers to a v01 endpoint of the ACME API, please change to %[2]q. Fallback to %[2]q.", caServerSrc, caServer)
|
||||
return caServer
|
||||
}
|
||||
|
||||
return caServerSrc
|
||||
}
|
||||
|
||||
// ValidateConfiguration validate that configuration is coherent
|
||||
func (gc *GlobalConfiguration) ValidateConfiguration() {
|
||||
if gc.ACME != nil {
|
||||
@@ -266,7 +422,15 @@ func (gc *GlobalConfiguration) ValidateConfiguration() {
|
||||
log.Fatalf("Unknown entrypoint %q for ACME configuration", gc.ACME.EntryPoint)
|
||||
} else {
|
||||
if gc.EntryPoints[gc.ACME.EntryPoint].TLS == nil {
|
||||
log.Fatalf("Entrypoint without TLS %q for ACME configuration", gc.ACME.EntryPoint)
|
||||
log.Fatalf("Entrypoint %q has no TLS configuration for ACME configuration", gc.ACME.EntryPoint)
|
||||
}
|
||||
}
|
||||
} else if acmeprovider.IsEnabled() {
|
||||
if _, ok := gc.EntryPoints[acmeprovider.Get().EntryPoint]; !ok {
|
||||
log.Fatalf("Unknown entrypoint %q for provider ACME configuration", acmeprovider.Get().EntryPoint)
|
||||
} else {
|
||||
if gc.EntryPoints[acmeprovider.Get().EntryPoint].TLS == nil {
|
||||
log.Fatalf("Entrypoint %q has no TLS configuration for provider ACME configuration", acmeprovider.Get().EntryPoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -297,12 +461,12 @@ func (dep *DefaultEntryPoints) Set(value string) error {
|
||||
|
||||
// Get return the EntryPoints map
|
||||
func (dep *DefaultEntryPoints) Get() interface{} {
|
||||
return DefaultEntryPoints(*dep)
|
||||
return *dep
|
||||
}
|
||||
|
||||
// SetValue sets the EntryPoints map with val
|
||||
func (dep *DefaultEntryPoints) SetValue(val interface{}) {
|
||||
*dep = DefaultEntryPoints(val.(DefaultEntryPoints))
|
||||
*dep = val.(DefaultEntryPoints)
|
||||
}
|
||||
|
||||
// Type is type of the struct
|
||||
@@ -310,157 +474,6 @@ func (dep *DefaultEntryPoints) Type() string {
|
||||
return "defaultentrypoints"
|
||||
}
|
||||
|
||||
// EntryPoints holds entry points configuration of the reverse proxy (ip, port, TLS...)
|
||||
type EntryPoints map[string]*EntryPoint
|
||||
|
||||
// String is the method to format the flag's value, part of the flag.Value interface.
|
||||
// The String method's output will be used in diagnostics.
|
||||
func (ep *EntryPoints) String() string {
|
||||
return fmt.Sprintf("%+v", *ep)
|
||||
}
|
||||
|
||||
// Set is the method to set the flag value, part of the flag.Value interface.
|
||||
// Set's argument is a string to be parsed to set the flag.
|
||||
// It's a comma-separated list, so we split it.
|
||||
func (ep *EntryPoints) Set(value string) error {
|
||||
result := parseEntryPointsConfiguration(value)
|
||||
|
||||
var configTLS *tls.TLS
|
||||
if len(result["tls"]) > 0 {
|
||||
certs := tls.Certificates{}
|
||||
if err := certs.Set(result["tls"]); err != nil {
|
||||
return err
|
||||
}
|
||||
configTLS = &tls.TLS{
|
||||
Certificates: certs,
|
||||
}
|
||||
} else if len(result["tls_acme"]) > 0 {
|
||||
configTLS = &tls.TLS{
|
||||
Certificates: tls.Certificates{},
|
||||
}
|
||||
}
|
||||
if len(result["ca"]) > 0 {
|
||||
files := strings.Split(result["ca"], ",")
|
||||
optional := toBool(result, "ca_optional")
|
||||
configTLS.ClientCA = tls.ClientCA{
|
||||
Files: files,
|
||||
Optional: optional,
|
||||
}
|
||||
}
|
||||
var redirect *types.Redirect
|
||||
if len(result["redirect_entrypoint"]) > 0 || len(result["redirect_regex"]) > 0 || len(result["redirect_replacement"]) > 0 {
|
||||
redirect = &types.Redirect{
|
||||
EntryPoint: result["redirect_entrypoint"],
|
||||
Regex: result["redirect_regex"],
|
||||
Replacement: result["redirect_replacement"],
|
||||
}
|
||||
}
|
||||
|
||||
whiteListSourceRange := []string{}
|
||||
if len(result["whitelistsourcerange"]) > 0 {
|
||||
whiteListSourceRange = strings.Split(result["whitelistsourcerange"], ",")
|
||||
}
|
||||
|
||||
compress := toBool(result, "compress")
|
||||
|
||||
var proxyProtocol *ProxyProtocol
|
||||
ppTrustedIPs := result["proxyprotocol_trustedips"]
|
||||
if len(result["proxyprotocol_insecure"]) > 0 || len(ppTrustedIPs) > 0 {
|
||||
proxyProtocol = &ProxyProtocol{
|
||||
Insecure: toBool(result, "proxyprotocol_insecure"),
|
||||
}
|
||||
if len(ppTrustedIPs) > 0 {
|
||||
proxyProtocol.TrustedIPs = strings.Split(ppTrustedIPs, ",")
|
||||
}
|
||||
}
|
||||
|
||||
// TODO must be changed to false by default in the next breaking version.
|
||||
forwardedHeaders := &ForwardedHeaders{Insecure: true}
|
||||
if _, ok := result["forwardedheaders_insecure"]; ok {
|
||||
forwardedHeaders.Insecure = toBool(result, "forwardedheaders_insecure")
|
||||
}
|
||||
|
||||
fhTrustedIPs := result["forwardedheaders_trustedips"]
|
||||
if len(fhTrustedIPs) > 0 {
|
||||
// TODO must be removed in the next breaking version.
|
||||
forwardedHeaders.Insecure = toBool(result, "forwardedheaders_insecure")
|
||||
forwardedHeaders.TrustedIPs = strings.Split(fhTrustedIPs, ",")
|
||||
}
|
||||
|
||||
if proxyProtocol != nil && proxyProtocol.Insecure {
|
||||
log.Warn("ProxyProtocol.Insecure:true is dangerous. Please use 'ProxyProtocol.TrustedIPs:IPs' and remove 'ProxyProtocol.Insecure:true'")
|
||||
}
|
||||
|
||||
(*ep)[result["name"]] = &EntryPoint{
|
||||
Address: result["address"],
|
||||
TLS: configTLS,
|
||||
Redirect: redirect,
|
||||
Compress: compress,
|
||||
WhitelistSourceRange: whiteListSourceRange,
|
||||
ProxyProtocol: proxyProtocol,
|
||||
ForwardedHeaders: forwardedHeaders,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseEntryPointsConfiguration(raw string) map[string]string {
|
||||
sections := strings.Fields(raw)
|
||||
|
||||
config := make(map[string]string)
|
||||
for _, part := range sections {
|
||||
field := strings.SplitN(part, ":", 2)
|
||||
name := strings.ToLower(strings.Replace(field[0], ".", "_", -1))
|
||||
if len(field) > 1 {
|
||||
config[name] = field[1]
|
||||
} else {
|
||||
if strings.EqualFold(name, "TLS") {
|
||||
config["tls_acme"] = "TLS"
|
||||
} else {
|
||||
config[name] = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
func toBool(conf map[string]string, key string) bool {
|
||||
if val, ok := conf[key]; ok {
|
||||
return strings.EqualFold(val, "true") ||
|
||||
strings.EqualFold(val, "enable") ||
|
||||
strings.EqualFold(val, "on")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Get return the EntryPoints map
|
||||
func (ep *EntryPoints) Get() interface{} {
|
||||
return EntryPoints(*ep)
|
||||
}
|
||||
|
||||
// SetValue sets the EntryPoints map with val
|
||||
func (ep *EntryPoints) SetValue(val interface{}) {
|
||||
*ep = EntryPoints(val.(EntryPoints))
|
||||
}
|
||||
|
||||
// Type is type of the struct
|
||||
func (ep *EntryPoints) Type() string {
|
||||
return "entrypoints"
|
||||
}
|
||||
|
||||
// EntryPoint holds an entry point configuration of the reverse proxy (ip, port, TLS...)
|
||||
type EntryPoint struct {
|
||||
Network string
|
||||
Address string
|
||||
TLS *tls.TLS `export:"true"`
|
||||
Redirect *types.Redirect `export:"true"`
|
||||
Auth *types.Auth `export:"true"`
|
||||
WhitelistSourceRange []string
|
||||
Compress bool `export:"true"`
|
||||
ProxyProtocol *ProxyProtocol `export:"true"`
|
||||
ForwardedHeaders *ForwardedHeaders `export:"true"`
|
||||
}
|
||||
|
||||
// Retry contains request retry config
|
||||
type Retry struct {
|
||||
Attempts int `description:"Number of attempts" export:"true"`
|
||||
@@ -484,18 +497,6 @@ type ForwardingTimeouts struct {
|
||||
ResponseHeaderTimeout flaeg.Duration `description:"The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists" export:"true"`
|
||||
}
|
||||
|
||||
// ProxyProtocol contains Proxy-Protocol configuration
|
||||
type ProxyProtocol struct {
|
||||
Insecure bool
|
||||
TrustedIPs []string
|
||||
}
|
||||
|
||||
// ForwardedHeaders Trust client forwarding headers
|
||||
type ForwardedHeaders struct {
|
||||
Insecure bool
|
||||
TrustedIPs []string
|
||||
}
|
||||
|
||||
// LifeCycle contains configurations relevant to the lifecycle (such as the
|
||||
// shutdown phase) of Traefik.
|
||||
type LifeCycle struct {
|
||||
|
@@ -5,309 +5,18 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/traefik/middlewares/tracing"
|
||||
"github.com/containous/traefik/middlewares/tracing/jaeger"
|
||||
"github.com/containous/traefik/middlewares/tracing/zipkin"
|
||||
"github.com/containous/traefik/provider"
|
||||
"github.com/containous/traefik/provider/file"
|
||||
"github.com/containous/traefik/tls"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const defaultConfigFile = "traefik.toml"
|
||||
|
||||
func Test_parseEntryPointsConfiguration(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
value string
|
||||
expectedResult map[string]string
|
||||
}{
|
||||
{
|
||||
name: "all parameters",
|
||||
value: "Name:foo TLS:goo TLS CA:car Redirect.EntryPoint:RedirectEntryPoint Redirect.Regex:RedirectRegex Redirect.Replacement:RedirectReplacement Compress:true WhiteListSourceRange:WhiteListSourceRange ProxyProtocol.TrustedIPs:192.168.0.1 ProxyProtocol.Insecure:false Address::8000",
|
||||
expectedResult: map[string]string{
|
||||
"name": "foo",
|
||||
"address": ":8000",
|
||||
"ca": "car",
|
||||
"tls": "goo",
|
||||
"tls_acme": "TLS",
|
||||
"redirect_entrypoint": "RedirectEntryPoint",
|
||||
"redirect_regex": "RedirectRegex",
|
||||
"redirect_replacement": "RedirectReplacement",
|
||||
"whitelistsourcerange": "WhiteListSourceRange",
|
||||
"proxyprotocol_trustedips": "192.168.0.1",
|
||||
"proxyprotocol_insecure": "false",
|
||||
"compress": "true",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "compress on",
|
||||
value: "name:foo Compress:on",
|
||||
expectedResult: map[string]string{
|
||||
"name": "foo",
|
||||
"compress": "on",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TLS",
|
||||
value: "Name:foo TLS:goo TLS",
|
||||
expectedResult: map[string]string{
|
||||
"name": "foo",
|
||||
"tls": "goo",
|
||||
"tls_acme": "TLS",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
conf := parseEntryPointsConfiguration(test.value)
|
||||
|
||||
assert.Len(t, conf, len(test.expectedResult))
|
||||
assert.Equal(t, test.expectedResult, conf)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_toBool(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
value string
|
||||
key string
|
||||
expectedBool bool
|
||||
}{
|
||||
{
|
||||
name: "on",
|
||||
value: "on",
|
||||
key: "foo",
|
||||
expectedBool: true,
|
||||
},
|
||||
{
|
||||
name: "true",
|
||||
value: "true",
|
||||
key: "foo",
|
||||
expectedBool: true,
|
||||
},
|
||||
{
|
||||
name: "enable",
|
||||
value: "enable",
|
||||
key: "foo",
|
||||
expectedBool: true,
|
||||
},
|
||||
{
|
||||
name: "arbitrary string",
|
||||
value: "bar",
|
||||
key: "foo",
|
||||
expectedBool: false,
|
||||
},
|
||||
{
|
||||
name: "no existing entry",
|
||||
value: "bar",
|
||||
key: "fii",
|
||||
expectedBool: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
conf := map[string]string{
|
||||
"foo": test.value,
|
||||
}
|
||||
|
||||
result := toBool(conf, test.key)
|
||||
|
||||
assert.Equal(t, test.expectedBool, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEntryPoints_Set(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
expression string
|
||||
expectedEntryPointName string
|
||||
expectedEntryPoint *EntryPoint
|
||||
}{
|
||||
{
|
||||
name: "all parameters camelcase",
|
||||
expression: "Name:foo Address::8000 TLS:goo,gii TLS CA:car CA.Optional:false Redirect.EntryPoint:RedirectEntryPoint Redirect.Regex:RedirectRegex Redirect.Replacement:RedirectReplacement Compress:true WhiteListSourceRange:Range ProxyProtocol.TrustedIPs:192.168.0.1 ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
Address: ":8000",
|
||||
Redirect: &types.Redirect{
|
||||
EntryPoint: "RedirectEntryPoint",
|
||||
Regex: "RedirectRegex",
|
||||
Replacement: "RedirectReplacement",
|
||||
},
|
||||
Compress: true,
|
||||
ProxyProtocol: &ProxyProtocol{
|
||||
TrustedIPs: []string{"192.168.0.1"},
|
||||
},
|
||||
ForwardedHeaders: &ForwardedHeaders{
|
||||
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
|
||||
},
|
||||
WhitelistSourceRange: []string{"Range"},
|
||||
TLS: &tls.TLS{
|
||||
ClientCA: tls.ClientCA{
|
||||
Files: []string{"car"},
|
||||
Optional: false,
|
||||
},
|
||||
Certificates: tls.Certificates{
|
||||
{
|
||||
CertFile: tls.FileOrContent("goo"),
|
||||
KeyFile: tls.FileOrContent("gii"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "all parameters lowercase",
|
||||
expression: "name:foo address::8000 tls:goo,gii tls ca:car ca.optional:true redirect.entryPoint:RedirectEntryPoint redirect.regex:RedirectRegex redirect.replacement:RedirectReplacement compress:true whiteListSourceRange:Range proxyProtocol.trustedIPs:192.168.0.1 forwardedHeaders.trustedIPs:10.0.0.3/24,20.0.0.3/24",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
Address: ":8000",
|
||||
Redirect: &types.Redirect{
|
||||
EntryPoint: "RedirectEntryPoint",
|
||||
Regex: "RedirectRegex",
|
||||
Replacement: "RedirectReplacement",
|
||||
},
|
||||
Compress: true,
|
||||
ProxyProtocol: &ProxyProtocol{
|
||||
TrustedIPs: []string{"192.168.0.1"},
|
||||
},
|
||||
ForwardedHeaders: &ForwardedHeaders{
|
||||
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
|
||||
},
|
||||
WhitelistSourceRange: []string{"Range"},
|
||||
TLS: &tls.TLS{
|
||||
ClientCA: tls.ClientCA{
|
||||
Files: []string{"car"},
|
||||
Optional: true,
|
||||
},
|
||||
Certificates: tls.Certificates{
|
||||
{
|
||||
CertFile: tls.FileOrContent("goo"),
|
||||
KeyFile: tls.FileOrContent("gii"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "default",
|
||||
expression: "Name:foo",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
WhitelistSourceRange: []string{},
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ForwardedHeaders insecure true",
|
||||
expression: "Name:foo ForwardedHeaders.Insecure:true",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
WhitelistSourceRange: []string{},
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ForwardedHeaders insecure false",
|
||||
expression: "Name:foo ForwardedHeaders.Insecure:false",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
WhitelistSourceRange: []string{},
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: false},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ForwardedHeaders TrustedIPs",
|
||||
expression: "Name:foo ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
WhitelistSourceRange: []string{},
|
||||
ForwardedHeaders: &ForwardedHeaders{
|
||||
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProxyProtocol insecure true",
|
||||
expression: "Name:foo ProxyProtocol.Insecure:true",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
WhitelistSourceRange: []string{},
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
ProxyProtocol: &ProxyProtocol{Insecure: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProxyProtocol insecure false",
|
||||
expression: "Name:foo ProxyProtocol.Insecure:false",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
WhitelistSourceRange: []string{},
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
ProxyProtocol: &ProxyProtocol{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProxyProtocol TrustedIPs",
|
||||
expression: "Name:foo ProxyProtocol.TrustedIPs:10.0.0.3/24,20.0.0.3/24",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
WhitelistSourceRange: []string{},
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
ProxyProtocol: &ProxyProtocol{
|
||||
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "compress on",
|
||||
expression: "Name:foo Compress:on",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
Compress: true,
|
||||
WhitelistSourceRange: []string{},
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "compress true",
|
||||
expression: "Name:foo Compress:true",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
Compress: true,
|
||||
WhitelistSourceRange: []string{},
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
eps := EntryPoints{}
|
||||
err := eps.Set(test.expression)
|
||||
require.NoError(t, err)
|
||||
|
||||
ep := eps[test.expectedEntryPointName]
|
||||
assert.EqualValues(t, test.expectedEntryPoint, ep)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetEffectiveConfigurationGraceTimeout(t *testing.T) {
|
||||
tests := []struct {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
legacyGraceTimeout time.Duration
|
||||
lifeCycleGraceTimeout time.Duration
|
||||
@@ -332,10 +41,11 @@ func TestSetEffectiveConfigurationGraceTimeout(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
gc := &GlobalConfiguration{
|
||||
GraceTimeOut: flaeg.Duration(test.legacyGraceTimeout),
|
||||
}
|
||||
@@ -347,47 +57,162 @@ func TestSetEffectiveConfigurationGraceTimeout(t *testing.T) {
|
||||
|
||||
gc.SetEffectiveConfiguration(defaultConfigFile)
|
||||
|
||||
gotGraceTimeout := time.Duration(gc.LifeCycle.GraceTimeOut)
|
||||
if gotGraceTimeout != test.wantGraceTimeout {
|
||||
t.Fatalf("got effective grace timeout %d, want %d", gotGraceTimeout, test.wantGraceTimeout)
|
||||
}
|
||||
assert.Equal(t, test.wantGraceTimeout, time.Duration(gc.LifeCycle.GraceTimeOut))
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetEffectiveConfigurationFileProviderFilename(t *testing.T) {
|
||||
tests := []struct {
|
||||
desc string
|
||||
fileProvider *file.Provider
|
||||
wantFileProviderFilename string
|
||||
testCases := []struct {
|
||||
desc string
|
||||
fileProvider *file.Provider
|
||||
wantFileProviderFilename string
|
||||
wantFileProviderTraefikFile string
|
||||
}{
|
||||
{
|
||||
desc: "no filename for file provider given",
|
||||
fileProvider: &file.Provider{},
|
||||
wantFileProviderFilename: defaultConfigFile,
|
||||
desc: "no filename for file provider given",
|
||||
fileProvider: &file.Provider{},
|
||||
wantFileProviderFilename: "",
|
||||
wantFileProviderTraefikFile: defaultConfigFile,
|
||||
},
|
||||
{
|
||||
desc: "filename for file provider given",
|
||||
fileProvider: &file.Provider{BaseProvider: provider.BaseProvider{Filename: "other.toml"}},
|
||||
wantFileProviderFilename: "other.toml",
|
||||
desc: "filename for file provider given",
|
||||
fileProvider: &file.Provider{BaseProvider: provider.BaseProvider{Filename: "other.toml"}},
|
||||
wantFileProviderFilename: "other.toml",
|
||||
wantFileProviderTraefikFile: defaultConfigFile,
|
||||
},
|
||||
{
|
||||
desc: "directory for file provider given",
|
||||
fileProvider: &file.Provider{Directory: "/"},
|
||||
wantFileProviderFilename: "",
|
||||
wantFileProviderTraefikFile: defaultConfigFile,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
gc := &GlobalConfiguration{
|
||||
File: test.fileProvider,
|
||||
}
|
||||
|
||||
gc.SetEffectiveConfiguration(defaultConfigFile)
|
||||
|
||||
gotFileProviderFilename := gc.File.Filename
|
||||
if gotFileProviderFilename != test.wantFileProviderFilename {
|
||||
t.Fatalf("got file provider file name %q, want %q", gotFileProviderFilename, test.wantFileProviderFilename)
|
||||
}
|
||||
assert.Equal(t, test.wantFileProviderFilename, gc.File.Filename)
|
||||
assert.Equal(t, test.wantFileProviderTraefikFile, gc.File.TraefikFile)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetEffectiveConfigurationTracing(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
tracing *tracing.Tracing
|
||||
expected *tracing.Tracing
|
||||
}{
|
||||
{
|
||||
desc: "no tracing configuration",
|
||||
tracing: &tracing.Tracing{},
|
||||
expected: &tracing.Tracing{},
|
||||
},
|
||||
{
|
||||
desc: "tracing bad backend name",
|
||||
tracing: &tracing.Tracing{
|
||||
Backend: "powpow",
|
||||
},
|
||||
expected: &tracing.Tracing{
|
||||
Backend: "powpow",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "tracing jaeger backend name",
|
||||
tracing: &tracing.Tracing{
|
||||
Backend: "jaeger",
|
||||
Zipkin: &zipkin.Config{
|
||||
HTTPEndpoint: "http://localhost:9411/api/v1/spans",
|
||||
SameSpan: false,
|
||||
ID128Bit: true,
|
||||
Debug: false,
|
||||
},
|
||||
},
|
||||
expected: &tracing.Tracing{
|
||||
Backend: "jaeger",
|
||||
Jaeger: &jaeger.Config{
|
||||
SamplingServerURL: "http://localhost:5778/sampling",
|
||||
SamplingType: "const",
|
||||
SamplingParam: 1.0,
|
||||
LocalAgentHostPort: "127.0.0.1:6831",
|
||||
},
|
||||
Zipkin: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "tracing zipkin backend name",
|
||||
tracing: &tracing.Tracing{
|
||||
Backend: "zipkin",
|
||||
Jaeger: &jaeger.Config{
|
||||
SamplingServerURL: "http://localhost:5778/sampling",
|
||||
SamplingType: "const",
|
||||
SamplingParam: 1.0,
|
||||
LocalAgentHostPort: "127.0.0.1:6831",
|
||||
},
|
||||
},
|
||||
expected: &tracing.Tracing{
|
||||
Backend: "zipkin",
|
||||
Jaeger: nil,
|
||||
Zipkin: &zipkin.Config{
|
||||
HTTPEndpoint: "http://localhost:9411/api/v1/spans",
|
||||
SameSpan: false,
|
||||
ID128Bit: true,
|
||||
Debug: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "tracing zipkin backend name value override",
|
||||
tracing: &tracing.Tracing{
|
||||
Backend: "zipkin",
|
||||
Jaeger: &jaeger.Config{
|
||||
SamplingServerURL: "http://localhost:5778/sampling",
|
||||
SamplingType: "const",
|
||||
SamplingParam: 1.0,
|
||||
LocalAgentHostPort: "127.0.0.1:6831",
|
||||
},
|
||||
Zipkin: &zipkin.Config{
|
||||
HTTPEndpoint: "http://powpow:9411/api/v1/spans",
|
||||
SameSpan: true,
|
||||
ID128Bit: true,
|
||||
Debug: true,
|
||||
},
|
||||
},
|
||||
expected: &tracing.Tracing{
|
||||
Backend: "zipkin",
|
||||
Jaeger: nil,
|
||||
Zipkin: &zipkin.Config{
|
||||
HTTPEndpoint: "http://powpow:9411/api/v1/spans",
|
||||
SameSpan: true,
|
||||
ID128Bit: true,
|
||||
Debug: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
gc := &GlobalConfiguration{
|
||||
Tracing: test.tracing,
|
||||
}
|
||||
|
||||
gc.SetEffectiveConfiguration(defaultConfigFile)
|
||||
|
||||
assert.Equal(t, test.expected, gc.Tracing)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
266
configuration/entrypoints.go
Normal file
@@ -0,0 +1,266 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/tls"
|
||||
"github.com/containous/traefik/types"
|
||||
)
|
||||
|
||||
// EntryPoint holds an entry point configuration of the reverse proxy (ip, port, TLS...)
|
||||
type EntryPoint struct {
|
||||
Address string
|
||||
TLS *tls.TLS `export:"true"`
|
||||
Redirect *types.Redirect `export:"true"`
|
||||
Auth *types.Auth `export:"true"`
|
||||
WhitelistSourceRange []string // Deprecated
|
||||
WhiteList *types.WhiteList `export:"true"`
|
||||
Compress bool `export:"true"`
|
||||
ProxyProtocol *ProxyProtocol `export:"true"`
|
||||
ForwardedHeaders *ForwardedHeaders `export:"true"`
|
||||
}
|
||||
|
||||
// ProxyProtocol contains Proxy-Protocol configuration
|
||||
type ProxyProtocol struct {
|
||||
Insecure bool `export:"true"`
|
||||
TrustedIPs []string
|
||||
}
|
||||
|
||||
// ForwardedHeaders Trust client forwarding headers
|
||||
type ForwardedHeaders struct {
|
||||
Insecure bool `export:"true"`
|
||||
TrustedIPs []string
|
||||
}
|
||||
|
||||
// EntryPoints holds entry points configuration of the reverse proxy (ip, port, TLS...)
|
||||
type EntryPoints map[string]*EntryPoint
|
||||
|
||||
// String is the method to format the flag's value, part of the flag.Value interface.
|
||||
// The String method's output will be used in diagnostics.
|
||||
func (ep EntryPoints) String() string {
|
||||
return fmt.Sprintf("%+v", map[string]*EntryPoint(ep))
|
||||
}
|
||||
|
||||
// Get return the EntryPoints map
|
||||
func (ep *EntryPoints) Get() interface{} {
|
||||
return *ep
|
||||
}
|
||||
|
||||
// SetValue sets the EntryPoints map with val
|
||||
func (ep *EntryPoints) SetValue(val interface{}) {
|
||||
*ep = val.(EntryPoints)
|
||||
}
|
||||
|
||||
// Type is type of the struct
|
||||
func (ep *EntryPoints) Type() string {
|
||||
return "entrypoints"
|
||||
}
|
||||
|
||||
// Set is the method to set the flag value, part of the flag.Value interface.
|
||||
// Set's argument is a string to be parsed to set the flag.
|
||||
// It's a comma-separated list, so we split it.
|
||||
func (ep *EntryPoints) Set(value string) error {
|
||||
result := parseEntryPointsConfiguration(value)
|
||||
|
||||
var whiteListSourceRange []string
|
||||
if len(result["whitelistsourcerange"]) > 0 {
|
||||
whiteListSourceRange = strings.Split(result["whitelistsourcerange"], ",")
|
||||
}
|
||||
|
||||
compress := toBool(result, "compress")
|
||||
|
||||
configTLS, err := makeEntryPointTLS(result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
(*ep)[result["name"]] = &EntryPoint{
|
||||
Address: result["address"],
|
||||
TLS: configTLS,
|
||||
Auth: makeEntryPointAuth(result),
|
||||
Redirect: makeEntryPointRedirect(result),
|
||||
Compress: compress,
|
||||
WhitelistSourceRange: whiteListSourceRange,
|
||||
WhiteList: makeWhiteList(result),
|
||||
ProxyProtocol: makeEntryPointProxyProtocol(result),
|
||||
ForwardedHeaders: makeEntryPointForwardedHeaders(result),
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeWhiteList(result map[string]string) *types.WhiteList {
|
||||
var wl *types.WhiteList
|
||||
if rawRange, ok := result["whitelist_sourcerange"]; ok {
|
||||
wl = &types.WhiteList{
|
||||
SourceRange: strings.Split(rawRange, ","),
|
||||
UseXForwardedFor: toBool(result, "whitelist_usexforwardedfor"),
|
||||
}
|
||||
}
|
||||
return wl
|
||||
}
|
||||
|
||||
func makeEntryPointAuth(result map[string]string) *types.Auth {
|
||||
var basic *types.Basic
|
||||
if v, ok := result["auth_basic_users"]; ok {
|
||||
basic = &types.Basic{
|
||||
Users: strings.Split(v, ","),
|
||||
}
|
||||
}
|
||||
|
||||
var digest *types.Digest
|
||||
if v, ok := result["auth_digest_users"]; ok {
|
||||
digest = &types.Digest{
|
||||
Users: strings.Split(v, ","),
|
||||
}
|
||||
}
|
||||
|
||||
var forward *types.Forward
|
||||
if address, ok := result["auth_forward_address"]; ok {
|
||||
var clientTLS *types.ClientTLS
|
||||
|
||||
cert := result["auth_forward_tls_cert"]
|
||||
key := result["auth_forward_tls_key"]
|
||||
insecureSkipVerify := toBool(result, "auth_forward_tls_insecureskipverify")
|
||||
|
||||
if len(cert) > 0 && len(key) > 0 || insecureSkipVerify {
|
||||
clientTLS = &types.ClientTLS{
|
||||
CA: result["auth_forward_tls_ca"],
|
||||
CAOptional: toBool(result, "auth_forward_tls_caoptional"),
|
||||
Cert: cert,
|
||||
Key: key,
|
||||
InsecureSkipVerify: insecureSkipVerify,
|
||||
}
|
||||
}
|
||||
|
||||
forward = &types.Forward{
|
||||
Address: address,
|
||||
TLS: clientTLS,
|
||||
TrustForwardHeader: toBool(result, "auth_forward_trustforwardheader"),
|
||||
}
|
||||
}
|
||||
|
||||
var auth *types.Auth
|
||||
if basic != nil || digest != nil || forward != nil {
|
||||
auth = &types.Auth{
|
||||
Basic: basic,
|
||||
Digest: digest,
|
||||
Forward: forward,
|
||||
HeaderField: result["auth_headerfield"],
|
||||
}
|
||||
}
|
||||
|
||||
return auth
|
||||
}
|
||||
|
||||
func makeEntryPointProxyProtocol(result map[string]string) *ProxyProtocol {
|
||||
var proxyProtocol *ProxyProtocol
|
||||
|
||||
ppTrustedIPs := result["proxyprotocol_trustedips"]
|
||||
if len(result["proxyprotocol_insecure"]) > 0 || len(ppTrustedIPs) > 0 {
|
||||
proxyProtocol = &ProxyProtocol{
|
||||
Insecure: toBool(result, "proxyprotocol_insecure"),
|
||||
}
|
||||
if len(ppTrustedIPs) > 0 {
|
||||
proxyProtocol.TrustedIPs = strings.Split(ppTrustedIPs, ",")
|
||||
}
|
||||
}
|
||||
|
||||
if proxyProtocol != nil && proxyProtocol.Insecure {
|
||||
log.Warn("ProxyProtocol.Insecure:true is dangerous. Please use 'ProxyProtocol.TrustedIPs:IPs' and remove 'ProxyProtocol.Insecure:true'")
|
||||
}
|
||||
|
||||
return proxyProtocol
|
||||
}
|
||||
|
||||
func makeEntryPointForwardedHeaders(result map[string]string) *ForwardedHeaders {
|
||||
// TODO must be changed to false by default in the next breaking version.
|
||||
forwardedHeaders := &ForwardedHeaders{Insecure: true}
|
||||
if _, ok := result["forwardedheaders_insecure"]; ok {
|
||||
forwardedHeaders.Insecure = toBool(result, "forwardedheaders_insecure")
|
||||
}
|
||||
|
||||
fhTrustedIPs := result["forwardedheaders_trustedips"]
|
||||
if len(fhTrustedIPs) > 0 {
|
||||
// TODO must be removed in the next breaking version.
|
||||
forwardedHeaders.Insecure = toBool(result, "forwardedheaders_insecure")
|
||||
forwardedHeaders.TrustedIPs = strings.Split(fhTrustedIPs, ",")
|
||||
}
|
||||
|
||||
return forwardedHeaders
|
||||
}
|
||||
|
||||
func makeEntryPointRedirect(result map[string]string) *types.Redirect {
|
||||
var redirect *types.Redirect
|
||||
|
||||
if len(result["redirect_entrypoint"]) > 0 || len(result["redirect_regex"]) > 0 || len(result["redirect_replacement"]) > 0 {
|
||||
redirect = &types.Redirect{
|
||||
EntryPoint: result["redirect_entrypoint"],
|
||||
Regex: result["redirect_regex"],
|
||||
Replacement: result["redirect_replacement"],
|
||||
Permanent: toBool(result, "redirect_permanent"),
|
||||
}
|
||||
}
|
||||
|
||||
return redirect
|
||||
}
|
||||
|
||||
func makeEntryPointTLS(result map[string]string) (*tls.TLS, error) {
|
||||
var configTLS *tls.TLS
|
||||
|
||||
if len(result["tls"]) > 0 {
|
||||
certs := tls.Certificates{}
|
||||
if err := certs.Set(result["tls"]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
configTLS = &tls.TLS{
|
||||
Certificates: certs,
|
||||
}
|
||||
} else if len(result["tls_acme"]) > 0 {
|
||||
configTLS = &tls.TLS{
|
||||
Certificates: tls.Certificates{},
|
||||
}
|
||||
}
|
||||
|
||||
if len(result["ca"]) > 0 {
|
||||
files := strings.Split(result["ca"], ",")
|
||||
optional := toBool(result, "ca_optional")
|
||||
configTLS.ClientCA = tls.ClientCA{
|
||||
Files: files,
|
||||
Optional: optional,
|
||||
}
|
||||
}
|
||||
|
||||
return configTLS, nil
|
||||
}
|
||||
|
||||
func parseEntryPointsConfiguration(raw string) map[string]string {
|
||||
sections := strings.Fields(raw)
|
||||
|
||||
config := make(map[string]string)
|
||||
for _, part := range sections {
|
||||
field := strings.SplitN(part, ":", 2)
|
||||
name := strings.ToLower(strings.Replace(field[0], ".", "_", -1))
|
||||
if len(field) > 1 {
|
||||
config[name] = field[1]
|
||||
} else {
|
||||
if strings.EqualFold(name, "TLS") {
|
||||
config["tls_acme"] = "TLS"
|
||||
} else {
|
||||
config[name] = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
func toBool(conf map[string]string, key string) bool {
|
||||
if val, ok := conf[key]; ok {
|
||||
return strings.EqualFold(val, "true") ||
|
||||
strings.EqualFold(val, "enable") ||
|
||||
strings.EqualFold(val, "on")
|
||||
}
|
||||
return false
|
||||
}
|
467
configuration/entrypoints_test.go
Normal file
@@ -0,0 +1,467 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/containous/traefik/tls"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_parseEntryPointsConfiguration(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
value string
|
||||
expectedResult map[string]string
|
||||
}{
|
||||
{
|
||||
name: "all parameters",
|
||||
value: "Name:foo " +
|
||||
"Address::8000 " +
|
||||
"TLS:goo,gii " +
|
||||
"TLS " +
|
||||
"CA:car " +
|
||||
"CA.Optional:true " +
|
||||
"Redirect.EntryPoint:https " +
|
||||
"Redirect.Regex:http://localhost/(.*) " +
|
||||
"Redirect.Replacement:http://mydomain/$1 " +
|
||||
"Redirect.Permanent:true " +
|
||||
"Compress:true " +
|
||||
"ProxyProtocol.TrustedIPs:192.168.0.1 " +
|
||||
"ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24 " +
|
||||
"Auth.Basic.Users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0 " +
|
||||
"Auth.Digest.Users:test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e " +
|
||||
"Auth.HeaderField:X-WebAuth-User " +
|
||||
"Auth.Forward.Address:https://authserver.com/auth " +
|
||||
"Auth.Forward.TrustForwardHeader:true " +
|
||||
"Auth.Forward.TLS.CA:path/to/local.crt " +
|
||||
"Auth.Forward.TLS.CAOptional:true " +
|
||||
"Auth.Forward.TLS.Cert:path/to/foo.cert " +
|
||||
"Auth.Forward.TLS.Key:path/to/foo.key " +
|
||||
"Auth.Forward.TLS.InsecureSkipVerify:true " +
|
||||
"WhiteListSourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " +
|
||||
"whiteList.sourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " +
|
||||
"whiteList.useXForwardedFor:true ",
|
||||
expectedResult: map[string]string{
|
||||
"address": ":8000",
|
||||
"auth_basic_users": "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
"auth_digest_users": "test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e",
|
||||
"auth_forward_address": "https://authserver.com/auth",
|
||||
"auth_forward_tls_ca": "path/to/local.crt",
|
||||
"auth_forward_tls_caoptional": "true",
|
||||
"auth_forward_tls_cert": "path/to/foo.cert",
|
||||
"auth_forward_tls_insecureskipverify": "true",
|
||||
"auth_forward_tls_key": "path/to/foo.key",
|
||||
"auth_forward_trustforwardheader": "true",
|
||||
"auth_headerfield": "X-WebAuth-User",
|
||||
"ca": "car",
|
||||
"ca_optional": "true",
|
||||
"compress": "true",
|
||||
"forwardedheaders_trustedips": "10.0.0.3/24,20.0.0.3/24",
|
||||
"name": "foo",
|
||||
"proxyprotocol_trustedips": "192.168.0.1",
|
||||
"redirect_entrypoint": "https",
|
||||
"redirect_permanent": "true",
|
||||
"redirect_regex": "http://localhost/(.*)",
|
||||
"redirect_replacement": "http://mydomain/$1",
|
||||
"tls": "goo,gii",
|
||||
"tls_acme": "TLS",
|
||||
"whitelistsourcerange": "10.42.0.0/16,152.89.1.33/32,afed:be44::/16",
|
||||
"whitelist_sourcerange": "10.42.0.0/16,152.89.1.33/32,afed:be44::/16",
|
||||
"whitelist_usexforwardedfor": "true",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "compress on",
|
||||
value: "name:foo Compress:on",
|
||||
expectedResult: map[string]string{
|
||||
"name": "foo",
|
||||
"compress": "on",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TLS",
|
||||
value: "Name:foo TLS:goo TLS",
|
||||
expectedResult: map[string]string{
|
||||
"name": "foo",
|
||||
"tls": "goo",
|
||||
"tls_acme": "TLS",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
conf := parseEntryPointsConfiguration(test.value)
|
||||
|
||||
assert.Len(t, conf, len(test.expectedResult))
|
||||
assert.Equal(t, test.expectedResult, conf)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_toBool(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
value string
|
||||
key string
|
||||
expectedBool bool
|
||||
}{
|
||||
{
|
||||
name: "on",
|
||||
value: "on",
|
||||
key: "foo",
|
||||
expectedBool: true,
|
||||
},
|
||||
{
|
||||
name: "true",
|
||||
value: "true",
|
||||
key: "foo",
|
||||
expectedBool: true,
|
||||
},
|
||||
{
|
||||
name: "enable",
|
||||
value: "enable",
|
||||
key: "foo",
|
||||
expectedBool: true,
|
||||
},
|
||||
{
|
||||
name: "arbitrary string",
|
||||
value: "bar",
|
||||
key: "foo",
|
||||
expectedBool: false,
|
||||
},
|
||||
{
|
||||
name: "no existing entry",
|
||||
value: "bar",
|
||||
key: "fii",
|
||||
expectedBool: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
conf := map[string]string{
|
||||
"foo": test.value,
|
||||
}
|
||||
|
||||
result := toBool(conf, test.key)
|
||||
|
||||
assert.Equal(t, test.expectedBool, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEntryPoints_Set(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
expression string
|
||||
expectedEntryPointName string
|
||||
expectedEntryPoint *EntryPoint
|
||||
}{
|
||||
{
|
||||
name: "all parameters camelcase",
|
||||
expression: "Name:foo " +
|
||||
"Address::8000 " +
|
||||
"TLS:goo,gii;foo,fii " +
|
||||
"TLS " +
|
||||
"CA:car " +
|
||||
"CA.Optional:true " +
|
||||
"Redirect.EntryPoint:https " +
|
||||
"Redirect.Regex:http://localhost/(.*) " +
|
||||
"Redirect.Replacement:http://mydomain/$1 " +
|
||||
"Redirect.Permanent:true " +
|
||||
"Compress:true " +
|
||||
"ProxyProtocol.TrustedIPs:192.168.0.1 " +
|
||||
"ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24 " +
|
||||
"Auth.Basic.Users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0 " +
|
||||
"Auth.Digest.Users:test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e " +
|
||||
"Auth.HeaderField:X-WebAuth-User " +
|
||||
"Auth.Forward.Address:https://authserver.com/auth " +
|
||||
"Auth.Forward.TrustForwardHeader:true " +
|
||||
"Auth.Forward.TLS.CA:path/to/local.crt " +
|
||||
"Auth.Forward.TLS.CAOptional:true " +
|
||||
"Auth.Forward.TLS.Cert:path/to/foo.cert " +
|
||||
"Auth.Forward.TLS.Key:path/to/foo.key " +
|
||||
"Auth.Forward.TLS.InsecureSkipVerify:true " +
|
||||
"WhiteListSourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " +
|
||||
"whiteList.sourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " +
|
||||
"whiteList.useXForwardedFor:true ",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
Address: ":8000",
|
||||
TLS: &tls.TLS{
|
||||
Certificates: tls.Certificates{
|
||||
{
|
||||
CertFile: tls.FileOrContent("goo"),
|
||||
KeyFile: tls.FileOrContent("gii"),
|
||||
},
|
||||
{
|
||||
CertFile: tls.FileOrContent("foo"),
|
||||
KeyFile: tls.FileOrContent("fii"),
|
||||
},
|
||||
},
|
||||
ClientCA: tls.ClientCA{
|
||||
Files: []string{"car"},
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
Redirect: &types.Redirect{
|
||||
EntryPoint: "https",
|
||||
Regex: "http://localhost/(.*)",
|
||||
Replacement: "http://mydomain/$1",
|
||||
Permanent: true,
|
||||
},
|
||||
Auth: &types.Auth{
|
||||
Basic: &types.Basic{
|
||||
Users: types.Users{
|
||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
},
|
||||
},
|
||||
Digest: &types.Digest{
|
||||
Users: types.Users{
|
||||
"test:traefik:a2688e031edb4be6a3797f3882655c05",
|
||||
"test2:traefik:518845800f9e2bfb1f1f740ec24f074e",
|
||||
},
|
||||
},
|
||||
Forward: &types.Forward{
|
||||
Address: "https://authserver.com/auth",
|
||||
TLS: &types.ClientTLS{
|
||||
CA: "path/to/local.crt",
|
||||
CAOptional: true,
|
||||
Cert: "path/to/foo.cert",
|
||||
Key: "path/to/foo.key",
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
TrustForwardHeader: true,
|
||||
},
|
||||
HeaderField: "X-WebAuth-User",
|
||||
},
|
||||
WhitelistSourceRange: []string{
|
||||
"10.42.0.0/16",
|
||||
"152.89.1.33/32",
|
||||
"afed:be44::/16",
|
||||
},
|
||||
WhiteList: &types.WhiteList{
|
||||
SourceRange: []string{
|
||||
"10.42.0.0/16",
|
||||
"152.89.1.33/32",
|
||||
"afed:be44::/16",
|
||||
},
|
||||
UseXForwardedFor: true,
|
||||
},
|
||||
Compress: true,
|
||||
ProxyProtocol: &ProxyProtocol{
|
||||
Insecure: false,
|
||||
TrustedIPs: []string{"192.168.0.1"},
|
||||
},
|
||||
ForwardedHeaders: &ForwardedHeaders{
|
||||
Insecure: false,
|
||||
TrustedIPs: []string{
|
||||
"10.0.0.3/24",
|
||||
"20.0.0.3/24",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "all parameters lowercase",
|
||||
expression: "Name:foo " +
|
||||
"address::8000 " +
|
||||
"tls:goo,gii;foo,fii " +
|
||||
"tls " +
|
||||
"ca:car " +
|
||||
"ca.Optional:true " +
|
||||
"redirect.entryPoint:https " +
|
||||
"redirect.regex:http://localhost/(.*) " +
|
||||
"redirect.replacement:http://mydomain/$1 " +
|
||||
"redirect.permanent:true " +
|
||||
"compress:true " +
|
||||
"whiteListSourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " +
|
||||
"proxyProtocol.TrustedIPs:192.168.0.1 " +
|
||||
"forwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24 " +
|
||||
"auth.basic.users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0 " +
|
||||
"auth.digest.users:test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e " +
|
||||
"auth.headerField:X-WebAuth-User " +
|
||||
"auth.forward.address:https://authserver.com/auth " +
|
||||
"auth.forward.trustForwardHeader:true " +
|
||||
"auth.forward.tls.ca:path/to/local.crt " +
|
||||
"auth.forward.tls.caOptional:true " +
|
||||
"auth.forward.tls.cert:path/to/foo.cert " +
|
||||
"auth.forward.tls.key:path/to/foo.key " +
|
||||
"auth.forward.tls.insecureSkipVerify:true ",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
Address: ":8000",
|
||||
TLS: &tls.TLS{
|
||||
Certificates: tls.Certificates{
|
||||
{
|
||||
CertFile: tls.FileOrContent("goo"),
|
||||
KeyFile: tls.FileOrContent("gii"),
|
||||
},
|
||||
{
|
||||
CertFile: tls.FileOrContent("foo"),
|
||||
KeyFile: tls.FileOrContent("fii"),
|
||||
},
|
||||
},
|
||||
ClientCA: tls.ClientCA{
|
||||
Files: []string{"car"},
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
Redirect: &types.Redirect{
|
||||
EntryPoint: "https",
|
||||
Regex: "http://localhost/(.*)",
|
||||
Replacement: "http://mydomain/$1",
|
||||
Permanent: true,
|
||||
},
|
||||
Auth: &types.Auth{
|
||||
Basic: &types.Basic{
|
||||
Users: types.Users{
|
||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
},
|
||||
},
|
||||
Digest: &types.Digest{
|
||||
Users: types.Users{
|
||||
"test:traefik:a2688e031edb4be6a3797f3882655c05",
|
||||
"test2:traefik:518845800f9e2bfb1f1f740ec24f074e",
|
||||
},
|
||||
},
|
||||
Forward: &types.Forward{
|
||||
Address: "https://authserver.com/auth",
|
||||
TLS: &types.ClientTLS{
|
||||
CA: "path/to/local.crt",
|
||||
CAOptional: true,
|
||||
Cert: "path/to/foo.cert",
|
||||
Key: "path/to/foo.key",
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
TrustForwardHeader: true,
|
||||
},
|
||||
HeaderField: "X-WebAuth-User",
|
||||
},
|
||||
WhitelistSourceRange: []string{
|
||||
"10.42.0.0/16",
|
||||
"152.89.1.33/32",
|
||||
"afed:be44::/16",
|
||||
},
|
||||
Compress: true,
|
||||
ProxyProtocol: &ProxyProtocol{
|
||||
Insecure: false,
|
||||
TrustedIPs: []string{"192.168.0.1"},
|
||||
},
|
||||
ForwardedHeaders: &ForwardedHeaders{
|
||||
Insecure: false,
|
||||
TrustedIPs: []string{
|
||||
"10.0.0.3/24",
|
||||
"20.0.0.3/24",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "default",
|
||||
expression: "Name:foo",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ForwardedHeaders insecure true",
|
||||
expression: "Name:foo ForwardedHeaders.Insecure:true",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ForwardedHeaders insecure false",
|
||||
expression: "Name:foo ForwardedHeaders.Insecure:false",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: false},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ForwardedHeaders TrustedIPs",
|
||||
expression: "Name:foo ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
ForwardedHeaders: &ForwardedHeaders{
|
||||
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProxyProtocol insecure true",
|
||||
expression: "Name:foo ProxyProtocol.Insecure:true",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
ProxyProtocol: &ProxyProtocol{Insecure: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProxyProtocol insecure false",
|
||||
expression: "Name:foo ProxyProtocol.Insecure:false",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
ProxyProtocol: &ProxyProtocol{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProxyProtocol TrustedIPs",
|
||||
expression: "Name:foo ProxyProtocol.TrustedIPs:10.0.0.3/24,20.0.0.3/24",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
ProxyProtocol: &ProxyProtocol{
|
||||
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "compress on",
|
||||
expression: "Name:foo Compress:on",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
Compress: true,
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "compress true",
|
||||
expression: "Name:foo Compress:true",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
Compress: true,
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
eps := EntryPoints{}
|
||||
err := eps.Set(test.expression)
|
||||
require.NoError(t, err)
|
||||
|
||||
ep := eps[test.expectedEntryPointName]
|
||||
assert.EqualValues(t, test.expectedEntryPoint, ep)
|
||||
})
|
||||
}
|
||||
}
|
97
configuration/provider_aggregator.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
|
||||
"github.com/containous/traefik/acme"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/provider"
|
||||
acmeprovider "github.com/containous/traefik/provider/acme"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/containous/traefik/types"
|
||||
)
|
||||
|
||||
type providerAggregator struct {
|
||||
providers []provider.Provider
|
||||
}
|
||||
|
||||
// NewProviderAggregator return an aggregate of all the providers configured in GlobalConfiguration
|
||||
func NewProviderAggregator(gc *GlobalConfiguration) provider.Provider {
|
||||
provider := providerAggregator{}
|
||||
if gc.Docker != nil {
|
||||
provider.providers = append(provider.providers, gc.Docker)
|
||||
}
|
||||
if gc.Marathon != nil {
|
||||
provider.providers = append(provider.providers, gc.Marathon)
|
||||
}
|
||||
if gc.File != nil {
|
||||
provider.providers = append(provider.providers, gc.File)
|
||||
}
|
||||
if gc.Rest != nil {
|
||||
provider.providers = append(provider.providers, gc.Rest)
|
||||
}
|
||||
if gc.Consul != nil {
|
||||
provider.providers = append(provider.providers, gc.Consul)
|
||||
}
|
||||
if gc.ConsulCatalog != nil {
|
||||
provider.providers = append(provider.providers, gc.ConsulCatalog)
|
||||
}
|
||||
if gc.Etcd != nil {
|
||||
provider.providers = append(provider.providers, gc.Etcd)
|
||||
}
|
||||
if gc.Zookeeper != nil {
|
||||
provider.providers = append(provider.providers, gc.Zookeeper)
|
||||
}
|
||||
if gc.Boltdb != nil {
|
||||
provider.providers = append(provider.providers, gc.Boltdb)
|
||||
}
|
||||
if gc.Kubernetes != nil {
|
||||
provider.providers = append(provider.providers, gc.Kubernetes)
|
||||
}
|
||||
if gc.Mesos != nil {
|
||||
provider.providers = append(provider.providers, gc.Mesos)
|
||||
}
|
||||
if gc.Eureka != nil {
|
||||
provider.providers = append(provider.providers, gc.Eureka)
|
||||
}
|
||||
if gc.ECS != nil {
|
||||
provider.providers = append(provider.providers, gc.ECS)
|
||||
}
|
||||
if gc.Rancher != nil {
|
||||
provider.providers = append(provider.providers, gc.Rancher)
|
||||
}
|
||||
if gc.DynamoDB != nil {
|
||||
provider.providers = append(provider.providers, gc.DynamoDB)
|
||||
}
|
||||
if gc.ServiceFabric != nil {
|
||||
provider.providers = append(provider.providers, gc.ServiceFabric)
|
||||
}
|
||||
if acmeprovider.IsEnabled() {
|
||||
provider.providers = append(provider.providers, acmeprovider.Get())
|
||||
acme.ConvertToNewFormat(acmeprovider.Get().Storage)
|
||||
}
|
||||
if len(provider.providers) == 1 {
|
||||
return provider.providers[0]
|
||||
}
|
||||
return provider
|
||||
}
|
||||
|
||||
func (p providerAggregator) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
|
||||
for _, p := range p.providers {
|
||||
providerType := reflect.TypeOf(p)
|
||||
jsonConf, err := json.Marshal(p)
|
||||
if err != nil {
|
||||
log.Debugf("Unable to marshal provider conf %v with error: %v", providerType, err)
|
||||
}
|
||||
log.Infof("Starting provider %v %s", providerType, jsonConf)
|
||||
currentProvider := p
|
||||
safe.Go(func() {
|
||||
err := currentProvider.Provide(configurationChan, pool, constraints)
|
||||
if err != nil {
|
||||
log.Errorf("Error starting provider %v: %s", providerType, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -104,7 +104,7 @@ fi
|
||||
|
||||
jq=$(command -v jq) || exit_jq
|
||||
|
||||
priv=$(${jq} -e -r '.PrivateKey' "${acmefile}") || bad_acme
|
||||
priv=$(${jq} -e -r '.Account.PrivateKey' "${acmefile}") || bad_acme
|
||||
|
||||
if [ ! -n "${priv}" ]; then
|
||||
echo "
|
||||
@@ -155,16 +155,16 @@ echo -e "-----BEGIN RSA PRIVATE KEY-----\n${priv}\n-----END RSA PRIVATE KEY-----
|
||||
| openssl rsa -inform pem -out "${pdir}/letsencrypt.key"
|
||||
|
||||
# Process the certificates for each of the domains in acme.json
|
||||
for domain in $(jq -r '.DomainsCertificate.Certs[].Certificate.Domain' ${acmefile}); do
|
||||
for domain in $(jq -r '.Certificates[].Domain.Main' ${acmefile}); do
|
||||
# Traefik stores a cert bundle for each domain. Within this cert
|
||||
# bundle there is both proper the certificate and the Let's Encrypt CA
|
||||
echo "Extracting cert bundle for ${domain}"
|
||||
cert=$(jq -e -r --arg domain "$domain" '.DomainsCertificate.Certs[].Certificate |
|
||||
select (.Domain == $domain )| .Certificate' ${acmefile}) || bad_acme
|
||||
cert=$(jq -e -r --arg domain "$domain" '.Certificates[] |
|
||||
select (.Domain.Main == $domain )| .Certificate' ${acmefile}) || bad_acme
|
||||
echo "${cert}" | ${CMD_DECODE_BASE64} > "${cdir}/${domain}.crt"
|
||||
|
||||
echo "Extracting private key for ${domain}"
|
||||
key=$(jq -e -r --arg domain "$domain" '.DomainsCertificate.Certs[].Certificate |
|
||||
select (.Domain == $domain )| .PrivateKey' ${acmefile}) || bad_acme
|
||||
key=$(jq -e -r --arg domain "$domain" '.Certificates[] |
|
||||
select (.Domain.Main == $domain )| .Key' ${acmefile}) || bad_acme
|
||||
echo "${key}" | ${CMD_DECODE_BASE64} > "${pdir}/${domain}.key"
|
||||
done
|
||||
|
171
docs/basics.md
@@ -62,13 +62,12 @@ And here is another example with client certificate authentication:
|
||||
[entryPoints.https]
|
||||
address = ":443"
|
||||
[entryPoints.https.tls]
|
||||
[entryPoints.https.tls]
|
||||
[entryPoints.https.tls.ClientCA]
|
||||
files = ["tests/clientca1.crt", "tests/clientca2.crt"]
|
||||
optional = false
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
certFile = "tests/traefik.crt"
|
||||
keyFile = "tests/traefik.key"
|
||||
[entryPoints.https.tls.ClientCA]
|
||||
files = ["tests/clientca1.crt", "tests/clientca2.crt"]
|
||||
optional = false
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
certFile = "tests/traefik.crt"
|
||||
keyFile = "tests/traefik.key"
|
||||
```
|
||||
|
||||
- We enable SSL on `https` by giving a certificate and a key.
|
||||
@@ -171,7 +170,7 @@ Here is an example of frontends definition:
|
||||
|
||||
- Three frontends are defined: `frontend1`, `frontend2` and `frontend3`
|
||||
- `frontend1` will forward the traffic to the `backend2` if the rule `Host:test.localhost,test2.localhost` is matched
|
||||
- `frontend2` will forward the traffic to the `backend1` if the rule `Host:localhost,{subdomain:[a-z]+}.localhost` is matched (forwarding client `Host` header to the backend)
|
||||
- `frontend2` will forward the traffic to the `backend1` if the rule `HostRegexp:localhost,{subdomain:[a-z]+}.localhost` is matched (forwarding client `Host` header to the backend)
|
||||
- `frontend3` will forward the traffic to the `backend2` if the rules `Host:test3.localhost` **AND** `Path:/test` are matched
|
||||
|
||||
#### Combining multiple rules
|
||||
@@ -263,7 +262,7 @@ This allows for setting headers such as `X-Script-Name` to be added to the reque
|
||||
!!! warning
|
||||
If the custom header name is the same as one header name of the request or response, it will be replaced.
|
||||
|
||||
In this example, all matches to the path `/cheese` will have the `X-Script-Name` header added to the proxied request, and the `X-Custom-Response-Header` added to the response.
|
||||
In this example, all matches to the path `/cheese` will have the `X-Script-Name` header added to the proxied request and the `X-Custom-Response-Header` header added to the response.
|
||||
|
||||
```toml
|
||||
[frontends]
|
||||
@@ -277,7 +276,7 @@ In this example, all matches to the path `/cheese` will have the `X-Script-Name`
|
||||
rule = "PathPrefixStrip:/cheese"
|
||||
```
|
||||
|
||||
In this second example, all matches to the path `/cheese` will have the `X-Script-Name` header added to the proxied request, the `X-Custom-Request-Header` removed to the request and the `X-Custom-Response-Header` removed to the response.
|
||||
In this second example, all matches to the path `/cheese` will have the `X-Script-Name` header added to the proxied request, the `X-Custom-Request-Header` header removed from the request, and the `X-Custom-Response-Header` header removed from the response.
|
||||
|
||||
```toml
|
||||
[frontends]
|
||||
@@ -324,12 +323,49 @@ In this example, traffic routed through the first frontend will have the `X-Fram
|
||||
|
||||
A backend is responsible to load-balance the traffic coming from one or more frontends to a set of http servers.
|
||||
|
||||
#### Servers
|
||||
|
||||
Servers are simply defined using a `url`. You can also apply a custom `weight` to each server (this will be used by load-balancing).
|
||||
|
||||
!!! note
|
||||
Paths in `url` are ignored. Use `Modifier` to specify paths instead.
|
||||
|
||||
Here is an example of backends and servers definition:
|
||||
|
||||
```toml
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
# ...
|
||||
[backends.backend1.servers.server1]
|
||||
url = "http://172.17.0.2:80"
|
||||
weight = 10
|
||||
[backends.backend1.servers.server2]
|
||||
url = "http://172.17.0.3:80"
|
||||
weight = 1
|
||||
[backends.backend2]
|
||||
# ...
|
||||
[backends.backend2.servers.server1]
|
||||
url = "http://172.17.0.4:80"
|
||||
weight = 1
|
||||
[backends.backend2.servers.server2]
|
||||
url = "http://172.17.0.5:80"
|
||||
weight = 2
|
||||
```
|
||||
|
||||
- Two backends are defined: `backend1` and `backend2`
|
||||
- `backend1` will forward the traffic to two servers: `http://172.17.0.2:80"` with weight `10` and `http://172.17.0.3:80` with weight `1`.
|
||||
- `backend2` will forward the traffic to two servers: `http://172.17.0.4:80"` with weight `1` and `http://172.17.0.5:80` with weight `2`.
|
||||
|
||||
#### Load-balancing
|
||||
|
||||
Various methods of load-balancing are supported:
|
||||
|
||||
- `wrr`: Weighted Round Robin.
|
||||
- `drr`: Dynamic Round Robin: increases weights on servers that perform better than others.
|
||||
It also rolls back to original weights if the servers have changed.
|
||||
|
||||
#### Circuit breakers
|
||||
|
||||
A circuit breaker can also be applied to a backend, preventing high loads on failing servers.
|
||||
Initial state is Standby. CB observes the statistics and does not modify the request.
|
||||
In case the condition matches, CB enters Tripped state, where it responds with predefined code or redirects to another frontend.
|
||||
@@ -347,6 +383,26 @@ For example:
|
||||
- `LatencyAtQuantileMS(50.0) > 50`: watch latency at quantile in milliseconds.
|
||||
- `ResponseCodeRatio(500, 600, 0, 600) > 0.5`: ratio of response codes in ranges [500-600) and [0-600).
|
||||
|
||||
Here is an example of backends and servers definition:
|
||||
|
||||
```toml
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.circuitbreaker]
|
||||
expression = "NetworkErrorRatio() > 0.5"
|
||||
[backends.backend1.servers.server1]
|
||||
url = "http://172.17.0.2:80"
|
||||
weight = 10
|
||||
[backends.backend1.servers.server2]
|
||||
url = "http://172.17.0.3:80"
|
||||
weight = 1
|
||||
```
|
||||
|
||||
- `backend1` will forward the traffic to two servers: `http://172.17.0.2:80"` with weight `10` and `http://172.17.0.3:80` with weight `1` using default `wrr` load-balancing strategy.
|
||||
- a circuit breaker is added on `backend1` using the expression `NetworkErrorRatio() > 0.5`: watch error ratio over 10 second sliding window
|
||||
|
||||
#### Maximum connections
|
||||
|
||||
To proactively prevent backends from being overwhelmed with high load, a maximum connection limit can also be applied to each backend.
|
||||
|
||||
Maximum connections can be configured by specifying an integer value for `maxconn.amount` and `maxconn.extractorfunc` which is a strategy used to determine how to categorize requests in order to evaluate the maximum connections.
|
||||
@@ -358,13 +414,14 @@ For example:
|
||||
[backends.backend1.maxconn]
|
||||
amount = 10
|
||||
extractorfunc = "request.host"
|
||||
# ...
|
||||
```
|
||||
|
||||
- `backend1` will return `HTTP code 429 Too Many Requests` if there are already 10 requests in progress for the same Host header.
|
||||
- Another possible value for `extractorfunc` is `client.ip` which will categorize requests based on client source ip.
|
||||
- Lastly `extractorfunc` can take the value of `request.header.ANY_HEADER` which will categorize requests based on `ANY_HEADER` that you provide.
|
||||
|
||||
### Sticky sessions
|
||||
#### Sticky sessions
|
||||
|
||||
Sticky sessions are supported with both load balancers.
|
||||
When sticky sessions are enabled, a cookie is set on the initial request.
|
||||
@@ -372,7 +429,6 @@ The default cookie name is an abbreviation of a sha1 (ex: `_1d52e`).
|
||||
On subsequent requests, the client will be directed to the backend stored in the cookie if it is still healthy.
|
||||
If not, a new backend will be assigned.
|
||||
|
||||
|
||||
```toml
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
@@ -396,10 +452,10 @@ The deprecated way:
|
||||
sticky = true
|
||||
```
|
||||
|
||||
### Health Check
|
||||
#### Health Check
|
||||
|
||||
A health check can be configured in order to remove a backend from LB rotation as long as it keeps returning HTTP status codes other than `200 OK` to HTTP GET requests periodically carried out by Traefik.
|
||||
The check is defined by a pathappended to the backend URL and an interval (given in a format understood by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration)) specifying how often the health check should be executed (the default being 30 seconds).
|
||||
The check is defined by a path appended to the backend URL and an interval (given in a format understood by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration)) specifying how often the health check should be executed (the default being 30 seconds).
|
||||
Each backend must respond to the health check within 5 seconds.
|
||||
By default, the port of the backend server is used, however, this may be overridden.
|
||||
|
||||
@@ -425,43 +481,6 @@ To use a different port for the healthcheck:
|
||||
port = 8080
|
||||
```
|
||||
|
||||
### Servers
|
||||
|
||||
Servers are simply defined using a `url`. You can also apply a custom `weight` to each server (this will be used by load-balancing).
|
||||
|
||||
!!! note
|
||||
Paths in `url` are ignored. Use `Modifier` to specify paths instead.
|
||||
|
||||
Here is an example of backends and servers definition:
|
||||
|
||||
```toml
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.circuitbreaker]
|
||||
expression = "NetworkErrorRatio() > 0.5"
|
||||
[backends.backend1.servers.server1]
|
||||
url = "http://172.17.0.2:80"
|
||||
weight = 10
|
||||
[backends.backend1.servers.server2]
|
||||
url = "http://172.17.0.3:80"
|
||||
weight = 1
|
||||
[backends.backend2]
|
||||
[backends.backend2.LoadBalancer]
|
||||
method = "drr"
|
||||
[backends.backend2.servers.server1]
|
||||
url = "http://172.17.0.4:80"
|
||||
weight = 1
|
||||
[backends.backend2.servers.server2]
|
||||
url = "http://172.17.0.5:80"
|
||||
weight = 2
|
||||
```
|
||||
|
||||
- Two backends are defined: `backend1` and `backend2`
|
||||
- `backend1` will forward the traffic to two servers: `http://172.17.0.2:80"` with weight `10` and `http://172.17.0.3:80` with weight `1` using default `wrr` load-balancing strategy.
|
||||
- `backend2` will forward the traffic to two servers: `http://172.17.0.4:80"` with weight `1` and `http://172.17.0.5:80` with weight `2` using `drr` load-balancing strategy.
|
||||
- a circuit breaker is added on `backend1` using the expression `NetworkErrorRatio() > 0.5`: watch error ratio over 10 second sliding window
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
Træfik's configuration has two parts:
|
||||
@@ -483,7 +502,7 @@ Each item takes precedence over the item below it:
|
||||
|
||||
It means that arguments override configuration file, and key-value store overrides arguments.
|
||||
|
||||
!!! note
|
||||
!!! note
|
||||
the provider-enabling argument parameters (e.g., `--docker`) set all default values for the specific provider.
|
||||
It must not be used if a configuration source with less precedence wants to set a non-default provider value.
|
||||
|
||||
@@ -646,18 +665,18 @@ Once a day (the first call begins 10 minutes after the start of Træfik), we col
|
||||
swarmMode = true
|
||||
|
||||
[Docker.TLS]
|
||||
CA = "dockerCA"
|
||||
Cert = "dockerCert"
|
||||
Key = "dockerKey"
|
||||
InsecureSkipVerify = true
|
||||
ca = "dockerCA"
|
||||
cert = "dockerCert"
|
||||
key = "dockerKey"
|
||||
insecureSkipVerify = true
|
||||
|
||||
[ECS]
|
||||
Domain = "foo.bar"
|
||||
ExposedByDefault = true
|
||||
Clusters = ["foo-bar"]
|
||||
Region = "us-west-2"
|
||||
AccessKeyID = "AccessKeyID"
|
||||
SecretAccessKey = "SecretAccessKey"
|
||||
domain = "foo.bar"
|
||||
exposedByDefault = true
|
||||
clusters = ["foo-bar"]
|
||||
region = "us-west-2"
|
||||
accessKeyID = "AccessKeyID"
|
||||
secretAccessKey = "SecretAccessKey"
|
||||
```
|
||||
|
||||
- Obfuscated and anonymous configuration:
|
||||
@@ -670,24 +689,24 @@ Once a day (the first call begins 10 minutes after the start of Træfik), we col
|
||||
[api]
|
||||
|
||||
[Docker]
|
||||
Endpoint = "xxxx"
|
||||
Domain = "xxxx"
|
||||
ExposedByDefault = true
|
||||
SwarmMode = true
|
||||
endpoint = "xxxx"
|
||||
domain = "xxxx"
|
||||
exposedByDefault = true
|
||||
swarmMode = true
|
||||
|
||||
[Docker.TLS]
|
||||
CA = "xxxx"
|
||||
Cert = "xxxx"
|
||||
Key = "xxxx"
|
||||
InsecureSkipVerify = false
|
||||
ca = "xxxx"
|
||||
cert = "xxxx"
|
||||
key = "xxxx"
|
||||
insecureSkipVerify = false
|
||||
|
||||
[ECS]
|
||||
Domain = "xxxx"
|
||||
ExposedByDefault = true
|
||||
Clusters = []
|
||||
Region = "us-west-2"
|
||||
AccessKeyID = "xxxx"
|
||||
SecretAccessKey = "xxxx"
|
||||
domain = "xxxx"
|
||||
exposedByDefault = true
|
||||
clusters = []
|
||||
region = "us-west-2"
|
||||
accessKeyID = "xxxx"
|
||||
secretAccessKey = "xxxx"
|
||||
```
|
||||
|
||||
### Show me the code !
|
||||
|
@@ -118,7 +118,7 @@ server {
|
||||
Here is the `traefik.toml` file used:
|
||||
|
||||
```toml
|
||||
MaxIdleConnsPerHost = 100000
|
||||
maxIdleConnsPerHost = 100000
|
||||
defaultEntryPoints = ["http"]
|
||||
|
||||
[entryPoints]
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# ACME (Let's Encrypt) configuration
|
||||
# ACME (Let's Encrypt) Configuration
|
||||
|
||||
See also [Let's Encrypt examples](/user-guide/examples/#lets-encrypt-support) and [Docker & Let's Encrypt user guide](/user-guide/docker-and-lets-encrypt).
|
||||
See [Let's Encrypt examples](/user-guide/examples/#lets-encrypt-support) and [Docker & Let's Encrypt user guide](/user-guide/docker-and-lets-encrypt) as well.
|
||||
|
||||
## Configuration
|
||||
|
||||
@@ -38,23 +38,20 @@ storage = "acme.json"
|
||||
# or `storage = "traefik/acme/account"` if using KV store.
|
||||
|
||||
# Entrypoint to proxy acme apply certificates to.
|
||||
# WARNING, if the TLS-SNI-01 challenge is used, it must point to an entrypoint on port 443
|
||||
#
|
||||
# Required
|
||||
#
|
||||
entryPoint = "https"
|
||||
|
||||
# Use a DNS-01 acme challenge rather than TLS-SNI-01 challenge
|
||||
# Deprecated, replaced by [acme.dnsChallenge].
|
||||
#
|
||||
# Optional (Deprecated, replaced by [acme.dnsChallenge])
|
||||
# Optional.
|
||||
#
|
||||
# dnsProvider = "digitalocean"
|
||||
|
||||
# By default, the dnsProvider will verify the TXT DNS challenge record before letting ACME verify.
|
||||
# If delayDontCheckDNS is greater than zero, avoid this & instead just wait so many seconds.
|
||||
# Useful if internal networks block external DNS queries.
|
||||
# Deprecated, replaced by [acme.dnsChallenge.delayBeforeCheck].
|
||||
#
|
||||
# Optional (Deprecated, replaced by [acme.dnsChallenge])
|
||||
# Optional
|
||||
# Default: 0
|
||||
#
|
||||
# delayDontCheckDNS = 0
|
||||
@@ -66,14 +63,14 @@ entryPoint = "https"
|
||||
#
|
||||
# acmeLogging = true
|
||||
|
||||
# Enable on demand certificate generation.
|
||||
# Deprecated. Enable on demand certificate generation.
|
||||
#
|
||||
# Optional (Deprecated)
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
# onDemand = true
|
||||
|
||||
# Enable certificate generation on frontends Host rules.
|
||||
# Enable certificate generation on frontends host rules.
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
@@ -81,53 +78,53 @@ entryPoint = "https"
|
||||
# onHostRule = true
|
||||
|
||||
# CA server to use.
|
||||
# - Uncomment the line to run on the staging let's encrypt server.
|
||||
# - Leave comment to go to prod.
|
||||
# Uncomment the line to use Let's Encrypt's staging server,
|
||||
# leave commented to go to prod.
|
||||
#
|
||||
# Optional
|
||||
# Default: "https://acme-v01.api.letsencrypt.org/directory"
|
||||
# Default: "https://acme-v02.api.letsencrypt.org/directory"
|
||||
#
|
||||
# caServer = "https://acme-staging.api.letsencrypt.org/directory"
|
||||
# caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
|
||||
# Domains list.
|
||||
# Only domains defined here can generate wildcard certificates.
|
||||
#
|
||||
# [[acme.domains]]
|
||||
# main = "local1.com"
|
||||
# sans = ["test1.local1.com", "test2.local1.com"]
|
||||
# [[acme.domains]]
|
||||
# main = "local2.com"
|
||||
# sans = ["test1.local2.com", "test2.local2.com"]
|
||||
# [[acme.domains]]
|
||||
# main = "local3.com"
|
||||
# [[acme.domains]]
|
||||
# main = "local4.com"
|
||||
# main = "*.local3.com"
|
||||
# sans = ["local3.com", "test1.test1.local3.com"]
|
||||
|
||||
# Use a HTTP-01 acme challenge rather than TLS-SNI-01 challenge
|
||||
# Use a HTTP-01 ACME challenge.
|
||||
#
|
||||
# Optional but recommend
|
||||
# Optional (but recommended)
|
||||
#
|
||||
[acme.httpChallenge]
|
||||
|
||||
# EntryPoint to use for the challenges.
|
||||
# EntryPoint to use for the HTTP-01 challenges.
|
||||
#
|
||||
# Required
|
||||
#
|
||||
entryPoint = "http"
|
||||
|
||||
# Use a DNS-01 acme challenge rather than TLS-SNI-01 challenge
|
||||
# Use a DNS-01 ACME challenge rather than HTTP-01 challenge.
|
||||
# Note: mandatory for wildcard certificate generation.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# [acme.dnsChallenge]
|
||||
|
||||
# Provider used.
|
||||
# DNS provider used.
|
||||
#
|
||||
# Required
|
||||
#
|
||||
# provider = "digitalocean"
|
||||
|
||||
# By default, the provider will verify the TXT DNS challenge record before letting ACME verify.
|
||||
# If delayBeforeCheck is greater than zero, avoid this & instead just wait so many seconds.
|
||||
# If delayBeforeCheck is greater than zero, this check is delayed for the configured duration in seconds.
|
||||
# Useful if internal networks block external DNS queries.
|
||||
#
|
||||
# Optional
|
||||
@@ -136,99 +133,134 @@ entryPoint = "https"
|
||||
# delayBeforeCheck = 0
|
||||
```
|
||||
|
||||
!!! note
|
||||
Even if `TLS-SNI-01` challenge is [disabled](https://community.letsencrypt.org/t/2018-01-11-update-regarding-acme-tls-sni-and-shared-hosting-infrastructure/50188) for the moment, it stays the _by default_ ACME Challenge in Træfik.
|
||||
If `TLS-SNI-01` challenge is not re-enabled in the future, it we will be removed from Træfik.
|
||||
### `caServer`
|
||||
|
||||
!!! note
|
||||
If `TLS-SNI-01` challenge is used, `acme.entryPoint` has to be reachable by Let's Encrypt through the port 443.
|
||||
If `HTTP-01` challenge is used, `acme.httpChallenge.entryPoint` has to be defined and reachable by Let's Encrypt through the port 80.
|
||||
These are Let's Encrypt limitations as described on the [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72).
|
||||
The CA server to use.
|
||||
|
||||
### Let's Encrypt downtime
|
||||
|
||||
Let's Encrypt functionality will be limited until Træfik is restarted.
|
||||
|
||||
If Let's Encrypt is not reachable, these certificates will be used :
|
||||
|
||||
- ACME certificates already generated before downtime
|
||||
- Expired ACME certificates
|
||||
- Provided certificates
|
||||
|
||||
!!! note
|
||||
Default Træfik certificate will be used instead of ACME certificates for new (sub)domains (which need Let's Encrypt challenge).
|
||||
|
||||
### `storage`
|
||||
This example shows the usage of Let's Encrypt's staging server:
|
||||
|
||||
```toml
|
||||
[acme]
|
||||
# ...
|
||||
storage = "acme.json"
|
||||
caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
# ...
|
||||
```
|
||||
|
||||
The `storage` option sets where are stored your ACME certificates.
|
||||
### `dnsChallenge`
|
||||
|
||||
There are two kind of `storage` :
|
||||
Use the `DNS-01` challenge to generate and renew ACME certificates by provisioning a DNS record.
|
||||
|
||||
- a JSON file,
|
||||
- a KV store entry.
|
||||
```toml
|
||||
[acme]
|
||||
# ...
|
||||
[acme.dnsChallenge]
|
||||
provider = "digitalocean"
|
||||
delayBeforeCheck = 0
|
||||
# ...
|
||||
```
|
||||
|
||||
!!! danger "DEPRECATED"
|
||||
`storage` replaces `storageFile` which is deprecated.
|
||||
#### `delayBeforeCheck`
|
||||
|
||||
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.
|
||||
|
||||
Useful if internal networks block external DNS queries.
|
||||
|
||||
!!! note
|
||||
During Træfik configuration migration from a configuration file to a KV store (thanks to `storeconfig` subcommand as described [here](/user-guide/kv-config/#store-configuration-in-key-value-store)), if ACME certificates have to be migrated too, use both `storageFile` and `storage`.
|
||||
A `provider` is mandatory.
|
||||
|
||||
- `storageFile` will contain the path to the `acme.json` file to migrate.
|
||||
- `storage` will contain the key where the certificates will be stored.
|
||||
#### `provider`
|
||||
|
||||
#### Store data in a file
|
||||
Here is a list of supported `provider`s, that can automate the DNS verification, along with the required environment variables and their [wildcard & root domain support](/configuration/acme/#wildcard-domains) for each. Do not hesitate to complete it.
|
||||
|
||||
ACME certificates can be stored in a JSON file which with the `600` right mode.
|
||||
| Provider Name | Provider Code | Environment Variables | Wildcard & Root Domain Support |
|
||||
|--------------------------------------------------------|----------------|-----------------------------------------------------------------------------------------------------------------------------|--------------------------------|
|
||||
| [Auroradns](https://www.pcextreme.com/aurora/dns) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` | Not tested yet |
|
||||
| [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP` | Not tested yet |
|
||||
| [Blue Cat](https://www.bluecatnetworks.com/) | `bluecat` | `BLUECAT_SERVER_URL`, `BLUECAT_USER_NAME`, `BLUECAT_PASSWORD`, `BLUECAT_CONFIG_NAME`, `BLUECAT_DNS_VIEW` | Not tested yet |
|
||||
| [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CLOUDFLARE_EMAIL`, `CLOUDFLARE_API_KEY` - The `Global API Key` needs to be used, not the `Origin CA Key` | YES |
|
||||
| [CloudXNS](https://www.cloudxns.net) | `cloudxns` | `CLOUDXNS_API_KEY`, `CLOUDXNS_SECRET_KEY` | Not tested yet |
|
||||
| [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` | YES |
|
||||
| [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` | Not tested yet |
|
||||
| [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` | Not tested yet |
|
||||
| [DNSPod](http://www.dnspod.net/) | `dnspod` | `DNSPOD_API_KEY` | Not tested yet |
|
||||
| [Duck DNS](https://www.duckdns.org/) | `duckdns` | `DUCKDNS_TOKEN` | Not tested yet |
|
||||
| [Dyn](https://dyn.com) | `dyn` | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` | Not tested yet |
|
||||
| External Program | `exec` | `EXEC_PATH` | Not tested yet |
|
||||
| [Exoscale](https://www.exoscale.ch) | `exoscale` | `EXOSCALE_API_KEY`, `EXOSCALE_API_SECRET`, `EXOSCALE_ENDPOINT` | YES |
|
||||
| [Fast DNS](https://www.akamai.com/) | `fastdns` | `AKAMAI_CLIENT_TOKEN`, `AKAMAI_CLIENT_SECRET`, `AKAMAI_ACCESS_TOKEN` | Not tested yet |
|
||||
| [Gandi](https://www.gandi.net) | `gandi` | `GANDI_API_KEY` | Not tested yet |
|
||||
| [Gandi V5](http://doc.livedns.gandi.net) | `gandiv5` | `GANDIV5_API_KEY` | YES |
|
||||
| [Glesys](https://glesys.com/) | `glesys` | `GLESYS_API_USER`, `GLESYS_API_KEY`, `GLESYS_DOMAIN` | Not tested yet |
|
||||
| [GoDaddy](https://godaddy.com/domains) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` | Not tested yet |
|
||||
| [Google Cloud DNS](https://cloud.google.com/dns/docs/) | `gcloud` | `GCE_PROJECT`, `GCE_SERVICE_ACCOUNT_FILE` | YES |
|
||||
| [Lightsail](https://aws.amazon.com/lightsail/) | `lightsail` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `DNS_ZONE` | Not tested yet |
|
||||
| [Linode](https://www.linode.com) | `linode` | `LINODE_API_KEY` | Not tested yet |
|
||||
| manual | - | none, but you need to run Træfik interactively, turn on `acmeLogging` to see instructions and press <kbd>Enter</kbd>. | YES |
|
||||
| [Namecheap](https://www.namecheap.com) | `namecheap` | `NAMECHEAP_API_USER`, `NAMECHEAP_API_KEY` | Not tested yet |
|
||||
| [name.com](https://www.name.com/) | `namedotcom` | `NAMECOM_USERNAME`, `NAMECOM_API_TOKEN`, `NAMECOM_SERVER` | Not tested yet |
|
||||
| [Ns1](https://ns1.com/) | `ns1` | `NS1_API_KEY` | Not tested yet |
|
||||
| [Open Telekom Cloud](https://cloud.telekom.de/en/) | `otc` | `OTC_DOMAIN_NAME`, `OTC_USER_NAME`, `OTC_PASSWORD`, `OTC_PROJECT_NAME`, `OTC_IDENTITY_ENDPOINT` | Not tested yet |
|
||||
| [OVH](https://www.ovh.com) | `ovh` | `OVH_ENDPOINT`, `OVH_APPLICATION_KEY`, `OVH_APPLICATION_SECRET`, `OVH_CONSUMER_KEY` | YES |
|
||||
| [PowerDNS](https://www.powerdns.com) | `pdns` | `PDNS_API_KEY`, `PDNS_API_URL` | Not tested yet |
|
||||
| [Rackspace](https://www.rackspace.com/cloud/dns) | `rackspace` | `RACKSPACE_USER`, `RACKSPACE_API_KEY` | Not tested yet |
|
||||
| [RFC2136](https://tools.ietf.org/html/rfc2136) | `rfc2136` | `RFC2136_TSIG_KEY`, `RFC2136_TSIG_SECRET`, `RFC2136_TSIG_ALGORITHM`, `RFC2136_NAMESERVER` | Not tested yet |
|
||||
| [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. | YES |
|
||||
| [VULTR](https://www.vultr.com) | `vultr` | `VULTR_API_KEY` | Not tested yet |
|
||||
|
||||
There are two ways to store ACME certificates in a file from Docker:
|
||||
### `domains`
|
||||
|
||||
You can provide SANs (alternative domains) to each main domain.
|
||||
All domains must have A/AAAA records pointing to Træfik.
|
||||
Each domain & SAN will lead to a certificate request.
|
||||
|
||||
- create a file on your host and mount it as a volume:
|
||||
```toml
|
||||
storage = "acme.json"
|
||||
```
|
||||
```bash
|
||||
docker run -v "/my/host/acme.json:acme.json" traefik
|
||||
```
|
||||
- mount the folder containing the file as a volume
|
||||
```toml
|
||||
storage = "/etc/traefik/acme/acme.json"
|
||||
```
|
||||
```bash
|
||||
docker run -v "/my/host/acme:/etc/traefik/acme" traefik
|
||||
[acme]
|
||||
# ...
|
||||
[[acme.domains]]
|
||||
main = "local1.com"
|
||||
sans = ["test1.local1.com", "test2.local1.com"]
|
||||
[[acme.domains]]
|
||||
main = "local2.com"
|
||||
[[acme.domains]]
|
||||
main = "*.local3.com"
|
||||
sans = ["local3.com", "test1.test1.local3.com"]
|
||||
# ...
|
||||
```
|
||||
|
||||
!!! warning
|
||||
This file cannot be shared per many instances of Træfik at the same time.
|
||||
If you have to use Træfik cluster mode, please use [a KV Store entry](/configuration/acme/#storage-kv-entry).
|
||||
|
||||
#### Store data in a KV store entry
|
||||
|
||||
ACME certificates can be stored in a KV Store entry.
|
||||
|
||||
```toml
|
||||
storage = "traefik/acme/account"
|
||||
```
|
||||
|
||||
**This kind of storage is mandatory in cluster mode.**
|
||||
|
||||
Because KV stores (like Consul) have limited entries size, the certificates list is compressed before to be set in a KV store entry.
|
||||
Take note that Let's Encrypt applies [rate limiting](https://letsencrypt.org/docs/rate-limits).
|
||||
|
||||
!!! note
|
||||
It's possible to store up to approximately 100 ACME certificates in Consul.
|
||||
Wildcard certificates can only be verified through a `DNS-01` challenge.
|
||||
|
||||
### `acme.httpChallenge`
|
||||
#### Wildcard Domains
|
||||
|
||||
Use `HTTP-01` challenge to generate/renew ACME certificates.
|
||||
[ACME V2](https://community.letsencrypt.org/t/acme-v2-and-wildcard-certificate-support-is-live/55579) allows wildcard certificate support.
|
||||
As described in [Let's Encrypt's post](https://community.letsencrypt.org/t/staging-endpoint-for-acme-v2/49605) wildcard certificates can only be generated through a [`DNS-01` challenge](/configuration/acme/#dnschallenge).
|
||||
|
||||
The redirection is fully compatible with the HTTP-01 challenge.
|
||||
You can use redirection with HTTP-01 challenge without problem.
|
||||
```toml
|
||||
[acme]
|
||||
# ...
|
||||
[[acme.domains]]
|
||||
main = "*.local1.com"
|
||||
sans = ["local1.com"]
|
||||
# ...
|
||||
```
|
||||
|
||||
It is not possible to request a double wildcard certificate for a domain (for example `*.*.local.com`).
|
||||
Due to ACME limitation it is not possible to define wildcards in SANs (alternative domains). Thus, the wildcard domain has to be defined as a main domain.
|
||||
Most likely the root domain should receive a certificate too, so it needs to be specified as SAN and 2 `DNS-01` challenges are executed.
|
||||
In this case the generated DNS TXT record for both domains is the same.
|
||||
Eventhough this behaviour is [DNS RFC](https://community.letsencrypt.org/t/wildcard-issuance-two-txt-records-for-the-same-name/54528/2) compliant, it can lead to problems as all DNS providers keep DNS records cached for a certain time (TTL) and this TTL can be superior to the challenge timeout making the `DNS-01` challenge fail.
|
||||
The Træfik ACME client library [LEGO](https://github.com/xenolf/lego) supports some but not all DNS providers to work around this issue.
|
||||
The [`provider` table](/configuration/acme/#provider) indicates if they allow generating certificates for a wildcard domain and its root domain.
|
||||
|
||||
### `httpChallenge`
|
||||
|
||||
Use the `HTTP-01` challenge to generate and renew ACME certificates by provisioning a HTTP resource under a well-known URI.
|
||||
|
||||
Redirection is fully compatible with the `HTTP-01` challenge.
|
||||
|
||||
```toml
|
||||
[acme]
|
||||
@@ -238,6 +270,10 @@ entryPoint = "https"
|
||||
entryPoint = "http"
|
||||
```
|
||||
|
||||
!!! note
|
||||
If the `HTTP-01` challenge is used, `acme.httpChallenge.entryPoint` has to be defined and reachable by Let's Encrypt through port 80.
|
||||
This is a Let's Encrypt limitation as described on the [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72).
|
||||
|
||||
#### `entryPoint`
|
||||
|
||||
Specify the entryPoint to use during the challenges.
|
||||
@@ -261,61 +297,7 @@ defaultEntryPoints = ["http", "https"]
|
||||
```
|
||||
|
||||
!!! note
|
||||
`acme.httpChallenge.entryPoint` has to be reachable by Let's Encrypt through the port 80.
|
||||
It's a Let's Encrypt limitation as described on the [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72).
|
||||
|
||||
### `acme.dnsChallenge`
|
||||
|
||||
Use `DNS-01` challenge to generate/renew ACME certificates.
|
||||
|
||||
```toml
|
||||
[acme]
|
||||
# ...
|
||||
[acme.dnsChallenge]
|
||||
provider = "digitalocean"
|
||||
delayBeforeCheck = 0
|
||||
# ...
|
||||
```
|
||||
|
||||
#### `provider`
|
||||
|
||||
Select the provider that matches the DNS domain that will host the challenge TXT record, and provide environment variables to enable setting it:
|
||||
|
||||
| Provider Name | Provider code | Configuration |
|
||||
|--------------------------------------------------------|----------------|---------------------------------------------------------------------------------------------------------------------------|
|
||||
| [Auroradns](https://www.pcextreme.com/aurora/dns) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` |
|
||||
| [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP` |
|
||||
| [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CLOUDFLARE_EMAIL`, `CLOUDFLARE_API_KEY` - The Cloudflare `Global API Key` needs to be used and not the `Origin CA Key` |
|
||||
| [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` |
|
||||
| [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` |
|
||||
| [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` |
|
||||
| [DNSPod](http://www.dnspod.net/) | `dnspod` | `DNSPOD_API_KEY` |
|
||||
| [Dyn](https://dyn.com) | `dyn` | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` |
|
||||
| [Exoscale](https://www.exoscale.ch) | `exoscale` | `EXOSCALE_API_KEY`, `EXOSCALE_API_SECRET`, `EXOSCALE_ENDPOINT` |
|
||||
| [Gandi](https://www.gandi.net) | `gandi` | `GANDI_API_KEY` |
|
||||
| [GoDaddy](https://godaddy.com/domains) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` |
|
||||
| [Google Cloud DNS](https://cloud.google.com/dns/docs/) | `gcloud` | `GCE_PROJECT`, `GCE_SERVICE_ACCOUNT_FILE` |
|
||||
| [Linode](https://www.linode.com) | `linode` | `LINODE_API_KEY` |
|
||||
| manual | - | none, but run Træfik interactively & turn on `acmeLogging` to see instructions & press <kbd>Enter</kbd>. |
|
||||
| [Namecheap](https://www.namecheap.com) | `namecheap` | `NAMECHEAP_API_USER`, `NAMECHEAP_API_KEY` |
|
||||
| [Ns1](https://ns1.com/) | `ns1` | `NS1_API_KEY` |
|
||||
| [Open Telekom Cloud](https://cloud.telekom.de/en/) | `otc` | `OTC_DOMAIN_NAME`, `OTC_USER_NAME`, `OTC_PASSWORD`, `OTC_PROJECT_NAME`, `OTC_IDENTITY_ENDPOINT` |
|
||||
| [OVH](https://www.ovh.com) | `ovh` | `OVH_ENDPOINT`, `OVH_APPLICATION_KEY`, `OVH_APPLICATION_SECRET`, `OVH_CONSUMER_KEY` |
|
||||
| [PowerDNS](https://www.powerdns.com) | `pdns` | `PDNS_API_KEY`, `PDNS_API_URL` |
|
||||
| [Rackspace](https://www.rackspace.com/cloud/dns) | `rackspace` | `RACKSPACE_USER`, `RACKSPACE_API_KEY` |
|
||||
| [RFC2136](https://tools.ietf.org/html/rfc2136) | `rfc2136` | `RFC2136_TSIG_KEY`, `RFC2136_TSIG_SECRET`, `RFC2136_TSIG_ALGORITHM`, `RFC2136_NAMESERVER` |
|
||||
| [Route 53](https://aws.amazon.com/route53/) | `route53` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`, `AWS_HOSTED_ZONE_ID` or configured user/instance IAM profile. |
|
||||
| [VULTR](https://www.vultr.com) | `vultr` | `VULTR_API_KEY` |
|
||||
|
||||
#### `delayBeforeCheck`
|
||||
|
||||
By default, the `provider` will verify the TXT DNS challenge record before letting ACME verify.
|
||||
If `delayBeforeCheck` is greater than zero, avoid this & instead just wait so many seconds.
|
||||
|
||||
Useful if internal networks block external DNS queries.
|
||||
|
||||
!!! note
|
||||
This field has no sense if a `provider` is not defined.
|
||||
`acme.httpChallenge.entryPoint` has to be reachable through port 80. It's a Let's Encrypt limitation as described on the [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72).
|
||||
|
||||
### `onDemand` (Deprecated)
|
||||
|
||||
@@ -329,15 +311,15 @@ onDemand = true
|
||||
# ...
|
||||
```
|
||||
|
||||
Enable on demand certificate.
|
||||
Enable on demand certificate generation.
|
||||
|
||||
This will request a certificate from Let's Encrypt during the first TLS handshake for a host name that does not yet have a certificate.
|
||||
This will request certificates from Let's Encrypt during the first TLS handshake for host names that do not yet have certificates.
|
||||
|
||||
!!! warning
|
||||
TLS handshakes will be slow when requesting a host name certificate for the first time, this can lead to DoS attacks.
|
||||
TLS handshakes are slow when requesting a host name certificate for the first time. This can lead to DoS attacks!
|
||||
|
||||
!!! warning
|
||||
Take note that Let's Encrypt have [rate limiting](https://letsencrypt.org/docs/rate-limits).
|
||||
Take note that Let's Encrypt applies [rate limiting](https://letsencrypt.org/docs/rate-limits).
|
||||
|
||||
### `onHostRule`
|
||||
|
||||
@@ -348,58 +330,94 @@ onHostRule = true
|
||||
# ...
|
||||
```
|
||||
|
||||
Enable certificate generation on frontends `Host` rules (for frontends wired on the `acme.entryPoint`).
|
||||
Enable certificate generation on frontend `Host` rules (for frontends wired to the `acme.entryPoint`).
|
||||
|
||||
This will request a certificate from Let's Encrypt for each frontend with a Host rule.
|
||||
|
||||
For example, a rule `Host:test1.traefik.io,test2.traefik.io` will request a certificate with main domain `test1.traefik.io` and SAN `test2.traefik.io`.
|
||||
|
||||
### `caServer`
|
||||
|
||||
```toml
|
||||
[acme]
|
||||
# ...
|
||||
caServer = "https://acme-staging.api.letsencrypt.org/directory"
|
||||
# ...
|
||||
```
|
||||
|
||||
CA server to use.
|
||||
|
||||
- Uncomment the line to run on the staging Let's Encrypt server.
|
||||
- Leave comment to go to prod.
|
||||
|
||||
### `acme.domains`
|
||||
|
||||
```toml
|
||||
[acme]
|
||||
# ...
|
||||
[[acme.domains]]
|
||||
main = "local1.com"
|
||||
sans = ["test1.local1.com", "test2.local1.com"]
|
||||
[[acme.domains]]
|
||||
main = "local2.com"
|
||||
sans = ["test1.local2.com", "test2.local2.com"]
|
||||
[[acme.domains]]
|
||||
main = "local3.com"
|
||||
[[acme.domains]]
|
||||
main = "local4.com"
|
||||
# ...
|
||||
```
|
||||
|
||||
You can provide SANs (alternative domains) to each main domain.
|
||||
All domains must have A/AAAA records pointing to Træfik.
|
||||
For example, the rule `Host:test1.traefik.io,test2.traefik.io` will request a certificate with main domain `test1.traefik.io` and SAN `test2.traefik.io`.
|
||||
|
||||
!!! warning
|
||||
Take note that Let's Encrypt have [rate limiting](https://letsencrypt.org/docs/rate-limits).
|
||||
`onHostRule` option can not be used to generate wildcard certificates.
|
||||
Refer to [wildcard generation](/configuration/acme/#wildcard-domain) for further information.
|
||||
|
||||
Each domain & SANs will lead to a certificate request.
|
||||
### `storage`
|
||||
|
||||
The `storage` option sets the location where your ACME certificates are saved to.
|
||||
|
||||
```toml
|
||||
[acme]
|
||||
# ...
|
||||
storage = "acme.json"
|
||||
# ...
|
||||
```
|
||||
|
||||
The value can refer to two kinds of storage:
|
||||
|
||||
- a JSON file
|
||||
- a KV store entry
|
||||
|
||||
!!! danger "DEPRECATED"
|
||||
`storage` replaces `storageFile` which is deprecated.
|
||||
|
||||
!!! note
|
||||
During migration to a KV store use both `storageFile` and `storage` to migrate ACME certificates too. See [`storeconfig` subcommand](/user-guide/kv-config/#store-configuration-in-key-value-store) for further information.
|
||||
|
||||
#### As a File
|
||||
|
||||
ACME certificates can be stored in a JSON file that needs to have file mode `600`.
|
||||
|
||||
In Docker you can either mount the JSON file or the folder containing it:
|
||||
|
||||
```bash
|
||||
docker run -v "/my/host/acme.json:acme.json" traefik
|
||||
```
|
||||
```bash
|
||||
docker run -v "/my/host/acme:/etc/traefik/acme" traefik
|
||||
```
|
||||
|
||||
!!! warning
|
||||
This file cannot be shared across multiple instances of Træfik at the same time. Please use a [KV Store entry](/configuration/acme/#as-a-key-value-store-entry) instead.
|
||||
|
||||
#### As a Key Value Store Entry
|
||||
|
||||
ACME certificates can be stored in a KV Store entry. This kind of storage is **mandatory in cluster mode**.
|
||||
|
||||
```toml
|
||||
storage = "traefik/acme/account"
|
||||
```
|
||||
|
||||
Because KV stores (like Consul) have limited entry size the certificates list is compressed before it is saved as KV store entry.
|
||||
|
||||
!!! note
|
||||
It is possible to store up to approximately 100 ACME certificates in Consul.
|
||||
|
||||
#### ACME v2 Migration
|
||||
|
||||
During migration from ACME v1 to ACME v2, using a storage file, a backup of the original file is created in the same place as the latter (with a `.bak` extension).
|
||||
|
||||
For example: if `acme.storage`'s value is `/etc/traefik/acme/acme.json`, the backup file will be `/etc/traefik/acme/acme.json.bak`.
|
||||
|
||||
!!! note
|
||||
When Træfik is launched in a container, the storage file's parent directory needs to be mounted to be able to access the backup file on the host.
|
||||
Otherwise the backup file will be deleted when the container is stopped. Træfik will only generate it once!
|
||||
|
||||
### `dnsProvider` (Deprecated)
|
||||
|
||||
!!! danger "DEPRECATED"
|
||||
This option is deprecated, use [dnsChallenge.provider](/configuration/acme/#acmednschallenge) instead.
|
||||
This option is deprecated. Please use [dnsChallenge.provider](/configuration/acme/#provider) instead.
|
||||
|
||||
### `delayDontCheckDNS` (Deprecated)
|
||||
|
||||
!!! danger "DEPRECATED"
|
||||
This option is deprecated, use [dnsChallenge.delayBeforeCheck](/configuration/acme/#acmednschallenge) instead.
|
||||
This option is deprecated. Please use [dnsChallenge.delayBeforeCheck](/configuration/acme/#dnschallenge) instead.
|
||||
|
||||
## Fallbacks
|
||||
|
||||
If Let's Encrypt is not reachable, these certificates will be used:
|
||||
|
||||
1. ACME certificates already generated before downtime
|
||||
1. Expired ACME certificates
|
||||
1. Provided certificates
|
||||
|
||||
!!! note
|
||||
For new (sub)domains which need Let's Encrypt authentification, the default Træfik certificate will be used until Træfik is restarted.
|
||||
|
@@ -43,6 +43,7 @@ For more customization, see [entry points](/configuration/entrypoints/) document
|
||||
| Path | Method | Description |
|
||||
|-----------------------------------------------------------------|------------------|-------------------------------------------|
|
||||
| `/` | `GET` | Provides a simple HTML frontend of Træfik |
|
||||
| `/cluster/leader` | `GET` | JSON leader true/false response |
|
||||
| `/health` | `GET` | JSON health metrics |
|
||||
| `/api` | `GET` | Configuration for all providers |
|
||||
| `/api/providers` | `GET` | Providers |
|
||||
@@ -222,6 +223,25 @@ curl -s "http://localhost:8080/api" | jq .
|
||||
}
|
||||
```
|
||||
|
||||
### Cluster Leadership
|
||||
|
||||
```shell
|
||||
curl -s "http://localhost:8080/cluster/leader" | jq .
|
||||
```
|
||||
```shell
|
||||
< HTTP/1.1 200 OK
|
||||
< Content-Type: application/json; charset=UTF-8
|
||||
< Date: xxx
|
||||
< Content-Length: 15
|
||||
```
|
||||
If the given node is not a cluster leader, an HTTP status of `429-Too-Many-Requests` will be returned.
|
||||
```json
|
||||
{
|
||||
// current leadership status of the queried node
|
||||
"leader": true
|
||||
}
|
||||
```
|
||||
|
||||
### Health
|
||||
|
||||
```shell
|
||||
|
@@ -1,13 +1,13 @@
|
||||
# BoltDB Backend
|
||||
# BoltDB Provider
|
||||
|
||||
Træfik can be configured to use BoltDB as a backend configuration.
|
||||
Træfik can be configured to use BoltDB as a provider.
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# BoltDB configuration backend
|
||||
# BoltDB Provider
|
||||
################################################################
|
||||
|
||||
# Enable BoltDB configuration backend.
|
||||
# Enable BoltDB Provider.
|
||||
[boltdb]
|
||||
|
||||
# BoltDB file.
|
||||
@@ -53,7 +53,7 @@ filename = "boltdb.tmpl"
|
||||
# ca = "/etc/ssl/ca.crt"
|
||||
# cert = "/etc/ssl/boltdb.crt"
|
||||
# key = "/etc/ssl/boltdb.key"
|
||||
# insecureskipverify = true
|
||||
# insecureSkipVerify = true
|
||||
```
|
||||
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
@@ -1,13 +1,13 @@
|
||||
# Consul Key-Value backend
|
||||
# Consul Key-Value Provider
|
||||
|
||||
Træfik can be configured to use Consul as a backend configuration.
|
||||
Træfik can be configured to use Consul as a provider.
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Consul KV configuration backend
|
||||
# Consul KV Provider
|
||||
################################################################
|
||||
|
||||
# Enable Consul KV configuration backend.
|
||||
# Enable Consul KV Provider.
|
||||
[consul]
|
||||
|
||||
# Consul server endpoint.
|
||||
@@ -53,9 +53,9 @@ prefix = "traefik"
|
||||
# ca = "/etc/ssl/ca.crt"
|
||||
# cert = "/etc/ssl/consul.crt"
|
||||
# key = "/etc/ssl/consul.key"
|
||||
# insecureskipverify = true
|
||||
# insecureSkipVerify = true
|
||||
```
|
||||
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
||||
Please refer to the [Key Value storage structure](/user-guide/kv-config/#key-value-storage-structure) section to get documentation on Traefik KV structure.
|
||||
|
@@ -1,13 +1,13 @@
|
||||
# Consul Catalog backend
|
||||
# Consul Catalog Provider
|
||||
|
||||
Træfik can be configured to use service discovery catalog of Consul as a backend configuration.
|
||||
Træfik can be configured to use service discovery catalog of Consul as a provider.
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Consul Catalog configuration backend
|
||||
# Consul Catalog Provider
|
||||
################################################################
|
||||
|
||||
# Enable Consul Catalog configuration backend.
|
||||
# Enable Consul Catalog Provider.
|
||||
[consulCatalog]
|
||||
|
||||
# Consul server endpoint.
|
||||
@@ -48,46 +48,136 @@ prefix = "traefik"
|
||||
# Default: "Host:{{.ServiceName}}.{{.Domain}}"
|
||||
#
|
||||
#frontEndRule = "Host:{{.ServiceName}}.{{.Domain}}"
|
||||
|
||||
# Enable Consul catalog TLS connection.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# [consulCatalog.tls]
|
||||
# ca = "/etc/ssl/ca.crt"
|
||||
# cert = "/etc/ssl/consul.crt"
|
||||
# key = "/etc/ssl/consul.key"
|
||||
# insecureSkipVerify = true
|
||||
|
||||
# Override default configuration template.
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# filename = "consulcatalog.tmpl"
|
||||
|
||||
# Override template version
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
# - "1": previous template version (must be used only with older custom templates, see "filename")
|
||||
# - "2": current template version (must be used to force template version when "filename" is used)
|
||||
#
|
||||
# templateVersion = 2
|
||||
```
|
||||
|
||||
This backend will create routes matching on hostname based on the service name used in Consul.
|
||||
This provider will create routes matching on hostname based on the service name used in Consul.
|
||||
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
||||
### Tags
|
||||
## Tags
|
||||
|
||||
Additional settings can be defined using Consul Catalog tags.
|
||||
|
||||
| Tag | Description |
|
||||
|-----------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.enable=false` | Disable this container in Træfik |
|
||||
| `traefik.protocol=https` | Override the default `http` protocol |
|
||||
| `traefik.backend.weight=10` | Assign this weight to the container |
|
||||
| `traefik.backend.circuitbreaker=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend, ex: `NetworkErrorRatio() > 0.` |
|
||||
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend. Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by. Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.frontend.rule=Host:test.traefik.io` | Override the default frontend rule (Default: `Host:{{.ServiceName}}.{{.Domain}}`). |
|
||||
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
|
||||
| `traefik.frontend.priority=10` | Override default frontend priority |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints`. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
|
||||
| `traefik.backend.loadbalancer=drr` | override the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | enable backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | enable backend sticky sessions (DEPRECATED) |
|
||||
!!! note
|
||||
The default prefix is `traefik`.
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `<prefix>.enable=false` | Disable this container in Træfik. |
|
||||
| `<prefix>.protocol=https` | Override the default `http` protocol. |
|
||||
| `<prefix>.weight=10` | Assign this weight to the container. |
|
||||
| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `<prefix>.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend. ex: `NetworkErrorRatio() > 0.` |
|
||||
| `<prefix>.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. |
|
||||
| `<prefix>.backend.healthcheck.port=8080` | Allow to use a different port for the health check. |
|
||||
| `<prefix>.backend.healthcheck.interval=1s` | Define the health check interval. |
|
||||
| `<prefix>.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm. |
|
||||
| `<prefix>.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions. |
|
||||
| `<prefix>.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions. |
|
||||
| `<prefix>.backend.loadbalancer.sticky=true` | Enable backend sticky sessions. (DEPRECATED) |
|
||||
| `<prefix>.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `<prefix>.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `<prefix>.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
|
||||
| `<prefix>.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `<prefix>.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `<prefix>.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `<prefix>.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `<prefix>.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
|
||||
| `<prefix>.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. |
|
||||
| `<prefix>.frontend.priority=10` | Override default frontend priority. |
|
||||
| `<prefix>.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `<prefix>.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `<prefix>.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `<prefix>.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `<prefix>.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS). |
|
||||
| `<prefix>.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `<prefix>.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `<prefix>.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
||||
| `<prefix>.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{{.ServiceName}}.{{.Domain}}`. |
|
||||
| `<prefix>.frontend.whiteList.sourceRange=RANGE` | List of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `<prefix>.frontend.whiteList.useXForwardedFor=true` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
|
||||
### Custom Headers
|
||||
|
||||
!!! note
|
||||
The default prefix is `traefik`.
|
||||
|
||||
| Label | Description |
|
||||
|--------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `<prefix>.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `<prefix>.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
|
||||
### Security Headers
|
||||
|
||||
!!! note
|
||||
The default prefix is `traefik`.
|
||||
|
||||
| Label | Description |
|
||||
|-----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `<prefix>.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
|
||||
| `<prefix>.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
|
||||
| `<prefix>.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
|
||||
| `<prefix>.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
|
||||
| `<prefix>.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
|
||||
| `<prefix>.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
|
||||
| `<prefix>.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
|
||||
| `<prefix>.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
|
||||
| `<prefix>.frontend.headers.hostsProxyHeaders=EXPR` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
|
||||
| `<prefix>.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
| `<prefix>.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
|
||||
| `<prefix>.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
|
||||
| `<prefix>.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
|
||||
| `<prefix>.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `<prefix>.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `<prefix>.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `<prefix>.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `<prefix>.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `<prefix>.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
|
||||
### Examples
|
||||
|
||||
If you want that Træfik uses Consul tags correctly you need to defined them like that:
|
||||
```json
|
||||
|
||||
```js
|
||||
traefik.enable=true
|
||||
traefik.tags=api
|
||||
traefik.tags=external
|
||||
```
|
||||
|
||||
If the prefix defined in Træfik configuration is `bla`, tags need to be defined like that:
|
||||
```json
|
||||
|
||||
```js
|
||||
bla.enable=true
|
||||
bla.tags=api
|
||||
bla.tags=external
|
||||
```
|
||||
```
|
||||
|
@@ -1,16 +1,16 @@
|
||||
|
||||
# Docker Backend
|
||||
# Docker Provider
|
||||
|
||||
Træfik can be configured to use Docker as a backend configuration.
|
||||
Træfik can be configured to use Docker as a provider.
|
||||
|
||||
## Docker
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Docker configuration backend
|
||||
# Docker Provider
|
||||
################################################################
|
||||
|
||||
# Enable Docker configuration backend.
|
||||
# Enable Docker Provider.
|
||||
[docker]
|
||||
|
||||
# Docker server endpoint. Can be a tcp or a unix socket endpoint.
|
||||
@@ -39,13 +39,22 @@ watch = true
|
||||
#
|
||||
# filename = "docker.tmpl"
|
||||
|
||||
# Override template version
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
# - "1": previous template version (must be used only with older custom templates, see "filename")
|
||||
# - "2": current template version (must be used to force template version when "filename" is used)
|
||||
#
|
||||
# templateVersion = 2
|
||||
|
||||
# Expose containers by default in Traefik.
|
||||
# If set to false, containers that don't have `traefik.enable=true` will be ignored.
|
||||
#
|
||||
# Optional
|
||||
# Default: true
|
||||
#
|
||||
exposedbydefault = true
|
||||
exposedByDefault = true
|
||||
|
||||
# Use the IP address from the binded port instead of the inner network one.
|
||||
# For specific use-case :)
|
||||
@@ -60,7 +69,7 @@ usebindportip = true
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
swarmmode = false
|
||||
swarmMode = false
|
||||
|
||||
# Enable docker TLS connection.
|
||||
#
|
||||
@@ -70,20 +79,20 @@ swarmmode = false
|
||||
# ca = "/etc/ssl/ca.crt"
|
||||
# cert = "/etc/ssl/docker.crt"
|
||||
# key = "/etc/ssl/docker.key"
|
||||
# insecureskipverify = true
|
||||
# insecureSkipVerify = true
|
||||
```
|
||||
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
||||
|
||||
## Docker Swarm Mode
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Docker Swarmmode configuration backend
|
||||
# Docker Swarm Mode Provider
|
||||
################################################################
|
||||
|
||||
# Enable Docker configuration backend.
|
||||
# Enable Docker Provider.
|
||||
[docker]
|
||||
|
||||
# Docker server endpoint.
|
||||
@@ -114,7 +123,7 @@ watch = true
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
swarmmode = true
|
||||
swarmMode = true
|
||||
|
||||
# Override default configuration template.
|
||||
# For advanced users :)
|
||||
@@ -123,12 +132,21 @@ swarmmode = true
|
||||
#
|
||||
# filename = "docker.tmpl"
|
||||
|
||||
# Override template version
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
# - "1": previous template version (must be used only with older custom templates, see "filename")
|
||||
# - "2": current template version (must be used to force template version when "filename" is used)
|
||||
#
|
||||
# templateVersion = 2
|
||||
|
||||
# Expose services by default in Traefik.
|
||||
#
|
||||
# Optional
|
||||
# Default: true
|
||||
#
|
||||
exposedbydefault = false
|
||||
exposedByDefault = false
|
||||
|
||||
# Enable docker TLS connection.
|
||||
#
|
||||
@@ -138,14 +156,14 @@ exposedbydefault = false
|
||||
# ca = "/etc/ssl/ca.crt"
|
||||
# cert = "/etc/ssl/docker.crt"
|
||||
# key = "/etc/ssl/docker.key"
|
||||
# insecureskipverify = true
|
||||
# insecureSkipVerify = true
|
||||
```
|
||||
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
||||
## Labels: overriding default behaviour
|
||||
## Labels: overriding default behavior
|
||||
|
||||
#### Using Docker with Swarm Mode
|
||||
### Using Docker with Swarm Mode
|
||||
|
||||
If you use a compose file with the Swarm mode, labels should be defined in the `deploy` part of your service.
|
||||
This behavior is only enabled for docker-compose version 3+ ([Compose file reference](https://docs.docker.com/compose/compose-file/#labels-1)).
|
||||
@@ -159,9 +177,9 @@ services:
|
||||
traefik.docker.network: traefik
|
||||
```
|
||||
|
||||
#### Using Docker Compose
|
||||
### Using Docker Compose
|
||||
|
||||
If you are intending to use only Docker Compose commands (e.g. `docker-compose up --scale whoami=2 -d`), labels should be under your service, otherwise they will be ignored.
|
||||
If you are intending to use only Docker Compose commands (e.g. `docker-compose up --scale whoami=2 -d`), labels should be under your service, otherwise they will be ignored.
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
@@ -173,89 +191,163 @@ services:
|
||||
|
||||
### On Containers
|
||||
|
||||
Labels can be used on containers to override default behaviour.
|
||||
Labels can be used on containers to override default behavior.
|
||||
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.backend=foo` | Give the name `foo` to the generated backend for this container. |
|
||||
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend. Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by. Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) |
|
||||
| `traefik.backend.loadbalancer.swarm=true` | Use Swarm's inbuilt load balancer (only relevant under Swarm Mode). |
|
||||
| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.port=80` | Register this port. Useful when the container exposes multiples ports. |
|
||||
| `traefik.protocol=https` | Override the default `http` protocol |
|
||||
| `traefik.weight=10` | Assign this weight to the container |
|
||||
| `traefik.enable=false` | Disable this container in Træfik |
|
||||
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{containerName}.{domain}` or `Host:{service}.{project_name}.{domain}` if you are using `docker-compose`. |
|
||||
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
|
||||
| `traefik.frontend.priority=10` | Override default frontend priority |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints` |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
|
||||
| `traefik.frontend.whitelistSourceRange:RANGE` | List of IP-Ranges which are allowed to access. An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.docker.network` | Set the docker network to use for connections to this container. [1] |
|
||||
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) |
|
||||
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend. Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend. Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.docker.network` | Set the docker network to use for connections to this container. [1] |
|
||||
| `traefik.domain` | Default domain used for frontend rules. |
|
||||
| `traefik.enable=false` | Disable this container in Træfik |
|
||||
| `traefik.port=80` | Register this port. Useful when the container exposes multiples ports. |
|
||||
| `traefik.protocol=https` | Override the default `http` protocol |
|
||||
| `traefik.weight=10` | Assign this weight to the container |
|
||||
| `traefik.backend=foo` | Give the name `foo` to the generated backend for this container. |
|
||||
| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. |
|
||||
| `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.interval=1s` | Define the health check interval. |
|
||||
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) |
|
||||
| `traefik.backend.loadbalancer.swarm=true` | Use Swarm's inbuilt load balancer (only relevant under Swarm Mode). |
|
||||
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` [2] |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. |
|
||||
| `traefik.frontend.priority=10` | Override default frontend priority |
|
||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) |
|
||||
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
||||
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{containerName}.{domain}` or `Host:{service}.{project_name}.{domain}` if you are using `docker-compose`. |
|
||||
| `traefik.frontend.whiteList.sourceRange=RANGE` | List of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access.<br>If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.frontend.whiteList.useXForwardedFor=true` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
|
||||
[1] `traefik.docker.network`:
|
||||
If a container is linked to several networks, be sure to set the proper network name (you can check with `docker inspect <container_id>`) otherwise it will randomly pick one (depending on how docker is returning them).
|
||||
For instance when deploying docker `stack` from compose files, the compose defined networks will be prefixed with the `stack` name.
|
||||
Or if your service references external network use it's name instead.
|
||||
|
||||
[2] `traefik.frontend.auth.basic=EXPR`:
|
||||
To create `user:password` pair, it's possible to use this command `echo $(htpasswd -nb user password) | sed -e s/\\$/\\$\\$/g`.
|
||||
The result will be `user:$$apr1$$9Cv/OMGj$$ZomWQzuQbL.3TRCS81A1g/`, note additional symbol `$` makes escaping.
|
||||
|
||||
#### Custom Headers
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
|
||||
#### Security Headers
|
||||
|
||||
| Label | Description |
|
||||
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed. Format: `Host1,Host2` |
|
||||
| `traefik.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container. Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client. Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored. Format: `HEADER1,HEADER2` |
|
||||
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
|
||||
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
|
||||
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
|
||||
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
|
||||
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
|
||||
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
|
||||
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
|
||||
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
|
||||
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
|
||||
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
|
||||
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
|
||||
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
|
||||
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`). Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
|
||||
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
|
||||
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
|
||||
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
|
||||
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
|
||||
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
|
||||
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
|
||||
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
|
||||
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
|
||||
### On Service
|
||||
### On containers with Multiple Ports (segment labels)
|
||||
|
||||
Services labels can be used for overriding default behaviour
|
||||
Segment labels are used to define routes to a container exposing multiple ports.
|
||||
A segment is a group of labels that apply to a port exposed by a container.
|
||||
You can define as many segments as ports exposed in a container.
|
||||
|
||||
| Label | Description |
|
||||
|---------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|
|
||||
| `traefik.<service-name>.port=PORT` | Overrides `traefik.port`. If several ports need to be exposed, the service labels could be used. |
|
||||
| `traefik.<service-name>.protocol` | Overrides `traefik.protocol`. |
|
||||
| `traefik.<service-name>.weight` | Assign this service weight. Overrides `traefik.weight`. |
|
||||
| `traefik.<service-name>.frontend.backend=BACKEND` | Assign this service frontend to `BACKEND`. Default is to assign to the service backend. |
|
||||
| `traefik.<service-name>.frontend.entryPoints` | Overrides `traefik.frontend.entrypoints` |
|
||||
| `traefik.<service-name>.frontend.auth.basic` | Sets a Basic Auth for that frontend |
|
||||
| `traefik.<service-name>.frontend.passHostHeader` | Overrides `traefik.frontend.passHostHeader`. |
|
||||
| `traefik.<service-name>.frontend.priority` | Overrides `traefik.frontend.priority`. |
|
||||
| `traefik.<service-name>.frontend.rule` | Overrides `traefik.frontend.rule`. |
|
||||
| `traefik.<service-name>.frontend.redirect` | Overrides `traefik.frontend.redirect`. |
|
||||
| `traefik.<service-name>.frontend.redirect.entryPoint=https` | Overrides `traefik.frontend.redirect.entryPoint`. |
|
||||
| `traefik.<service-name>.frontend.redirect.regex=^http://localhost/(.*)` | Overrides `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.<service-name>.frontend.redirect.replacement=http://mydomain/$1` | Overrides `traefik.frontend.redirect.replacement`. |
|
||||
Segment labels override the default behavior.
|
||||
|
||||
| Label | Description |
|
||||
|---------------------------------------------------------------------------|-------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
||||
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
||||
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
||||
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
||||
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
||||
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
|
||||
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
|
||||
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
|
||||
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
|
||||
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
|
||||
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
|
||||
|
||||
#### Custom Headers
|
||||
|
||||
| Label | Description |
|
||||
|----------------------------------------------------------------------|----------------------------------------------------------|
|
||||
| `traefik.<segment_name>.frontend.headers.customRequestHeaders=EXPR ` | Same as `traefik.frontend.headers.customRequestHeaders` |
|
||||
| `traefik.<segment_name>.frontend.headers.customResponseHeaders=EXPR` | Same as `traefik.frontend.headers.customResponseHeaders` |
|
||||
|
||||
#### Security Headers
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------------------------|--------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.frontend.headers.allowedHosts=EXPR` | Same as `traefik.frontend.headers.allowedHosts` |
|
||||
| `traefik.<segment_name>.frontend.headers.browserXSSFilter=true` | Same as `traefik.frontend.headers.browserXSSFilter` |
|
||||
| `traefik.<segment_name>.frontend.headers.contentSecurityPolicy=VALUE` | Same as `traefik.frontend.headers.contentSecurityPolicy` |
|
||||
| `traefik.<segment_name>.frontend.headers.contentTypeNosniff=true` | Same as `traefik.frontend.headers.contentTypeNosniff` |
|
||||
| `traefik.<segment_name>.frontend.headers.customBrowserXSSValue=VALUE` | Same as `traefik.frontend.headers.customBrowserXSSValue` |
|
||||
| `traefik.<segment_name>.frontend.headers.customFrameOptionsValue=VALUE` | Same as `traefik.frontend.headers.customFrameOptionsValue` |
|
||||
| `traefik.<segment_name>.frontend.headers.forceSTSHeader=false` | Same as `traefik.frontend.headers.forceSTSHeader` |
|
||||
| `traefik.<segment_name>.frontend.headers.frameDeny=false` | Same as `traefik.frontend.headers.frameDeny` |
|
||||
| `traefik.<segment_name>.frontend.headers.hostsProxyHeaders=EXPR` | Same as `traefik.frontend.headers.hostsProxyHeaders` |
|
||||
| `traefik.<segment_name>.frontend.headers.isDevelopment=false` | Same as `traefik.frontend.headers.isDevelopment` |
|
||||
| `traefik.<segment_name>.frontend.headers.publicKey=VALUE` | Same as `traefik.frontend.headers.publicKey` |
|
||||
| `traefik.<segment_name>.frontend.headers.referrerPolicy=VALUE` | Same as `traefik.frontend.headers.referrerPolicy` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLRedirect=true` | Same as `traefik.frontend.headers.SSLRedirect` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLTemporaryRedirect=true` | Same as `traefik.frontend.headers.SSLTemporaryRedirect` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLHost=HOST` | Same as `traefik.frontend.headers.SSLHost` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLProxyHeaders=EXPR` | Same as `traefik.frontend.headers.SSLProxyHeaders=EXPR` |
|
||||
| `traefik.<segment_name>.frontend.headers.STSSeconds=315360000` | Same as `traefik.frontend.headers.STSSeconds=315360000` |
|
||||
| `traefik.<segment_name>.frontend.headers.STSIncludeSubdomains=true` | Same as `traefik.frontend.headers.STSIncludeSubdomains=true` |
|
||||
| `traefik.<segment_name>.frontend.headers.STSPreload=true` | Same as `traefik.frontend.headers.STSPreload=true` |
|
||||
|
||||
!!! note
|
||||
If a label is defined both as a `container label` and a `service label` (for example `traefik.<service-name>.port=PORT` and `traefik.port=PORT` ), the `service label` is used to defined the `<service-name>` property (`port` in the example).
|
||||
If a label is defined both as a `container label` and a `segment label` (for example `traefik.<segment_name>.port=PORT` and `traefik.port=PORT` ), the `segment label` is used to defined the `<segment_name>` property (`port` in the example).
|
||||
|
||||
It's possible to mix `container labels` and `service labels`, in this case `container labels` are used as default value for missing `service labels` but no frontends are going to be created with the `container labels`.
|
||||
It's possible to mix `container labels` and `segment labels`, in this case `container labels` are used as default value for missing `segment labels` but no frontends are going to be created with the `container labels`.
|
||||
|
||||
More details in this [example](/user-guide/docker-and-lets-encrypt/#labels).
|
||||
|
||||
|
@@ -1,15 +1,15 @@
|
||||
# DynamoDB Backend
|
||||
# DynamoDB Provider
|
||||
|
||||
Træfik can be configured to use Amazon DynamoDB as a backend configuration.
|
||||
Træfik can be configured to use Amazon DynamoDB as a provider.
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# DynamoDB configuration backend
|
||||
# DynamoDB Provider
|
||||
################################################################
|
||||
|
||||
# Enable DynamoDB configuration backend.
|
||||
# Enable DynamoDB Provider.
|
||||
[dynamodb]
|
||||
|
||||
# Region to use when connecting to AWS.
|
||||
@@ -39,13 +39,13 @@ watch = true
|
||||
#
|
||||
refreshSeconds = 15
|
||||
|
||||
# AccessKeyID to use when connecting to AWS.
|
||||
# Access Key ID to use when connecting to AWS.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
accessKeyID = "abc"
|
||||
|
||||
# SecretAccessKey to use when connecting to AWS.
|
||||
# Secret Access Key to use when connecting to AWS.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
@@ -68,4 +68,3 @@ Items in the `dynamodb` table must have three attributes:
|
||||
See `types/types.go` for details.
|
||||
The presence or absence of this attribute determines its type.
|
||||
So an item should never have both a `frontend` and a `backend` attribute.
|
||||
|
||||
|
@@ -1,15 +1,15 @@
|
||||
# ECS Backend
|
||||
# ECS Provider
|
||||
|
||||
Træfik can be configured to use Amazon ECS as a backend configuration.
|
||||
Træfik can be configured to use Amazon ECS as a provider.
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# ECS configuration backend
|
||||
# ECS Provider
|
||||
################################################################
|
||||
|
||||
# Enable ECS configuration backend.
|
||||
# Enable ECS Provider.
|
||||
[ecs]
|
||||
|
||||
# ECS Cluster Name.
|
||||
@@ -33,6 +33,7 @@ clusters = ["default"]
|
||||
watch = true
|
||||
|
||||
# Default domain used.
|
||||
# Can be overridden by setting the "traefik.domain" label.
|
||||
#
|
||||
# Optional
|
||||
# Default: ""
|
||||
@@ -66,13 +67,13 @@ exposedByDefault = false
|
||||
#
|
||||
region = "us-east-1"
|
||||
|
||||
# AccessKeyID to use when connecting to AWS.
|
||||
# Access Key ID to use when connecting to AWS.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
accessKeyID = "abc"
|
||||
|
||||
# SecretAccessKey to use when connecting to AWS.
|
||||
# Secret Access Key to use when connecting to AWS.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
@@ -84,9 +85,18 @@ secretAccessKey = "123"
|
||||
# Optional
|
||||
#
|
||||
# filename = "ecs.tmpl"
|
||||
|
||||
# Override template version
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
# - "1": previous template version (must be used only with older custom templates, see "filename")
|
||||
# - "2": current template version (must be used to force template version when "filename" is used)
|
||||
#
|
||||
# templateVersion = 2
|
||||
```
|
||||
|
||||
If `AccessKeyID`/`SecretAccessKey` is not given credentials will be resolved in the following order:
|
||||
If `accessKeyID`/`secretAccessKey` is not given credentials will be resolved in the following order:
|
||||
|
||||
- From environment variables; `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN`.
|
||||
- Shared credentials, determined by `AWS_PROFILE` and `AWS_SHARED_CREDENTIALS_FILE`, defaults to `default` and `~/.aws/credentials`.
|
||||
@@ -124,20 +134,76 @@ Træfik needs the following policy to read ECS information:
|
||||
|
||||
Labels can be used on task containers to override default behaviour:
|
||||
|
||||
| Label | Description |
|
||||
|-----------------------------------------------------------|------------------------------------------------------------------------------------------|
|
||||
| `traefik.protocol=https` | override the default `http` protocol |
|
||||
| `traefik.weight=10` | assign this weight to the container |
|
||||
| `traefik.enable=false` | disable this container in Træfik |
|
||||
| `traefik.port=80` | override the default `port` value. Overrides `NetworkBindings` from Docker Container |
|
||||
| `traefik.backend.loadbalancer.method=drr` | override the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | enable backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | enable backend sticky sessions (DEPRECATED) |
|
||||
| `traefik.backend.healthcheck.path=/health` | enable health checks for the backend, hitting the container at `path` |
|
||||
| `traefik.backend.healthcheck.interval=1s` | configure the health check interval |
|
||||
| `traefik.frontend.rule=Host:test.traefik.io` | override the default frontend rule (Default: `Host:{containerName}.{domain}`). |
|
||||
| `traefik.frontend.passHostHeader=true` | forward client `Host` header to the backend. |
|
||||
| `traefik.frontend.priority=10` | override default frontend priority |
|
||||
| `traefik.frontend.entryPoints=http,https` | assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints`. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.domain` | Default domain used for frontend rules. |
|
||||
| `traefik.enable=false` | Disable this container in Træfik |
|
||||
| `traefik.port=80` | Override the default `port` value. Overrides `NetworkBindings` from Docker Container |
|
||||
| `traefik.protocol=https` | Override the default `http` protocol |
|
||||
| `traefik.weight=10` | Assign this weight to the container |
|
||||
| `traefik.backend=foo` | Give the name `foo` to the generated backend for this container. |
|
||||
| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. |
|
||||
| `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.interval=1s` | Define the health check interval. (Default: 30s) |
|
||||
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) |
|
||||
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. |
|
||||
| `traefik.frontend.priority=10` | Override default frontend priority |
|
||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) |
|
||||
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
||||
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{instance_name}.{domain}`. |
|
||||
| `traefik.frontend.whiteList.sourceRange=RANGE` | List of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.frontend.whiteList.useXForwardedFor=true` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
|
||||
### Custom Headers
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
|
||||
### Security Headers
|
||||
|
||||
| Label | Description |
|
||||
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
|
||||
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
|
||||
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
|
||||
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
|
||||
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
|
||||
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
|
||||
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
|
||||
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
|
||||
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
|
||||
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
|
||||
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
|
||||
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
|
||||
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
|
@@ -1,13 +1,13 @@
|
||||
# Etcd Backend
|
||||
# Etcd Provider
|
||||
|
||||
Træfik can be configured to use Etcd as a backend configuration.
|
||||
Træfik can be configured to use Etcd as a provider.
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Etcd configuration backend
|
||||
# Etcd Provider
|
||||
################################################################
|
||||
|
||||
# Enable Etcd configuration backend.
|
||||
# Enable Etcd Provider.
|
||||
[etcd]
|
||||
|
||||
# Etcd server endpoint.
|
||||
@@ -63,10 +63,10 @@ useAPIV3 = true
|
||||
# ca = "/etc/ssl/ca.crt"
|
||||
# cert = "/etc/ssl/etcd.crt"
|
||||
# key = "/etc/ssl/etcd.key"
|
||||
# insecureskipverify = true
|
||||
# insecureSkipVerify = true
|
||||
```
|
||||
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
||||
Please refer to the [Key Value storage structure](/user-guide/kv-config/#key-value-storage-structure) section to get documentation on Traefik KV structure.
|
||||
|
||||
|
@@ -1,13 +1,13 @@
|
||||
# Eureka Backend
|
||||
# Eureka Provider
|
||||
|
||||
Træfik can be configured to use Eureka as a backend configuration.
|
||||
Træfik can be configured to use Eureka as a provider.
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Eureka configuration backend
|
||||
# Eureka Provider
|
||||
################################################################
|
||||
|
||||
# Enable Eureka configuration backend.
|
||||
# Enable Eureka Provider.
|
||||
[eureka]
|
||||
|
||||
# Eureka server endpoint.
|
||||
@@ -21,7 +21,7 @@ endpoint = "http://my.eureka.server/eureka"
|
||||
# Optional
|
||||
# Default: 30s
|
||||
#
|
||||
delay = "1m"
|
||||
refreshSeconds = "1m"
|
||||
|
||||
# Override default configuration template.
|
||||
# For advanced users :)
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# File Backends
|
||||
# File Provider
|
||||
|
||||
Træfik can be configured with a file.
|
||||
|
||||
@@ -54,7 +54,10 @@ Træfik can be configured with a file.
|
||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
]
|
||||
whitelistSourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
|
||||
|
||||
[frontends.frontend1.whiteList]
|
||||
sourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
|
||||
useXForwardedFor = true
|
||||
|
||||
[frontends.frontend1.routes]
|
||||
[frontends.frontend1.routes.route0]
|
||||
@@ -121,6 +124,7 @@ Træfik can be configured with a file.
|
||||
entryPoint = "https"
|
||||
regex = "^http://localhost/(.*)"
|
||||
replacement = "http://mydomain/$1"
|
||||
permanent = true
|
||||
|
||||
[frontends.frontend2]
|
||||
# ...
|
||||
@@ -136,19 +140,20 @@ Træfik can be configured with a file.
|
||||
# ...
|
||||
```
|
||||
|
||||
## Configuration mode
|
||||
## Configuration Mode
|
||||
|
||||
You have three choices:
|
||||
You have two choices:
|
||||
|
||||
- [Simple](/configuration/backends/file/#simple)
|
||||
- [Rules in a Separate File](/configuration/backends/file/#rules-in-a-separate-file)
|
||||
- [Multiple `.toml` Files](/configuration/backends/file/#multiple-toml-files)
|
||||
- [Rules in Træfik configuration file](/configuration/backends/file/#rules-in-trfik-configuration-file)
|
||||
- [Rules in dedicated files](/configuration/backends/file/#rules-in-dedicated-files)
|
||||
|
||||
To enable the file backend, you must either pass the `--file` option to the Træfik binary or put the `[file]` section (with or without inner settings) in the configuration file.
|
||||
|
||||
The configuration file allows managing both backends/frontends and HTTPS certificates (which are not [Let's Encrypt](https://letsencrypt.org) certificates generated through Træfik).
|
||||
|
||||
### Simple
|
||||
TOML templating can be used if rules are not defined in the Træfik configuration file.
|
||||
|
||||
### Rules in Træfik Configuration File
|
||||
|
||||
Add your configuration at the end of the global configuration file `traefik.toml`:
|
||||
|
||||
@@ -187,12 +192,22 @@ defaultEntryPoints = ["http", "https"]
|
||||
```
|
||||
|
||||
!!! note
|
||||
adding certificates directly to the entrypoint is still maintained but certificates declared in this way cannot be managed dynamically.
|
||||
If `tls.entryPoints` is not defined, the certificate is attached to all the `defaultEntryPoints` with a TLS configuration.
|
||||
|
||||
!!! note
|
||||
Adding certificates directly to the entryPoint is still maintained but certificates declared in this way cannot be managed dynamically.
|
||||
It's recommended to use the file provider to declare certificates.
|
||||
|
||||
### Rules in a Separate File
|
||||
!!! warning
|
||||
TOML templating cannot be used if rules are defined in the Træfik configuration file.
|
||||
|
||||
Put your rules in a separate file, for example `rules.toml`:
|
||||
### Rules in Dedicated Files
|
||||
|
||||
Træfik allows defining rules in one or more separate files.
|
||||
|
||||
#### One Separate File
|
||||
|
||||
You have to specify the file path in the `file.filename` option.
|
||||
|
||||
```toml
|
||||
# traefik.toml
|
||||
@@ -206,8 +221,31 @@ defaultEntryPoints = ["http", "https"]
|
||||
|
||||
[file]
|
||||
filename = "rules.toml"
|
||||
watch = true
|
||||
```
|
||||
|
||||
The option `file.watch` allows Træfik to watch file changes automatically.
|
||||
|
||||
#### Multiple Separated Files
|
||||
|
||||
You could have multiple `.toml` files in a directory (and recursively in its sub-directories):
|
||||
|
||||
```toml
|
||||
[file]
|
||||
directory = "/path/to/config/"
|
||||
watch = true
|
||||
```
|
||||
|
||||
The option `file.watch` allows Træfik to watch file changes automatically.
|
||||
|
||||
#### Separate Files Content
|
||||
|
||||
If you are defining rules in one or more separate files, you can use two formats.
|
||||
|
||||
##### Simple Format
|
||||
|
||||
Backends, Frontends and TLS certificates are defined one at time, as described in the file `rules.toml`:
|
||||
|
||||
```toml
|
||||
# rules.toml
|
||||
[backends]
|
||||
@@ -232,18 +270,34 @@ defaultEntryPoints = ["http", "https"]
|
||||
# ...
|
||||
```
|
||||
|
||||
### Multiple `.toml` Files
|
||||
##### TOML Templating
|
||||
|
||||
You could have multiple `.toml` files in a directory (and recursively in its sub-directories):
|
||||
!!! warning
|
||||
TOML templating can only be used **if rules are defined in one or more separate files**.
|
||||
Templating will not work in the Træfik configuration file.
|
||||
|
||||
Træfik allows using TOML templating.
|
||||
|
||||
Thus, it's possible to define easily lot of Backends, Frontends and TLS certificates as described in the file `template-rules.toml` :
|
||||
|
||||
```toml
|
||||
[file]
|
||||
directory = "/path/to/config/"
|
||||
```
|
||||
# template-rules.toml
|
||||
[backends]
|
||||
{{ range $i, $e := until 100 }}
|
||||
[backends.backend{{ $e }}]
|
||||
#...
|
||||
{{ end }}
|
||||
|
||||
If you want Træfik to watch file changes automatically, just add:
|
||||
[frontends]
|
||||
{{ range $i, $e := until 100 }}
|
||||
[frontends.frontend{{ $e }}]
|
||||
#...
|
||||
{{ end }}
|
||||
|
||||
```toml
|
||||
[file]
|
||||
watch = true
|
||||
|
||||
# HTTPS certificate
|
||||
{{ range $i, $e := until 100 }}
|
||||
[[tls]]
|
||||
#...
|
||||
{{ end }}
|
||||
```
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# Kubernetes Ingress Backend
|
||||
# Kubernetes Ingress Provider
|
||||
|
||||
Træfik can be configured to use Kubernetes Ingress as a backend configuration.
|
||||
Træfik can be configured to use Kubernetes Ingress as a provider.
|
||||
|
||||
See also [Kubernetes user guide](/user-guide/kubernetes).
|
||||
|
||||
@@ -8,10 +8,10 @@ See also [Kubernetes user guide](/user-guide/kubernetes).
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Kubernetes Ingress configuration backend
|
||||
# Kubernetes Ingress Provider
|
||||
################################################################
|
||||
|
||||
# Enable Kubernetes Ingress configuration backend.
|
||||
# Enable Kubernetes Ingress Provider.
|
||||
[kubernetes]
|
||||
|
||||
# Kubernetes server endpoint.
|
||||
@@ -50,6 +50,17 @@ See also [Kubernetes user guide](/user-guide/kubernetes).
|
||||
#
|
||||
# labelselector = "A and not B"
|
||||
|
||||
# Value of `kubernetes.io/ingress.class` annotation that identifies Ingress objects to be processed.
|
||||
# If the parameter is non-empty, only Ingresses containing an annotation with the same value are processed.
|
||||
# Otherwise, Ingresses missing the annotation, having an empty value, or the value `traefik` are processed.
|
||||
#
|
||||
# Note : `ingressClass` option must begin with the "traefik" prefix.
|
||||
#
|
||||
# Optional
|
||||
# Default: empty
|
||||
#
|
||||
# ingressClass = "traefik-internal"
|
||||
|
||||
# Disable PassHost Headers.
|
||||
#
|
||||
# Optional
|
||||
@@ -101,87 +112,141 @@ Although traefik will connect directly to the endpoints (pods), it still checks
|
||||
If the service port defined in the ingress spec is 443, then the backend communication protocol is assumed to be TLS, and will connect via TLS automatically.
|
||||
|
||||
!!! note
|
||||
Please note that by enabling TLS communication between traefik and your pods, you will have to have trusted certificates that have the proper trust chain and IP subject name.
|
||||
Please note that by enabling TLS communication between traefik and your pods, you will have to have trusted certificates that have the proper trust chain and IP subject name.
|
||||
If this is not an option, you may need to skip TLS certificate verification.
|
||||
See the [InsecureSkipVerify](/configuration/commons/#main-section) setting for more details.
|
||||
|
||||
See the [insecureSkipVerify](/configuration/commons/#main-section) setting for more details.
|
||||
|
||||
## Annotations
|
||||
|
||||
### General annotations
|
||||
|
||||
The following general annotations are applicable on the Ingress object:
|
||||
|
||||
- `traefik.frontend.rule.type: PathPrefixStrip`
|
||||
Override the default frontend rule type. Default: `PathPrefix`.
|
||||
- `traefik.frontend.priority: "3"`
|
||||
Override the default frontend rule priority.
|
||||
- `traefik.frontend.redirect.entryPoint: https`:
|
||||
Enables Redirect to another entryPoint for that frontend (e.g. HTTPS).
|
||||
- `traefik.frontend.redirect.regex: ^http://localhost/(.*)`:
|
||||
Redirect to another URL for that frontend. Must be set with `traefik.frontend.redirect.replacement`.
|
||||
- `traefik.frontend.redirect.replacement: http://mydomain/$1`:
|
||||
Redirect to another URL for that frontend. Must be set with `traefik.frontend.redirect.regex`.
|
||||
- `traefik.frontend.entryPoints: http,https`
|
||||
Override the default frontend endpoints.
|
||||
- `traefik.frontend.passTLSCert: true`
|
||||
Override the default frontend PassTLSCert value. Default: `false`.
|
||||
- `ingress.kubernetes.io/rewrite-target: /users`
|
||||
Replaces each matched Ingress path with the specified one, and adds the old path to the `X-Replaced-Path` header.
|
||||
- `ingress.kubernetes.io/whitelist-source-range: "1.2.3.0/24, fe80::/16"`
|
||||
A comma-separated list of IP ranges permitted for access. all source IPs are permitted if the list is empty or a single range is ill-formatted.
|
||||
| Annotation | Description |
|
||||
|---------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.ingress.kubernetes.io/buffering: <YML>` | (3) See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.ingress.kubernetes.io/error-pages: <YML>` | (1) See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.ingress.kubernetes.io/frontend-entry-points: http,https` | Override the default frontend endpoints. |
|
||||
| `traefik.ingress.kubernetes.io/pass-tls-cert: "true"` | Override the default frontend PassTLSCert value. Default: `false`. |
|
||||
| `traefik.ingress.kubernetes.io/preserve-host: "true"` | Forward client `Host` header to the backend. |
|
||||
| `traefik.ingress.kubernetes.io/priority: "3"` | Override the default frontend rule priority. |
|
||||
| `traefik.ingress.kubernetes.io/rate-limit: <YML>` | (2) See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.ingress.kubernetes.io/redirect-entry-point: https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS). |
|
||||
| `traefik.ingress.kubernetes.io/redirect-permanent: "true"` | Return 301 instead of 302. |
|
||||
| `traefik.ingress.kubernetes.io/redirect-regex: ^http://localhost/(.*)` | Redirect to another URL for that frontend. Must be set with `traefik.ingress.kubernetes.io/redirect-replacement`. |
|
||||
| `traefik.ingress.kubernetes.io/redirect-replacement: http://mydomain/$1` | Redirect to another URL for that frontend. Must be set with `traefik.ingress.kubernetes.io/redirect-regex`. |
|
||||
| `traefik.ingress.kubernetes.io/rewrite-target: /users` | Replaces each matched Ingress path with the specified one, and adds the old path to the `X-Replaced-Path` header. |
|
||||
| `traefik.ingress.kubernetes.io/rule-type: PathPrefixStrip` | Override the default frontend rule type. Default: `PathPrefix`. |
|
||||
| `traefik.ingress.kubernetes.io/whitelist-source-range: "1.2.3.0/24, fe80::/16"` | A comma-separated list of IP ranges permitted for access. all source IPs are permitted if the list is empty or a single range is ill-formatted. Please note, you may have to set `service.spec.externalTrafficPolicy` to the value `Local` to preserve the source IP of the request for filtering. Please see [this link](https://kubernetes.io/docs/tutorials/services/source-ip/) for more information.|
|
||||
| `ingress.kubernetes.io/whitelist-x-forwarded-for: "true"` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
| `traefik.ingress.kubernetes.io/app-root: "/index.html"` | Redirects all requests for `/` to the defined path. (4) |
|
||||
|
||||
<1> `traefik.ingress.kubernetes.io/error-pages` example:
|
||||
|
||||
```yaml
|
||||
foo:
|
||||
status:
|
||||
- "404"
|
||||
backend: bar
|
||||
query: /bar
|
||||
fii:
|
||||
status:
|
||||
- "503"
|
||||
- "500"
|
||||
backend: bar
|
||||
query: /bir
|
||||
```
|
||||
|
||||
<2> `traefik.ingress.kubernetes.io/rate-limit` example:
|
||||
|
||||
```yaml
|
||||
extractorfunc: client.ip
|
||||
rateset:
|
||||
bar:
|
||||
period: 3s
|
||||
average: 6
|
||||
burst: 9
|
||||
foo:
|
||||
period: 6s
|
||||
average: 12
|
||||
burst: 18
|
||||
```
|
||||
|
||||
<3> `traefik.ingress.kubernetes.io/buffering` example:
|
||||
|
||||
```yaml
|
||||
maxrequestbodybytes: 10485760
|
||||
memrequestbodybytes: 2097153
|
||||
maxresponsebodybytes: 10485761
|
||||
memresponsebodybytes: 2097152
|
||||
retryexpression: IsNetworkError() && Attempts() <= 2
|
||||
```
|
||||
|
||||
<4> `traefik.ingress.kubernetes.io/app-root`:
|
||||
Non-root paths will not be affected by this annotation and handled normally.
|
||||
This annotation may not be combined with the `ReplacePath` rule type or any other annotation leveraging that rule type.
|
||||
Trying to do so leads to an error and the corresponding Ingress object being ignored.
|
||||
|
||||
!!! note
|
||||
Please note that `traefik.frontend.redirect.regex` and `traefik.frontend.redirect.replacement` do not have to be set if `traefik.frontend.redirect.entryPoint` is defined for the redirection (they will not be used in this case).
|
||||
Please note that `traefik.ingress.kubernetes.io/redirect-regex` and `traefik.ingress.kubernetes.io/redirect-replacement` do not have to be set if `traefik.ingress.kubernetes.io/redirect-entry-point` is defined for the redirection (they will not be used in this case).
|
||||
|
||||
The following annotations are applicable on the Service object associated with a particular Ingress object:
|
||||
|
||||
- `traefik.backend.loadbalancer.method=drr`
|
||||
Override the default `wrr` load balancer algorithm.
|
||||
- `traefik.backend.loadbalancer.stickiness=true`
|
||||
Enable backend sticky sessions.
|
||||
- `traefik.backend.loadbalancer.stickiness.cookieName=NAME`
|
||||
Manually set the cookie name for sticky sessions.
|
||||
- `traefik.backend.loadbalancer.sticky=true`
|
||||
Enable backend sticky sessions (DEPRECATED).
|
||||
- `traefik.backend.circuitbreaker: <expression>`
|
||||
Set the circuit breaker expression for the backend.
|
||||
| Annotation | Description |
|
||||
|--------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.backend.loadbalancer.sticky: "true"` | Enable backend sticky sessions (DEPRECATED). |
|
||||
| `traefik.ingress.kubernetes.io/affinity: "true"` | Enable backend sticky sessions. |
|
||||
| `traefik.ingress.kubernetes.io/circuit-breaker-expression: <expression>` | Set the circuit breaker expression for the backend. |
|
||||
| `traefik.ingress.kubernetes.io/load-balancer-method: drr` | Override the default `wrr` load balancer algorithm. |
|
||||
| `traefik.ingress.kubernetes.io/max-conn-amount: 10` | Set a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.ingress.kubernetes.io/max-conn-extractor-func: client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.ingress.kubernetes.io/session-cookie-name: <NAME>` | Manually set the cookie name for sticky sessions. |
|
||||
|
||||
### Security annotations
|
||||
!!! note
|
||||
`traefik.ingress.kubernetes.io/` and `ingress.kubernetes.io/` are supported prefixes.
|
||||
|
||||
### Custom Headers Annotations
|
||||
|
||||
| Annotation | Description |
|
||||
| ------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `ingress.kubernetes.io/custom-request-headers: EXPR` | Provides the container with custom request headers that will be appended to each request forwarded to the container. Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `ingress.kubernetes.io/custom-response-headers: EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client. Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
|
||||
### Security Headers Annotations
|
||||
|
||||
The following security annotations are applicable on the Ingress object:
|
||||
|
||||
| Annotation | Description |
|
||||
| -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `ingress.kubernetes.io/allowed-hosts:EXPR` | Provides a list of allowed hosts that requests will be processed. Format: `Host1,Host2` |
|
||||
| `ingress.kubernetes.io/custom-request-headers:EXPR` | Provides the container with custom request headers that will be appended to each request forwarded to the container. Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `ingress.kubernetes.io/custom-response-headers:EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client. Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `ingress.kubernetes.io/proxy-headers:EXPR` | Provides a list of headers that the proxied hostname may be stored. Format: `HEADER1,HEADER2` |
|
||||
| `ingress.kubernetes.io/ssl-redirect:true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
|
||||
| `ingress.kubernetes.io/ssl-temporary-redirect:true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `ingress.kubernetes.io/ssl-host:HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `ingress.kubernetes.io/ssl-proxy-headers:EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`). Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `ingress.kubernetes.io/hsts-max-age:315360000` | Sets the max-age of the HSTS header. |
|
||||
| `ingress.kubernetes.io/hsts-include-subdomains:true` | Adds the IncludeSubdomains section of the STS header. |
|
||||
| `ingress.kubernetes.io/hsts-preload:true` | Adds the preload flag to the HSTS header. |
|
||||
| `ingress.kubernetes.io/force-hsts:false` | Adds the STS header to non-SSL requests. |
|
||||
| `ingress.kubernetes.io/frame-deny:false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
|
||||
| `ingress.kubernetes.io/custom-frame-options-value:VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
|
||||
| `ingress.kubernetes.io/content-type-nosniff:true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
|
||||
| `ingress.kubernetes.io/browser-xss-filter:true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
|
||||
| `ingress.kubernetes.io/content-security-policy:VALUE` | Adds CSP Header with the custom value. |
|
||||
| `ingress.kubernetes.io/public-key:VALUE` | Adds pinned HTST public key header. |
|
||||
| `ingress.kubernetes.io/referrer-policy:VALUE` | Adds referrer policy header. |
|
||||
| `ingress.kubernetes.io/is-development:false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
| Annotation | Description |
|
||||
| ----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `ingress.kubernetes.io/allowed-hosts: EXPR` | Provides a list of allowed hosts that requests will be processed. Format: `Host1,Host2` |
|
||||
| `ingress.kubernetes.io/browser-xss-filter: "true"` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
|
||||
| `ingress.kubernetes.io/content-security-policy: VALUE` | Adds CSP Header with the custom value. |
|
||||
| `ingress.kubernetes.io/content-type-nosniff: "true"` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
|
||||
| `ingress.kubernetes.io/custom-browser-xss-value: VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
|
||||
| `ingress.kubernetes.io/custom-frame-options-value: VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
|
||||
| `ingress.kubernetes.io/force-hsts: "false"` | Adds the STS header to non-SSL requests. |
|
||||
| `ingress.kubernetes.io/frame-deny: "false"` | Adds the `X-Frame-Options` header with the value of `DENY`. |
|
||||
| `ingress.kubernetes.io/hsts-max-age: "315360000"` | Sets the max-age of the HSTS header. |
|
||||
| `ingress.kubernetes.io/hsts-include-subdomains: "true"` | Adds the IncludeSubdomains section of the STS header. |
|
||||
| `ingress.kubernetes.io/hsts-preload: "true"` | Adds the preload flag to the HSTS header. |
|
||||
| `ingress.kubernetes.io/is-development: "false"` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
| `ingress.kubernetes.io/proxy-headers: EXPR` | Provides a list of headers that the proxied hostname may be stored. Format: `HEADER1,HEADER2` |
|
||||
| `ingress.kubernetes.io/public-key: VALUE` | Adds pinned HTST public key header. |
|
||||
| `ingress.kubernetes.io/referrer-policy: VALUE` | Adds referrer policy header. |
|
||||
| `ingress.kubernetes.io/ssl-redirect: "true"` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
|
||||
| `ingress.kubernetes.io/ssl-temporary-redirect: "true"` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `ingress.kubernetes.io/ssl-host: HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `ingress.kubernetes.io/ssl-proxy-headers: EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`). Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
|
||||
### Authentication
|
||||
|
||||
Is possible to add additional authentication annotations to the Ingress object.
|
||||
Additional authentication annotations can be added to the Ingress object.
|
||||
The source of the authentication is a Secret object that contains the credentials.
|
||||
|
||||
- `ingress.kubernetes.io/auth-type`: `basic`
|
||||
Contains the authentication type. The only permitted type is `basic`.
|
||||
- `ingress.kubernetes.io/auth-secret`: `mysecret`
|
||||
Contains the username and password with access to the paths defined in the Ingress object.
|
||||
| Annotation | Description |
|
||||
|-----------------------------------------------|-------------------------------------------------------------------------------------------------------------|
|
||||
| `ingress.kubernetes.io/auth-type: basic` | Contains the authentication type. The only permitted type is `basic`. |
|
||||
| `ingress.kubernetes.io/auth-secret: mysecret` | Name of Secret containing the username and password with access to the paths defined in the Ingress object. |
|
||||
|
||||
The secret must be created in the same namespace as the Ingress object.
|
||||
|
||||
@@ -189,3 +254,12 @@ The following limitations hold:
|
||||
|
||||
- The realm is not configurable; the only supported (and default) value is `traefik`.
|
||||
- The Secret must contain a single file only.
|
||||
|
||||
### TLS certificates management
|
||||
|
||||
TLS certificates can be managed in Secrets objects.
|
||||
More information are available in the [User Guide](/user-guide/kubernetes/#add-a-tls-certificate-to-the-ingress).
|
||||
|
||||
!!! note
|
||||
Only TLS certificates provided by users can be stored in Kubernetes Secrets.
|
||||
[Let's Encrypt](https://letsencrypt.org) certificates cannot be managed in Kubernets Secrets yet.
|
||||
|
@@ -1,18 +1,18 @@
|
||||
# Marathon Backend
|
||||
# Marathon Provider
|
||||
|
||||
Træfik can be configured to use Marathon as a backend configuration.
|
||||
Træfik can be configured to use Marathon as a provider.
|
||||
|
||||
See also [Marathon user guide](/user-guide/marathon).
|
||||
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Mesos/Marathon configuration backend
|
||||
# Mesos/Marathon Provider
|
||||
################################################################
|
||||
|
||||
# Enable Marathon configuration backend.
|
||||
# Enable Marathon Provider.
|
||||
[marathon]
|
||||
|
||||
# Marathon server endpoint.
|
||||
@@ -45,6 +45,15 @@ domain = "marathon.localhost"
|
||||
#
|
||||
# filename = "marathon.tmpl"
|
||||
|
||||
# Override template version
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
# - "1": previous template version (must be used only with older custom templates, see "filename")
|
||||
# - "2": current template version (must be used to force template version when "filename" is used)
|
||||
#
|
||||
# templateVersion = 2
|
||||
|
||||
# Expose Marathon apps by default in Traefik.
|
||||
#
|
||||
# Optional
|
||||
@@ -70,7 +79,7 @@ domain = "marathon.localhost"
|
||||
|
||||
# Enable filtering using Marathon constraints..
|
||||
# If enabled, Traefik will read Marathon constraints, as defined in https://mesosphere.github.io/marathon/docs/constraints.html
|
||||
# Each individual constraint will be treated as a verbatim compounded tag.
|
||||
# Each individual constraint will be treated as a verbatim compounded tag.
|
||||
# i.e. "rack_id:CLUSTER:rack-1", with all constraint groups concatenated together using ":"
|
||||
#
|
||||
# Optional
|
||||
@@ -94,7 +103,7 @@ domain = "marathon.localhost"
|
||||
# CA = "/etc/ssl/ca.crt"
|
||||
# Cert = "/etc/ssl/marathon.cert"
|
||||
# Key = "/etc/ssl/marathon.key"
|
||||
# InsecureSkipVerify = true
|
||||
# insecureSkipVerify = true
|
||||
|
||||
# DCOSToken for DCOS environment.
|
||||
# This will override the Authorization header.
|
||||
@@ -148,54 +157,157 @@ domain = "marathon.localhost"
|
||||
# respectReadinessChecks = true
|
||||
```
|
||||
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
||||
## Labels: overriding default behaviour
|
||||
## Labels: overriding default behavior
|
||||
|
||||
Marathon labels may be used to dynamically change the routing and forwarding behaviour.
|
||||
Marathon labels may be used to dynamically change the routing and forwarding behavior.
|
||||
|
||||
They may be specified on one of two levels: Application or service.
|
||||
|
||||
### Application Level
|
||||
|
||||
The following labels can be defined on Marathon applications. They adjust the behaviour for the entire application.
|
||||
The following labels can be defined on Marathon applications. They adjust the behavior for the entire application.
|
||||
|
||||
| Label | Description |
|
||||
|-----------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.backend=foo` | assign the application to `foo` backend |
|
||||
| `traefik.backend.maxconn.amount=10` | set a maximum number of connections to the backend. Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | set the function to be used against the request to determine what to limit maximum connections to the backend by. Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.backend.loadbalancer.method=drr` | override the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | enable backend sticky sessions (DEPRECATED) |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | enable backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
|
||||
| `traefik.backend.circuitbreaker.expression=NetworkErrorRatio() > 0.5` | create a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.backend.healthcheck.path=/health` | set the Traefik health check path [default: no health checks] |
|
||||
| `traefik.backend.healthcheck.interval=5s` | sets a custom health check interval in Go-parseable (`time.ParseDuration`) format [default: 30s] |
|
||||
| `traefik.portIndex=1` | register port by index in the application's ports array. Useful when the application exposes multiple ports. |
|
||||
| `traefik.port=80` | register the explicit application port value. Cannot be used alongside `traefik.portIndex`. |
|
||||
| `traefik.protocol=https` | override the default `http` protocol |
|
||||
| `traefik.weight=10` | assign this weight to the application |
|
||||
| `traefik.enable=false` | disable this application in Træfik |
|
||||
| `traefik.frontend.rule=Host:test.traefik.io` | override the default frontend rule (Default: `Host:{containerName}.{domain}`). |
|
||||
| `traefik.frontend.passHostHeader=true` | forward client `Host` header to the backend. |
|
||||
| `traefik.frontend.priority=10` | override default frontend priority |
|
||||
| `traefik.frontend.entryPoints=http,https` | assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints`. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash`. |
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.domain` | Default domain used for frontend rules. |
|
||||
| `traefik.enable=false` | Disable this container in Træfik |
|
||||
| `traefik.port=80` | Register this port. Useful when the container exposes multiples ports. |
|
||||
| `traefik.portIndex=1` | Register port by index in the application's ports array. Useful when the application exposes multiple ports. |
|
||||
| `traefik.protocol=https` | Override the default `http` protocol |
|
||||
| `traefik.weight=10` | Assign this weight to the container |
|
||||
| `traefik.backend=foo` | Give the name `foo` to the generated backend for this container. |
|
||||
| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. |
|
||||
| `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.interval=1s` | Define the health check interval. (Default: 30s) |
|
||||
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) |
|
||||
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. |
|
||||
| `traefik.frontend.priority=10` | Override default frontend priority |
|
||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) |
|
||||
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
||||
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{sub_domain}.{domain}`. |
|
||||
| `traefik.frontend.whiteList.sourceRange=RANGE` | List of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.frontend.whiteList.useXForwardedFor=true` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
|
||||
### Service Level
|
||||
#### Custom Headers
|
||||
|
||||
For applications that expose multiple ports, specific labels can be used to extract one frontend/backend configuration pair per port. Each such pair is called a _service_. The (freely choosable) name of the service is an integral part of the service label name.
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
|
|
||||
|
||||
| Label | Description |
|
||||
|--------------------------------------------------------|------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.<service-name>.port=443` | create a service binding with frontend/backend using this port. Overrides `traefik.port`. |
|
||||
| `traefik.<service-name>.portIndex=1` | create a service binding with frontend/backend using this port index. Overrides `traefik.portIndex`. |
|
||||
| `traefik.<service-name>.protocol=https` | assign `https` protocol. Overrides `traefik.protocol`. |
|
||||
| `traefik.<service-name>.weight=10` | assign this service weight. Overrides `traefik.weight`. |
|
||||
| `traefik.<service-name>.frontend.backend=fooBackend` | assign this service frontend to `foobackend`. Default is to assign to the service backend. |
|
||||
| `traefik.<service-name>.frontend.entryPoints=http` | assign this service entrypoints. Overrides `traefik.frontend.entrypoints`. |
|
||||
| `traefik.<service-name>.frontend.auth.basic=test:EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
|
||||
| `traefik.<service-name>.frontend.passHostHeader=true` | Forward client `Host` header to the backend. Overrides `traefik.frontend.passHostHeader`. |
|
||||
| `traefik.<service-name>.frontend.priority=10` | assign the service frontend priority. Overrides `traefik.frontend.priority`. |
|
||||
| `traefik.<service-name>.frontend.rule=Path:/foo` | assign the service frontend rule. Overrides `traefik.frontend.rule`. |
|
||||
#### Security Headers
|
||||
|
||||
| Label | Description |
|
||||
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
|
||||
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
|
||||
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
|
||||
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
|
||||
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
|
||||
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
|
||||
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
|
||||
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
|
||||
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
|
||||
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
|
||||
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
|
||||
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
|
||||
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
|
||||
### Applications with Multiple Ports (segment labels)
|
||||
|
||||
Segment labels are used to define routes to an application exposing multiple ports.
|
||||
A segment is a group of labels that apply to a port exposed by an application.
|
||||
You can define as many segments as ports exposed in an application.
|
||||
|
||||
Segment labels override the default behavior.
|
||||
|
||||
| Label | Description |
|
||||
|---------------------------------------------------------------------------|-------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||
| `traefik.<segment_name>.portIndex=1` | Same as `traefik.portIndex` |
|
||||
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
||||
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
||||
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
||||
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
||||
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
||||
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
|
||||
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
|
||||
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
|
||||
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
|
||||
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
|
||||
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
|
||||
|
||||
#### Custom Headers
|
||||
|
||||
| Label | Description |
|
||||
|----------------------------------------------------------------------|----------------------------------------------------------|
|
||||
| `traefik.<segment_name>.frontend.headers.customRequestHeaders=EXPR ` | Same as `traefik.frontend.headers.customRequestHeaders` |
|
||||
| `traefik.<segment_name>.frontend.headers.customResponseHeaders=EXPR` | Same as `traefik.frontend.headers.customResponseHeaders` |
|
||||
|
||||
#### Security Headers
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------------------------|--------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.frontend.headers.allowedHosts=EXPR` | Same as `traefik.frontend.headers.allowedHosts` |
|
||||
| `traefik.<segment_name>.frontend.headers.browserXSSFilter=true` | Same as `traefik.frontend.headers.browserXSSFilter` |
|
||||
| `traefik.<segment_name>.frontend.headers.contentSecurityPolicy=VALUE` | Same as `traefik.frontend.headers.contentSecurityPolicy` |
|
||||
| `traefik.<segment_name>.frontend.headers.contentTypeNosniff=true` | Same as `traefik.frontend.headers.contentTypeNosniff` |
|
||||
| `traefik.<segment_name>.frontend.headers.customBrowserXSSValue=VALUE` | Same as `traefik.frontend.headers.customBrowserXSSValue` |
|
||||
| `traefik.<segment_name>.frontend.headers.customFrameOptionsValue=VALUE` | Same as `traefik.frontend.headers.customFrameOptionsValue` |
|
||||
| `traefik.<segment_name>.frontend.headers.forceSTSHeader=false` | Same as `traefik.frontend.headers.forceSTSHeader` |
|
||||
| `traefik.<segment_name>.frontend.headers.frameDeny=false` | Same as `traefik.frontend.headers.frameDeny` |
|
||||
| `traefik.<segment_name>.frontend.headers.hostsProxyHeaders=EXPR` | Same as `traefik.frontend.headers.hostsProxyHeaders` |
|
||||
| `traefik.<segment_name>.frontend.headers.isDevelopment=false` | Same as `traefik.frontend.headers.isDevelopment` |
|
||||
| `traefik.<segment_name>.frontend.headers.publicKey=VALUE` | Same as `traefik.frontend.headers.publicKey` |
|
||||
| `traefik.<segment_name>.frontend.headers.referrerPolicy=VALUE` | Same as `traefik.frontend.headers.referrerPolicy` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLRedirect=true` | Same as `traefik.frontend.headers.SSLRedirect` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLTemporaryRedirect=true` | Same as `traefik.frontend.headers.SSLTemporaryRedirect` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLHost=HOST` | Same as `traefik.frontend.headers.SSLHost` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLProxyHeaders=EXPR` | Same as `traefik.frontend.headers.SSLProxyHeaders=EXPR` |
|
||||
| `traefik.<segment_name>.frontend.headers.STSSeconds=315360000` | Same as `traefik.frontend.headers.STSSeconds=315360000` |
|
||||
| `traefik.<segment_name>.frontend.headers.STSIncludeSubdomains=true` | Same as `traefik.frontend.headers.STSIncludeSubdomains=true` |
|
||||
| `traefik.<segment_name>.frontend.headers.STSPreload=true` | Same as `traefik.frontend.headers.STSPreload=true` |
|
||||
|
@@ -1,13 +1,13 @@
|
||||
# Mesos Generic Backend
|
||||
# Mesos Generic Provider
|
||||
|
||||
Træfik can be configured to use Mesos as a backend configuration.
|
||||
Træfik can be configured to use Mesos as a provider.
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Mesos configuration backend
|
||||
# Mesos Provider
|
||||
################################################################
|
||||
|
||||
# Enable Mesos configuration backend.
|
||||
# Enable Mesos Provider.
|
||||
[mesos]
|
||||
|
||||
# Mesos server endpoint.
|
||||
@@ -34,6 +34,13 @@ watch = true
|
||||
#
|
||||
domain = "mesos.localhost"
|
||||
|
||||
# Expose Mesos apps by default in Traefik.
|
||||
#
|
||||
# Optional
|
||||
# Default: true
|
||||
#
|
||||
# exposedByDefault = false
|
||||
|
||||
# Override default configuration template.
|
||||
# For advanced users :)
|
||||
#
|
||||
@@ -41,46 +48,48 @@ domain = "mesos.localhost"
|
||||
#
|
||||
# filename = "mesos.tmpl"
|
||||
|
||||
# Expose Mesos apps by default in Traefik.
|
||||
# Override template version
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
# Default: true
|
||||
# - "1": previous template version (must be used only with older custom templates, see "filename")
|
||||
# - "2": current template version (must be used to force template version when "filename" is used)
|
||||
#
|
||||
# ExposedByDefault = false
|
||||
# templateVersion = 2
|
||||
|
||||
# TLS client configuration. https://golang.org/pkg/crypto/tls/#Config
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# [mesos.TLS]
|
||||
# InsecureSkipVerify = true
|
||||
# insecureSkipVerify = true
|
||||
|
||||
# Zookeeper timeout (in seconds).
|
||||
#
|
||||
# Optional
|
||||
# Default: 30
|
||||
#
|
||||
# ZkDetectionTimeout = 30
|
||||
# zkDetectionTimeout = 30
|
||||
|
||||
# Polling interval (in seconds).
|
||||
#
|
||||
# Optional
|
||||
# Default: 30
|
||||
#
|
||||
# RefreshSeconds = 30
|
||||
# refreshSeconds = 30
|
||||
|
||||
# IP sources (e.g. host, docker, mesos, rkt).
|
||||
# IP sources (e.g. host, docker, mesos, netinfo).
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# IPSources = "host"
|
||||
# ipSources = "host"
|
||||
|
||||
# HTTP Timeout (in seconds).
|
||||
#
|
||||
# Optional
|
||||
# Default: 30
|
||||
#
|
||||
# StateTimeoutSecond = "30"
|
||||
# stateTimeoutSecond = "30"
|
||||
|
||||
# Convert groups to subdomains.
|
||||
# Default behavior: /foo/bar/myapp => foo-bar-myapp.{defaultDomain}
|
||||
@@ -90,4 +99,83 @@ domain = "mesos.localhost"
|
||||
# Default: false
|
||||
#
|
||||
# groupsAsSubDomains = true
|
||||
|
||||
```
|
||||
|
||||
## Labels: overriding default behavior
|
||||
|
||||
The following labels can be defined on Mesos tasks. They adjust the behavior for the entire application.
|
||||
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.domain` | Default domain used for frontend rules. |
|
||||
| `traefik.enable=false` | Disable this container in Træfik |
|
||||
| `traefik.port=80` | Register this port. Useful when the container exposes multiples ports. |
|
||||
| `traefik.portIndex=1` | Register port by index in the application's ports array. Useful when the application exposes multiple ports. |
|
||||
| `traefik.protocol=https` | Override the default `http` protocol |
|
||||
| `traefik.weight=10` | Assign this weight to the container |
|
||||
| `traefik.backend=foo` | Give the name `foo` to the generated backend for this container. |
|
||||
| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. |
|
||||
| `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.interval=1s` | Define the health check interval. (Default: 30s) |
|
||||
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
|
||||
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. |
|
||||
| `traefik.frontend.priority=10` | Override default frontend priority |
|
||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) |
|
||||
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
||||
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{discovery_name}.{domain}`. |
|
||||
| `traefik.frontend.whiteList.sourceRange=RANGE` | List of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.frontend.whiteList.useXForwardedFor=true` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
|
||||
### Custom Headers
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
|
||||
### Security Headers
|
||||
|
||||
| Label | Description |
|
||||
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
|
||||
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
|
||||
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
|
||||
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
|
||||
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
|
||||
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
|
||||
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
|
||||
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
|
||||
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
|
||||
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
|
||||
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
|
||||
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
|
||||
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
|
@@ -1,15 +1,15 @@
|
||||
# Rancher Backend
|
||||
# Rancher Provider
|
||||
|
||||
Træfik can be configured to use Rancher as a backend configuration.
|
||||
Træfik can be configured to use Rancher as a provider.
|
||||
|
||||
## Global Configuration
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Rancher configuration backend
|
||||
# Rancher Provider
|
||||
################################################################
|
||||
|
||||
# Enable Rancher configuration backend.
|
||||
# Enable Rancher Provider.
|
||||
[rancher]
|
||||
|
||||
# Default domain used.
|
||||
@@ -46,22 +46,38 @@ exposedByDefault = false
|
||||
# Default: false
|
||||
#
|
||||
enableServiceHealthFilter = true
|
||||
|
||||
# Override default configuration template.
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# filename = "rancher.tmpl"
|
||||
|
||||
# Override template version
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
# - "1": previous template version (must be used only with older custom templates, see "filename")
|
||||
# - "2": current template version (must be used to force template version when "filename" is used)
|
||||
#
|
||||
# templateVersion = 2
|
||||
```
|
||||
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
||||
## Rancher Metadata Service
|
||||
|
||||
```toml
|
||||
# Enable Rancher metadata service configuration backend instead of the API
|
||||
# configuration backend.
|
||||
# Enable Rancher metadata service provider instead of the API
|
||||
# provider.
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
[rancher.metadata]
|
||||
|
||||
# Poll the Rancher metadata service for changes every `rancher.RefreshSeconds`.
|
||||
# Poll the Rancher metadata service for changes every `rancher.refreshSeconds`.
|
||||
# NOTE: this is less accurate than the default long polling technique which
|
||||
# will provide near instantaneous updates to Traefik
|
||||
#
|
||||
@@ -81,7 +97,7 @@ prefix = "/2016-07-29"
|
||||
## Rancher API
|
||||
|
||||
```toml
|
||||
# Enable Rancher API configuration backend.
|
||||
# Enable Rancher API provider.
|
||||
#
|
||||
# Optional
|
||||
# Default: true
|
||||
@@ -116,25 +132,148 @@ secretKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
io.rancher.container.create_agent: true
|
||||
```
|
||||
|
||||
## Labels: overriding default behaviour
|
||||
## Labels: overriding default behavior
|
||||
|
||||
Labels can be used on task containers to override default behaviour:
|
||||
### On Containers
|
||||
|
||||
| Label | Description |
|
||||
|-----------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.protocol=https` | Override the default `http` protocol |
|
||||
| `traefik.weight=10` | Assign this weight to the container |
|
||||
| `traefik.enable=false` | Disable this container in Træfik |
|
||||
| `traefik.frontend.rule=Host:test.traefik.io` | Override the default frontend rule (Default: `Host:{containerName}.{domain}`). |
|
||||
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
|
||||
| `traefik.frontend.priority=10` | Override default frontend priority |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints`. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash`. |
|
||||
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) |
|
||||
| `traefik.frontend.redirect.regex: ^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.frontend.redirect.replacement: http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.backend.circuitbreaker.expression=NetworkErrorRatio() > 0.5` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) |
|
||||
Labels can be used on task containers to override default behavior:
|
||||
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.domain` | Default domain used for frontend rules. |
|
||||
| `traefik.enable=false` | Disable this container in Træfik |
|
||||
| `traefik.port=80` | Register this port. Useful when the container exposes multiples ports. |
|
||||
| `traefik.protocol=https` | Override the default `http` protocol |
|
||||
| `traefik.weight=10` | Assign this weight to the container |
|
||||
| `traefik.backend=foo` | Give the name `foo` to the generated backend for this container. |
|
||||
| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. |
|
||||
| `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.interval=1s` | Define the health check interval. |
|
||||
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) |
|
||||
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. |
|
||||
| `traefik.frontend.priority=10` | Override default frontend priority |
|
||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) |
|
||||
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
||||
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{service_name}.{stack_name}.{domain}`. |
|
||||
| `traefik.frontend.whiteList.sourceRange=RANGE` | List of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access.<br>If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.frontend.whiteList.useXForwardedFor=true` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
|
||||
#### Custom Headers
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
|
||||
#### Security Headers
|
||||
|
||||
| Label | Description |
|
||||
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
|
||||
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
|
||||
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
|
||||
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
|
||||
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
|
||||
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
|
||||
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
|
||||
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
|
||||
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
|
||||
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
|
||||
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
|
||||
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
|
||||
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
|
||||
### On containers with Multiple Ports (segment labels)
|
||||
|
||||
Segment labels are used to define routes to a container exposing multiple ports.
|
||||
A segment is a group of labels that apply to a port exposed by a container.
|
||||
You can define as many segments as ports exposed in a container.
|
||||
|
||||
Segment labels override the default behavior.
|
||||
|
||||
| Label | Description |
|
||||
|---------------------------------------------------------------------------|-------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
||||
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
||||
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
||||
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
||||
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
||||
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
|
||||
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
|
||||
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
|
||||
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
|
||||
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
|
||||
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
|
||||
|
||||
#### Custom Headers
|
||||
|
||||
| Label | Description |
|
||||
|----------------------------------------------------------------------|------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.frontend.headers.customRequestHeaders=EXPR ` | overrides `traefik.frontend.headers.customRequestHeaders` |
|
||||
| `traefik.<segment_name>.frontend.headers.customResponseHeaders=EXPR` | overrides `traefik.frontend.headers.customResponseHeaders` |
|
||||
|
||||
#### Security Headers
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------------------------|--------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.frontend.headers.allowedHosts=EXPR` | overrides `traefik.frontend.headers.allowedHosts` |
|
||||
| `traefik.<segment_name>.frontend.headers.browserXSSFilter=true` | overrides `traefik.frontend.headers.browserXSSFilter` |
|
||||
| `traefik.<segment_name>.frontend.headers.contentSecurityPolicy=VALUE` | overrides `traefik.frontend.headers.contentSecurityPolicy` |
|
||||
| `traefik.<segment_name>.frontend.headers.contentTypeNosniff=true` | overrides `traefik.frontend.headers.contentTypeNosniff` |
|
||||
| `traefik.<segment_name>.frontend.headers.customBrowserXSSValue=VALUE` | overrides `traefik.frontend.headers.customBrowserXSSValue` |
|
||||
| `traefik.<segment_name>.frontend.headers.customFrameOptionsValue=VALUE` | overrides `traefik.frontend.headers.customFrameOptionsValue` |
|
||||
| `traefik.<segment_name>.frontend.headers.forceSTSHeader=false` | overrides `traefik.frontend.headers.forceSTSHeader` |
|
||||
| `traefik.<segment_name>.frontend.headers.frameDeny=false` | overrides `traefik.frontend.headers.frameDeny` |
|
||||
| `traefik.<segment_name>.frontend.headers.hostsProxyHeaders=EXPR` | overrides `traefik.frontend.headers.hostsProxyHeaders` |
|
||||
| `traefik.<segment_name>.frontend.headers.isDevelopment=false` | overrides `traefik.frontend.headers.isDevelopment` |
|
||||
| `traefik.<segment_name>.frontend.headers.publicKey=VALUE` | overrides `traefik.frontend.headers.publicKey` |
|
||||
| `traefik.<segment_name>.frontend.headers.referrerPolicy=VALUE` | overrides `traefik.frontend.headers.referrerPolicy` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLRedirect=true` | overrides `traefik.frontend.headers.SSLRedirect` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLTemporaryRedirect=true` | overrides `traefik.frontend.headers.SSLTemporaryRedirect` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLHost=HOST` | overrides `traefik.frontend.headers.SSLHost` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLProxyHeaders=EXPR` | overrides `traefik.frontend.headers.SSLProxyHeaders` |
|
||||
| `traefik.<segment_name>.frontend.headers.STSSeconds=315360000` | overrides `traefik.frontend.headers.STSSeconds` |
|
||||
| `traefik.<segment_name>.frontend.headers.STSIncludeSubdomains=true` | overrides `traefik.frontend.headers.STSIncludeSubdomains` |
|
||||
| `traefik.<segment_name>.frontend.headers.STSPreload=true` | overrides `traefik.frontend.headers.STSPreload` |
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# Rest Backend
|
||||
# Rest Provider
|
||||
|
||||
Træfik can be configured:
|
||||
|
||||
@@ -7,7 +7,7 @@ Træfik can be configured:
|
||||
## Configuration
|
||||
|
||||
```toml
|
||||
# Enable rest backend.
|
||||
# Enable REST Provider.
|
||||
[rest]
|
||||
# Name of the related entry point
|
||||
#
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# Azure Service Fabric Backend
|
||||
# Azure Service Fabric Provider
|
||||
|
||||
Træfik can be configured to use Azure Service Fabric as a backend configuration.
|
||||
Træfik can be configured to use Azure Service Fabric as a provider.
|
||||
|
||||
See [this repository for an example deployment package and further documentation.](https://aka.ms/traefikonsf)
|
||||
|
||||
@@ -8,10 +8,10 @@ See [this repository for an example deployment package and further documentation
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Azure Service Fabric provider
|
||||
# Azure Service Fabric Provider
|
||||
################################################################
|
||||
|
||||
# Enable Azure Service Fabric configuration backend
|
||||
# Enable Azure Service Fabric Provider
|
||||
[serviceFabric]
|
||||
|
||||
# Azure Service Fabric Management Endpoint
|
||||
@@ -42,7 +42,7 @@ refreshSeconds = 10
|
||||
# ca = "/etc/ssl/ca.crt"
|
||||
# cert = "/etc/ssl/servicefabric.crt"
|
||||
# key = "/etc/ssl/servicefabric.key"
|
||||
# insecureskipverify = true
|
||||
# insecureSkipVerify = true
|
||||
```
|
||||
|
||||
## Labels
|
||||
@@ -61,7 +61,7 @@ Here is an example of an extension setting Træfik labels:
|
||||
<Extension Name="Traefik">
|
||||
<Labels xmlns="http://schemas.microsoft.com/2015/03/fabact-no-schema">
|
||||
<Label Key="traefik.frontend.rule.example2">PathPrefixStrip: /a/path/to/strip</Label>
|
||||
<Label Key="traefik.expose">true</Label>
|
||||
<Label Key="traefik.enable">true</Label>
|
||||
<Label Key="traefik.frontend.passHostHeader">true</Label>
|
||||
</Labels>
|
||||
</Extension>
|
||||
@@ -69,10 +69,10 @@ Here is an example of an extension setting Træfik labels:
|
||||
</StatelessServiceType>
|
||||
```
|
||||
|
||||
#### Property Manager
|
||||
#### Property Manager
|
||||
|
||||
Set Labels with the property manager API to overwrite and add labels, while your service is running.
|
||||
Here is an example of adding a frontend rule using the property manager API.
|
||||
Here is an example of adding a frontend rule using the property manager API.
|
||||
|
||||
```shell
|
||||
curl -X PUT \
|
||||
@@ -92,23 +92,65 @@ curl -X PUT \
|
||||
|
||||
## Available Labels
|
||||
|
||||
Labels, set through extensions or the property manager, can be used on services to override default behaviour.
|
||||
Labels, set through extensions or the property manager, can be used on services to override default behavior.
|
||||
|
||||
| Label | Description |
|
||||
|-----------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
|
||||
| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.backend.weight=10` | Assign this weight to the container |
|
||||
| `traefik.expose=true` | Expose this service using træfik |
|
||||
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Defaults to SF address. |
|
||||
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
|
||||
| `traefik.frontend.priority=10` | Override default frontend priority |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints` |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Set basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
|
||||
| `traefik.frontend.whitelistSourceRange:RANGE` | List of IP-Ranges which are allowed to access. An unset or empty list allows all Source-IPs to access.<br>If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.backend.group.name` | Group all services with the same name into a single backend in Træfik |
|
||||
| `traefik.backend.group.weight` | Set the weighting of the current services nodes in the backend group |
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.enable=false` | Disable this container in Træfik |
|
||||
| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.servicefabric.groupname` | Group all services with the same name into a single backend in Træfik |
|
||||
| `traefik.servicefabric.groupweight` | Set the weighting of the current services nodes in the backend group |
|
||||
| `traefik.servicefabric.enablelabeloverrides` | Toggle whether labels can be overridden using the Service Fabric Property Manager API |
|
||||
| `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. |
|
||||
| `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.interval=1s` | Define the health check interval. |
|
||||
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) |
|
||||
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.backend.weight=10` | Assign this weight to the container |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. |
|
||||
| `traefik.frontend.priority=10` | Override default frontend priority |
|
||||
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) |
|
||||
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
||||
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Defaults to SF address. |
|
||||
| `traefik.frontend.whiteList.sourceRange=RANGE` | List of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access.<br>If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.frontend.whiteList.useXForwardedFor=true` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
|
||||
### Custom Headers
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
|
||||
### Security Headers
|
||||
|
||||
| Label | Description |
|
||||
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
|
||||
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
|
||||
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
|
||||
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
|
||||
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
|
||||
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
|
||||
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
|
||||
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
|
||||
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
|
||||
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
|
||||
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
|
||||
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
|
||||
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# Web Backend
|
||||
# Web Provider
|
||||
|
||||
!!! danger "DEPRECATED"
|
||||
The web provider is deprecated, please use the [api](/configuration/api.md), the [ping](/configuration/ping.md), the [metrics](/configuration/metrics) and the [rest](/configuration/backends/rest.md) provider.
|
||||
@@ -12,7 +12,7 @@ Træfik can be configured:
|
||||
## Configuration
|
||||
|
||||
```toml
|
||||
# Enable web backend.
|
||||
# Enable Web Provider.
|
||||
[web]
|
||||
|
||||
# Web administration port.
|
||||
|
@@ -1,13 +1,13 @@
|
||||
# Zookeeper Backend
|
||||
# Zookeeper Provider
|
||||
|
||||
Træfik can be configured to use Zookeeper as a backend configuration.
|
||||
Træfik can be configured to use Zookeeper as a provider.
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Zookeeper configuration backend
|
||||
# Zookeeper Provider
|
||||
################################################################
|
||||
|
||||
# Enable Zookeeperconfiguration backend.
|
||||
# Enable Zookeeper Provider.
|
||||
[zookeeper]
|
||||
|
||||
# Zookeeper server endpoint.
|
||||
@@ -53,9 +53,9 @@ prefix = "traefik"
|
||||
# ca = "/etc/ssl/ca.crt"
|
||||
# cert = "/etc/ssl/zookeeper.crt"
|
||||
# key = "/etc/ssl/zookeeper.key"
|
||||
# insecureskipverify = true
|
||||
# insecureSkipVerify = true
|
||||
```
|
||||
|
||||
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
||||
Please refer to the [Key Value storage structure](/user-guide/kv-config/#key-value-storage-structure) section to get documentation on Traefik KV structure.
|
||||
|
@@ -19,7 +19,7 @@
|
||||
# Enable debug mode.
|
||||
# This will install HTTP handlers to expose Go expvars under /debug/vars and
|
||||
# pprof profiling data under /debug/pprof.
|
||||
# Additionally, the log level will be set to DEBUG.
|
||||
# The log level will be set to DEBUG unless `logLevel` is specified.
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
@@ -33,19 +33,19 @@
|
||||
#
|
||||
# checkNewVersion = false
|
||||
|
||||
# Backends throttle duration.
|
||||
# Providers throttle duration.
|
||||
#
|
||||
# Optional
|
||||
# Default: "2s"
|
||||
#
|
||||
# ProvidersThrottleDuration = "2s"
|
||||
# providersThrottleDuration = "2s"
|
||||
|
||||
# Controls the maximum idle (keep-alive) connections to keep per-host.
|
||||
#
|
||||
# Optional
|
||||
# Default: 200
|
||||
#
|
||||
# MaxIdleConnsPerHost = 200
|
||||
# maxIdleConnsPerHost = 200
|
||||
|
||||
# If set to true invalid SSL certificates are accepted for backends.
|
||||
# This disables detection of man-in-the-middle attacks so should only be used on secure backend networks.
|
||||
@@ -53,14 +53,14 @@
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
# InsecureSkipVerify = true
|
||||
# insecureSkipVerify = true
|
||||
|
||||
# Register Certificates in the RootCA.
|
||||
# Register Certificates in the rootCA.
|
||||
#
|
||||
# Optional
|
||||
# Default: []
|
||||
#
|
||||
# RootCAs = [ "/mycert.cert" ]
|
||||
# rootCAs = [ "/mycert.cert" ]
|
||||
|
||||
# Entrypoints to be used by frontends that do not specify any entrypoint.
|
||||
# Each frontend can specify its own entrypoints.
|
||||
@@ -69,6 +69,15 @@
|
||||
# Default: ["http"]
|
||||
#
|
||||
# defaultEntryPoints = ["http", "https"]
|
||||
|
||||
# Allow the use of 0 as server weight.
|
||||
# - false: a weight 0 means internally a weight of 1.
|
||||
# - true: a weight 0 means internally a weight of 0 (a server with a weight of 0 is removed from the available servers).
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
# AllowMinWeightZero = true
|
||||
```
|
||||
|
||||
- `graceTimeOut`: Duration to give active requests a chance to finish before Traefik stops.
|
||||
@@ -76,19 +85,19 @@ Can be provided in a format supported by [time.ParseDuration](https://golang.org
|
||||
If no units are provided, the value is parsed assuming seconds.
|
||||
**Note:** in this time frame no new requests are accepted.
|
||||
|
||||
- `ProvidersThrottleDuration`: Backends throttle duration: minimum duration in seconds between 2 events from providers before applying a new configuration.
|
||||
- `providersThrottleDuration`: Providers throttle duration: minimum duration in seconds between 2 events from providers before applying a new configuration.
|
||||
It avoids unnecessary reloads if multiples events are sent in a short amount of time.
|
||||
Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw values (digits).
|
||||
If no units are provided, the value is parsed assuming seconds.
|
||||
|
||||
- `MaxIdleConnsPerHost`: Controls the maximum idle (keep-alive) connections to keep per-host.
|
||||
- `maxIdleConnsPerHost`: Controls the maximum idle (keep-alive) connections to keep per-host.
|
||||
If zero, `DefaultMaxIdleConnsPerHost` from the Go standard library net/http module is used.
|
||||
If you encounter 'too many open files' errors, you can either increase this value or change the `ulimit`.
|
||||
|
||||
- `InsecureSkipVerify` : If set to true invalid SSL certificates are accepted for backends.
|
||||
- `insecureSkipVerify` : If set to true invalid SSL certificates are accepted for backends.
|
||||
**Note:** This disables detection of man-in-the-middle attacks so should only be used on secure backend networks.
|
||||
|
||||
- `RootCAs`: Register Certificates in the RootCA. This certificates will be use for backends calls.
|
||||
- `rootCAs`: Register Certificates in the RootCA. This certificates will be use for backends calls.
|
||||
**Note** You can use file path or cert content directly
|
||||
|
||||
- `defaultEntryPoints`: Entrypoints to be used by frontends that do not specify any entrypoint.
|
||||
@@ -99,7 +108,7 @@ Each frontend can specify its own entrypoints.
|
||||
|
||||
In a micro-service architecture, with a central service discovery, setting constraints limits Træfik scope to a smaller number of routes.
|
||||
|
||||
Træfik filters services according to service attributes/tags set in your configuration backends.
|
||||
Træfik filters services according to service attributes/tags set in your providers.
|
||||
|
||||
Supported filters:
|
||||
|
||||
@@ -127,9 +136,9 @@ constraints = ["tag==us-*"]
|
||||
constraints = ["tag!=us-*", "tag!=asia-*"]
|
||||
```
|
||||
|
||||
### Backend-specific
|
||||
### provider-specific
|
||||
|
||||
Supported backends:
|
||||
Supported Providers:
|
||||
|
||||
- Docker
|
||||
- Consul K/V
|
||||
@@ -142,101 +151,18 @@ Supported backends:
|
||||
- Kubernetes (using a provider-specific mechanism based on label selectors)
|
||||
|
||||
```toml
|
||||
# Backend-specific constraint
|
||||
# Provider-specific constraint
|
||||
[consulCatalog]
|
||||
# ...
|
||||
constraints = ["tag==api"]
|
||||
|
||||
# Backend-specific constraint
|
||||
# Provider-specific constraint
|
||||
[marathon]
|
||||
# ...
|
||||
constraints = ["tag==api", "tag!=v*-beta"]
|
||||
```
|
||||
|
||||
|
||||
## Logs Definition
|
||||
|
||||
### Traefik logs
|
||||
|
||||
```toml
|
||||
# Traefik logs file
|
||||
# If not defined, logs to stdout
|
||||
#
|
||||
# DEPRECATED - see [traefikLog] lower down
|
||||
# In case both traefikLogsFile and traefikLog.filePath are specified, the latter will take precedence.
|
||||
# Optional
|
||||
#
|
||||
traefikLogsFile = "log/traefik.log"
|
||||
|
||||
# Log level
|
||||
#
|
||||
# Optional
|
||||
# Default: "ERROR"
|
||||
#
|
||||
# Accepted values, in order of severity: "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "PANIC"
|
||||
# Messages at and above the selected level will be logged.
|
||||
#
|
||||
logLevel = "ERROR"
|
||||
```
|
||||
|
||||
## Traefik Logs
|
||||
|
||||
By default the Traefik log is written to stdout in text format.
|
||||
|
||||
To write the logs into a logfile specify the `filePath`.
|
||||
```toml
|
||||
[traefikLog]
|
||||
filePath = "/path/to/traefik.log"
|
||||
```
|
||||
|
||||
To write JSON format logs, specify `json` as the format:
|
||||
```toml
|
||||
[traefikLog]
|
||||
filePath = "/path/to/traefik.log"
|
||||
format = "json"
|
||||
```
|
||||
|
||||
### Access Logs
|
||||
|
||||
Access logs are written when `[accessLog]` is defined.
|
||||
By default it will write to stdout and produce logs in the textual Common Log Format (CLF), extended with additional fields.
|
||||
|
||||
To enable access logs using the default settings just add the `[accessLog]` entry.
|
||||
```toml
|
||||
[accessLog]
|
||||
```
|
||||
|
||||
To write the logs into a logfile specify the `filePath`.
|
||||
```toml
|
||||
[accessLog]
|
||||
filePath = "/path/to/access.log"
|
||||
```
|
||||
|
||||
To write JSON format logs, specify `json` as the format:
|
||||
```toml
|
||||
[accessLog]
|
||||
filePath = "/path/to/access.log"
|
||||
format = "json"
|
||||
```
|
||||
|
||||
Deprecated way (before 1.4):
|
||||
```toml
|
||||
# Access logs file
|
||||
#
|
||||
# DEPRECATED - see [accessLog] lower down
|
||||
#
|
||||
accessLogsFile = "log/access.log"
|
||||
```
|
||||
|
||||
### Log Rotation
|
||||
|
||||
Traefik will close and reopen its log files, assuming they're configured, on receipt of a USR1 signal.
|
||||
This allows the logs to be rotated and processed by an external program, such as `logrotate`.
|
||||
|
||||
!!! note
|
||||
This does not work on Windows due to the lack of USR signals.
|
||||
|
||||
|
||||
## Custom Error pages
|
||||
|
||||
Custom error pages can be returned, in lieu of the default, according to frontend-configured ranges of HTTP Status codes.
|
||||
@@ -273,9 +199,6 @@ Instead, the query parameter can also be set to some generic error page like so:
|
||||
Now the `500s.html` error page is returned for the configured code range.
|
||||
The configured status code ranges are inclusive; that is, in the above example, the `500s.html` page will be returned for status codes `500` through, and including, `599`.
|
||||
|
||||
Custom error pages are easiest to implement using the file provider.
|
||||
For dynamic providers, the corresponding template file needs to be customized accordingly and referenced in the Traefik configuration.
|
||||
|
||||
|
||||
## Rate limiting
|
||||
|
||||
@@ -302,6 +225,26 @@ In the above example, frontend1 is configured to limit requests by the client's
|
||||
An average of 5 requests every 3 seconds is allowed and an average of 100 requests every 10 seconds.
|
||||
These can "burst" up to 10 and 200 in each period respectively.
|
||||
|
||||
## Buffering
|
||||
|
||||
In some cases request/buffering can be enabled for a specific backend.
|
||||
By enabling this, Træfik will read the entire request into memory (possibly buffering large requests into disk) and will reject requests that are over a specified limit.
|
||||
This may help services deal with large data (multipart/form-data for example) more efficiently and should minimise time spent when sending data to a backend server.
|
||||
|
||||
For more information please check [oxy/buffer](http://godoc.org/github.com/vulcand/oxy/buffer) documentation.
|
||||
|
||||
Example configuration:
|
||||
|
||||
```toml
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.buffering]
|
||||
maxRequestBodyBytes = 10485760
|
||||
memRequestBodyBytes = 2097152
|
||||
maxResponseBodyBytes = 10485760
|
||||
memResponseBodyBytes = 2097152
|
||||
retryExpression = "IsNetworkError() && Attempts() <= 2"
|
||||
```
|
||||
|
||||
## Retry Configuration
|
||||
|
||||
@@ -452,24 +395,24 @@ If no units are provided, the value is parsed assuming seconds.
|
||||
|
||||
### Idle Timeout (deprecated)
|
||||
|
||||
Use [respondingTimeouts](/configuration/commons/#responding-timeouts) instead of `IdleTimeout`.
|
||||
Use [respondingTimeouts](/configuration/commons/#responding-timeouts) instead of `idleTimeout`.
|
||||
In the case both settings are configured, the deprecated option will be overwritten.
|
||||
|
||||
`IdleTimeout` is the maximum amount of time an idle (keep-alive) connection will remain idle before closing itself.
|
||||
`idleTimeout` is the maximum amount of time an idle (keep-alive) connection will remain idle before closing itself.
|
||||
This is set to enforce closing of stale client connections.
|
||||
|
||||
Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw values (digits).
|
||||
If no units are provided, the value is parsed assuming seconds.
|
||||
|
||||
```toml
|
||||
# IdleTimeout
|
||||
# idleTimeout
|
||||
#
|
||||
# DEPRECATED - see [respondingTimeouts] section.
|
||||
#
|
||||
# Optional
|
||||
# Default: "180s"
|
||||
#
|
||||
IdleTimeout = "360s"
|
||||
idleTimeout = "360s"
|
||||
```
|
||||
|
||||
|
||||
@@ -478,12 +421,12 @@ IdleTimeout = "360s"
|
||||
!!! warning
|
||||
For advanced users only.
|
||||
|
||||
Supported by all backends except: File backend, Web backend and DynamoDB backend.
|
||||
Supported by all providers except: File Provider, Web Provider and DynamoDB Provider.
|
||||
|
||||
```toml
|
||||
[backend_name]
|
||||
[provider_name]
|
||||
|
||||
# Override default configuration template. For advanced users :)
|
||||
# Override default provider configuration template. For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
# Default: ""
|
||||
|
@@ -8,9 +8,12 @@
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
whitelistSourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
|
||||
compress = true
|
||||
|
||||
[entryPoints.http.whitelist]
|
||||
sourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
|
||||
useXForwardedFor = true
|
||||
|
||||
[entryPoints.http.tls]
|
||||
minVersion = "VersionTLS12"
|
||||
cipherSuites = [
|
||||
@@ -32,6 +35,7 @@
|
||||
entryPoint = "https"
|
||||
regex = "^http://localhost/(.*)"
|
||||
replacement = "http://mydomain/$1"
|
||||
permanent = true
|
||||
|
||||
[entryPoints.http.auth]
|
||||
headerField = "X-WebAuth-User"
|
||||
@@ -102,18 +106,30 @@ traefik:
|
||||
```ini
|
||||
Name:foo
|
||||
Address::80
|
||||
TLS:goo,gii
|
||||
TLS:/my/path/foo.cert,/my/path/foo.key;/my/path/goo.cert,/my/path/goo.key;/my/path/hoo.cert,/my/path/hoo.key
|
||||
TLS
|
||||
CA:car
|
||||
CA.Optional:true
|
||||
Redirect.EntryPoint:https
|
||||
Redirect.Regex:http://localhost/(.*)
|
||||
Redirect.Replacement:http://mydomain/$1
|
||||
Redirect.Permanent:true
|
||||
Compress:true
|
||||
WhiteListSourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16
|
||||
WhiteList.SourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16
|
||||
WhiteList.UseXForwardedFor:true
|
||||
ProxyProtocol.TrustedIPs:192.168.0.1
|
||||
ProxyProtocol.Insecure:tue
|
||||
ProxyProtocol.Insecure:true
|
||||
ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24
|
||||
Auth.Basic.Users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0
|
||||
Auth.Digest.Users:test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e
|
||||
Auth.HeaderField:X-WebAuth-User
|
||||
Auth.Forward.Address:https://authserver.com/auth
|
||||
Auth.Forward.TrustForwardHeader:true
|
||||
Auth.Forward.TLS.CA:path/to/local.crt
|
||||
Auth.Forward.TLS.CAOptional:true
|
||||
Auth.Forward.TLS.Cert:path/to/foo.cert
|
||||
Auth.Forward.TLS.Key:path/to/foo.key
|
||||
Auth.Forward.TLS.InsecureSkipVerify:true
|
||||
```
|
||||
|
||||
## Basic
|
||||
@@ -340,15 +356,18 @@ Responses are compressed when:
|
||||
* And the `Accept-Encoding` request header contains `gzip`
|
||||
* And the response is not already compressed, i.e. the `Content-Encoding` response header is not already set.
|
||||
|
||||
## Whitelisting
|
||||
## White Listing
|
||||
|
||||
To enable IP whitelisting at the entrypoint level.
|
||||
To enable IP white listing at the entry point level.
|
||||
|
||||
```toml
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
whiteListSourceRange = ["127.0.0.1/32", "192.168.1.7"]
|
||||
address = ":80"
|
||||
|
||||
[entryPoints.http.whiteList]
|
||||
sourceRange = ["127.0.0.1/32", "192.168.1.7"]
|
||||
# useXForwardedFor = true
|
||||
```
|
||||
|
||||
## ProxyProtocol
|
||||
|
252
docs/configuration/logs.md
Normal file
@@ -0,0 +1,252 @@
|
||||
# Logs Definition
|
||||
|
||||
## Reference
|
||||
|
||||
### TOML
|
||||
|
||||
```toml
|
||||
logLevel = "INFO"
|
||||
|
||||
[traefikLog]
|
||||
filePath = "/path/to/traefik.log"
|
||||
format = "json"
|
||||
|
||||
[accessLog]
|
||||
filePath = "/path/to/access.log"
|
||||
format = "json"
|
||||
|
||||
[accessLog.filters]
|
||||
statusCodes = ["200", "300-302"]
|
||||
retryAttempts = true
|
||||
|
||||
[accessLog.fields]
|
||||
defaultMode = "keep"
|
||||
[accessLog.fields.names]
|
||||
"ClientUsername" = "drop"
|
||||
# ...
|
||||
|
||||
[accessLog.fields.headers]
|
||||
defaultMode = "keep"
|
||||
[accessLog.fields.headers.names]
|
||||
"User-Agent" = "redact"
|
||||
"Authorization" = "drop"
|
||||
"Content-Type" = "keep"
|
||||
# ...
|
||||
```
|
||||
|
||||
### CLI
|
||||
|
||||
For more information about the CLI, see the documentation about [Traefik command](/basics/#traefik).
|
||||
|
||||
```shell
|
||||
--logLevel="DEBUG"
|
||||
--traefikLog.filePath="/path/to/traefik.log"
|
||||
--traefikLog.format="json"
|
||||
--accessLog.filePath="/path/to/access.log"
|
||||
--accessLog.format="json"
|
||||
--accessLog.filters.statusCodes="200,300-302"
|
||||
--accessLog.filters.retryAttempts="true"
|
||||
--accessLog.fields.defaultMode="keep"
|
||||
--accessLog.fields.names="Username=drop Hostname=drop"
|
||||
--accessLog.fields.headers.defaultMode="keep"
|
||||
--accessLog.fields.headers.names="User-Agent=redact Authorization=drop Content-Type=keep"
|
||||
```
|
||||
|
||||
|
||||
## Traefik Logs
|
||||
|
||||
By default the Traefik log is written to stdout in text format.
|
||||
|
||||
To write the logs into a log file specify the `filePath`:
|
||||
```toml
|
||||
[traefikLog]
|
||||
filePath = "/path/to/traefik.log"
|
||||
```
|
||||
|
||||
To write JSON format logs, specify `json` as the format:
|
||||
```toml
|
||||
[traefikLog]
|
||||
filePath = "/path/to/traefik.log"
|
||||
format = "json"
|
||||
```
|
||||
|
||||
|
||||
Deprecated way (before 1.4):
|
||||
|
||||
!!! danger "DEPRECATED"
|
||||
`traefikLogsFile` is deprecated, use [traefikLog](/configuration/logs/#traefik-logs) instead.
|
||||
|
||||
```toml
|
||||
# Traefik logs file
|
||||
# If not defined, logs to stdout
|
||||
#
|
||||
# DEPRECATED - see [traefikLog] lower down
|
||||
# In case both traefikLogsFile and traefikLog.filePath are specified, the latter will take precedence.
|
||||
# Optional
|
||||
#
|
||||
traefikLogsFile = "log/traefik.log"
|
||||
```
|
||||
|
||||
To customize the log level:
|
||||
```toml
|
||||
# Log level
|
||||
#
|
||||
# Optional
|
||||
# Default: "ERROR"
|
||||
#
|
||||
# Accepted values, in order of severity: "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "PANIC"
|
||||
# Messages at and above the selected level will be logged.
|
||||
#
|
||||
logLevel = "ERROR"
|
||||
```
|
||||
|
||||
|
||||
## Access Logs
|
||||
|
||||
Access logs are written when `[accessLog]` is defined.
|
||||
By default it will write to stdout and produce logs in the textual Common Log Format (CLF), extended with additional fields.
|
||||
|
||||
To enable access logs using the default settings just add the `[accessLog]` entry:
|
||||
```toml
|
||||
[accessLog]
|
||||
```
|
||||
|
||||
To write the logs into a log file specify the `filePath`:
|
||||
```toml
|
||||
[accessLog]
|
||||
filePath = "/path/to/access.log"
|
||||
```
|
||||
|
||||
To write JSON format logs, specify `json` as the format:
|
||||
```toml
|
||||
[accessLog]
|
||||
filePath = "/path/to/access.log"
|
||||
format = "json"
|
||||
```
|
||||
|
||||
To filter logs you can specify a set of filters which are logically "OR-connected". Thus, specifying multiple filters will keep more access logs than specifying only one:
|
||||
```toml
|
||||
[accessLog]
|
||||
filePath = "/path/to/access.log"
|
||||
format = "json"
|
||||
|
||||
[accessLog.filters]
|
||||
|
||||
# statusCodes keep access logs with status codes in the specified range
|
||||
#
|
||||
# Optional
|
||||
# Default: []
|
||||
#
|
||||
statusCodes = ["200", "300-302"]
|
||||
|
||||
# retryAttempts keep access logs when at least one retry happened
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
retryAttempts = true
|
||||
```
|
||||
|
||||
To customize logs format:
|
||||
```toml
|
||||
[accessLog]
|
||||
filePath = "/path/to/access.log"
|
||||
format = "json"
|
||||
|
||||
[accessLog.filters]
|
||||
|
||||
# statusCodes keep only access logs with status codes in the specified range
|
||||
#
|
||||
# Optional
|
||||
# Default: []
|
||||
#
|
||||
statusCodes = ["200", "300-302"]
|
||||
|
||||
[accessLog.fields]
|
||||
|
||||
# defaultMode
|
||||
#
|
||||
# Optional
|
||||
# Default: "keep"
|
||||
#
|
||||
# Accepted values "keep", "drop"
|
||||
#
|
||||
defaultMode = "keep"
|
||||
|
||||
# Fields map which is used to override fields defaultMode
|
||||
[accessLog.fields.names]
|
||||
"ClientUsername" = "drop"
|
||||
# ...
|
||||
|
||||
[accessLog.fields.headers]
|
||||
# defaultMode
|
||||
#
|
||||
# Optional
|
||||
# Default: "keep"
|
||||
#
|
||||
# Accepted values "keep", "drop", "redact"
|
||||
#
|
||||
defaultMode = "keep"
|
||||
# Fields map which is used to override headers defaultMode
|
||||
[accessLog.fields.headers.names]
|
||||
"User-Agent" = "redact"
|
||||
"Authorization" = "drop"
|
||||
"Content-Type" = "keep"
|
||||
# ...
|
||||
```
|
||||
|
||||
#### List of all available fields
|
||||
|
||||
```ini
|
||||
StartUTC
|
||||
StartLocal
|
||||
Duration
|
||||
FrontendName
|
||||
BackendName
|
||||
BackendURL
|
||||
BackendAddr
|
||||
ClientAddr
|
||||
ClientHost
|
||||
ClientPort
|
||||
ClientUsername
|
||||
RequestAddr
|
||||
RequestHost
|
||||
RequestPort
|
||||
RequestMethod
|
||||
RequestPath
|
||||
RequestProtocol
|
||||
RequestLine
|
||||
RequestContentSize
|
||||
OriginDuration
|
||||
OriginContentSize
|
||||
OriginStatus
|
||||
OriginStatusLine
|
||||
DownstreamStatus
|
||||
DownstreamStatusLine
|
||||
DownstreamContentSize
|
||||
RequestCount
|
||||
GzipRatio
|
||||
Overhead
|
||||
RetryAttempts
|
||||
```
|
||||
|
||||
Deprecated way (before 1.4):
|
||||
|
||||
!!! danger "DEPRECATED"
|
||||
`accessLogsFile` is deprecated, use [accessLog](/configuration/logs/#access-logs) instead.
|
||||
|
||||
```toml
|
||||
# Access logs file
|
||||
#
|
||||
# DEPRECATED - see [accessLog]
|
||||
#
|
||||
accessLogsFile = "log/access.log"
|
||||
```
|
||||
|
||||
## Log Rotation
|
||||
|
||||
Traefik will close and reopen its log files, assuming they're configured, on receipt of a USR1 signal.
|
||||
This allows the logs to be rotated and processed by an external program, such as `logrotate`.
|
||||
|
||||
!!! note
|
||||
This does not work on Windows due to the lack of USR signals.
|
@@ -80,7 +80,8 @@
|
||||
|
||||
# ...
|
||||
```
|
||||
### InfluxDB
|
||||
|
||||
## InfluxDB
|
||||
|
||||
```toml
|
||||
[metrics]
|
||||
@@ -105,22 +106,3 @@
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
## Statistics
|
||||
|
||||
```toml
|
||||
# Metrics definition
|
||||
[metrics]
|
||||
# ...
|
||||
|
||||
# Enable more detailed statistics.
|
||||
[metrics.statistics]
|
||||
|
||||
# Number of recent errors logged.
|
||||
#
|
||||
# Default: 10
|
||||
#
|
||||
recentErrors = 10
|
||||
|
||||
# ...
|
||||
```
|
||||
|
@@ -85,3 +85,7 @@ Note the dedicated port `:8082` for `/ping`.
|
||||
|
||||
In the above example, it is _very_ important to create a named dedicated entry point, and do **not** include it in `defaultEntryPoints`.
|
||||
Otherwise, you are likely to expose _all_ services via this entry point.
|
||||
|
||||
### Using ping for external Load-balancer rotation health check
|
||||
|
||||
If you are running traefik behind a external Load-balancer, and want to configure rotation health check on the Load-balancer to take a traefik instance out of rotation gracefully, you can configure [lifecycle.requestAcceptGraceTimeout](/configuration/commons.md#life-cycle) and the ping endpoint will return `503` response on traefik server termination, so that the Load-balancer can take the terminating traefik instance out of rotation, before it stops responding.
|
||||
|
100
docs/configuration/tracing.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# Tracing
|
||||
|
||||
Tracing system allows developers to visualize call flows in there infrastructures.
|
||||
|
||||
We use [OpenTracing](http://opentracing.io). It is an open standard designed for distributed tracing.
|
||||
|
||||
Træfik supports two backends: Jaeger and Zipkin.
|
||||
|
||||
## Jaeger
|
||||
|
||||
```toml
|
||||
# Tracing definition
|
||||
[tracing]
|
||||
# Backend name used to send tracing data
|
||||
#
|
||||
# Default: "jaeger"
|
||||
#
|
||||
backend = "jaeger"
|
||||
|
||||
# Service name used in Jaeger backend
|
||||
#
|
||||
# Default: "traefik"
|
||||
#
|
||||
serviceName = "traefik"
|
||||
|
||||
[tracing.jaeger]
|
||||
# Sampling Server URL is the address of jaeger-agent's HTTP sampling server
|
||||
#
|
||||
# Default: "http://localhost:5778/sampling"
|
||||
#
|
||||
samplingServerURL = "http://localhost:5778/sampling"
|
||||
|
||||
# Sampling Type specifies the type of the sampler: const, probabilistic, rateLimiting
|
||||
#
|
||||
# Default: "const"
|
||||
#
|
||||
samplingType = "const"
|
||||
|
||||
# Sampling Param is a value passed to the sampler.
|
||||
# Valid values for Param field are:
|
||||
# - for "const" sampler, 0 or 1 for always false/true respectively
|
||||
# - for "probabilistic" sampler, a probability between 0 and 1
|
||||
# - for "rateLimiting" sampler, the number of spans per second
|
||||
#
|
||||
# Default: 1.0
|
||||
#
|
||||
samplingParam = 1.0
|
||||
|
||||
# Local Agent Host Port instructs reporter to send spans to jaeger-agent at this address
|
||||
#
|
||||
# Default: "127.0.0.1:6831"
|
||||
#
|
||||
localAgentHostPort = "127.0.0.1:6831"
|
||||
```
|
||||
|
||||
!!! warning
|
||||
Træfik is only able to send data over compact thrift protocol to the [Jaeger agent](https://www.jaegertracing.io/docs/deployment/#agent).
|
||||
|
||||
## Zipkin
|
||||
|
||||
```toml
|
||||
# Tracing definition
|
||||
[tracing]
|
||||
# Backend name used to send tracing data
|
||||
#
|
||||
# Default: "jaeger"
|
||||
#
|
||||
backend = "zipkin"
|
||||
|
||||
# Service name used in Zipkin backend
|
||||
#
|
||||
# Default: "traefik"
|
||||
#
|
||||
serviceName = "traefik"
|
||||
|
||||
[tracing.zipkin]
|
||||
# Zipking HTTP endpoint used to send data
|
||||
#
|
||||
# Default: "http://localhost:9411/api/v1/spans"
|
||||
#
|
||||
httpEndpoint = "http://localhost:9411/api/v1/spans"
|
||||
|
||||
# Enable Zipkin debug
|
||||
#
|
||||
# Default: false
|
||||
#
|
||||
debug = false
|
||||
|
||||
# Use ZipKin SameSpan RPC style traces
|
||||
#
|
||||
# Default: false
|
||||
#
|
||||
sameSpan = false
|
||||
|
||||
# Use ZipKin 128 bit root span IDs
|
||||
#
|
||||
# Default: true
|
||||
#
|
||||
id128Bit = true
|
||||
```
|
Before Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 208 KiB |
Before Width: | Height: | Size: 255 KiB After Width: | Height: | Size: 274 KiB |
@@ -12,20 +12,21 @@
|
||||
|
||||
Træfik is a modern HTTP reverse proxy and load balancer that makes deploying microservices easy.
|
||||
Træfik integrates with your existing infrastructure components ([Docker](https://www.docker.com/), [Swarm mode](https://docs.docker.com/engine/swarm/), [Kubernetes](https://kubernetes.io), [Marathon](https://mesosphere.github.io/marathon/), [Consul](https://www.consul.io/), [Etcd](https://coreos.com/etcd/), [Rancher](https://rancher.com), [Amazon ECS](https://aws.amazon.com/ecs), ...) and configures itself automatically and dynamically.
|
||||
Telling Træfik where your orchestrator is could be the _only_ configuration step you need to do.
|
||||
Pointing Træfik at your orchestrator should be the _only_ configuration step you need.
|
||||
|
||||
## Overview
|
||||
|
||||
Imagine that you have deployed a bunch of microservices with the help of an orchestrator (like Swarm or Kubernetes) or a service registry (like etcd or consul).
|
||||
Now you want users to access these microservices, and you need a reverse proxy.
|
||||
|
||||
Traditional reverse-proxies require that you configure _each_ route that will connect paths and subdomains to _each_ microservice. In an environment where you add, remove, kill, upgrade, or scale your services _many_ times a day, the task of keeping the routes up to date becomes tedious.
|
||||
Traditional reverse-proxies require that you configure _each_ route that will connect paths and subdomains to _each_ microservice.
|
||||
In an environment where you add, remove, kill, upgrade, or scale your services _many_ times a day, the task of keeping the routes up to date becomes tedious.
|
||||
|
||||
**This is when Træfik can help you!**
|
||||
|
||||
Træfik listens to your service registry/orchestrator API and instantly generates the routes so your microservices are connected to the outside world -- without further intervention from your part.
|
||||
Træfik listens to your service registry/orchestrator API and instantly generates the routes so your microservices are connected to the outside world -- without further intervention from your part.
|
||||
|
||||
**Run Træfik and let it do the work for you!**
|
||||
**Run Træfik and let it do the work for you!**
|
||||
_(But if you'd rather configure some of your routes manually, Træfik supports that too!)_
|
||||
|
||||

|
||||
@@ -34,19 +35,19 @@ _(But if you'd rather configure some of your routes manually, Træfik supports t
|
||||
|
||||
- Continuously updates its configuration (No restarts!)
|
||||
- Supports multiple load balancing algorithms
|
||||
- Provides HTTPS to your microservices by leveraging [Let's Encrypt](https://letsencrypt.org)
|
||||
- Provides HTTPS to your microservices by leveraging [Let's Encrypt](https://letsencrypt.org) (wildcard certificates support)
|
||||
- Circuit breakers, retry
|
||||
- High Availability with cluster mode (beta)
|
||||
- See the magic through its clean web UI
|
||||
- Websocket, HTTP/2, GRPC ready
|
||||
- Provides metrics (Rest, Prometheus, Datadog, Statsd, InfluxDB)
|
||||
- Keeps access logs (JSON, CLF)
|
||||
- [Fast](/benchmarks) ... which is nice
|
||||
- Fast
|
||||
- Exposes a Rest API
|
||||
- Packaged as a single binary file (made with :heart: with go) and available as a [tiny](https://microbadger.com/images/traefik) [official](https://hub.docker.com/r/_/traefik/) docker image
|
||||
|
||||
|
||||
## Supported backends
|
||||
## Supported Providers
|
||||
|
||||
- [Docker](/configuration/backends/docker/) / [Swarm mode](/configuration/backends/docker/#docker-swarm-mode)
|
||||
- [Kubernetes](/configuration/backends/kubernetes/)
|
||||
@@ -76,13 +77,13 @@ version: '3'
|
||||
|
||||
services:
|
||||
reverse-proxy:
|
||||
image: traefik #The official Traefik docker image
|
||||
command: --api --docker #Enables the web UI and tells Træfik to listen to docker
|
||||
image: traefik # The official Traefik docker image
|
||||
command: --api --docker # Enables the web UI and tells Træfik to listen to docker
|
||||
ports:
|
||||
- "80:80" #The HTTP port
|
||||
- "8080:8080" #The Web UI (enabled by --api)
|
||||
- "80:80" # The HTTP port
|
||||
- "8080:8080" # The Web UI (enabled by --api)
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock #So that Traefik can listen to the Docker events
|
||||
- /var/run/docker.sock:/var/run/docker.sock # So that Traefik can listen to the Docker events
|
||||
```
|
||||
|
||||
**That's it. Now you can launch Træfik!**
|
||||
@@ -90,21 +91,21 @@ services:
|
||||
Start your `reverse-proxy` with the following command:
|
||||
|
||||
```shell
|
||||
docker-compose up -d reverse-proxy
|
||||
docker-compose up -d reverse-proxy
|
||||
```
|
||||
|
||||
You can open a browser and go to [http://localhost:8080](http://localhost:8080) to see Træfik's dashboard (we'll go back there once we have launched a service in step 2).
|
||||
|
||||
### 2 — Launch a Service — Træfik Detects It and Creates a Route for You
|
||||
### 2 — Launch a Service — Træfik Detects It and Creates a Route for You
|
||||
|
||||
Now that we have a Træfik instance up and running, we will deploy new services.
|
||||
Now that we have a Træfik instance up and running, we will deploy new services.
|
||||
|
||||
Edit your `docker-compose.yml` file and add the following at the end of your file.
|
||||
Edit your `docker-compose.yml` file and add the following at the end of your file.
|
||||
|
||||
```yaml
|
||||
# ...
|
||||
# ...
|
||||
whoami:
|
||||
image: emilevauge/whoami #A container that exposes an API to show it's IP address
|
||||
image: emilevauge/whoami # A container that exposes an API to show its IP address
|
||||
labels:
|
||||
- "traefik.frontend.rule=Host:whoami.docker.localhost"
|
||||
```
|
||||
@@ -112,7 +113,7 @@ Edit your `docker-compose.yml` file and add the following at the end of your fil
|
||||
The above defines `whoami`: a simple web service that outputs information about the machine it is deployed on (its IP address, host, and so on).
|
||||
|
||||
Start the `whoami` service with the following command:
|
||||
|
||||
|
||||
```shell
|
||||
docker-compose up -d whoami
|
||||
```
|
||||
@@ -135,9 +136,9 @@ IP: 172.27.0.3
|
||||
### 3 — Launch More Instances — Traefik Load Balances Them
|
||||
|
||||
Run more instances of your `whoami` service with the following command:
|
||||
|
||||
|
||||
```shell
|
||||
docker-compose up -d --scale whoami=2
|
||||
docker-compose up -d --scale whoami=2
|
||||
```
|
||||
|
||||
Go back to your browser ([http://localhost:8080](http://localhost:8080)) and see that Træfik has automatically detected the new instance of the container.
|
||||
@@ -164,9 +165,10 @@ IP: 172.27.0.4
|
||||
|
||||
### 4 — Enjoy Træfik's Magic
|
||||
|
||||
Now that you have a basic understanding of how Træfik can automatically create the routes to your services and load balance them, it might be time to dive into [the documentation](https://docs.traefik.io/) and let Træfik work for you! Whatever your infrastructure is, there is probably [an available Træfik backend](https://docs.traefik.io/configuration/backends/available) that will do the job.
|
||||
Now that you have a basic understanding of how Træfik can automatically create the routes to your services and load balance them, it might be time to dive into [the documentation](/) and let Træfik work for you!
|
||||
Whatever your infrastructure is, there is probably [an available Træfik provider](/#supported-providers) that will do the job.
|
||||
|
||||
Our recommendation would be to see for yourself how simple it is to enable HTTPS with [Træfik's let's encrypt integration](https://docs.traefik.io/user-guide/examples/#lets-encrypt-support) using the dedicated [user guide](https://docs.traefik.io/user-guide/docker-and-lets-encrypt/).
|
||||
Our recommendation would be to see for yourself how simple it is to enable HTTPS with [Træfik's let's encrypt integration](/user-guide/examples/#lets-encrypt-support) using the dedicated [user guide](/user-guide/docker-and-lets-encrypt/).
|
||||
|
||||
## Resources
|
||||
|
||||
@@ -196,4 +198,4 @@ Using the tiny Docker image:
|
||||
|
||||
```shell
|
||||
docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/etc/traefik/traefik.toml traefik
|
||||
```
|
||||
```
|
||||
|
@@ -9,9 +9,9 @@ If you want to use Let's Encrypt with Træfik, sharing configuration or TLS cert
|
||||
Ok, could we mount a shared volume used by all my instances? Yes, you can, but it will not work.
|
||||
When you use Let's Encrypt, you need to store certificates, but not only.
|
||||
When Træfik generates a new certificate, it configures a challenge and once Let's Encrypt will verify the ownership of the domain, it will ping back the challenge.
|
||||
If the challenge is not knowing by other Træfik instances, the validation will fail.
|
||||
If the challenge is not known by other Træfik instances, the validation will fail.
|
||||
|
||||
For more information about challenge: [Automatic Certificate Management Environment (ACME)](https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#tls-with-server-name-indication-tls-sni)
|
||||
For more information about the challenge: [Automatic Certificate Management Environment (ACME)](https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#http-challenge)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@@ -77,12 +77,12 @@ TL;DR:
|
||||
```shell
|
||||
$ traefik \
|
||||
--docker \
|
||||
--docker.swarmmode \
|
||||
--docker.swarmMode \
|
||||
--docker.domain=mydomain.ca \
|
||||
--docker.watch
|
||||
```
|
||||
|
||||
To enable docker and swarm-mode support, you need to add `--docker` and `--docker.swarmmode` flags.
|
||||
To enable docker and swarm-mode support, you need to add `--docker` and `--docker.swarmMode` flags.
|
||||
To watch docker events, add `--docker.watch`.
|
||||
|
||||
### Full docker-compose file
|
||||
@@ -101,11 +101,11 @@ services:
|
||||
- "--acme.storage=/etc/traefik/acme/acme.json"
|
||||
- "--acme.entryPoint=https"
|
||||
- "--acme.httpChallenge.entryPoint=http"
|
||||
- "--acme.OnHostRule=true"
|
||||
- "--acme.onHostRule=true"
|
||||
- "--acme.onDemand=false"
|
||||
- "--acme.email=contact@mydomain.ca"
|
||||
- "--docker"
|
||||
- "--docker.swarmmode"
|
||||
- "--docker.swarmMode"
|
||||
- "--docker.domain=mydomain.ca"
|
||||
- "--docker.watch"
|
||||
volumes:
|
||||
@@ -211,11 +211,11 @@ services:
|
||||
- "--acme.storage=traefik/acme/account"
|
||||
- "--acme.entryPoint=https"
|
||||
- "--acme.httpChallenge.entryPoint=http"
|
||||
- "--acme.OnHostRule=true"
|
||||
- "--acme.onHostRule=true"
|
||||
- "--acme.onDemand=false"
|
||||
- "--acme.email=foobar@example.com"
|
||||
- "--docker"
|
||||
- "--docker.swarmmode"
|
||||
- "--docker.swarmMode"
|
||||
- "--docker.domain=example.com"
|
||||
- "--docker.watch"
|
||||
- "--consul"
|
||||
|
@@ -97,13 +97,13 @@ defaultEntryPoints = ["https","http"]
|
||||
endpoint = "unix:///var/run/docker.sock"
|
||||
domain = "my-awesome-app.org"
|
||||
watch = true
|
||||
exposedbydefault = false
|
||||
exposedByDefault = false
|
||||
|
||||
[acme]
|
||||
email = "your-email-here@my-awesome-app.org"
|
||||
storage = "acme.json"
|
||||
entryPoint = "https"
|
||||
OnHostRule = true
|
||||
onHostRule = true
|
||||
[acme.httpChallenge]
|
||||
entryPoint = "http"
|
||||
```
|
||||
@@ -113,7 +113,7 @@ This is the minimum configuration required to do the following:
|
||||
- Log `ERROR`-level messages (or more severe) to the console, but silence `DEBUG`-level messages
|
||||
- Check for new versions of Træfik periodically
|
||||
- Create two entry points, namely an `HTTP` endpoint on port `80`, and an `HTTPS` endpoint on port `443` where all incoming traffic on port `80` will immediately get redirected to `HTTPS`.
|
||||
- Enable the Docker configuration backend and listen for container events on the Docker unix socket we've mounted earlier. However, **new containers will not be exposed by Træfik by default, we'll get into this in a bit!**
|
||||
- Enable the Docker provider and listen for container events on the Docker unix socket we've mounted earlier. However, **new containers will not be exposed by Træfik by default, we'll get into this in a bit!**
|
||||
- Enable automatic request and configuration of SSL certificates using Let's Encrypt.
|
||||
These certificates will be stored in the `acme.json` file, which you can back-up yourself and store off-premises.
|
||||
|
||||
@@ -123,7 +123,7 @@ Alright, let's boot the container. From the `/opt/traefik` directory, run `docke
|
||||
|
||||
Now that we've fully configured and started Træfik, it's time to get our applications running!
|
||||
|
||||
Let's take a simple example of a micro-service project consisting of various services, where some will be exposed to the outside world and some will not.
|
||||
Let's take a simple example of a micro-service project consisting of various services, where some will be exposed to the outside world and some will not.
|
||||
|
||||
The `docker-compose.yml` of our project looks like this:
|
||||
|
||||
@@ -145,12 +145,11 @@ services:
|
||||
expose:
|
||||
- "9000"
|
||||
labels:
|
||||
- "traefik.backend=my-awesome-app-app"
|
||||
- "traefik.docker.network=web"
|
||||
- "traefik.frontend.rule=Host:app.my-awesome-app.org"
|
||||
- "traefik.enable=true"
|
||||
- "traefik.port=9000"
|
||||
- "traefik.default.protocol=http"
|
||||
- "traefik.basic.frontend.rule=Host:app.my-awesome-app.org"
|
||||
- "traefik.basic.port=9000"
|
||||
- "traefik.basic.protocol=http"
|
||||
- "traefik.admin.frontend.rule=Host:admin-app.my-awesome-app.org"
|
||||
- "traefik.admin.protocol=https"
|
||||
- "traefik.admin.port=9443"
|
||||
@@ -204,12 +203,11 @@ Thanks to Docker labels, we can tell Træfik how to create its internal routing
|
||||
Let's take a look at the labels themselves for the `app` service, which is a HTTP webservice listing on port 9000:
|
||||
|
||||
```yaml
|
||||
- "traefik.backend=my-awesome-app-app"
|
||||
- "traefik.docker.network=web"
|
||||
- "traefik.frontend.rule=Host:app.my-awesome-app.org"
|
||||
- "traefik.enable=true"
|
||||
- "traefik.port=9000"
|
||||
- "traefik.default.protocol=http"
|
||||
- "traefik.basic.frontend.rule=Host:app.my-awesome-app.org"
|
||||
- "traefik.basic.port=9000"
|
||||
- "traefik.basic.protocol=http"
|
||||
- "traefik.admin.frontend.rule=Host:admin-app.my-awesome-app.org"
|
||||
- "traefik.admin.protocol=https"
|
||||
- "traefik.admin.port=9443"
|
||||
@@ -221,11 +219,11 @@ We use both `container labels` and `service labels`.
|
||||
|
||||
First, we specify the `backend` name which corresponds to the actual service we're routing **to**.
|
||||
|
||||
We also tell Træfik to use the `web` network to route HTTP traffic to this container.
|
||||
We also tell Træfik to use the `web` network to route HTTP traffic to this container.
|
||||
With the `traefik.enable` label, we tell Træfik to include this container in its internal configuration.
|
||||
|
||||
With the `frontend.rule` label, we tell Træfik that we want to route to this container if the incoming HTTP request contains the `Host` `app.my-awesome-app.org`.
|
||||
Essentially, this is the actual rule used for Layer-7 load balancing.
|
||||
Essentially, this is the actual rule used for Layer-7 load balancing.
|
||||
|
||||
Finally but not unimportantly, we tell Træfik to route **to** port `9000`, since that is the actual TCP/IP port the container actually listens on.
|
||||
|
||||
@@ -236,11 +234,11 @@ Finally but not unimportantly, we tell Træfik to route **to** port `9000`, sinc
|
||||
When both `container labels` and `service labels` are defined, `container labels` are just used as default values for missing `service labels` but no frontend/backend are going to be defined only with these labels.
|
||||
Obviously, labels `traefik.frontend.rule` and `traefik.port` described above, will only be used to complete information set in `service labels` during the container frontends/bakends creation.
|
||||
|
||||
In the example, two service names are defined : `default` and `admin`.
|
||||
In the example, two service names are defined : `basic` and `admin`.
|
||||
They allow creating two frontends and two backends.
|
||||
|
||||
- `default` has only one `service label` : `traefik.default.protocol`.
|
||||
Træfik will use values set in `traefik.frontend.rule` and `traefik.port` to create the `default` frontend and backend.
|
||||
- `basic` has only one `service label` : `traefik.basic.protocol`.
|
||||
Træfik will use values set in `traefik.frontend.rule` and `traefik.port` to create the `basic` frontend and backend.
|
||||
The frontend listens to incoming HTTP requests which contain the `Host` `app.my-awesome-app.org` and redirect them in `HTTP` to the port `9000` of the backend.
|
||||
- `admin` has all the `services labels` needed to create the `admin` frontend and backend (`traefik.admin.frontend.rule`, `traefik.admin.protocol`, `traefik.admin.port`).
|
||||
Træfik will create a frontend to listen to incoming HTTP requests which contain the `Host` `admin-app.my-awesome-app.org` and redirect them in `HTTPS` to the port `9443` of the backend.
|
||||
@@ -250,7 +248,7 @@ Træfik will create a frontend to listen to incoming HTTP requests which contain
|
||||
- Always specify the correct port where the container expects HTTP traffic using `traefik.port` label.
|
||||
If a container exposes multiple ports, Træfik may forward traffic to the wrong port.
|
||||
Even if a container only exposes one port, you should always write configuration defensively and explicitly.
|
||||
- Should you choose to enable the `exposedbydefault` flag in the `traefik.toml` configuration, be aware that all containers that are placed in the same network as Træfik will automatically be reachable from the outside world, for everyone and everyone to see.
|
||||
- Should you choose to enable the `exposedByDefault` flag in the `traefik.toml` configuration, be aware that all containers that are placed in the same network as Træfik will automatically be reachable from the outside world, for everyone and everyone to see.
|
||||
Usually, this is a bad idea.
|
||||
- With the `traefik.frontend.auth.basic` label, it's possible for Træfik to provide a HTTP basic-auth challenge for the endpoints you provide the label for.
|
||||
- Træfik has built-in support to automatically export [Prometheus](https://prometheus.io) metrics
|
||||
|
@@ -55,10 +55,6 @@ defaultEntryPoints = ["http", "https"]
|
||||
|
||||
## Let's Encrypt support
|
||||
|
||||
!!! note
|
||||
Even if `TLS-SNI-01` challenge is [disabled](https://community.letsencrypt.org/t/2018-01-11-update-regarding-acme-tls-sni-and-shared-hosting-infrastructure/50188), for the moment, it stays the _by default_ ACME Challenge in Træfik but all the examples use the `HTTP-01` challenge (except DNS challenge examples).
|
||||
If `TLS-SNI-01` challenge is not re-enabled in the future, it we will be removed from Træfik.
|
||||
|
||||
### Basic example with HTTP challenge
|
||||
|
||||
```toml
|
||||
@@ -72,7 +68,7 @@ defaultEntryPoints = ["http", "https"]
|
||||
[acme]
|
||||
email = "test@traefik.io"
|
||||
storage = "acme.json"
|
||||
caServer = "http://172.18.0.1:4000/directory"
|
||||
caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
entryPoint = "https"
|
||||
[acme.httpChallenge]
|
||||
entryPoint = "http"
|
||||
@@ -93,7 +89,7 @@ This configuration allows generating Let's Encrypt certificates (thanks to `HTTP
|
||||
|
||||
Træfik generates these certificates when it starts and it needs to be restart if new domains are added.
|
||||
|
||||
### OnHostRule option (with HTTP challenge)
|
||||
### onHostRule option (with HTTP challenge)
|
||||
|
||||
```toml
|
||||
[entryPoints]
|
||||
@@ -107,7 +103,7 @@ Træfik generates these certificates when it starts and it needs to be restart i
|
||||
email = "test@traefik.io"
|
||||
storage = "acme.json"
|
||||
onHostRule = true
|
||||
caServer = "http://172.18.0.1:4000/directory"
|
||||
caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
entryPoint = "https"
|
||||
[acme.httpChallenge]
|
||||
entryPoint = "http"
|
||||
@@ -144,7 +140,7 @@ If a backend is added with a `onHost` rule, Træfik will automatically generate
|
||||
email = "test@traefik.io"
|
||||
storage = "acme.json"
|
||||
onDemand = true
|
||||
caServer = "http://172.18.0.1:4000/directory"
|
||||
caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
entryPoint = "https"
|
||||
[acme.httpChallenge]
|
||||
entryPoint = "http"
|
||||
@@ -171,7 +167,7 @@ This configuration allows generating a Let's Encrypt certificate (thanks to `HTT
|
||||
[acme]
|
||||
email = "test@traefik.io"
|
||||
storage = "acme.json"
|
||||
caServer = "http://172.18.0.1:4000/directory"
|
||||
caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
entryPoint = "https"
|
||||
[acme.dnsChallenge]
|
||||
provider = "digitalocean" # DNS Provider name (cloudflare, OVH, gandi...)
|
||||
@@ -190,11 +186,46 @@ entryPoint = "https"
|
||||
```
|
||||
|
||||
DNS challenge needs environment variables to be executed.
|
||||
These variables have to be set on the machine/container which host Træfik.
|
||||
These variables have to be set on the machine/container that host Træfik.
|
||||
|
||||
These variables are described [in this section](/configuration/acme/#provider).
|
||||
|
||||
### OnHostRule option and provided certificates (with HTTP challenge)
|
||||
### DNS challenge with wildcard domains
|
||||
|
||||
```toml
|
||||
[entryPoints]
|
||||
[entryPoints.https]
|
||||
address = ":443"
|
||||
[entryPoints.https.tls]
|
||||
|
||||
[acme]
|
||||
email = "test@traefik.io"
|
||||
storage = "acme.json"
|
||||
caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
entryPoint = "https"
|
||||
[acme.dnsChallenge]
|
||||
provider = "digitalocean" # DNS Provider name (cloudflare, OVH, gandi...)
|
||||
delayBeforeCheck = 0
|
||||
|
||||
[[acme.domains]]
|
||||
main = "*.local1.com"
|
||||
[[acme.domains]]
|
||||
main = "local2.com"
|
||||
sans = ["test1.local2.com", "test2x.local2.com"]
|
||||
[[acme.domains]]
|
||||
main = "*.local3.com"
|
||||
[[acme.domains]]
|
||||
main = "*.local4.com"
|
||||
```
|
||||
|
||||
DNS challenge needs environment variables to be executed.
|
||||
These variables have to be set on the machine/container that host Træfik.
|
||||
|
||||
These variables are described [in this section](/configuration/acme/#provider).
|
||||
|
||||
More information about wildcard certificates are available [in this section](/configuration/acme/#wildcard-domain).
|
||||
|
||||
### onHostRule option and provided certificates (with HTTP challenge)
|
||||
|
||||
```toml
|
||||
[entryPoints]
|
||||
@@ -327,7 +358,7 @@ defaultEntryPoints = ["http"]
|
||||
users = ["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"]
|
||||
```
|
||||
|
||||
## Override the Traefik HTTP server IdleTimeout and/or throttle configurations from re-loading too quickly
|
||||
## Override the Traefik HTTP server idleTimeout and/or throttle configurations from re-loading too quickly
|
||||
|
||||
```toml
|
||||
providersThrottleDuration = "5s"
|
||||
|
@@ -45,7 +45,7 @@ At last, we configure our Træfik instance to use both self-signed certificates.
|
||||
defaultEntryPoints = ["https"]
|
||||
|
||||
# For secure connection on backend.local
|
||||
RootCAs = [ "./backend.cert" ]
|
||||
rootCAs = [ "./backend.cert" ]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.https]
|
||||
@@ -76,7 +76,7 @@ RootCAs = [ "./backend.cert" ]
|
||||
```
|
||||
|
||||
!!! warning
|
||||
With some backends, the server URLs use the IP, so you may need to configure `InsecureSkipVerify` instead of the `RootCAS` to activate HTTPS without hostname verification.
|
||||
With some backends, the server URLs use the IP, so you may need to configure `insecureSkipVerify` instead of the `rootCAS` to activate HTTPS without hostname verification.
|
||||
|
||||
## Conclusion
|
||||
|
||||
|
@@ -81,9 +81,11 @@ For namespaced restrictions, one RoleBinding is required per watched namespace a
|
||||
It is possible to use Træfik with a [Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) or a [DaemonSet](https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/) object,
|
||||
whereas both options have their own pros and cons:
|
||||
|
||||
- The scalability is much better when using a Deployment, because you will have a Single-Pod-per-Node model when using the DeaemonSet.
|
||||
- It is possible to exclusively run a Service on a dedicated set of machines using taints and tolerations with a DaemonSet.
|
||||
- On the other hand the DaemonSet allows you to access any Node directly on Port 80 and 443, where you have to setup a [Service](https://kubernetes.io/docs/concepts/services-networking/service/) object with a Deployment.
|
||||
- The scalability can be much better when using a Deployment, because you will have a Single-Pod-per-Node model when using a DaemonSet, whereas you may need less replicas based on your environment when using a Deployment.
|
||||
- DaemonSets automatically scale to new nodes, when the nodes join the cluster, whereas Deployment pods are only scheduled on new nodes if required.
|
||||
- DaemonSets ensure that only one replica of pods run on any single node. Deployments require affinity settings if you want to ensure that two pods don't end up on the same node.
|
||||
- DaemonSets can be run with the `NET_BIND_SERVICE` capability, which will allow it to bind to port 80/443/etc on each host. This will allow bypassing the kube-proxy, and reduce traffic hops. Note that this is against the Kubernetes Best Practices [Guidelines](https://kubernetes.io/docs/concepts/configuration/overview/#services), and raises the potential for scheduling/scaling issues. Despite potential issues, this remains the choice for most ingress controllers.
|
||||
- If you are unsure which to choose, start with the Daemonset.
|
||||
|
||||
The Deployment objects looks like this:
|
||||
|
||||
@@ -118,9 +120,15 @@ spec:
|
||||
containers:
|
||||
- image: traefik
|
||||
name: traefik-ingress-lb
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
- name: admin
|
||||
containerPort: 8080
|
||||
args:
|
||||
- --api
|
||||
- --kubernetes
|
||||
- --logLevel=INFO
|
||||
---
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
@@ -171,7 +179,6 @@ spec:
|
||||
spec:
|
||||
serviceAccountName: traefik-ingress-controller
|
||||
terminationGracePeriodSeconds: 60
|
||||
hostNetwork: true
|
||||
containers:
|
||||
- image: traefik
|
||||
name: traefik-ingress-lb
|
||||
@@ -182,7 +189,11 @@ spec:
|
||||
- name: admin
|
||||
containerPort: 8080
|
||||
securityContext:
|
||||
privileged: true
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
add:
|
||||
- NET_BIND_SERVICE
|
||||
args:
|
||||
- --api
|
||||
- --kubernetes
|
||||
@@ -203,11 +214,13 @@ spec:
|
||||
- protocol: TCP
|
||||
port: 8080
|
||||
name: admin
|
||||
type: NodePort
|
||||
```
|
||||
|
||||
[examples/k8s/traefik-ds.yaml](https://github.com/containous/traefik/tree/master/examples/k8s/traefik-ds.yaml)
|
||||
|
||||
!!! note
|
||||
This will create a Daemonset that uses privileged ports 80/8080 on the host. This may not work on all providers, but illustrates the static (non-NodePort) hostPort binding. The `traefik-ingress-service` can still be used inside the cluster to access the DaemonSet pods.
|
||||
|
||||
To deploy Træfik to your cluster start by submitting one of the YAML files to the cluster with `kubectl`:
|
||||
|
||||
```shell
|
||||
@@ -244,7 +257,7 @@ traefik-ingress-controller-678226159-eqseo 1/1 Running 0 7m
|
||||
```
|
||||
|
||||
You should see that after submitting the Deployment or DaemonSet to Kubernetes it has launched a Pod, and it is now running.
|
||||
_It might take a few moments for kubernetes to pull the Træfik image and start the container._
|
||||
_It might take a few moments for Kubernetes to pull the Træfik image and start the container._
|
||||
|
||||
!!! note
|
||||
You could also check the deployment with the Kubernetes dashboard, run
|
||||
@@ -279,7 +292,7 @@ All further examples below assume a DaemonSet installation. Deployment users wil
|
||||
## Deploy Træfik using Helm Chart
|
||||
|
||||
!!! note
|
||||
The Helm Chart is maintained by the community, not the Traefik project maintainers.
|
||||
The Helm Chart is maintained by the community, not the Træfik project maintainers.
|
||||
|
||||
Instead of installing Træfik via Kubernetes object directly, you can also use the Træfik Helm chart.
|
||||
|
||||
@@ -288,7 +301,21 @@ Install the Træfik chart by:
|
||||
```shell
|
||||
helm install stable/traefik
|
||||
```
|
||||
Install the Træfik chart using a values.yaml file.
|
||||
|
||||
```shell
|
||||
helm install --values values.yaml stable/traefik
|
||||
```
|
||||
|
||||
```yaml
|
||||
dashboard:
|
||||
enabled: true
|
||||
domain: traefik-ui.minikube
|
||||
kubernetes:
|
||||
namespaces:
|
||||
- default
|
||||
- kube-system
|
||||
```
|
||||
For more information, check out [the documentation](https://github.com/kubernetes/charts/tree/master/stable/traefik).
|
||||
|
||||
## Submitting an Ingress to the Cluster
|
||||
@@ -342,13 +369,63 @@ echo "$(minikube ip) traefik-ui.minikube" | sudo tee -a /etc/hosts
|
||||
|
||||
We should now be able to visit [traefik-ui.minikube](http://traefik-ui.minikube) in the browser and view the Træfik web UI.
|
||||
|
||||
### Add a TLS Certificate to the Ingress
|
||||
|
||||
!!! note
|
||||
For this example to work you need a TLS entrypoint. You don't have to provide a TLS certificate at this point.
|
||||
For more details see [here](/configuration/entrypoints/).
|
||||
|
||||
To setup an HTTPS-protected ingress, you can leverage the TLS feature of the ingress resource.
|
||||
|
||||
```yaml
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: traefik-web-ui
|
||||
namespace: kube-system
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: traefik
|
||||
spec:
|
||||
rules:
|
||||
- host: traefik-ui.minikube
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
serviceName: traefik-web-ui
|
||||
servicePort: 80
|
||||
tls:
|
||||
- secretName: traefik-ui-tls-cert
|
||||
```
|
||||
|
||||
In addition to the modified ingress you need to provide the TLS certificate via a Kubernetes secret in the same namespace as the ingress.
|
||||
The following two commands will generate a new certificate and create a secret containing the key and cert files.
|
||||
|
||||
```shell
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=traefik-ui.minikube"
|
||||
kubectl -n kube-system create secret tls traefik-ui-tls-cert --key=tls.key --cert=tls.crt
|
||||
```
|
||||
|
||||
If there are any errors while loading the TLS section of an ingress, the whole ingress will be skipped.
|
||||
|
||||
!!! note
|
||||
The secret must have two entries named `tls.key`and `tls.crt`.
|
||||
See the [Kubernetes documentation](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) for more details.
|
||||
|
||||
!!! note
|
||||
The TLS certificates will be added to all entrypoints defined by the ingress annotation `traefik.frontend.entryPoints`.
|
||||
If no such annotation is provided, the TLS certificates will be added to all TLS-enabled `defaultEntryPoints`.
|
||||
|
||||
!!! note
|
||||
The field `hosts` in the TLS configuration is ignored. Instead, the domains provided by the certificate are used for this purpose.
|
||||
It is recommended to not use wildcard certificates as they will match globally.
|
||||
|
||||
## Basic Authentication
|
||||
|
||||
It's possible to protect access to Traefik through basic authentication. (See the [Kubernetes Ingress](/configuration/backends/kubernetes) configuration page for syntactical details and restrictions.)
|
||||
It's possible to protect access to Træfik through basic authentication. (See the [Kubernetes Ingress](/configuration/backends/kubernetes) configuration page for syntactical details and restrictions.)
|
||||
|
||||
### Creating the Secret
|
||||
|
||||
A. Use `htpasswd` to create a file containing the username and the base64-encoded password:
|
||||
A. Use `htpasswd` to create a file containing the username and the MD5-encoded password:
|
||||
|
||||
```shell
|
||||
htpasswd -c ./auth myusername
|
||||
@@ -781,13 +858,21 @@ Sometimes Træfik runs along other Ingress controller implementations. One such
|
||||
|
||||
The `kubernetes.io/ingress.class` annotation can be attached to any Ingress object in order to control whether Træfik should handle it.
|
||||
|
||||
If the annotation is missing, contains an empty value, or the value `traefik`, then the Træfik controller will take responsibility and process the associated Ingress object. If the annotation contains any other value (usually the name of a different Ingress controller), Træfik will ignore the object.
|
||||
If the annotation is missing, contains an empty value, or the value `traefik`, then the Træfik controller will take responsibility and process the associated Ingress object.
|
||||
If the annotation contains any other value (usually the name of a different Ingress controller), Træfik will ignore the object.
|
||||
|
||||
It is also possible to set the `ingressClass` option in Træfik to a particular value.
|
||||
If that's the case and the value contains a `traefik` prefix, then only those Ingress objects matching the same value will be processed.
|
||||
For instance, setting the option to `traefik-internal` causes Træfik to process Ingress objects with the same `kubernetes.io/ingress.class` annotation value, ignoring all other objects (including those with a `traefik` value, empty value, and missing annotation).
|
||||
|
||||
### Between multiple Træfik Deployments
|
||||
|
||||
Sometimes multiple Træfik Deployments are supposed to run concurrently. For instance, it is conceivable to have one Deployment deal with internal and another one with external traffic.
|
||||
Sometimes multiple Træfik Deployments are supposed to run concurrently.
|
||||
For instance, it is conceivable to have one Deployment deal with internal and another one with external traffic.
|
||||
|
||||
For such cases, it is advisable to classify Ingress objects through a label and configure the `labelSelector` option per each Træfik Deployment accordingly. To stick with the internal/external example above, all Ingress objects meant for internal traffic could receive a `traffic-type: internal` label while objects designated for external traffic receive a `traffic-type: external` label. The label selectors on the Træfik Deployments would then be `traffic-type=internal` and `traffic-type=external`, respectively.
|
||||
For such cases, it is advisable to classify Ingress objects through a label and configure the `labelSelector` option per each Træfik Deployment accordingly.
|
||||
To stick with the internal/external example above, all Ingress objects meant for internal traffic could receive a `traffic-type: internal` label while objects designated for external traffic receive a `traffic-type: external` label.
|
||||
The label selectors on the Træfik Deployments would then be `traffic-type=internal` and `traffic-type=external`, respectively.
|
||||
|
||||
## Production advice
|
||||
|
||||
@@ -797,7 +882,7 @@ The examples shown deliberately do not specify any [resource limitations](https:
|
||||
|
||||
In a production environment, however, it is important to set proper bounds, especially with regards to CPU:
|
||||
|
||||
- too strict and Traefik will be throttled while serving requests (as Kubernetes imposes hard quotas)
|
||||
- too loose and Traefik may waste resources not available for other containers
|
||||
- too strict and Træfik will be throttled while serving requests (as Kubernetes imposes hard quotas)
|
||||
- too loose and Træfik may waste resources not available for other containers
|
||||
|
||||
When in doubt, you should measure your resource needs, and adjust requests and limits accordingly.
|
||||
|
@@ -76,7 +76,7 @@ defaultEntryPoints = ["http", "https"]
|
||||
address = ":80"
|
||||
[entryPoints.https]
|
||||
address = ":443"
|
||||
|
||||
|
||||
[entryPoints.https.tls]
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
certFile = "integration/fixtures/https/snitest.com.cert"
|
||||
@@ -85,9 +85,9 @@ defaultEntryPoints = ["http", "https"]
|
||||
certFile = """-----BEGIN CERTIFICATE-----
|
||||
<cert file content>
|
||||
-----END CERTIFICATE-----"""
|
||||
keyFile = """-----BEGIN CERTIFICATE-----
|
||||
keyFile = """-----BEGIN PRIVATE KEY-----
|
||||
<key file content>
|
||||
-----END CERTIFICATE-----"""
|
||||
-----END PRIVATE KEY-----"""
|
||||
[entryPoints.other-https]
|
||||
address = ":4443"
|
||||
[entryPoints.other-https.tls]
|
||||
@@ -164,7 +164,7 @@ If a Consul ACL is used to restrict Træfik read/write access, one of the follow
|
||||
key "traefik" {
|
||||
policy = "write"
|
||||
},
|
||||
|
||||
|
||||
session "" {
|
||||
policy = "write"
|
||||
}
|
||||
@@ -266,6 +266,10 @@ Here is the toml configuration we would like to store in the store :
|
||||
backend = "backend1"
|
||||
passHostHeader = true
|
||||
priority = 10
|
||||
basicAuth = [
|
||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
]
|
||||
entrypoints = ["https"] # overrides defaultEntryPoints
|
||||
[frontends.frontend2.routes.test_1]
|
||||
rule = "Host:{subdomain:[a-z]+}.localhost"
|
||||
@@ -275,12 +279,12 @@ Here is the toml configuration we would like to store in the store :
|
||||
rule = "Path:/test"
|
||||
|
||||
[[tls]]
|
||||
entryPoints = ["https"]
|
||||
[tls.certificate]
|
||||
certFile = "path/to/your.cert"
|
||||
keyFile = "path/to/your.key"
|
||||
|
||||
[[tls]]
|
||||
entryPoints = ["https","other-https"]
|
||||
entryPoints = ["https","other-https"]
|
||||
[tls.certificate]
|
||||
certFile = """-----BEGIN CERTIFICATE-----
|
||||
<cert file content>
|
||||
@@ -325,29 +329,33 @@ And there, the same dynamic configuration in a KV Store (using `prefix = "traefi
|
||||
|
||||
- frontend 2
|
||||
|
||||
| Key | Value |
|
||||
|----------------------------------------------------|--------------------|
|
||||
| `/traefik/frontends/frontend2/backend` | `backend1` |
|
||||
| `/traefik/frontends/frontend2/passhostheader` | `true` |
|
||||
| `/traefik/frontends/frontend2/priority` | `10` |
|
||||
| `/traefik/frontends/frontend2/entrypoints` | `http,https` |
|
||||
| `/traefik/frontends/frontend2/routes/test_2/rule` | `PathPrefix:/test` |
|
||||
| Key | Value |
|
||||
|----------------------------------------------------|-----------------------------------------------|
|
||||
| `/traefik/frontends/frontend2/backend` | `backend1` |
|
||||
| `/traefik/frontends/frontend2/passhostheader` | `true` |
|
||||
| `/traefik/frontends/frontend2/priority` | `10` |
|
||||
| `/traefik/frontends/frontend2/basicauth/0` | `test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/` |
|
||||
| `/traefik/frontends/frontend2/basicauth/1` | `test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0` |
|
||||
| `/traefik/frontends/frontend2/entrypoints` | `http,https` |
|
||||
| `/traefik/frontends/frontend2/routes/test_2/rule` | `PathPrefix:/test` |
|
||||
|
||||
- certificate 1
|
||||
|
||||
| Key | Value |
|
||||
|---------------------------------------|--------------------|
|
||||
| `/traefik/tls/1/entrypoints` | `https` |
|
||||
| `/traefik/tls/1/certificate/certfile` | `path/to/your.cert`|
|
||||
| `/traefik/tls/1/certificate/keyfile` | `path/to/your.key` |
|
||||
|
||||
!!! note
|
||||
As `/traefik/tls/1/entrypoints` is not defined, the certificate will be attached to all `defaulEntryPoints` with a TLS configuration (in the example, the entryPoint `https`)
|
||||
|
||||
- certificate 2
|
||||
|
||||
| Key | Value |
|
||||
|---------------------------------------|-----------------------|
|
||||
| `/traefik/tls/2/entrypoints` | `https,other-https` |
|
||||
| `/traefik/tls/2/certificate/certfile` | `<cert file content>` |
|
||||
| `/traefik/tls/2/certificate/certfile` | `<key file content>` |
|
||||
| `/traefik/tls/2/certificate/keyfile` | `<key file content>` |
|
||||
|
||||
### Atomic configuration changes
|
||||
|
||||
@@ -420,7 +428,7 @@ Træfik will not start but the [static configuration](/basics/#static-trfik-conf
|
||||
|
||||
If you configured ACME (Let's Encrypt), your registration account and your certificates will also be uploaded.
|
||||
|
||||
If you configured a file backend `[file]`, all your dynamic configuration (backends, frontends...) will be uploaded to the Key-value store.
|
||||
If you configured a file provider `[file]`, all your dynamic configuration (backends, frontends...) will be uploaded to the Key-value store.
|
||||
|
||||
To upload your ACME certificates to the KV store, get your Traefik TOML file and add the new `storage` option in the `acme` section:
|
||||
|
||||
|
@@ -130,7 +130,6 @@ As such, there is no way to handle this situation deterministically.
|
||||
Finally, Marathon health checks are not mandatory (the default is to use the task state as reported by Mesos), so requiring them for Traefik would raise the entry barrier for Marathon users.
|
||||
|
||||
Traefik used to use the health check results as a strict requirement but moved away from it as [users reported the dramatic consequences](https://github.com/containous/traefik/issues/653).
|
||||
If health check results are known to exist, however, they will be used to signal task availability.
|
||||
|
||||
#### Draining
|
||||
|
||||
|
@@ -87,7 +87,7 @@ docker-machine ssh manager "docker service create \
|
||||
--network traefik-net \
|
||||
traefik \
|
||||
--docker \
|
||||
--docker.swarmmode \
|
||||
--docker.swarmMode \
|
||||
--docker.domain=traefik \
|
||||
--docker.watch \
|
||||
--api"
|
||||
@@ -101,8 +101,8 @@ Let's explain this command:
|
||||
| `--constraint=node.role==manager` | we ask docker to schedule Træfik on a manager node. |
|
||||
| `--mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock` | we bind mount the docker socket where Træfik is scheduled to be able to speak to the daemon. |
|
||||
| `--network traefik-net` | we attach the Træfik service (and thus the underlying container) to the `traefik-net` network. |
|
||||
| `--docker` | enable docker backend, and `--docker.swarmmode` to enable the swarm mode on Træfik. |
|
||||
| `--api | activate the webUI on port 8080 |
|
||||
| `--docker` | enable docker provider, and `--docker.swarmMode` to enable the swarm mode on Træfik. |
|
||||
| `--api` | activate the webUI on port 8080 |
|
||||
|
||||
|
||||
## Deploy your apps
|
||||
|
@@ -104,7 +104,7 @@ Let's explain this command:
|
||||
| `--net=my-net` | run the container on the network my-net |
|
||||
| `-v /var/lib/boot2docker/:/ssl` | mount the ssl keys generated by docker-machine |
|
||||
| `-c /dev/null` | empty config file |
|
||||
| `--docker` | enable docker backend |
|
||||
| `--docker` | enable docker provider |
|
||||
| `--docker.endpoint=tcp://172.18.0.1:2376` | connect to the swarm master using the docker_gwbridge network |
|
||||
| `--docker.tls` | enable TLS using the docker-machine keys |
|
||||
| `--api` | activate the webUI on port 8080 |
|
||||
|
@@ -29,10 +29,13 @@ entryPoint = "api"
|
||||
method = "drr"
|
||||
[backends.backend.servers.server1]
|
||||
url = "http://127.0.0.1:8081"
|
||||
weight = 1
|
||||
[backends.backend.servers.server2]
|
||||
url = "http://127.0.0.1:8082"
|
||||
weight = 1
|
||||
[backends.backend.servers.server3]
|
||||
url = "http://127.0.0.1:8083"
|
||||
weight = 1
|
||||
[frontends]
|
||||
[frontends.frontend]
|
||||
backend = "backend"
|
||||
|
@@ -27,18 +27,24 @@ entryPoint = "api"
|
||||
[backends.backend1]
|
||||
[backends.backend1.servers.server1]
|
||||
url = "http://127.0.0.1:8081"
|
||||
weight = 1
|
||||
|
||||
[backends.backend2]
|
||||
[backends.backend2.LoadBalancer]
|
||||
method = "drr"
|
||||
[backends.backend2.servers.server1]
|
||||
url = "http://127.0.0.1:8082"
|
||||
weight = 1
|
||||
[backends.backend2.servers.server2]
|
||||
url = "http://127.0.0.1:8083"
|
||||
weight = 1
|
||||
|
||||
[frontends]
|
||||
[frontends.frontend1]
|
||||
backend = "backend1"
|
||||
[frontends.frontend1.routes.test_1]
|
||||
rule = "Path: /test1"
|
||||
|
||||
[frontends.frontend2]
|
||||
backend = "backend2"
|
||||
passHostHeader = true
|
||||
|
@@ -11,7 +11,7 @@ The provided Boulder stack is based on the environment used during integration t
|
||||
|
||||
## Directory content
|
||||
|
||||
* **compose-acme.yml** : Docker-Compose file which contains the description of Traefik and all the boulder stack containers to get,
|
||||
* **docker-compose.yml** : Docker-Compose file which contains the description of Traefik and all the boulder stack containers to get,
|
||||
* **acme.toml** : Traefik configuration file used by the Traefik container described above,
|
||||
* **manage_acme_docker_environment.sh** Shell script which does all needed checks and manages the docker-compose environment.
|
||||
|
||||
@@ -25,6 +25,7 @@ To work fine, boulder needs a domain name, with a related IP and storage file. T
|
||||
|
||||
The script **manage_acme_docker_environment.sh** requires one argument. This argument can have 3 values :
|
||||
|
||||
* **--start** : Check environment and launch a new Docker environment.
|
||||
* **--start** : Launch a new Docker environment Boulder + Traefik.
|
||||
* **--stop** : Stop and delete the current Docker environment.
|
||||
* **--restart--** : Concatenate **--stop** and **--start** actions.
|
||||
* **--restart--** : Concatenate **--stop** and **--start** actions.
|
||||
* **--dev** : Launch a new Boulder Docker environment.
|
@@ -16,8 +16,8 @@ email = "test@traefik.io"
|
||||
storage = "/etc/traefik/conf/acme.json"
|
||||
entryPoint = "https"
|
||||
onDemand = false
|
||||
OnHostRule = true
|
||||
caServer = "http://traefik.localhost.com:4000/directory"
|
||||
onHostRule = true
|
||||
caServer = "http://traefik.boulder.com:4001/directory"
|
||||
[acme.httpChallenge]
|
||||
entryPoint="http"
|
||||
|
||||
@@ -27,6 +27,6 @@ caServer = "http://traefik.localhost.com:4000/directory"
|
||||
endpoint = "unix:///var/run/docker.sock"
|
||||
domain = "traefik.localhost.com"
|
||||
watch = true
|
||||
exposedbydefault = false
|
||||
exposedByDefault = false
|
||||
|
||||
|
||||
|
@@ -1,92 +0,0 @@
|
||||
version: "2"
|
||||
|
||||
# IP_HOST : Docker host IP (not 127.0.0.1)
|
||||
|
||||
services :
|
||||
boulder:
|
||||
image: containous/boulder:release
|
||||
environment:
|
||||
FAKE_DNS: $IP_HOST
|
||||
PKCS11_PROXY_SOCKET: tcp://boulder-hsm:5657
|
||||
extra_hosts:
|
||||
- le.wtf:127.0.0.1
|
||||
- boulder:127.0.0.1
|
||||
ports:
|
||||
- 4000:4000 # ACME
|
||||
- 4002:4002 # OCSP
|
||||
- 4003:4003 # OCSP
|
||||
- 4500:4500 # ct-test-srv
|
||||
- 8000:8000 # debug ports
|
||||
- 8001:8001
|
||||
- 8002:8002
|
||||
- 8003:8003
|
||||
- 8004:8004
|
||||
- 8055:8055 # dns-test-srv updates
|
||||
- 9380:9380 # mail-test-srv
|
||||
- 9381:9381 # mail-test-srv
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- bhsm
|
||||
- bmysql
|
||||
- brabbitmq
|
||||
volumes:
|
||||
- "./rate-limit-policies.yml:/go/src/github.com/letsencrypt/boulder/test/rate-limit-policies.yml:ro"
|
||||
|
||||
bhsm:
|
||||
image: letsencrypt/boulder-tools:2016-11-02
|
||||
hostname: boulder-hsm
|
||||
networks:
|
||||
default:
|
||||
aliases:
|
||||
- boulder-hsm
|
||||
environment:
|
||||
PKCS11_DAEMON_SOCKET: tcp://0.0.0.0:5657
|
||||
command: /usr/local/bin/pkcs11-daemon /usr/lib/softhsm/libsofthsm.so
|
||||
expose:
|
||||
- 5657
|
||||
bmysql:
|
||||
image: mariadb:10.1
|
||||
hostname: boulder-mysql
|
||||
networks:
|
||||
default:
|
||||
aliases:
|
||||
- boulder-mysql
|
||||
environment:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
|
||||
|
||||
brabbitmq:
|
||||
image: rabbitmq:3-alpine
|
||||
hostname: boulder-rabbitmq
|
||||
networks:
|
||||
default:
|
||||
aliases:
|
||||
- boulder-rabbitmq
|
||||
environment:
|
||||
RABBITMQ_NODE_IP_ADDRESS: "0.0.0.0"
|
||||
|
||||
traefik:
|
||||
build:
|
||||
context: ../..
|
||||
image: containous/traefik:latest
|
||||
command: --configFile=/etc/traefik/conf/acme.toml
|
||||
restart: unless-stopped
|
||||
extra_hosts:
|
||||
- traefik.localhost.com:$IP_HOST
|
||||
volumes:
|
||||
- "./acme.toml:/etc/traefik/conf/acme.toml:ro"
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
- "./acme.json:/etc/traefik/conf/acme.json:rw"
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "5001:443" # Needed for SNI challenge
|
||||
- "5002:80" # Needed for HTTP challenge
|
||||
expose:
|
||||
- "8080"
|
||||
labels:
|
||||
- "traefik.port=8080"
|
||||
- "traefik.backend=traefikception"
|
||||
- "traefik.frontend.rule=Host:traefik.localhost.com"
|
||||
- "traefik.enable=true"
|
||||
depends_on:
|
||||
- boulder
|
97
examples/acme/docker-compose.yml
Normal file
@@ -0,0 +1,97 @@
|
||||
version: "2"
|
||||
|
||||
services :
|
||||
|
||||
boulder:
|
||||
# To minimize fetching this should be the same version used below
|
||||
image: containous/boulder:containous-acmev2
|
||||
environment:
|
||||
FAKE_DNS: 172.17.0.1
|
||||
PKCS11_PROXY_SOCKET: tcp://boulder-hsm:5657
|
||||
restart: unless-stopped
|
||||
extra_hosts:
|
||||
- le.wtf:127.0.0.1
|
||||
- boulder:127.0.0.1
|
||||
ports:
|
||||
- 4000:4000 # ACME
|
||||
- 4001:4001 # ACMEv2
|
||||
- 4002:4002 # OCSP
|
||||
- 4003:4003 # OCSP
|
||||
- 4430:4430 # ACME via HTTPS
|
||||
- 4431:4431 # ACMEv2 via HTTPS
|
||||
- 4500:4500 # ct-test-srv
|
||||
- 6000:6000 # gsb-test-srv
|
||||
- 8000:8000 # debug ports
|
||||
- 8001:8001
|
||||
- 8002:8002
|
||||
- 8003:8003
|
||||
- 8004:8004
|
||||
- 8005:8005
|
||||
- 8006:8006
|
||||
- 8008:8008
|
||||
- 8009:8009
|
||||
- 8010:8010
|
||||
- 8055:8055 # dns-test-srv updates
|
||||
- 9380:9380 # mail-test-srv
|
||||
- 9381:9381 # mail-test-srv
|
||||
depends_on:
|
||||
- bhsm
|
||||
- bmysql
|
||||
networks:
|
||||
- default
|
||||
|
||||
bhsm:
|
||||
# To minimize fetching this should be the same version used above
|
||||
image: letsencrypt/boulder-tools:2018-03-07
|
||||
hostname: boulder-hsm
|
||||
environment:
|
||||
PKCS11_DAEMON_SOCKET: tcp://0.0.0.0:5657
|
||||
command: /usr/local/bin/pkcs11-daemon /usr/lib/softhsm/libsofthsm2.so
|
||||
expose:
|
||||
- 5657
|
||||
networks:
|
||||
default:
|
||||
aliases:
|
||||
- boulder-hsm
|
||||
|
||||
bmysql:
|
||||
image: mariadb:10.1
|
||||
hostname: boulder-mysql
|
||||
environment:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
|
||||
command: mysqld --bind-address=0.0.0.0
|
||||
logging:
|
||||
driver: none
|
||||
networks:
|
||||
default:
|
||||
aliases:
|
||||
- boulder-mysql
|
||||
|
||||
## TRAEFIK part ##
|
||||
|
||||
traefik:
|
||||
build:
|
||||
context: ../..
|
||||
image: containous/traefik:latest
|
||||
command: --configFile=/etc/traefik/conf/acme.toml
|
||||
restart: unless-stopped
|
||||
extra_hosts:
|
||||
- traefik.boulder.com:172.17.0.1
|
||||
volumes:
|
||||
- "./acme.toml:/etc/traefik/conf/acme.toml:ro"
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
- "./acme.json:/etc/traefik/conf/acme.json:rw"
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "5001:443" # Needed for SNI challenge
|
||||
- "5002:80" # Needed for HTTP challenge
|
||||
expose:
|
||||
- "8080"
|
||||
labels:
|
||||
- "traefik.port=8080"
|
||||
- "traefik.backend=traefikception"
|
||||
- "traefik.frontend.rule=Host:traefik.localhost.com"
|
||||
- "traefik.enable=true"
|
||||
depends_on:
|
||||
- boulder
|
@@ -3,7 +3,7 @@
|
||||
# Initialize variables
|
||||
readonly traefik_url="traefik.localhost.com"
|
||||
readonly basedir=$(dirname $0)
|
||||
readonly doc_file=$basedir"/compose-acme.yml"
|
||||
readonly doc_file=$basedir"/docker-compose.yml"
|
||||
|
||||
# Stop and remove Docker environment
|
||||
down_environment() {
|
||||
@@ -22,21 +22,6 @@ up_environment() {
|
||||
|
||||
# Init the environment : get IP address and create needed files
|
||||
init_environment() {
|
||||
for netw in $(ip addr show | grep -v "LOOPBACK" | grep -v docker | grep -oE "^[0-9]{1}: .*:" | cut -d ':' -f2); do
|
||||
ip_addr=$(ip addr show $netw | grep -E "inet " | grep -Eo "[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*" | head -n 1)
|
||||
[[ ! -z $ip_addr ]] && break
|
||||
done
|
||||
|
||||
[[ -z $ip_addr ]] && \
|
||||
echo "[ERROR] Impossible to find an IP address for the Docker host" && exit 31
|
||||
|
||||
# The $traefik_url entry must exist into /etc/hosts file
|
||||
# It has to refer to the $ip_addr IP address
|
||||
[[ $(cat /etc/hosts | grep $traefik_url | grep -vE "^#" | grep -oE "([0-9]+(\.)?){4}") != $ip_addr ]] && \
|
||||
echo "[ERROR] Domain ${traefik_url} has to refer to ${ip_addr} into /etc/hosts file." && exit 32
|
||||
# Export IP_HOST to use it in the DOcker COmpose file
|
||||
export IP_HOST=$ip_addr
|
||||
|
||||
echo "CREATE empty acme.json file"
|
||||
rm -f $basedir/acme.json && \
|
||||
touch $basedir/acme.json && \
|
||||
@@ -44,14 +29,14 @@ init_environment() {
|
||||
}
|
||||
|
||||
# Start all the environement
|
||||
start() {
|
||||
start_boulder() {
|
||||
init_environment
|
||||
echo "Start boulder environment"
|
||||
up_environment bmysql brabbitmq bhsm boulder
|
||||
up_environment bmysql bhsm boulder
|
||||
waiting_counter=12
|
||||
# Not start Traefik if boulder is not started
|
||||
echo "WAIT for boulder..."
|
||||
while [[ -z $(curl -s http://$traefik_url:4000/directory) ]]; do
|
||||
while [[ -z $(curl -s http://127.0.0.1:4000/directory) ]]; do
|
||||
sleep 5
|
||||
let waiting_counter-=1
|
||||
if [[ $waiting_counter -eq 0 ]]; then
|
||||
@@ -60,8 +45,6 @@ start() {
|
||||
exit 41
|
||||
fi
|
||||
done
|
||||
echo "START Traefik container"
|
||||
up_environment traefik
|
||||
}
|
||||
|
||||
# Script usage
|
||||
@@ -78,9 +61,14 @@ main() {
|
||||
[[ $# -ne 1 ]] && show_usage && exit 1
|
||||
|
||||
case $1 in
|
||||
"--dev")
|
||||
start_boulder
|
||||
;;
|
||||
"--start")
|
||||
# Start boulder environment
|
||||
start
|
||||
start_boulder
|
||||
echo "START Traefik container"
|
||||
up_environment traefik
|
||||
echo "ENVIRONMENT SUCCESSFULLY STARTED"
|
||||
;;
|
||||
"--stop")
|
||||
@@ -89,8 +77,10 @@ main() {
|
||||
;;
|
||||
"--restart")
|
||||
down_environment
|
||||
start
|
||||
echo "ENVIRONMENT SUCCESSFULLY STARTED"
|
||||
start_boulder
|
||||
echo "START Traefik container"
|
||||
up_environment traefik
|
||||
echo "ENVIRONMENT SUCCESSFULLY RESTARTED"
|
||||
;;
|
||||
*)
|
||||
show_usage && exit 2
|
||||
|
@@ -1,42 +0,0 @@
|
||||
totalCertificates:
|
||||
window: 1h
|
||||
threshold: 100000
|
||||
certificatesPerName:
|
||||
window: 1h
|
||||
threshold: 100000
|
||||
overrides:
|
||||
ratelimit.me: 1
|
||||
lim.it: 0
|
||||
# Hostnames used by the letsencrypt client integration test.
|
||||
le.wtf: 10000
|
||||
le1.wtf: 10000
|
||||
le2.wtf: 10000
|
||||
le3.wtf: 10000
|
||||
nginx.wtf: 10000
|
||||
good-caa-reserved.com: 10000
|
||||
bad-caa-reserved.com: 10000
|
||||
ecdsa.le.wtf: 10000
|
||||
must-staple.le.wtf: 10000
|
||||
registrationOverrides:
|
||||
101: 1000
|
||||
registrationsPerIP:
|
||||
window: 1h
|
||||
threshold: 100000
|
||||
overrides:
|
||||
127.0.0.1: 1000000
|
||||
pendingAuthorizationsPerAccount:
|
||||
window: 1h
|
||||
threshold: 100000
|
||||
certificatesPerFQDNSet:
|
||||
window: 1h
|
||||
threshold: 100000
|
||||
overrides:
|
||||
le.wtf: 10000
|
||||
le1.wtf: 10000
|
||||
le2.wtf: 10000
|
||||
le3.wtf: 10000
|
||||
le.wtf,le1.wtf: 10000
|
||||
good-caa-reserved.com: 10000
|
||||
nginx.wtf: 10000
|
||||
ecdsa.le.wtf: 10000
|
||||
must-staple.le.wtf: 10000
|
@@ -7,195 +7,195 @@ services:
|
||||
# CONSUL
|
||||
|
||||
consul:
|
||||
image: progrium/consul
|
||||
command: -server -bootstrap -log-level debug -ui-dir /ui
|
||||
ports:
|
||||
- "8400:8400"
|
||||
- "8500:8500"
|
||||
- "8600:53/udp"
|
||||
expose:
|
||||
- "8300"
|
||||
- "8301"
|
||||
- "8301/udp"
|
||||
- "8302"
|
||||
- "8302/udp"
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.2
|
||||
image: progrium/consul
|
||||
command: -server -bootstrap -log-level debug -ui-dir /ui
|
||||
ports:
|
||||
- "8400:8400"
|
||||
- "8500:8500"
|
||||
- "8600:53/udp"
|
||||
expose:
|
||||
- "8300"
|
||||
- "8301"
|
||||
- "8301/udp"
|
||||
- "8302"
|
||||
- "8302/udp"
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.2
|
||||
|
||||
# ETCD V3
|
||||
|
||||
etcd3:
|
||||
image: quay.io/coreos/etcd:v3.2.9
|
||||
command: /usr/local/bin/etcd --data-dir=/etcd-data --name node1 --initial-advertise-peer-urls http://10.0.1.12:2380 --listen-peer-urls http://10.0.1.12:2380 --advertise-client-urls http://10.0.1.12:2379,http://10.0.1.12:4001 --listen-client-urls http://10.0.1.12:2379,http://10.0.1.12:4001 --initial-cluster node1=http://10.0.1.12:2380 --debug
|
||||
ports:
|
||||
- "4001:4001"
|
||||
- "2380:2380"
|
||||
- "2379:2379"
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.12
|
||||
image: quay.io/coreos/etcd:v3.2.9
|
||||
command: /usr/local/bin/etcd --data-dir=/etcd-data --name node1 --initial-advertise-peer-urls http://10.0.1.12:2380 --listen-peer-urls http://10.0.1.12:2380 --advertise-client-urls http://10.0.1.12:2379,http://10.0.1.12:4001 --listen-client-urls http://10.0.1.12:2379,http://10.0.1.12:4001 --initial-cluster node1=http://10.0.1.12:2380 --debug
|
||||
ports:
|
||||
- "4001:4001"
|
||||
- "2380:2380"
|
||||
- "2379:2379"
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.12
|
||||
|
||||
etcdctl-ping:
|
||||
image: tenstartups/etcdctl
|
||||
command: --endpoints=[10.0.1.12:2379] get "traefik/acme/storage"
|
||||
environment:
|
||||
ETCDCTL_DIAL_: "TIMEOUT 10s"
|
||||
ETCDCTL_API : "3"
|
||||
networks:
|
||||
image: tenstartups/etcdctl
|
||||
command: --endpoints=[10.0.1.12:2379] get "traefik/acme/storage"
|
||||
environment:
|
||||
ETCDCTL_DIAL_: "TIMEOUT 10s"
|
||||
ETCDCTL_API : "3"
|
||||
networks:
|
||||
- net
|
||||
|
||||
## BOULDER part ##
|
||||
|
||||
boulder:
|
||||
image: containous/boulder:release
|
||||
environment:
|
||||
FAKE_DNS: 172.17.0.1
|
||||
PKCS11_PROXY_SOCKET: tcp://boulder-hsm:5657
|
||||
extra_hosts:
|
||||
- le.wtf:127.0.0.1
|
||||
- boulder:127.0.0.1
|
||||
ports:
|
||||
- 4000:4000 # ACME
|
||||
- 4002:4002 # OCSP
|
||||
- 4003:4003 # OCSP
|
||||
- 4500:4500 # ct-test-srv
|
||||
- 8000:8000 # debug ports
|
||||
- 8001:8001
|
||||
- 8002:8002
|
||||
- 8003:8003
|
||||
- 8004:8004
|
||||
- 8055:8055 # dns-test-srv updates
|
||||
- 9380:9380 # mail-test-srv
|
||||
- 9381:9381 # mail-test-srv
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- bhsm
|
||||
- bmysql
|
||||
- brabbitmq
|
||||
volumes:
|
||||
- "./rate-limit-policies.yml:/go/src/github.com/letsencrypt/boulder/test/rate-limit-policies.yml:ro"
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.3
|
||||
# To minimize fetching this should be the same version used below
|
||||
image: containous/boulder:containous-acmev2
|
||||
environment:
|
||||
FAKE_DNS: 172.17.0.1
|
||||
PKCS11_PROXY_SOCKET: tcp://boulder-hsm:5657
|
||||
restart: unless-stopped
|
||||
extra_hosts:
|
||||
- le.wtf:127.0.0.1
|
||||
- boulder:127.0.0.1
|
||||
ports:
|
||||
- 4000:4000 # ACME
|
||||
- 4001:4001 # ACMEv2
|
||||
- 4002:4002 # OCSP
|
||||
- 4003:4003 # OCSP
|
||||
- 4430:4430 # ACME via HTTPS
|
||||
- 4431:4431 # ACMEv2 via HTTPS
|
||||
- 4500:4500 # ct-test-srv
|
||||
- 6000:6000 # gsb-test-srv
|
||||
- 8000:8000 # debug ports
|
||||
- 8001:8001
|
||||
- 8002:8002
|
||||
- 8003:8003
|
||||
- 8004:8004
|
||||
- 8005:8005
|
||||
- 8006:8006
|
||||
- 8008:8008
|
||||
- 8009:8009
|
||||
- 8010:8010
|
||||
- 8055:8055 # dns-test-srv updates
|
||||
- 9380:9380 # mail-test-srv
|
||||
- 9381:9381 # mail-test-srv
|
||||
depends_on:
|
||||
- bhsm
|
||||
- bmysql
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.3
|
||||
|
||||
bhsm:
|
||||
image: letsencrypt/boulder-tools:2016-11-02
|
||||
hostname: boulder-hsm
|
||||
environment:
|
||||
PKCS11_DAEMON_SOCKET: tcp://0.0.0.0:5657
|
||||
command: /usr/local/bin/pkcs11-daemon /usr/lib/softhsm/libsofthsm.so
|
||||
expose:
|
||||
- 5657
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.4
|
||||
aliases:
|
||||
- boulder-hsm
|
||||
# To minimize fetching this should be the same version used above
|
||||
image: letsencrypt/boulder-tools:2018-03-07
|
||||
hostname: boulder-hsm
|
||||
environment:
|
||||
PKCS11_DAEMON_SOCKET: tcp://0.0.0.0:5657
|
||||
command: /usr/local/bin/pkcs11-daemon /usr/lib/softhsm/libsofthsm2.so
|
||||
expose:
|
||||
- 5657
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.4
|
||||
aliases:
|
||||
- boulder-hsm
|
||||
bmysql:
|
||||
image: mariadb:10.1
|
||||
hostname: boulder-mysql
|
||||
environment:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.5
|
||||
aliases:
|
||||
- boulder-mysql
|
||||
|
||||
brabbitmq:
|
||||
image: rabbitmq:3-alpine
|
||||
hostname: boulder-rabbitmq
|
||||
environment:
|
||||
RABBITMQ_NODE_IP_ADDRESS: "0.0.0.0"
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.6
|
||||
aliases:
|
||||
- boulder-rabbitmq
|
||||
image: mariadb:10.1
|
||||
hostname: boulder-mysql
|
||||
environment:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
|
||||
command: mysqld --bind-address=0.0.0.0
|
||||
logging:
|
||||
driver: none
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.5
|
||||
aliases:
|
||||
- boulder-mysql
|
||||
|
||||
## TRAEFIK part ##
|
||||
|
||||
traefik-storeconfig:
|
||||
build:
|
||||
context: ../..
|
||||
image: containous/traefik
|
||||
volumes:
|
||||
- "./traefik.toml:/traefik.toml:ro"
|
||||
command: storeconfig --debug
|
||||
networks:
|
||||
- net
|
||||
storeconfig:
|
||||
build:
|
||||
context: ../..
|
||||
image: containous/traefik
|
||||
volumes:
|
||||
- "./traefik.toml:/traefik.toml:ro"
|
||||
command: storeconfig --debug
|
||||
networks:
|
||||
- net
|
||||
|
||||
traefik01:
|
||||
build:
|
||||
context: ../..
|
||||
image: containous/traefik
|
||||
command: ${TRAEFIK_CMD}
|
||||
extra_hosts:
|
||||
- traefik.boulder.com:172.17.0.1
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
expose:
|
||||
- "443"
|
||||
- "5001"
|
||||
- "5002"
|
||||
ports:
|
||||
- "80:80"
|
||||
- "8080:8080"
|
||||
- "443:443"
|
||||
- "5001:443" # Needed for SNI challenge
|
||||
- "5002:80" # Needed for HTTP challenge
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.8
|
||||
build:
|
||||
context: ../..
|
||||
image: containous/traefik
|
||||
command: ${TRAEFIK_CMD}
|
||||
extra_hosts:
|
||||
- traefik.boulder.com:172.17.0.1
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
expose:
|
||||
- "443"
|
||||
- "5001"
|
||||
- "5002"
|
||||
ports:
|
||||
- "80:80"
|
||||
- "8080:8080"
|
||||
- "443:443"
|
||||
- "5001:443" # Needed for SNI challenge
|
||||
- "5002:80" # Needed for HTTP challenge
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.8
|
||||
|
||||
traefik02:
|
||||
build:
|
||||
context: ../..
|
||||
image: containous/traefik
|
||||
command: ${TRAEFIK_CMD}
|
||||
extra_hosts:
|
||||
- traefik.boulder.com:172.17.0.1
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
expose:
|
||||
- "443"
|
||||
- "5001"
|
||||
- "5002"
|
||||
ports:
|
||||
- "88:80"
|
||||
- "8888:8080"
|
||||
- "8443:443"
|
||||
depends_on:
|
||||
- traefik01
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.9
|
||||
build:
|
||||
context: ../..
|
||||
image: containous/traefik
|
||||
command: ${TRAEFIK_CMD}
|
||||
extra_hosts:
|
||||
- traefik.boulder.com:172.17.0.1
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
expose:
|
||||
- "443"
|
||||
- "5001"
|
||||
- "5002"
|
||||
ports:
|
||||
- "88:80"
|
||||
- "8888:8080"
|
||||
- "8443:443"
|
||||
depends_on:
|
||||
- traefik01
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.9
|
||||
|
||||
whoami01:
|
||||
image: emilevauge/whoami
|
||||
expose:
|
||||
- "80"
|
||||
labels:
|
||||
- "traefik.port=80"
|
||||
- "traefik.backend=wam01"
|
||||
- "traefik.frontend.rule=Host:who01.localhost.com"
|
||||
- "traefik.enable=true"
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.10
|
||||
image: emilevauge/whoami
|
||||
expose:
|
||||
- "80"
|
||||
labels:
|
||||
- "traefik.port=80"
|
||||
- "traefik.backend=wam01"
|
||||
- "traefik.frontend.rule=Host:who01.localhost.com"
|
||||
- "traefik.enable=true"
|
||||
networks:
|
||||
net:
|
||||
ipv4_address: 10.0.1.10
|
||||
|
||||
whoami02:
|
||||
image: emilevauge/whoami
|
||||
expose:
|
||||
- "80"
|
||||
labels:
|
||||
- "traefik.port=80"
|
||||
- "traefik.backend=wam02"
|
||||
- "traefik.frontend.rule=Host:who02.localhost.com"
|
||||
- "traefik.enable=true"
|
||||
networks:
|
||||
- net
|
||||
image: emilevauge/whoami
|
||||
expose:
|
||||
- "80"
|
||||
labels:
|
||||
- "traefik.port=80"
|
||||
- "traefik.backend=wam02"
|
||||
- "traefik.frontend.rule=Host:who02.localhost.com"
|
||||
- "traefik.enable=true"
|
||||
networks:
|
||||
- net
|
||||
|
||||
networks:
|
||||
net:
|
||||
|
@@ -74,10 +74,10 @@ start_storeconfig_consul() {
|
||||
endpoint = "10.0.1.2:8500"
|
||||
watch = true
|
||||
prefix = "traefik"' >> $basedir/traefik.toml
|
||||
up_environment traefik-storeconfig
|
||||
up_environment storeconfig
|
||||
rm -f $basedir/traefik.toml
|
||||
waiting_counter=5
|
||||
delete_services traefik-storeconfig
|
||||
delete_services storeconfig
|
||||
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ start_storeconfig_etcd3() {
|
||||
watch = true
|
||||
prefix = "/traefik"
|
||||
useAPIV3 = true' >> $basedir/traefik.toml
|
||||
up_environment traefik-storeconfig
|
||||
up_environment storeconfig
|
||||
rm -f $basedir/traefik.toml
|
||||
waiting_counter=5
|
||||
# Don't start Traefik store config if ETCD3 is not started
|
||||
@@ -99,7 +99,7 @@ start_storeconfig_etcd3() {
|
||||
sleep 5
|
||||
let waiting_counter-=1
|
||||
done
|
||||
delete_services traefik-storeconfig etcdctl-ping
|
||||
delete_services storeconfig etcdctl-ping
|
||||
}
|
||||
|
||||
start_traefik() {
|
||||
@@ -136,11 +136,11 @@ start_traefik() {
|
||||
# Start boulder services
|
||||
start_boulder() {
|
||||
echo "Start boulder environment"
|
||||
up_environment bmysql brabbitmq bhsm boulder
|
||||
up_environment bmysql bhsm boulder
|
||||
waiting_counter=12
|
||||
# Not start Traefik if boulder is not started
|
||||
echo "WAIT for boulder..."
|
||||
while [[ -z $(curl -s http://10.0.1.3:4000/directory) ]]; do
|
||||
while [[ -z $(curl -s http://10.0.1.3:4001/directory) ]]; do
|
||||
sleep 5
|
||||
let waiting_counter-=1
|
||||
if [[ $waiting_counter -eq 0 ]]; then
|
||||
|
@@ -1,42 +0,0 @@
|
||||
totalCertificates:
|
||||
window: 1h
|
||||
threshold: 100000
|
||||
certificatesPerName:
|
||||
window: 1h
|
||||
threshold: 100000
|
||||
overrides:
|
||||
ratelimit.me: 1
|
||||
lim.it: 0
|
||||
# Hostnames used by the letsencrypt client integration test.
|
||||
le.wtf: 10000
|
||||
le1.wtf: 10000
|
||||
le2.wtf: 10000
|
||||
le3.wtf: 10000
|
||||
nginx.wtf: 10000
|
||||
good-caa-reserved.com: 10000
|
||||
bad-caa-reserved.com: 10000
|
||||
ecdsa.le.wtf: 10000
|
||||
must-staple.le.wtf: 10000
|
||||
registrationOverrides:
|
||||
101: 1000
|
||||
registrationsPerIP:
|
||||
window: 1h
|
||||
threshold: 100000
|
||||
overrides:
|
||||
127.0.0.1: 1000000
|
||||
pendingAuthorizationsPerAccount:
|
||||
window: 1h
|
||||
threshold: 100000
|
||||
certificatesPerFQDNSet:
|
||||
window: 1h
|
||||
threshold: 100000
|
||||
overrides:
|
||||
le.wtf: 10000
|
||||
le1.wtf: 10000
|
||||
le2.wtf: 10000
|
||||
le3.wtf: 10000
|
||||
le.wtf,le1.wtf: 10000
|
||||
good-caa-reserved.com: 10000
|
||||
nginx.wtf: 10000
|
||||
ecdsa.le.wtf: 10000
|
||||
must-staple.le.wtf: 10000
|
@@ -13,8 +13,8 @@ defaultEntryPoints = ["http", "https"]
|
||||
email = "test@traefik.io"
|
||||
storage = "traefik/acme/account"
|
||||
entryPoint = "https"
|
||||
OnHostRule = true
|
||||
caServer = "http://traefik.boulder.com:4000/directory"
|
||||
onHostRule = true
|
||||
caServer = "http://traefik.boulder.com:4001/directory"
|
||||
[acme.httpChallenge]
|
||||
entryPoint="http"
|
||||
|
||||
@@ -25,4 +25,4 @@ entryPoint="http"
|
||||
endpoint = "unix:///var/run/docker.sock"
|
||||
domain = "localhost.com"
|
||||
watch = true
|
||||
exposedbydefault = false
|
||||
exposedByDefault = false
|
||||
|
@@ -28,6 +28,11 @@ spec:
|
||||
containers:
|
||||
- image: traefik
|
||||
name: traefik-ingress-lb
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 80
|
||||
- name: admin
|
||||
containerPort: 8080
|
||||
args:
|
||||
- --api
|
||||
- --kubernetes
|
||||
|