mirror of
https://github.com/containous/traefik.git
synced 2025-10-23 23:33:33 +03:00
Compare commits
477 Commits
v1.3.6
...
v1.5.0-rc3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89a79d0f1b | ||
|
|
9e41485ff1 | ||
|
|
3c7c6c4d9f | ||
|
|
cd1b3904da | ||
|
|
b23b2611b3 | ||
|
|
877770f7cf | ||
|
|
3142a4f4b3 | ||
|
|
b4dc96527d | ||
|
|
35b5ca4c63 | ||
|
|
daf3023b02 | ||
|
|
b17d5b80b8 | ||
|
|
48b4eb5c0d | ||
|
|
7ecd6d20ba | ||
|
|
bddad57a7b | ||
|
|
799136a714 | ||
|
|
350d61b4a6 | ||
|
|
b6f5a66fab | ||
|
|
b0c12e2422 | ||
|
|
623a7dc7e6 | ||
|
|
709c7e5707 | ||
|
|
ee04f52a16 | ||
|
|
7d98c1c4e0 | ||
|
|
4387cf38d7 | ||
|
|
a9d38570ab | ||
|
|
0e619369fd | ||
|
|
cda09c843a | ||
|
|
6333bfe6e8 | ||
|
|
41d8863d2f | ||
|
|
523b7f96f8 | ||
|
|
ab1a930705 | ||
|
|
3a99c86cb3 | ||
|
|
d6ad7e2e64 | ||
|
|
aaf120f263 | ||
|
|
c228e73b26 | ||
|
|
e27e65eb76 | ||
|
|
1c8acf3929 | ||
|
|
40b3c17703 | ||
|
|
313357a6b3 | ||
|
|
37a1aaad64 | ||
|
|
f084d2a28b | ||
|
|
077b39d7c6 | ||
|
|
7081f3df58 | ||
|
|
9fe6a0a894 | ||
|
|
3d452fd5b9 | ||
|
|
47a5cfbd3e | ||
|
|
4cb6241e93 | ||
|
|
b572879691 | ||
|
|
ad07a6ab2b | ||
|
|
4bdeb33ac1 | ||
|
|
101a4d0d8d | ||
|
|
89e07d0c55 | ||
|
|
39c1cc1b3c | ||
|
|
9f6f637527 | ||
|
|
0f09551a76 | ||
|
|
8cd72cfc1b | ||
|
|
7a141c8616 | ||
|
|
0ca65f955d | ||
|
|
011b748a55 | ||
|
|
f6181ef3e2 | ||
|
|
24368747ab | ||
|
|
66591cf216 | ||
|
|
1feeeb2eec | ||
|
|
419d46c958 | ||
|
|
7063da1c7d | ||
|
|
bee8ebb00b | ||
|
|
da5e4a13bf | ||
|
|
5dc1ec68a3 | ||
|
|
3d2e5ebe39 | ||
|
|
f5130db6b0 | ||
|
|
676b79db42 | ||
|
|
6d2f4a0813 | ||
|
|
4b91204686 | ||
|
|
7ddefcef72 | ||
|
|
0f3e42d463 | ||
|
|
c9129b8ecf | ||
|
|
a6955ecf59 | ||
|
|
6619a787a3 | ||
|
|
aae17c817b | ||
|
|
ab87bad952 | ||
|
|
be306d651e | ||
|
|
8fe5c22075 | ||
|
|
05a9350e57 | ||
|
|
7ed4ae2f8c | ||
|
|
5d6384e101 | ||
|
|
1a4564d998 | ||
|
|
66e489addb | ||
|
|
cdab6b1796 | ||
|
|
722f299306 | ||
|
|
66be04f39e | ||
|
|
8719f2836e | ||
|
|
0c702b0b6b | ||
|
|
6fcab72ec7 | ||
|
|
77b111702b | ||
|
|
96a7cc483f | ||
|
|
1e3506848a | ||
|
|
5ee2cae85c | ||
|
|
5c119fe2d6 | ||
|
|
d55115844a | ||
|
|
4f4491c247 | ||
|
|
1691f586d7 | ||
|
|
04dfe0de84 | ||
|
|
27d1b46835 | ||
|
|
2f62ec3632 | ||
|
|
384488ac02 | ||
|
|
c469e669fd | ||
|
|
56affb90ae | ||
|
|
f6aa147c78 | ||
|
|
9bd0fff319 | ||
|
|
00d7c5972f | ||
|
|
58a438167b | ||
|
|
e3131481e9 | ||
|
|
bc8d68bd31 | ||
|
|
07c6e33598 | ||
|
|
70812c70fc | ||
|
|
d89b234cad | ||
|
|
2070aa9443 | ||
|
|
91ff94ea56 | ||
|
|
0347537f43 | ||
|
|
db9b18f121 | ||
|
|
ee70001be3 | ||
|
|
972eea97fe | ||
|
|
2b4d33e919 | ||
|
|
fc4d670c88 | ||
|
|
02035d4942 | ||
|
|
93a46089ce | ||
|
|
e8d63b2a3b | ||
|
|
d3c7681bc5 | ||
|
|
dc66db4abe | ||
|
|
a0e1cf8376 | ||
|
|
5292b84f4f | ||
|
|
b27455a36f | ||
|
|
5042c5bf40 | ||
|
|
da7b6f0baf | ||
|
|
9b5845f1cb | ||
|
|
e8633d17e8 | ||
|
|
d1d8b01dfb | ||
|
|
7c4353a0ac | ||
|
|
1b2cb53d4f | ||
|
|
3158e51c62 | ||
|
|
a0c72cdf00 | ||
|
|
f0371da838 | ||
|
|
44b82e6231 | ||
|
|
04f0bf3070 | ||
|
|
7400c39511 | ||
|
|
008a5af6d6 | ||
|
|
35ca40c3de | ||
|
|
de821fc305 | ||
|
|
e3cac7d0e5 | ||
|
|
81f7aa9df2 | ||
|
|
6bce298d90 | ||
|
|
afbad56012 | ||
|
|
d973096464 | ||
|
|
7192aa86b5 | ||
|
|
9c8df8b9ce | ||
|
|
ff4c7b82bc | ||
|
|
47ff51e640 | ||
|
|
08503655d9 | ||
|
|
3afd6024b5 | ||
|
|
aa308b7a3a | ||
|
|
9598f646f5 | ||
|
|
8af39bdaf7 | ||
|
|
914f3d1fa3 | ||
|
|
8cb3f0835a | ||
|
|
cba0898e4f | ||
|
|
8d158402f3 | ||
|
|
7f2582e3b6 | ||
|
|
dbc796359f | ||
|
|
4d1285d8e5 | ||
|
|
871d097b30 | ||
|
|
1532033a7f | ||
|
|
9faae7387e | ||
|
|
a5c644e719 | ||
|
|
7a2ce59563 | ||
|
|
14cec7e610 | ||
|
|
6287a3dd53 | ||
|
|
93a1db77c5 | ||
|
|
a9d4b09bdb | ||
|
|
ed2eb7b5a6 | ||
|
|
18d8537d29 | ||
|
|
72f3b1ed39 | ||
|
|
fd70e6edb1 | ||
|
|
5a578c5375 | ||
|
|
9db8773055 | ||
|
|
8a67434380 | ||
|
|
c94e5f3589 | ||
|
|
adef7200f6 | ||
|
|
cf508b6d48 | ||
|
|
f8d36fda28 | ||
|
|
4fe9cc7730 | ||
|
|
758b7f875b | ||
|
|
0b97a67cfa | ||
|
|
ec5976bbc9 | ||
|
|
5cc49e2931 | ||
|
|
b6752a2c02 | ||
|
|
d41e28fc36 | ||
|
|
64c52a6921 | ||
|
|
691a678b19 | ||
|
|
1ba7fd91ff | ||
|
|
1c98a9ad3e | ||
|
|
dd23ceeead | ||
|
|
058fa1367b | ||
|
|
9db12374ea | ||
|
|
fc550ac1fc | ||
|
|
d6ef8ec3d1 | ||
|
|
837db9a2d9 | ||
|
|
a941739f8a | ||
|
|
795a346006 | ||
|
|
9d00da7285 | ||
|
|
52c1909f24 | ||
|
|
2cbf9cae71 | ||
|
|
f9225c54ff | ||
|
|
cb05f36976 | ||
|
|
49e0e20ce2 | ||
|
|
7c35337999 | ||
|
|
2296aab5a8 | ||
|
|
ce3b255f1a | ||
|
|
3942f3366d | ||
|
|
df76cc33a5 | ||
|
|
cf387d5a6d | ||
|
|
0a0cf87625 | ||
|
|
1a2544610d | ||
|
|
5229b7cfba | ||
|
|
243b45881d | ||
|
|
883028d981 | ||
|
|
bdeb7bfb9f | ||
|
|
808ffb0491 | ||
|
|
5305a16350 | ||
|
|
63b581935d | ||
|
|
c7c9349b00 | ||
|
|
d54417acfe | ||
|
|
9fba37b409 | ||
|
|
6d28c52f59 | ||
|
|
f80a6ef2a6 | ||
|
|
ecf31097ea | ||
|
|
16fc3675db | ||
|
|
651d993d9c | ||
|
|
03eb5139a2 | ||
|
|
286d882f1e | ||
|
|
3b6afdf80c | ||
|
|
c19cce69fa | ||
|
|
5c4931e235 | ||
|
|
b705e64a8a | ||
|
|
7fd1eb3780 | ||
|
|
8c5514612f | ||
|
|
924e82ab0c | ||
|
|
adcb99d330 | ||
|
|
8339139400 | ||
|
|
a43cf8d2b8 | ||
|
|
2b863d9bc2 | ||
|
|
9ce4f94818 | ||
|
|
5157a6ad47 | ||
|
|
cd6c58a372 | ||
|
|
03ba8396f3 | ||
|
|
b0a0e16136 | ||
|
|
732d73dd43 | ||
|
|
e075dfe911 | ||
|
|
425b53585a | ||
|
|
d5bbb103d4 | ||
|
|
5c2849ea07 | ||
|
|
723418e2cc | ||
|
|
45e2e8baec | ||
|
|
b0ae6bc049 | ||
|
|
ffb53c07b8 | ||
|
|
f329b3b51d | ||
|
|
5b27aba3e1 | ||
|
|
7c2ba62b56 | ||
|
|
24862402e5 | ||
|
|
d568d2f55a | ||
|
|
dae7e7a80a | ||
|
|
23cdb37165 | ||
|
|
2c82dfd444 | ||
|
|
c8c31aea62 | ||
|
|
89b0037ec1 | ||
|
|
b75fb23887 | ||
|
|
52b69fbcb8 | ||
|
|
f16219f90a | ||
|
|
7b0cef0fac | ||
|
|
e0af17a17a | ||
|
|
92fb86b66f | ||
|
|
919295cffc | ||
|
|
086a85d2f0 | ||
|
|
8235cd3645 | ||
|
|
f1a257abf8 | ||
|
|
98dfd2ba0e | ||
|
|
87e6285cf6 | ||
|
|
0d56a98836 | ||
|
|
8105f1c379 | ||
|
|
e6c2040ea8 | ||
|
|
c1b5b740ff | ||
|
|
1d2d0cefaa | ||
|
|
04e65958ee | ||
|
|
8765494cbd | ||
|
|
05665f4eec | ||
|
|
396449c07f | ||
|
|
eda679776e | ||
|
|
69d57d602f | ||
|
|
32b2736efd | ||
|
|
3f650bbd11 | ||
|
|
5313922bb7 | ||
|
|
ec3e2c08b8 | ||
|
|
0367034f93 | ||
|
|
b80ecd51a7 | ||
|
|
14a0d66410 | ||
|
|
d84ccbc52a | ||
|
|
1190768f4b | ||
|
|
ea3510d1f3 | ||
|
|
3f76f73e8c | ||
|
|
759c269dee | ||
|
|
c360395afc | ||
|
|
60a35c8aba | ||
|
|
50dd2b8cff | ||
|
|
4e5fcac9cb | ||
|
|
64b8fc52c3 | ||
|
|
19a5ba3264 | ||
|
|
7ff6c32452 | ||
|
|
ff11467022 | ||
|
|
7d3878214a | ||
|
|
984817d3a0 | ||
|
|
6b133e24b9 | ||
|
|
990ee89650 | ||
|
|
8071f31721 | ||
|
|
d456c2ce6a | ||
|
|
94f922cd28 | ||
|
|
29390a3c4a | ||
|
|
765c44d77f | ||
|
|
64ee68763b | ||
|
|
4122aef12e | ||
|
|
8cb44598c0 | ||
|
|
69c628b626 | ||
|
|
cd28e7b24f | ||
|
|
40d9058bb6 | ||
|
|
c36e0b3b06 | ||
|
|
3174fb8861 | ||
|
|
074b31b5e9 | ||
|
|
16609cd485 | ||
|
|
70ab34cfb8 | ||
|
|
c53be185f4 | ||
|
|
779eeba650 | ||
|
|
58ffea6627 | ||
|
|
a2d68ed881 | ||
|
|
d653a348b1 | ||
|
|
2e84b1e556 | ||
|
|
bbb133d94c | ||
|
|
d90fa5ab3e | ||
|
|
759a19bc4f | ||
|
|
a7ec785994 | ||
|
|
46faa7a745 | ||
|
|
54e3f08833 | ||
|
|
b365836c57 | ||
|
|
242f1b9c3c | ||
|
|
4dfbb6d489 | ||
|
|
c31b4c55c2 | ||
|
|
ca5bbab20a | ||
|
|
41dd124a4b | ||
|
|
dbf6161fa1 | ||
|
|
7aabd6e385 | ||
|
|
cb203f8e7e | ||
|
|
8f845bac74 | ||
|
|
a89eb122a0 | ||
|
|
b7daa2f3a4 | ||
|
|
7d178f49b4 | ||
|
|
85f4f26942 | ||
|
|
eee8ba8a53 | ||
|
|
22aceec426 | ||
|
|
121c057b90 | ||
|
|
2c976227dd | ||
|
|
81d011e57d | ||
|
|
3776e58041 | ||
|
|
36c88111de | ||
|
|
2201dcd505 | ||
|
|
7a7cafcbaa | ||
|
|
efb671401d | ||
|
|
4128c1ac8d | ||
|
|
73e10c96cc | ||
|
|
0055965295 | ||
|
|
34e60a8404 | ||
|
|
ceec81011b | ||
|
|
927003329e | ||
|
|
01bb0a80ab | ||
|
|
db1baf80a9 | ||
|
|
9cb07d026f | ||
|
|
984ea1040f | ||
|
|
447109e868 | ||
|
|
f79317a435 | ||
|
|
131d8dd765 | ||
|
|
b452695c20 | ||
|
|
f17785c3ab | ||
|
|
2a578748fd | ||
|
|
2ddae2e856 | ||
|
|
885b9f371c | ||
|
|
f275e4ad3c | ||
|
|
aea7bc0c07 | ||
|
|
a457392ec3 | ||
|
|
37ec7d0505 | ||
|
|
8f6404ab3a | ||
|
|
1538b16b21 | ||
|
|
a6477fbd95 | ||
|
|
e802dcd189 | ||
|
|
931dc02c09 | ||
|
|
7017cdcf49 | ||
|
|
5aa017d9b5 | ||
|
|
a7297b49a4 | ||
|
|
3eaeb81831 | ||
|
|
7d6c778211 | ||
|
|
9c27a98821 | ||
|
|
ad54c5a278 | ||
|
|
96939e2990 | ||
|
|
5268db47a1 | ||
|
|
3048509807 | ||
|
|
7399a83c74 | ||
|
|
18c3d8dc62 | ||
|
|
2d1ddcf28b | ||
|
|
a1a0420314 | ||
|
|
2223587fc0 | ||
|
|
63f9bccf9f | ||
|
|
18d11e02d0 | ||
|
|
a71d69cc3c | ||
|
|
e007bb7546 | ||
|
|
7874ffd506 | ||
|
|
a9216e24f5 | ||
|
|
39388a2199 | ||
|
|
71111708d4 | ||
|
|
d5efc99876 | ||
|
|
1e84e77a67 | ||
|
|
d6b448f430 | ||
|
|
e426b27581 | ||
|
|
b6c5c14447 | ||
|
|
cbccdd51c5 | ||
|
|
994e135368 | ||
|
|
87e5cda506 | ||
|
|
2833d68f15 | ||
|
|
64e8b31d49 | ||
|
|
2643271053 | ||
|
|
5b36b274a3 | ||
|
|
8ad31d6eb4 | ||
|
|
13e8a875cf | ||
|
|
c7281df230 | ||
|
|
5f0b215e90 | ||
|
|
55f610422a | ||
|
|
a04ef15bcd | ||
|
|
81754840ff | ||
|
|
2610023131 | ||
|
|
ff3481f06b | ||
|
|
f8ea19d29c | ||
|
|
3b8ebf7d33 | ||
|
|
5e14f20786 | ||
|
|
96b19deac5 | ||
|
|
a6aff7c85c | ||
|
|
1310347395 | ||
|
|
40c94d80d7 | ||
|
|
921a704c24 | ||
|
|
3f490f95c6 | ||
|
|
24d80b1909 | ||
|
|
78b2fba033 | ||
|
|
218b76275c | ||
|
|
cf5b6d837f | ||
|
|
0babc7bb64 | ||
|
|
8a551d91fd | ||
|
|
eeed035ef0 | ||
|
|
33404a7772 | ||
|
|
bd90745528 | ||
|
|
ede1212cb0 | ||
|
|
2dcbc01e51 | ||
|
|
61ba50fac9 | ||
|
|
b24b5e20b4 | ||
|
|
3112432480 | ||
|
|
94f5b0d9ff | ||
|
|
d2c8824902 | ||
|
|
db09007dbc | ||
|
|
5b2e8990f1 | ||
|
|
2f6068decc | ||
|
|
1e591dd188 | ||
|
|
6838a81e50 | ||
|
|
ceef5e39b7 | ||
|
|
ef339af623 | ||
|
|
acc7865542 | ||
|
|
c00c240c14 |
24
.github/CODEOWNERS
vendored
Normal file
24
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
provider/kubernetes/** @containous/kubernetes
|
||||||
|
provider/rancher/** @containous/rancher
|
||||||
|
provider/marathon/** @containous/marathon
|
||||||
|
provider/docker/** @containous/docker
|
||||||
|
|
||||||
|
docs/user-guide/kubernetes.md @containous/kubernetes
|
||||||
|
docs/user-guide/marathon.md @containous/marathon
|
||||||
|
docs/user-guide/swarm.md @containous/docker
|
||||||
|
docs/user-guide/swarm-mode.md @containous/docker
|
||||||
|
|
||||||
|
docs/configuration/backends/docker.md @containous/docker
|
||||||
|
docs/configuration/backends/kubernetes.md @containous/kubernetes
|
||||||
|
docs/configuration/backends/marathon.md @containous/marathon
|
||||||
|
docs/configuration/backends/rancher.md @containous/rancher
|
||||||
|
|
||||||
|
examples/k8s/ @containous/kubernetes
|
||||||
|
examples/compose-k8s.yaml @containous/kubernetes
|
||||||
|
examples/k8s.namespace.yaml @containous/kubernetes
|
||||||
|
examples/compose-rancher.yml @containous/rancher
|
||||||
|
examples/compose-marathon.yml @containous/marathon
|
||||||
|
|
||||||
|
vendor/github.com/gambol99/go-marathon @containous/marathon
|
||||||
|
vendor/github.com/rancher @containous/rancher
|
||||||
|
vendor/k8s.io/ @containous/kubernetes
|
||||||
150
.github/CONTRIBUTING.md
vendored
150
.github/CONTRIBUTING.md
vendored
@@ -1,150 +0,0 @@
|
|||||||
# Contributing
|
|
||||||
|
|
||||||
### Building
|
|
||||||
|
|
||||||
You need either [Docker](https://github.com/docker/docker) and `make` (Method 1), or `go` (Method 2) in order to build traefik. For changes to its dependencies, the `glide` dependency management tool and `glide-vc` plugin are required.
|
|
||||||
|
|
||||||
#### Method 1: Using `Docker` and `Makefile`
|
|
||||||
|
|
||||||
You need to run the `binary` target. This will create binaries for Linux platform in the `dist` folder.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ 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.7
|
|
||||||
---> 8c6473912976
|
|
||||||
Step 1 : RUN go get github.com/Masterminds/glide
|
|
||||||
[...]
|
|
||||||
docker run --rm -v "/var/run/docker.sock:/var/run/docker.sock" -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -v "/home/emile/dev/go/src/github.com/containous/traefik/"dist":/go/src/github.com/containous/traefik/"dist"" "traefik-dev:no-more-godep-ever" ./script/make.sh generate binary
|
|
||||||
---> Making bundle: generate (in .)
|
|
||||||
removed 'gen.go'
|
|
||||||
|
|
||||||
---> Making bundle: binary (in .)
|
|
||||||
|
|
||||||
$ ls dist/
|
|
||||||
traefik*
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Method 2: Using `go`
|
|
||||||
|
|
||||||
###### Setting up your `go` environment
|
|
||||||
|
|
||||||
- You need `go` v1.7+
|
|
||||||
- It is recommended you clone Træfik into a directory like `~/go/src/github.com/containous/traefik` (This is the official golang workspace hierarchy, and will allow dependencies to resolve properly)
|
|
||||||
- This will allow your `GOPATH` and `PATH` variable to be set to `~/go` via:
|
|
||||||
```bash
|
|
||||||
$ export GOPATH=~/go
|
|
||||||
$ export PATH=$PATH:$GOPATH/bin
|
|
||||||
```
|
|
||||||
|
|
||||||
This can be verified via `$ go env`
|
|
||||||
- You will want to add those 2 export lines to your `.bashrc` or `.bash_profile`
|
|
||||||
- You need `go-bindata` to be able to use `go generate` command (needed to build) : `$ go get github.com/jteeuwen/go-bindata/...` (Please note, the ellipses are required)
|
|
||||||
|
|
||||||
#### Setting up `glide` and `glide-vc` for dependency management
|
|
||||||
|
|
||||||
- Glide is not required for building; however, it is necessary to modify dependencies (i.e., add, update, or remove third-party packages)
|
|
||||||
- Glide can be installed either via homebrew: `$ brew install glide` or via the official glide script: `$ curl https://glide.sh/get | sh`
|
|
||||||
- The glide plugin `glide-vc` must be installed from source: `go get github.com/sgotti/glide-vc`
|
|
||||||
|
|
||||||
If you want to add a dependency, use `$ glide get` to have glide put it into the vendor folder and update the glide manifest/lock files (`glide.yaml` and `glide.lock`, respectively). A following `glide-vc` run should be triggered to trim down the size of the vendor folder. The final result must be committed into VCS.
|
|
||||||
|
|
||||||
Dependencies for the integration tests in the `integration` folder are managed in a separate `integration/glide.yaml` file using the same toolset.
|
|
||||||
|
|
||||||
Care must be taken to choose the right arguments to `glide` when dealing with either main or integration test dependencies, or otherwise risk ending up with a broken build. For that reason, the helper script `script/glide.sh` encapsulates the gory details and conveniently calls `glide-vc` as well. Call it without parameters for basic usage instructions.
|
|
||||||
|
|
||||||
Here's a full example:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# install the new main dependency github.com/foo/bar and minimize vendor size
|
|
||||||
$ ./script/glide.sh get github.com/foo/bar
|
|
||||||
# install another dependency, this time for the integration tests
|
|
||||||
$ ( cd integration && ../script/glide.sh get github.com/baz/quuz )
|
|
||||||
# generate (Only required to integrate other components such as web dashboard)
|
|
||||||
$ go generate
|
|
||||||
# Standard go build
|
|
||||||
$ go build
|
|
||||||
# Using gox to build multiple platform
|
|
||||||
$ gox "linux darwin" "386 amd64 arm" \
|
|
||||||
-output="dist/traefik_{{.OS}}-{{.Arch}}" \
|
|
||||||
./cmd/traefik
|
|
||||||
# run other commands like tests
|
|
||||||
```
|
|
||||||
|
|
||||||
### Tests
|
|
||||||
|
|
||||||
##### Method 1: `Docker` and `make`
|
|
||||||
|
|
||||||
You can run unit tests using the `test-unit` target and the
|
|
||||||
integration test using the `test-integration` target.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ make test-unit
|
|
||||||
docker build -t "traefik-dev:your-feature-branch" -f build.Dockerfile .
|
|
||||||
# […]
|
|
||||||
docker run --rm -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -v "/home/vincent/src/github/vdemeester/traefik/dist:/go/src/github.com/containous/traefik/dist" "traefik-dev:your-feature-branch" ./script/make.sh generate test-unit
|
|
||||||
---> Making bundle: generate (in .)
|
|
||||||
removed 'gen.go'
|
|
||||||
|
|
||||||
---> Making bundle: test-unit (in .)
|
|
||||||
+ go test -cover -coverprofile=cover.out .
|
|
||||||
ok github.com/containous/traefik 0.005s coverage: 4.1% of statements
|
|
||||||
|
|
||||||
Test success
|
|
||||||
```
|
|
||||||
|
|
||||||
For development purposes, you can specify which tests to run by using:
|
|
||||||
```bash
|
|
||||||
# Run every tests in the MyTest suite
|
|
||||||
TESTFLAGS="-check.f MyTestSuite" make test-integration
|
|
||||||
|
|
||||||
# Run the test "MyTest" in the MyTest suite
|
|
||||||
TESTFLAGS="-check.f MyTestSuite.MyTest" make test-integration
|
|
||||||
|
|
||||||
# Run every tests starting with "My", in the MyTest suite
|
|
||||||
TESTFLAGS="-check.f MyTestSuite.My" make test-integration
|
|
||||||
|
|
||||||
# Run every tests ending with "Test", in the MyTest suite
|
|
||||||
TESTFLAGS="-check.f MyTestSuite.*Test" make test-integration
|
|
||||||
```
|
|
||||||
|
|
||||||
More: https://labix.org/gocheck
|
|
||||||
|
|
||||||
##### Method 2: `go`
|
|
||||||
|
|
||||||
- Tests can be run from the cloned directory, by `$ go test ./...` which should return `ok` similar to:
|
|
||||||
```
|
|
||||||
ok _/home/vincent/src/github/vdemeester/traefik 0.004s
|
|
||||||
```
|
|
||||||
|
|
||||||
### Documentation
|
|
||||||
|
|
||||||
The [documentation site](http://docs.traefik.io/) is built with [mkdocs](http://mkdocs.org/)
|
|
||||||
|
|
||||||
First make sure you have python and pip installed
|
|
||||||
|
|
||||||
```shell
|
|
||||||
$ python --version
|
|
||||||
Python 2.7.2
|
|
||||||
$ pip --version
|
|
||||||
pip 1.5.2
|
|
||||||
```
|
|
||||||
|
|
||||||
Then install mkdocs with pip
|
|
||||||
|
|
||||||
```shell
|
|
||||||
$ pip install mkdocs
|
|
||||||
```
|
|
||||||
|
|
||||||
To test documentation locally run `mkdocs serve` in the root directory, this should start a server locally to preview your changes.
|
|
||||||
|
|
||||||
```shell
|
|
||||||
$ mkdocs serve
|
|
||||||
INFO - Building documentation...
|
|
||||||
WARNING - Config value: 'theme'. Warning: The theme 'united' will be removed in an upcoming MkDocs release. See http://www.mkdocs.org/about/release-notes/ for more details
|
|
||||||
INFO - Cleaning site directory
|
|
||||||
[I 160505 22:31:24 server:281] Serving on http://127.0.0.1:8000
|
|
||||||
[I 160505 22:31:24 handlers:59] Start watching changes
|
|
||||||
[I 160505 22:31:24 handlers:61] Start detecting changes
|
|
||||||
```
|
|
||||||
39
.github/ISSUE_TEMPLATE.md
vendored
39
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,16 +1,29 @@
|
|||||||
<!--
|
<!--
|
||||||
PLEASE READ THIS MESSAGE.
|
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
|
||||||
|
|
||||||
Please keep in mind that the GitHub issue tracker is not intended as a general support forum, but for reporting bugs and feature requests.
|
The issue tracker is for reporting bugs and feature requests only.
|
||||||
|
For end-user related support questions, refer to one of the following:
|
||||||
For other type of questions, consider using one of:
|
|
||||||
|
|
||||||
|
- Stack Overflow (using the "traefik" tag): https://stackoverflow.com/questions/tagged/traefik
|
||||||
- the Traefik community Slack channel: https://traefik.herokuapp.com
|
- the Traefik community Slack channel: https://traefik.herokuapp.com
|
||||||
- StackOverflow: https://stackoverflow.com/questions/tagged/traefik
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
### Do you want to request a *feature* or report a *bug*?
|
||||||
|
|
||||||
|
<!--
|
||||||
|
If you intend to ask a support question: DO NOT FILE AN ISSUE.
|
||||||
|
-->
|
||||||
|
|
||||||
|
### What did you do?
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
HOW TO WRITE A GOOD ISSUE?
|
HOW TO WRITE A GOOD ISSUE?
|
||||||
|
|
||||||
- if it's possible use the command `traefik bug`. See https://www.youtube.com/watch?v=Lyz62L8m93I.
|
- Respect the issue template as more as possible.
|
||||||
|
- If it's possible use the command `traefik bug`. See https://www.youtube.com/watch?v=Lyz62L8m93I.
|
||||||
- The title must be short and descriptive.
|
- The title must be short and descriptive.
|
||||||
- Explain the conditions which led you to write this issue: the context.
|
- Explain the conditions which led you to write this issue: the context.
|
||||||
- The context should lead to something, an idea or a problem that you’re facing.
|
- The context should lead to something, an idea or a problem that you’re facing.
|
||||||
@@ -19,14 +32,6 @@ HOW TO WRITE A GOOD ISSUE?
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
### Do you want to request a *feature* or report a *bug*?
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### What did you do?
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### What did you expect to see?
|
### What did you expect to see?
|
||||||
|
|
||||||
|
|
||||||
@@ -37,6 +42,12 @@ HOW TO WRITE A GOOD ISSUE?
|
|||||||
|
|
||||||
### Output of `traefik version`: (_What version of Traefik are you using?_)
|
### Output of `traefik version`: (_What version of Traefik are you using?_)
|
||||||
|
|
||||||
|
<!--
|
||||||
|
For the Traefik Docker image:
|
||||||
|
docker run [IMAGE] version
|
||||||
|
ex: docker run traefik version
|
||||||
|
-->
|
||||||
|
|
||||||
```
|
```
|
||||||
(paste your output here)
|
(paste your output here)
|
||||||
```
|
```
|
||||||
|
|||||||
21
.github/PULL_REQUEST_TEMPLATE.md
vendored
21
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -16,8 +16,21 @@ HOW TO WRITE A GOOD PULL REQUEST?
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
### Description
|
### What does this PR do?
|
||||||
|
|
||||||
<!--
|
<!-- A brief description of the change being made with this pull request. -->
|
||||||
Briefly describe the pull request in a few paragraphs.
|
|
||||||
-->
|
|
||||||
|
### Motivation
|
||||||
|
|
||||||
|
<!-- What inspired you to submit this pull request? -->
|
||||||
|
|
||||||
|
|
||||||
|
### More
|
||||||
|
|
||||||
|
- [ ] Added/updated tests
|
||||||
|
- [ ] Added/updated documentation
|
||||||
|
|
||||||
|
### Additional Notes
|
||||||
|
|
||||||
|
<!-- Anything else we should know when reviewing? -->
|
||||||
|
|||||||
26
.github/cpr.sh
vendored
26
.github/cpr.sh
vendored
@@ -1,26 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
#
|
|
||||||
# git config --global alias.cpr '!sh .github/cpr.sh'
|
|
||||||
|
|
||||||
set -e # stop on error
|
|
||||||
|
|
||||||
usage="$(basename "$0") pr -- Checkout a Pull Request locally"
|
|
||||||
|
|
||||||
if [ "$#" -ne 1 ]; then
|
|
||||||
echo "Illegal number of parameters"
|
|
||||||
echo "$usage" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
command -v jq >/dev/null 2>&1 || { echo "I require jq but it's not installed. Aborting." >&2; exit 1; }
|
|
||||||
|
|
||||||
set -x # echo on
|
|
||||||
|
|
||||||
initial=$(git rev-parse --abbrev-ref HEAD)
|
|
||||||
pr=$1
|
|
||||||
remote=$(curl -s https://api.github.com/repos/containous/traefik/pulls/$pr | jq -r .head.repo.owner.login)
|
|
||||||
branch=$(curl -s https://api.github.com/repos/containous/traefik/pulls/$pr | jq -r .head.ref)
|
|
||||||
|
|
||||||
git remote add $remote git@github.com:$remote/traefik.git
|
|
||||||
git fetch $remote $branch
|
|
||||||
git checkout -t -b "$pr--$branch" $remote/$branch
|
|
||||||
27
.github/rmpr.sh
vendored
27
.github/rmpr.sh
vendored
@@ -1,27 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
#
|
|
||||||
# git config --global alias.rmpr '!sh .github/rmpr.sh'
|
|
||||||
|
|
||||||
set -e # stop on error
|
|
||||||
|
|
||||||
usage="$(basename "$0") pr -- remove a Pull Request local branch & remote"
|
|
||||||
|
|
||||||
if [ "$#" -ne 1 ]; then
|
|
||||||
echo "Illegal number of parameters"
|
|
||||||
echo "$usage" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
command -v jq >/dev/null 2>&1 || { echo "I require jq but it's not installed. Aborting." >&2; exit 1; }
|
|
||||||
|
|
||||||
set -x # echo on
|
|
||||||
|
|
||||||
initial=$(git rev-parse --abbrev-ref HEAD)
|
|
||||||
pr=$1
|
|
||||||
remote=$(curl -s https://api.github.com/repos/containous/traefik/pulls/$pr | jq -r .head.repo.owner.login)
|
|
||||||
branch=$(curl -s https://api.github.com/repos/containous/traefik/pulls/$pr | jq -r .head.ref)
|
|
||||||
|
|
||||||
# clean
|
|
||||||
git checkout $initial
|
|
||||||
git branch -D "$pr--$branch"
|
|
||||||
git remote remove $remote
|
|
||||||
36
.github/rpr.sh
vendored
36
.github/rpr.sh
vendored
@@ -1,36 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
#
|
|
||||||
# git config --global alias.rpr '!sh .github/rpr.sh'
|
|
||||||
|
|
||||||
set -e # stop on error
|
|
||||||
|
|
||||||
usage="$(basename "$0") pr remote/branch -- rebase a Pull Request against a remote branch"
|
|
||||||
|
|
||||||
if [ "$#" -ne 2 ]; then
|
|
||||||
echo "Illegal number of parameters"
|
|
||||||
echo "$usage" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
command -v jq >/dev/null 2>&1 || { echo "I require jq but it's not installed. Aborting." >&2; exit 1; }
|
|
||||||
|
|
||||||
set -x # echo on
|
|
||||||
|
|
||||||
initial=$(git rev-parse --abbrev-ref HEAD)
|
|
||||||
pr=$1
|
|
||||||
base=$2
|
|
||||||
remote=$(curl -s https://api.github.com/repos/containous/traefik/pulls/$pr | jq -r .head.repo.owner.login)
|
|
||||||
branch=$(curl -s https://api.github.com/repos/containous/traefik/pulls/$pr | jq -r .head.ref)
|
|
||||||
|
|
||||||
clean ()
|
|
||||||
{
|
|
||||||
git checkout $initial
|
|
||||||
.github/rmpr.sh $pr
|
|
||||||
}
|
|
||||||
|
|
||||||
trap clean EXIT
|
|
||||||
|
|
||||||
.github/cpr.sh $pr
|
|
||||||
|
|
||||||
git rebase $base
|
|
||||||
git push --force-with-lease $remote "$pr--$branch"
|
|
||||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,7 +1,7 @@
|
|||||||
/dist
|
/dist
|
||||||
/autogen/gen.go
|
/autogen/genstatic/gen.go
|
||||||
.idea
|
.idea/
|
||||||
.intellij
|
.intellij/
|
||||||
*.iml
|
*.iml
|
||||||
/traefik
|
/traefik
|
||||||
/traefik.toml
|
/traefik.toml
|
||||||
@@ -11,3 +11,4 @@
|
|||||||
*.log
|
*.log
|
||||||
*.exe
|
*.exe
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
/example/acme/acme.json
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
export secure='btt4r13t09gQlHb6gYrvGC2yGCMMHfnp1Mz1RQedc4Mpf/FfT8aE6xmK2a2i9CCvskjrP0t/BFaS4yxIURjnFRn+ugQIEa0pLspB9UJArW/vgOSpIWM9/OQ/fg8z5XuMxN6Md4DL1/iLypMNSageA1x0TRdt89+D1N1dALpg5XRCXLFbC84TLi0gjlFuib9ibPKzEhLT+anCRJ6iZMzeupDSoaCVbAtJMoDvXw4+4AcRZ1+k4MybBLyCib5boaEOt4pTT88mz4Kk0YaMwPVJyg9Qv36VqyUcPS09Yd95LuyVQ4+tZt8Y1ccbIzULsK+sLM3hLCzxlmlpN3dQBlZJiiRtQde0mgGAKyC0P0A1XjuDTywcsa5edB+fTk1Dsewz9xZ9V0NmMz8t+UNZnaSsAPga9i86jULbXUUwMVSzVRc+Xgx02liB/8qI1xYC9FM6ilStt7rn7mF0k3KbiWhcptgeXjO6Lah9FjEKd5w4MXsdUSTi/86rQaLo+kj+XdaTrXCTulKHyRyQEUj+8V1w0oVz7pcGjePHd7y5oU9ByifVQy6sytuFBfRZvugM5bKHo+i0pcWvixrZS42DrzwxZJsspANOvqSe5ifVbvOkfUppQdCBIwptxV5N1b49XPKU3W/w34QJ8xGmKp3TFA7WwVCztriFHjPgiRpB3EG99Bg='
|
|
||||||
|
|
||||||
export REPO='containous/traefik'
|
export REPO='containous/traefik'
|
||||||
|
|
||||||
if VERSION=$(git describe --exact-match --abbrev=0 --tags);
|
if VERSION=$(git describe --exact-match --abbrev=0 --tags);
|
||||||
@@ -12,7 +10,7 @@ else
|
|||||||
export VERSION=''
|
export VERSION=''
|
||||||
fi
|
fi
|
||||||
|
|
||||||
export CODENAME=raclette
|
export CODENAME=cancoillotte
|
||||||
|
|
||||||
export N_MAKE_JOBS=2
|
export N_MAKE_JOBS=2
|
||||||
|
|
||||||
|
|||||||
@@ -6,10 +6,9 @@ services:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- secure: btt4r13t09gQlHb6gYrvGC2yGCMMHfnp1Mz1RQedc4Mpf/FfT8aE6xmK2a2i9CCvskjrP0t/BFaS4yxIURjnFRn+ugQIEa0pLspB9UJArW/vgOSpIWM9/OQ/fg8z5XuMxN6Md4DL1/iLypMNSageA1x0TRdt89+D1N1dALpg5XRCXLFbC84TLi0gjlFuib9ibPKzEhLT+anCRJ6iZMzeupDSoaCVbAtJMoDvXw4+4AcRZ1+k4MybBLyCib5boaEOt4pTT88mz4Kk0YaMwPVJyg9Qv36VqyUcPS09Yd95LuyVQ4+tZt8Y1ccbIzULsK+sLM3hLCzxlmlpN3dQBlZJiiRtQde0mgGAKyC0P0A1XjuDTywcsa5edB+fTk1Dsewz9xZ9V0NmMz8t+UNZnaSsAPga9i86jULbXUUwMVSzVRc+Xgx02liB/8qI1xYC9FM6ilStt7rn7mF0k3KbiWhcptgeXjO6Lah9FjEKd5w4MXsdUSTi/86rQaLo+kj+XdaTrXCTulKHyRyQEUj+8V1w0oVz7pcGjePHd7y5oU9ByifVQy6sytuFBfRZvugM5bKHo+i0pcWvixrZS42DrzwxZJsspANOvqSe5ifVbvOkfUppQdCBIwptxV5N1b49XPKU3W/w34QJ8xGmKp3TFA7WwVCztriFHjPgiRpB3EG99Bg=
|
|
||||||
- REPO: $TRAVIS_REPO_SLUG
|
- REPO: $TRAVIS_REPO_SLUG
|
||||||
- VERSION: $TRAVIS_TAG
|
- VERSION: $TRAVIS_TAG
|
||||||
- CODENAME: raclette
|
- CODENAME: cancoillotte
|
||||||
- N_MAKE_JOBS: 2
|
- N_MAKE_JOBS: 2
|
||||||
|
|
||||||
script:
|
script:
|
||||||
@@ -24,7 +23,7 @@ before_deploy:
|
|||||||
docker version;
|
docker version;
|
||||||
pip install --user -r requirements.txt;
|
pip install --user -r requirements.txt;
|
||||||
make -j${N_MAKE_JOBS} crossbinary-parallel;
|
make -j${N_MAKE_JOBS} crossbinary-parallel;
|
||||||
make image;
|
make image-dirty;
|
||||||
mkdocs build --clean;
|
mkdocs build --clean;
|
||||||
tar cfz dist/traefik-${VERSION}.src.tar.gz --exclude-vcs --exclude dist .;
|
tar cfz dist/traefik-${VERSION}.src.tar.gz --exclude-vcs --exclude dist .;
|
||||||
fi
|
fi
|
||||||
@@ -37,6 +36,7 @@ deploy:
|
|||||||
on:
|
on:
|
||||||
repo: containous/traefik
|
repo: containous/traefik
|
||||||
tags: true
|
tags: true
|
||||||
|
condition: ${TRAVIS_TAG} =~ ^v[0-9]+\.[0-9]+\.[0-9]+$
|
||||||
- provider: releases
|
- provider: releases
|
||||||
api_key: ${GITHUB_TOKEN}
|
api_key: ${GITHUB_TOKEN}
|
||||||
file: dist/traefik*
|
file: dist/traefik*
|
||||||
|
|||||||
Binary file not shown.
BIN
.travis/traefiker_rsa.enc
Normal file
BIN
.travis/traefiker_rsa.enc
Normal file
Binary file not shown.
691
CHANGELOG.md
691
CHANGELOG.md
@@ -1,5 +1,688 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## [v1.5.0-rc3](https://github.com/containous/traefik/tree/v1.5.0-rc3) (2017-12-20)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v1.5.0-rc2...v1.5.0-rc3)
|
||||||
|
|
||||||
|
**Enhancements:**
|
||||||
|
- **[docker,k8s,rancher]** Support regex redirect by frontend ([#2570](https://github.com/containous/traefik/pull/2570) by [ldez](https://github.com/ldez))
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[acme,docker]** Modify ACME configuration migration into KV store ([#2598](https://github.com/containous/traefik/pull/2598) by [nmengin](https://github.com/nmengin))
|
||||||
|
- **[consulcatalog]** Reload configuration when port change for one service ([#2574](https://github.com/containous/traefik/pull/2574) by [mmatur](https://github.com/mmatur))
|
||||||
|
- **[consulcatalog]** Fix bad Træfik update on Consul Catalog ([#2573](https://github.com/containous/traefik/pull/2573) by [mmatur](https://github.com/mmatur))
|
||||||
|
- **[k8s]** Add missing entrypoints template. ([#2594](https://github.com/containous/traefik/pull/2594) by [ldez](https://github.com/ldez))
|
||||||
|
- **[kv]** Fix stickiness bug due to template syntax error ([#2591](https://github.com/containous/traefik/pull/2591) by [dahefanteng](https://github.com/dahefanteng))
|
||||||
|
- **[marathon]** Update go-marathon ([#2585](https://github.com/containous/traefik/pull/2585) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[mesos]** Mesos: Use slave.PID.Host as task SlaveIP. ([#2590](https://github.com/containous/traefik/pull/2590) by [nemosupremo](https://github.com/nemosupremo))
|
||||||
|
- **[middleware]** Fix RawPath handling in addPrefix ([#2560](https://github.com/containous/traefik/pull/2560) by [risdenk](https://github.com/risdenk))
|
||||||
|
- **[rules]** Add non regex pathPrefix ([#2592](https://github.com/containous/traefik/pull/2592) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- **[servicefabric]** Fix backend name for Stateful services. (Service Fabric) ([#2559](https://github.com/containous/traefik/pull/2559) by [ldez](https://github.com/ldez))
|
||||||
|
- **[servicefabric]** Fix isHealthy logic. ([#2577](https://github.com/containous/traefik/pull/2577) by [ldez](https://github.com/ldez))
|
||||||
|
- **[zk]** Change Zookeeper default prefix. ([#2580](https://github.com/containous/traefik/pull/2580) by [ldez](https://github.com/ldez))
|
||||||
|
- Fix frontend redirect ([#2544](https://github.com/containous/traefik/pull/2544) by [ldez](https://github.com/ldez))
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- **[acme]** Improve documentation for Cloudflare API key ([#2558](https://github.com/containous/traefik/pull/2558) by [mmatur](https://github.com/mmatur))
|
||||||
|
- Move rate limit documentation. ([#2588](https://github.com/containous/traefik/pull/2588) by [ldez](https://github.com/ldez))
|
||||||
|
- Grammar ([#2562](https://github.com/containous/traefik/pull/2562) by [geraldcroes](https://github.com/geraldcroes))
|
||||||
|
- Fix broken links and improve ResponseCodeRatio() description ([#2538](https://github.com/containous/traefik/pull/2538) by [mvasin](https://github.com/mvasin))
|
||||||
|
|
||||||
|
## [v1.5.0-rc2](https://github.com/containous/traefik/tree/v1.5.0-rc2) (2017-12-06)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v1.5.0-rc1...v1.5.0-rc2)
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[acme]** Modify the ACME renewing logs level ([#2520](https://github.com/containous/traefik/pull/2520) by [nmengin](https://github.com/nmengin))
|
||||||
|
- **[api]** Fix pprof route order. ([#2523](https://github.com/containous/traefik/pull/2523) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[docker,k8s]** Change custom headers separator ([#2509](https://github.com/containous/traefik/pull/2509) by [ldez](https://github.com/ldez))
|
||||||
|
- **[docker,k8s]** Fix Labels/annotation logs and values. ([#2488](https://github.com/containous/traefik/pull/2488) by [ldez](https://github.com/ldez))
|
||||||
|
- **[docker]** Quote template strings ([#2496](https://github.com/containous/traefik/pull/2496) by [dtomcej](https://github.com/dtomcej))
|
||||||
|
- **[docker]** Fix empty IP for backend when dnsrr in Docker swarm mode ([#2490](https://github.com/containous/traefik/pull/2490) by [mmatur](https://github.com/mmatur))
|
||||||
|
- **[healthcheck]** Fix healthcheck when web is not specified ([#2529](https://github.com/containous/traefik/pull/2529) by [Juliens](https://github.com/Juliens))
|
||||||
|
- **[k8s]** Reduce logs with new Kubernetes security annotations ([#2506](https://github.com/containous/traefik/pull/2506) by [ldez](https://github.com/ldez))
|
||||||
|
- **[metrics]** Do not ignore web params when web.metrics.prometheus is set ([#2499](https://github.com/containous/traefik/pull/2499) by [Juliens](https://github.com/Juliens))
|
||||||
|
- **[metrics]** Fix metrics problem on multiple entrypoints ([#2492](https://github.com/containous/traefik/pull/2492) by [Juliens](https://github.com/Juliens))
|
||||||
|
- Close ring buffer used in throttling function. ([#2532](https://github.com/containous/traefik/pull/2532) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- Fix wrong default entrypoint and non-existing entrypoint issue ([#2501](https://github.com/containous/traefik/pull/2501) by [Juliens](https://github.com/Juliens))
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- **[consul]** Improve Consul documentation ([#2485](https://github.com/containous/traefik/pull/2485) by [mmatur](https://github.com/mmatur))
|
||||||
|
- **[docker]** Fix Docker labels documentation render. ([#2505](https://github.com/containous/traefik/pull/2505) by [ldez](https://github.com/ldez))
|
||||||
|
- **[k8s]** Add note to Kubernetes RBAC docs about RoleBindings and namespaces ([#2498](https://github.com/containous/traefik/pull/2498) by [jmara](https://github.com/jmara))
|
||||||
|
|
||||||
|
**Misc:**
|
||||||
|
- Merge v1.4.5 into v1.5 ([#2530](https://github.com/containous/traefik/pull/2530) by [mmatur](https://github.com/mmatur))
|
||||||
|
|
||||||
|
## [v1.4.5](https://github.com/containous/traefik/tree/v1.4.5) (2017-12-05)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v1.4.4...v1.4.5)
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[docker]** Fix empty ip when container is stopped ([#2478](https://github.com/containous/traefik/pull/2478) by [mmatur](https://github.com/mmatur))
|
||||||
|
- **[k8s]** Fix kubernetes path prefix rule with rewrite-target ([#2461](https://github.com/containous/traefik/pull/2461) by [cheungpat](https://github.com/cheungpat))
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- **[file]** Emphasize the necessity of enabling file backend ([#2483](https://github.com/containous/traefik/pull/2483) by [mvasin](https://github.com/mvasin))
|
||||||
|
- Add link to future 1.5 documentation. ([#2477](https://github.com/containous/traefik/pull/2477) by [ldez](https://github.com/ldez))
|
||||||
|
|
||||||
|
## [v1.5.0-rc1](https://github.com/containous/traefik/tree/v1.5.0-rc1) (2017-11-28)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v1.4.0-rc1...v1.5.0-rc1)
|
||||||
|
|
||||||
|
**Enhancements:**
|
||||||
|
- **[acme,provider,docker,tls]** Make the TLS certificates management dynamic. ([#2233](https://github.com/containous/traefik/pull/2233) by [nmengin](https://github.com/nmengin))
|
||||||
|
- **[acme]** Update github.com/xenolf/lego to 0.4.1 ([#2304](https://github.com/containous/traefik/pull/2304) by [oldmantaiter](https://github.com/oldmantaiter))
|
||||||
|
- **[api,healthcheck,metrics,provider,webui]** Split Web into API/Dashboard, ping, metric and Rest Provider ([#2335](https://github.com/containous/traefik/pull/2335) by [Juliens](https://github.com/Juliens))
|
||||||
|
- **[authentication]** Pass through certain forward auth negative response headers ([#2127](https://github.com/containous/traefik/pull/2127) by [wheresmysocks](https://github.com/wheresmysocks))
|
||||||
|
- **[cluster,consul,file]** Add file to storeconfig ([#2419](https://github.com/containous/traefik/pull/2419) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- **[cluster,provider]** Support Etcd v3, enhance KV support ([#2407](https://github.com/containous/traefik/pull/2407) by [nmengin](https://github.com/nmengin))
|
||||||
|
- **[docker,k8s,rancher,webui]** redirect to another entryPoint per frontend ([#2133](https://github.com/containous/traefik/pull/2133) by [SantoDE](https://github.com/SantoDE))
|
||||||
|
- **[docker]** Add Custom header parsing to Docker Provider ([#2030](https://github.com/containous/traefik/pull/2030) by [dtomcej](https://github.com/dtomcej))
|
||||||
|
- **[docker]** Docker labels ([#2473](https://github.com/containous/traefik/pull/2473) by [ldez](https://github.com/ldez))
|
||||||
|
- **[docker]** Add docker security headers via labels ([#2334](https://github.com/containous/traefik/pull/2334) by [dtomcej](https://github.com/dtomcej))
|
||||||
|
- **[docker]** Use Node IP in Swarm Standalone with "host" NetworkMode ([#2274](https://github.com/containous/traefik/pull/2274) by [BlakeMesdag](https://github.com/BlakeMesdag))
|
||||||
|
- **[ecs]** ECS provider refactoring ([#2050](https://github.com/containous/traefik/pull/2050) by [mmatur](https://github.com/mmatur))
|
||||||
|
- **[ecs]** Add health check label to ECS ([#2421](https://github.com/containous/traefik/pull/2421) by [oldmantaiter](https://github.com/oldmantaiter))
|
||||||
|
- **[ecs]** Support Host NetworkMode for ECS provider ([#2320](https://github.com/containous/traefik/pull/2320) by [FriggaHel](https://github.com/FriggaHel))
|
||||||
|
- **[etcd]** Manage certificates dynamically in kv store ([#2411](https://github.com/containous/traefik/pull/2411) by [dahefanteng](https://github.com/dahefanteng))
|
||||||
|
- **[healthcheck]** Use healthcheck for systemd watchdog ([#2283](https://github.com/containous/traefik/pull/2283) by [guilhem](https://github.com/guilhem))
|
||||||
|
- **[k8s]** Kubernetes security header annotations ([#2460](https://github.com/containous/traefik/pull/2460) by [dtomcej](https://github.com/dtomcej))
|
||||||
|
- **[k8s]** Add labels for `traefik.frontend.entryPoints` & `PassTLSCert` to Kubernetes ([#2324](https://github.com/containous/traefik/pull/2324) by [ryarnyah](https://github.com/ryarnyah))
|
||||||
|
- **[k8s]** Only listen to configured k8s namespaces. ([#1895](https://github.com/containous/traefik/pull/1895) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[logs,middleware,consul,docker]** Use constants from http package. ([#2425](https://github.com/containous/traefik/pull/2425) by [ldez](https://github.com/ldez))
|
||||||
|
- **[logs]** Add json format support for Traefik logs ([#2056](https://github.com/containous/traefik/pull/2056) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[marathon]** Marathon constraints filtering ([#2388](https://github.com/containous/traefik/pull/2388) by [aantono](https://github.com/aantono))
|
||||||
|
- **[marathon]** Remove unused lightMarathonClient. ([#2383](https://github.com/containous/traefik/pull/2383) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[metrics]** Add InfluxDB support for traefik metrics ([#2289](https://github.com/containous/traefik/pull/2289) by [adityacs](https://github.com/adityacs))
|
||||||
|
- **[middleware]** Added ReplacePathRegex middleware ([#2033](https://github.com/containous/traefik/pull/2033) by [Tiscs](https://github.com/Tiscs))
|
||||||
|
- **[middleware]** Fix custom headers replacement ([#2455](https://github.com/containous/traefik/pull/2455) by [mmatur](https://github.com/mmatur))
|
||||||
|
- **[oxy]** Resync oxy with original repository ([#2451](https://github.com/containous/traefik/pull/2451) by [Juliens](https://github.com/Juliens))
|
||||||
|
- **[provider]** Support template as raw string. ([#2413](https://github.com/containous/traefik/pull/2413) by [ldez](https://github.com/ldez))
|
||||||
|
- **[rancher]** Run Rancher tests cases in parallel. ([#2424](https://github.com/containous/traefik/pull/2424) by [ldez](https://github.com/ldez))
|
||||||
|
- **[rancher]** Update Rancher API integration to go-rancher client v2. ([#2291](https://github.com/containous/traefik/pull/2291) by [rawmind0](https://github.com/rawmind0))
|
||||||
|
- **[servicefabric]** Add Service Fabric Provider ([#2117](https://github.com/containous/traefik/pull/2117) by [lawrencegripper](https://github.com/lawrencegripper))
|
||||||
|
- **[tls]** Allow adding optional Client CA files ([#2306](https://github.com/containous/traefik/pull/2306) by [nmengin](https://github.com/nmengin))
|
||||||
|
- **[websocket]** Add tests for websocket headers ([#2379](https://github.com/containous/traefik/pull/2379) by [Juliens](https://github.com/Juliens))
|
||||||
|
- Upgrade libkermit/compose version ([#2071](https://github.com/containous/traefik/pull/2071) by [nmengin](https://github.com/nmengin))
|
||||||
|
- Add proxy protocol tests ([#2325](https://github.com/containous/traefik/pull/2325) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- Register pprof handlers. ([#2428](https://github.com/containous/traefik/pull/2428) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- Rate limiting for frontends ([#2034](https://github.com/containous/traefik/pull/2034) by [bparli](https://github.com/bparli))
|
||||||
|
- Stats collection. ([#2447](https://github.com/containous/traefik/pull/2447) by [ldez](https://github.com/ldez))
|
||||||
|
- Add request accepting grace period delaying graceful shutdown. ([#1971](https://github.com/containous/traefik/pull/1971) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- Put subcommand in dedicated files. ([#2265](https://github.com/containous/traefik/pull/2265) by [ldez](https://github.com/ldez))
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[ecs]** Add missing functions for ECS template ([#2312](https://github.com/containous/traefik/pull/2312) by [oldmantaiter](https://github.com/oldmantaiter))
|
||||||
|
- **[logs]** Fix traefik logs to behave like configured ([#2176](https://github.com/containous/traefik/pull/2176) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[metrics]** Flaky test Influxdb. ([#2386](https://github.com/containous/traefik/pull/2386) by [ldez](https://github.com/ldez))
|
||||||
|
- **[provider]** Fix typo in frontend.headers.customresponseheaders label ([#2356](https://github.com/containous/traefik/pull/2356) by [nmandery](https://github.com/nmandery))
|
||||||
|
- **[provider]** fix concurrent provider config reloads ([#2276](https://github.com/containous/traefik/pull/2276) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[servicefabric]** Service Fabric 'expose' as boolean. ([#2476](https://github.com/containous/traefik/pull/2476) by [ldez](https://github.com/ldez))
|
||||||
|
- **[websocket]** RawPath and Transfer TLSConfig in websocket ([#2077](https://github.com/containous/traefik/pull/2077) by [Juliens](https://github.com/Juliens))
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- **[acme]** Update Let's Encrypt provider list ([#2347](https://github.com/containous/traefik/pull/2347) by [mmatur](https://github.com/mmatur))
|
||||||
|
- **[etcd]** Fix typo in examples ([#2446](https://github.com/containous/traefik/pull/2446) by [dahefanteng](https://github.com/dahefanteng))
|
||||||
|
- **[k8s]** Remove obsolete links in k8s docs ([#2465](https://github.com/containous/traefik/pull/2465) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[k8s]** Document filename parameter for Kubernetes. ([#2464](https://github.com/containous/traefik/pull/2464) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[metrics]** Add entrypoint in Prometheus doc and remove web on Influxdb doc ([#2452](https://github.com/containous/traefik/pull/2452) by [Juliens](https://github.com/Juliens))
|
||||||
|
- **[servicefabric]** Describe 'refreshSecond' configuration. ([#2471](https://github.com/containous/traefik/pull/2471) by [ldez](https://github.com/ldez))
|
||||||
|
- **[tls]** Add link to crypto/tls godoc. ([#2470](https://github.com/containous/traefik/pull/2470) by [ldez](https://github.com/ldez))
|
||||||
|
- Fix typos in changelog ([#2387](https://github.com/containous/traefik/pull/2387) by [ferhatelmas](https://github.com/ferhatelmas))
|
||||||
|
- Add mmatur to maintainers ([#2303](https://github.com/containous/traefik/pull/2303) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- Add a note about redirection rule to precise how regex/replacement work. ([#2243](https://github.com/containous/traefik/pull/2243) by [nmengin](https://github.com/nmengin))
|
||||||
|
- Add docker things for documentation ([#2020](https://github.com/containous/traefik/pull/2020) by [tcoupin](https://github.com/tcoupin))
|
||||||
|
|
||||||
|
**Misc:**
|
||||||
|
- **[acme]** dumpcerts.sh: Fix call to "base64" for Alpine ([#2344](https://github.com/containous/traefik/pull/2344) by [nknapp](https://github.com/nknapp))
|
||||||
|
- **[acme]** Dumpcerts.sh: fixed sed, extracted domain keys ([#2161](https://github.com/containous/traefik/pull/2161) by [sjawhar](https://github.com/sjawhar))
|
||||||
|
- Merge current v1.4 into master ([#2469](https://github.com/containous/traefik/pull/2469) by [ldez](https://github.com/ldez))
|
||||||
|
- Revert "Merge v1.4.2 into master" ([#2414](https://github.com/containous/traefik/pull/2414) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge v1.4.3 into master ([#2406](https://github.com/containous/traefik/pull/2406) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge v1.4.2 into master ([#2358](https://github.com/containous/traefik/pull/2358) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge v1.4.3 into master ([#2415](https://github.com/containous/traefik/pull/2415) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge v1.4.1 into master ([#2318](https://github.com/containous/traefik/pull/2318) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge v1.4.0 ([#2271](https://github.com/containous/traefik/pull/2271) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge v1.4.0-rc5 into master ([#2242](https://github.com/containous/traefik/pull/2242) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge v1.4.0-rc4 into master ([#2202](https://github.com/containous/traefik/pull/2202) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge v1.4.4 into master ([#2457](https://github.com/containous/traefik/pull/2457) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge current v1.4 ([#2154](https://github.com/containous/traefik/pull/2154) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge v1.4.0-rc3 into master ([#2140](https://github.com/containous/traefik/pull/2140) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge v1.4.0-rc2 into master ([#2092](https://github.com/containous/traefik/pull/2092) by [ldez](https://github.com/ldez))
|
||||||
|
- Upgrade libkermit/compose version ([#2074](https://github.com/containous/traefik/pull/2074) by [nmengin](https://github.com/nmengin))
|
||||||
|
- Merge current 1.4 ([#2064](https://github.com/containous/traefik/pull/2064) by [ldez](https://github.com/ldez))
|
||||||
|
|
||||||
|
## [v1.4.4](https://github.com/containous/traefik/tree/v1.4.4) (2017-11-21)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v1.4.3...v1.4.4)
|
||||||
|
|
||||||
|
**Enhancements:**
|
||||||
|
- **[middleware]** Remove GzipHandler Fork ([#2436](https://github.com/containous/traefik/pull/2436) by [ldez](https://github.com/ldez))
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[docker]** Fix problems about duplicated and missing Docker backends/frontends. ([#2434](https://github.com/containous/traefik/pull/2434) by [nmengin](https://github.com/nmengin))
|
||||||
|
- **[middleware]** Fix raw path handling in strip prefix ([#2382](https://github.com/containous/traefik/pull/2382) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[rancher]** Fix issue with label traefik.backend.loadbalancer.stickiness.cookieName ([#2423](https://github.com/containous/traefik/pull/2423) by [rawmind0](https://github.com/rawmind0))
|
||||||
|
- http.Server log goes to Debug level. ([#2420](https://github.com/containous/traefik/pull/2420) by [ldez](https://github.com/ldez))
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- Documentation archive ([#2405](https://github.com/containous/traefik/pull/2405) by [ldez](https://github.com/ldez))
|
||||||
|
|
||||||
|
## [v1.4.3](https://github.com/containous/traefik/tree/v1.4.3) (2017-11-14)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v1.4.2...v1.4.3)
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[consulcatalog]** Fix Traefik reload if Consul Catalog tags change ([#2389](https://github.com/containous/traefik/pull/2389) by [mmatur](https://github.com/mmatur))
|
||||||
|
- **[kv]** Add Traefik prefix to the KV key ([#2400](https://github.com/containous/traefik/pull/2400) by [nmengin](https://github.com/nmengin))
|
||||||
|
- **[middleware]** Flush and Status code ([#2403](https://github.com/containous/traefik/pull/2403) by [ldez](https://github.com/ldez))
|
||||||
|
- **[middleware]** Exclude GRPC from compress ([#2391](https://github.com/containous/traefik/pull/2391) by [ldez](https://github.com/ldez))
|
||||||
|
- **[middleware]** Keep status when stream mode and compress ([#2380](https://github.com/containous/traefik/pull/2380) by [Juliens](https://github.com/Juliens))
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- **[acme]** Fix some typos ([#2363](https://github.com/containous/traefik/pull/2363) by [tomsaleeba](https://github.com/tomsaleeba))
|
||||||
|
- **[docker]** Minor fix for docker volume vs created directory ([#2372](https://github.com/containous/traefik/pull/2372) by [visibilityspots](https://github.com/visibilityspots))
|
||||||
|
- **[k8s]** Link corrected ([#2385](https://github.com/containous/traefik/pull/2385) by [xlazex](https://github.com/xlazex))
|
||||||
|
|
||||||
|
**Misc:**
|
||||||
|
- **[k8s]** Add secret creation to docs for kubernetes backend ([#2374](https://github.com/containous/traefik/pull/2374) by [shadycuz](https://github.com/shadycuz))
|
||||||
|
|
||||||
|
## [v1.4.2](https://github.com/containous/traefik/tree/v1.4.2) (2017-11-02)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v1.4.1...v1.4.2)
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[cluster]** Fix datastore corruption on reload due to shrinking config size ([#2340](https://github.com/containous/traefik/pull/2340) by [else](https://github.com/else))
|
||||||
|
- **[docker,docker/swarm]** Make frontend names differents for similar routes ([#2338](https://github.com/containous/traefik/pull/2338) by [nmengin](https://github.com/nmengin))
|
||||||
|
- **[docker]** Fix IP address when Docker container network mode is container ([#2331](https://github.com/containous/traefik/pull/2331) by [nmengin](https://github.com/nmengin))
|
||||||
|
- **[docker]** Make the traefik.port label optional when using service labels in Docker containers. ([#2330](https://github.com/containous/traefik/pull/2330) by [nmengin](https://github.com/nmengin))
|
||||||
|
- **[docker]** Add unique ID to Docker services replicas ([#2314](https://github.com/containous/traefik/pull/2314) by [nmengin](https://github.com/nmengin))
|
||||||
|
- **[marathon]** Missing Backend key in configuration when application has no tasks ([#2333](https://github.com/containous/traefik/pull/2333) by [aantono](https://github.com/aantono))
|
||||||
|
- Remove hardcoded runtime.GOMAXPROCS. ([#2317](https://github.com/containous/traefik/pull/2317) by [ldez](https://github.com/ldez))
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- **[k8s]** fixed dead link in kubernetes backend config docs ([#2337](https://github.com/containous/traefik/pull/2337) by [perplexa](https://github.com/perplexa))
|
||||||
|
- **[k8s]** Fix the k8s docs example deployment yaml ([#2308](https://github.com/containous/traefik/pull/2308) by [gnur](https://github.com/gnur))
|
||||||
|
- Minor grammar change ([#2350](https://github.com/containous/traefik/pull/2350) by [haxorjim](https://github.com/haxorjim))
|
||||||
|
- Minor typo ([#2343](https://github.com/containous/traefik/pull/2343) by [burningTyger](https://github.com/burningTyger))
|
||||||
|
|
||||||
|
## [v1.4.1](https://github.com/containous/traefik/tree/v1.4.1) (2017-10-24)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v1.4.0...v1.4.1)
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[docker]** Network filter ([#2301](https://github.com/containous/traefik/pull/2301) by [ldez](https://github.com/ldez))
|
||||||
|
- **[healthcheck]** Fix healthcheck path ([#2295](https://github.com/containous/traefik/pull/2295) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- **[rules]** Regex capturing group. ([#2296](https://github.com/containous/traefik/pull/2296) by [ldez](https://github.com/ldez))
|
||||||
|
- **[websocket]** Force http/1.1 for websocket ([#2292](https://github.com/containous/traefik/pull/2292) by [Juliens](https://github.com/Juliens))
|
||||||
|
- Stream mode when http2 ([#2309](https://github.com/containous/traefik/pull/2309) by [Juliens](https://github.com/Juliens))
|
||||||
|
- Enhance Trust Forwarded Headers ([#2302](https://github.com/containous/traefik/pull/2302) by [ldez](https://github.com/ldez))
|
||||||
|
|
||||||
|
## [v1.4.0](https://github.com/containous/traefik/tree/v1.4.0) (2017-10-16)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v1.3.0-rc1...v1.4.0)
|
||||||
|
|
||||||
|
**Enhancements:**
|
||||||
|
- **[acme]** Display Traefik logs in integration tests ([#2114](https://github.com/containous/traefik/pull/2114) by [ldez](https://github.com/ldez))
|
||||||
|
- **[acme]** Make the ACME developments testing easier ([#1769](https://github.com/containous/traefik/pull/1769) by [nmengin](https://github.com/nmengin))
|
||||||
|
- **[acme]** contrib: Dump keys/certs from acme.json to files ([#1484](https://github.com/containous/traefik/pull/1484) by [brianredbeard](https://github.com/brianredbeard))
|
||||||
|
- **[api]** Add HTTP HEAD handling to /ping endpoint ([#1768](https://github.com/containous/traefik/pull/1768) by [martinbaillie](https://github.com/martinbaillie))
|
||||||
|
- **[authentication,consulcatalog]** Add Basic auth for consul catalog ([#2027](https://github.com/containous/traefik/pull/2027) by [mmatur](https://github.com/mmatur))
|
||||||
|
- **[authentication,marathon]** Add marathon label to configure basic auth ([#1799](https://github.com/containous/traefik/pull/1799) by [nikore](https://github.com/nikore))
|
||||||
|
- **[authentication,ecs]** Add basic auth for ecs ([#2026](https://github.com/containous/traefik/pull/2026) by [mmatur](https://github.com/mmatur))
|
||||||
|
- **[authentication,middleware]** Add forward authentication option ([#1972](https://github.com/containous/traefik/pull/1972) by [drampelt](https://github.com/drampelt))
|
||||||
|
- **[authentication]** Manage Headers for the Authentication forwarding. ([#2132](https://github.com/containous/traefik/pull/2132) by [ldez](https://github.com/ldez))
|
||||||
|
- **[consulcatalog,sticky-session]** Enable loadbalancer.sticky for Consul Catalog ([#1917](https://github.com/containous/traefik/pull/1917) by [nbonneval](https://github.com/nbonneval))
|
||||||
|
- **[consulcatalog]** Exposed by default feature in Consul Catalog ([#2006](https://github.com/containous/traefik/pull/2006) by [mmatur](https://github.com/mmatur))
|
||||||
|
- **[consulcatalog]** Speeding up consul catalog health change detection ([#1694](https://github.com/containous/traefik/pull/1694) by [vholovko](https://github.com/vholovko))
|
||||||
|
- **[consulcatalog]** Enhanced flexibility in Consul Catalog configuration ([#1565](https://github.com/containous/traefik/pull/1565) by [aantono](https://github.com/aantono))
|
||||||
|
- **[docker,k8s]** IP Whitelists for Frontend (with Docker- & Kubernetes-Provider Support) ([#1332](https://github.com/containous/traefik/pull/1332) by [MaZderMind](https://github.com/MaZderMind))
|
||||||
|
- **[ecs,sticky-session]** Enable loadbalancer.sticky for ECS ([#1925](https://github.com/containous/traefik/pull/1925) by [mmatur](https://github.com/mmatur))
|
||||||
|
- **[ecs]** Add support for several ECS backends ([#1913](https://github.com/containous/traefik/pull/1913) by [mmatur](https://github.com/mmatur))
|
||||||
|
- **[file]** Allow file provider to load service config from files in a directory. ([#1672](https://github.com/containous/traefik/pull/1672) by [rjshep](https://github.com/rjshep))
|
||||||
|
- **[healthcheck]** Add healthcheck command ([#1982](https://github.com/containous/traefik/pull/1982) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- **[healthcheck]** Allow overriding the port used for healthchecks ([#1567](https://github.com/containous/traefik/pull/1567) by [bakins](https://github.com/bakins))
|
||||||
|
- **[k8s,rules]** kubernetes ingress rewrite-target implementation ([#1723](https://github.com/containous/traefik/pull/1723) by [mlaccetti](https://github.com/mlaccetti))
|
||||||
|
- **[k8s]** Added ability to override frontend priority for k8s ingress router ([#1874](https://github.com/containous/traefik/pull/1874) by [DiverOfDark](https://github.com/DiverOfDark))
|
||||||
|
- **[kv]** Adds definitions to backend kv template for health checking ([#1644](https://github.com/containous/traefik/pull/1644) by [zachomedia](https://github.com/zachomedia))
|
||||||
|
- **[logs,dynamodb,ecs,marathon]** Link some providers logs to Traefik ([#1746](https://github.com/containous/traefik/pull/1746) by [ldez](https://github.com/ldez))
|
||||||
|
- **[logs,marathon]** remove confusing go-marathon log message ([#1810](https://github.com/containous/traefik/pull/1810) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[logs]** Send traefik logs to stdout instead stderr ([#2054](https://github.com/containous/traefik/pull/2054) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[logs]** enable logging to stdout for access logs ([#1683](https://github.com/containous/traefik/pull/1683) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[logs]** Logs & errors review ([#1673](https://github.com/containous/traefik/pull/1673) by [ldez](https://github.com/ldez))
|
||||||
|
- **[logs]** Switch access logging to logrus ([#1647](https://github.com/containous/traefik/pull/1647) by [rjshep](https://github.com/rjshep))
|
||||||
|
- **[logs]** log X-Forwarded-For as ClientHost if present ([#1946](https://github.com/containous/traefik/pull/1946) by [mildis](https://github.com/mildis))
|
||||||
|
- **[logs]** Restore: First stage of access logging middleware. ([#1571](https://github.com/containous/traefik/pull/1571) by [ldez](https://github.com/ldez))
|
||||||
|
- **[logs]** Add log file close and reopen on receipt of SIGUSR1 ([#1761](https://github.com/containous/traefik/pull/1761) by [rjshep](https://github.com/rjshep))
|
||||||
|
- **[logs]** add RetryAttempts to AccessLog in JSON format ([#1793](https://github.com/containous/traefik/pull/1793) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[logs]** Add JSON as access logging format ([#1669](https://github.com/containous/traefik/pull/1669) by [rjshep](https://github.com/rjshep))
|
||||||
|
- **[marathon]** Support multi-port service routing for containers running on Marathon ([#1742](https://github.com/containous/traefik/pull/1742) by [aantono](https://github.com/aantono))
|
||||||
|
- **[marathon]** Improve Marathon integration tests. ([#1406](https://github.com/containous/traefik/pull/1406) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[marathon]** Exported getSubDomain function from Marathon provider ([#1693](https://github.com/containous/traefik/pull/1693) by [aantono](https://github.com/aantono))
|
||||||
|
- **[marathon]** Use test builder. ([#1871](https://github.com/containous/traefik/pull/1871) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[marathon]** Add support for readiness checks. ([#1883](https://github.com/containous/traefik/pull/1883) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[marathon]** Move marathon mock ([#1732](https://github.com/containous/traefik/pull/1732) by [ldez](https://github.com/ldez))
|
||||||
|
- **[marathon]** Use single API call to fetch Marathon resources. ([#1815](https://github.com/containous/traefik/pull/1815) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[metrics]** Added RetryMetrics to DataDog and StatsD providers ([#1884](https://github.com/containous/traefik/pull/1884) by [aantono](https://github.com/aantono))
|
||||||
|
- **[metrics]** Extract metrics to own package and refactor implementations ([#1968](https://github.com/containous/traefik/pull/1968) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[metrics]** Add metrics for backend_retries_total ([#1504](https://github.com/containous/traefik/pull/1504) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[metrics]** Add status code to request duration metric ([#1755](https://github.com/containous/traefik/pull/1755) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[middleware]** Add trusted whitelist proxy protocol ([#2234](https://github.com/containous/traefik/pull/2234) by [emilevauge](https://github.com/emilevauge)))
|
||||||
|
- **[metrics]** DataDog and StatsD Metrics Support ([#1701](https://github.com/containous/traefik/pull/1701) by [aantono](https://github.com/aantono))
|
||||||
|
- **[middleware]** Create Header Middleware ([#1236](https://github.com/containous/traefik/pull/1236) by [dtomcej](https://github.com/dtomcej))
|
||||||
|
- **[middleware]** Add configurable timeouts and curate default timeout settings ([#1873](https://github.com/containous/traefik/pull/1873) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[middleware]** Fix command bug content. ([#2002](https://github.com/containous/traefik/pull/2002) by [ldez](https://github.com/ldez))
|
||||||
|
- **[middleware]** Retry only on real network errors ([#1549](https://github.com/containous/traefik/pull/1549) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[middleware]** Return 503 on empty backend ([#1748](https://github.com/containous/traefik/pull/1748) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[middleware]** Custom Error Pages ([#1675](https://github.com/containous/traefik/pull/1675) by [bparli](https://github.com/bparli))
|
||||||
|
- **[oxy]** Support X-Forwarded-Port. ([#1960](https://github.com/containous/traefik/pull/1960) by [ldez](https://github.com/ldez))
|
||||||
|
- **[provider,tls]** Added a check to ensure clientTLS configuration contains either a cert or a key ([#1932](https://github.com/containous/traefik/pull/1932) by [aantono](https://github.com/aantono))
|
||||||
|
- **[provider]** Deflake integration tests ([#1599](https://github.com/containous/traefik/pull/1599) by [ldez](https://github.com/ldez))
|
||||||
|
- **[provider]** Factorize labels ([#1843](https://github.com/containous/traefik/pull/1843) by [ldez](https://github.com/ldez))
|
||||||
|
- **[provider]** Replace go routine by Safe.Go ([#1879](https://github.com/containous/traefik/pull/1879) by [ldez](https://github.com/ldez))
|
||||||
|
- **[rancher]** Refactor into dual Rancher API/Metadata providers ([#1563](https://github.com/containous/traefik/pull/1563) by [martinbaillie](https://github.com/martinbaillie))
|
||||||
|
- **[rules]** Add support for Query String filtering ([#1934](https://github.com/containous/traefik/pull/1934) by [driverpt](https://github.com/driverpt))
|
||||||
|
- **[rules]** Simplify stripPrefix and stripPrefixRegex tests ([#1699](https://github.com/containous/traefik/pull/1699) by [ldez](https://github.com/ldez))
|
||||||
|
- **[rules]** Enhance rules tests. ([#1679](https://github.com/containous/traefik/pull/1679) by [ldez](https://github.com/ldez))
|
||||||
|
- **[sticky-session]** make the cookie name unique to the backend being served ([#1716](https://github.com/containous/traefik/pull/1716) by [richardjq](https://github.com/richardjq))
|
||||||
|
- **[tls]** Handle RootCAs certificate ([#1789](https://github.com/containous/traefik/pull/1789) by [Juliens](https://github.com/Juliens))
|
||||||
|
- **[tls]** enable TLS client forwarding ([#1446](https://github.com/containous/traefik/pull/1446) by [drewwells](https://github.com/drewwells))
|
||||||
|
- **[websocket]** Add tests for urlencoded part in url ([#2199](https://github.com/containous/traefik/pull/2199) by [Juliens](https://github.com/Juliens))
|
||||||
|
- **[websocket]** Add test for SSL TERMINATION in Websocket IT ([#2063](https://github.com/containous/traefik/pull/2063) by [Juliens](https://github.com/Juliens)
|
||||||
|
- **[webui]** Proxy in dev mode ([#1544](https://github.com/containous/traefik/pull/1544) by [maxwo](https://github.com/maxwo))
|
||||||
|
- **[webui]** Minor Health UI fixes ([#1651](https://github.com/containous/traefik/pull/1651) by [mihaitodor](https://github.com/mihaitodor))
|
||||||
|
- Fail fast in IT and fix some flaky tests ([#2126](https://github.com/containous/traefik/pull/2126) by [ldez](https://github.com/ldez))
|
||||||
|
- extract lb configuration steps into method ([#1841](https://github.com/containous/traefik/pull/1841) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- Add whitelist configuration option for entrypoints ([#1702](https://github.com/containous/traefik/pull/1702) by [christopherobin](https://github.com/christopherobin))
|
||||||
|
- Enhance integration tests ([#1842](https://github.com/containous/traefik/pull/1842) by [ldez](https://github.com/ldez))
|
||||||
|
- Add helloworld tests with gRPC ([#1845](https://github.com/containous/traefik/pull/1845) by [Juliens](https://github.com/Juliens))
|
||||||
|
- Add the sprig functions in the template engine ([#1891](https://github.com/containous/traefik/pull/1891) by [thomasbach76](https://github.com/thomasbach76))
|
||||||
|
- Refactor globalConfiguration / WebProvider ([#1938](https://github.com/containous/traefik/pull/1938) by [Juliens](https://github.com/Juliens))
|
||||||
|
- Code cleaning. ([#1956](https://github.com/containous/traefik/pull/1956) by [ldez](https://github.com/ldez))
|
||||||
|
- Add proxy protocol ([#2004](https://github.com/containous/traefik/pull/2004) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- Bump gorilla/mux version. ([#1954](https://github.com/containous/traefik/pull/1954) by [ldez](https://github.com/ldez))
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[cluster,kv]** Be certain to clear our marshalled representation before reloading it ([#2165](https://github.com/containous/traefik/pull/2165) by [gozer](https://github.com/gozer))
|
||||||
|
- **[consulcatalog,docker,ecs,k8s,kv,marathon,rancher,sticky-session]** Backward compatibility for sticky ([#2266](https://github.com/containous/traefik/pull/2266) by [ldez](https://github.com/ldez))
|
||||||
|
- **[consulcatalog,docker,ecs,k8s,marathon,rancher,sticky-session]** Stickiness cookie name ([#2232](https://github.com/containous/traefik/pull/2232) by [ldez](https://github.com/ldez))
|
||||||
|
- **[consulcatalog,docker,ecs,k8s,marathon,rancher,sticky-session]** Stickiness cookie name. ([#2251](https://github.com/containous/traefik/pull/2251) by [ldez](https://github.com/ldez))
|
||||||
|
- **[consulcatalog]** Fix consul catalog retry ([#2263](https://github.com/containous/traefik/pull/2263) by [mmatur](https://github.com/mmatur))
|
||||||
|
- **[consulcatalog]** Flaky tests and refresh problem in consul catalog ([#2148](https://github.com/containous/traefik/pull/2148) by [Juliens](https://github.com/Juliens))
|
||||||
|
- **[consulcatalog]** Consul catalog failed to remove service ([#2157](https://github.com/containous/traefik/pull/2157) by [Juliens](https://github.com/Juliens))
|
||||||
|
- **[consulcatalog]** Fix Consul Catalog refresh ([#2089](https://github.com/containous/traefik/pull/2089) by [Juliens](https://github.com/Juliens))
|
||||||
|
- **[docker]** Changed Docker network filter to allow any swarm network ([#2244](https://github.com/containous/traefik/pull/2244) by [pistolero](https://github.com/pistolero))
|
||||||
|
- **[docker]** Error handling for docker swarm mode ([#1533](https://github.com/containous/traefik/pull/1533) by [tanyadegurechaff](https://github.com/tanyadegurechaff))
|
||||||
|
- **[ecs]** Handle empty ECS Clusters properly ([#2170](https://github.com/containous/traefik/pull/2170) by [jeffreykoetsier](https://github.com/jeffreykoetsier))
|
||||||
|
- **[healthcheck]** Fix healthcheck port ([#2131](https://github.com/containous/traefik/pull/2131) by [fredix](https://github.com/fredix))
|
||||||
|
- **[healthcheck]** Bind healthcheck to backend by entryPointName ([#1868](https://github.com/containous/traefik/pull/1868) by [chrigl](https://github.com/chrigl))
|
||||||
|
- **[k8s]** Continue processing on invalid auth-realm annotation. ([#2252](https://github.com/containous/traefik/pull/2252) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[k8s]** Use default frontend priority of zero. ([#1906](https://github.com/containous/traefik/pull/1906) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[kv]** add retry backoff to staert config loading ([#2268](https://github.com/containous/traefik/pull/2268) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- **[logs,middleware]** Enable loss less rotation of log files ([#2062](https://github.com/containous/traefik/pull/2062) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[logs,middleware]** Access log default values ([#2061](https://github.com/containous/traefik/pull/2061) by [ldez](https://github.com/ldez))
|
||||||
|
- **[logs]** Fix flakiness in log rotation test ([#2213](https://github.com/containous/traefik/pull/2213) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[marathon]** Assign filtered tasks to apps contained in slice. ([#1881](https://github.com/containous/traefik/pull/1881) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[marathon]** Fix fallback to other nodes for Marathon ([#1740](https://github.com/containous/traefik/pull/1740) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[metrics]** prometheus, HTTP method and utf8 ([#2081](https://github.com/containous/traefik/pull/2081) by [ldez](https://github.com/ldez))
|
||||||
|
- **[middleware]** Enable prefix matching within slash boundaries ([#2214](https://github.com/containous/traefik/pull/2214) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[middleware]** Fix SSE subscriptions when retries are enabled ([#2145](https://github.com/containous/traefik/pull/2145) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[middleware]** compress: preserve status code ([#1948](https://github.com/containous/traefik/pull/1948) by [ldez](https://github.com/ldez))
|
||||||
|
- **[rancher]** Add stack name to backend name generation to fix rancher metadata backend ([#2107](https://github.com/containous/traefik/pull/2107) by [SantoDE](https://github.com/SantoDE))
|
||||||
|
- **[rancher]** Rancher host IP address ([#2101](https://github.com/containous/traefik/pull/2101) by [matq007](https://github.com/matq007))
|
||||||
|
- **[rancher]** fix seconds to really be seconds ([#2259](https://github.com/containous/traefik/pull/2259) by [SantoDE](https://github.com/SantoDE))
|
||||||
|
- **[rancher]** fix rancher api environment get ([#2053](https://github.com/containous/traefik/pull/2053) by [SantoDE](https://github.com/SantoDE))
|
||||||
|
- **[sticky-session]** Sanitize cookie names. ([#2216](https://github.com/containous/traefik/pull/2216) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[sticky-session]** Setting the Cookie Path explicitly to root ([#1950](https://github.com/containous/traefik/pull/1950) by [marcopaga](https://github.com/marcopaga))
|
||||||
|
- **[websocket]** Forward upgrade error from backend ([#2187](https://github.com/containous/traefik/pull/2187) by [Juliens](https://github.com/Juliens))
|
||||||
|
- **[websocket]** RawPath and Transfer TLSConfig in websocket ([#2088](https://github.com/containous/traefik/pull/2088) by [Juliens](https://github.com/Juliens))
|
||||||
|
- Nil body retries ([#2258](https://github.com/containous/traefik/pull/2258) by [Juliens](https://github.com/Juliens))
|
||||||
|
- Fix deprecated IdleTimeout config ([#2143](https://github.com/containous/traefik/pull/2143) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- Fixes entry points configuration. ([#2120](https://github.com/containous/traefik/pull/2120) by [ldez](https://github.com/ldez))
|
||||||
|
- Delay first version check ([#2215](https://github.com/containous/traefik/pull/2215) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- Move http2 configure transport ([#2231](https://github.com/containous/traefik/pull/2231) by [Juliens](https://github.com/Juliens))
|
||||||
|
- Fix error in prepareServer ([#2076](https://github.com/containous/traefik/pull/2076) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- New entry point parser. ([#2248](https://github.com/containous/traefik/pull/2248) by [ldez](https://github.com/ldez))
|
||||||
|
- Add TrustForwardHeader options. ([#2262](https://github.com/containous/traefik/pull/2262) by [ldez](https://github.com/ldez))
|
||||||
|
- `bug` command. ([#2178](https://github.com/containous/traefik/pull/2178) by [ldez](https://github.com/ldez))
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- **[acme,provider]** Enhance documentation readability. ([#2095](https://github.com/containous/traefik/pull/2095) by [ldez](https://github.com/ldez))
|
||||||
|
- **[acme,provider]** Fix whitespaces ([#2075](https://github.com/containous/traefik/pull/2075) by [chulkilee](https://github.com/chulkilee))
|
||||||
|
- **[acme,provider]** Re-organize documentation ([#2012](https://github.com/containous/traefik/pull/2012) by [jmaitrehenry](https://github.com/jmaitrehenry))
|
||||||
|
- **[acme]** Fix grammar ([#2208](https://github.com/containous/traefik/pull/2208) by [mvasin](https://github.com/mvasin))
|
||||||
|
- **[acme]** Add guide for Docker, Traefik & Letsencrypt ([#1923](https://github.com/containous/traefik/pull/1923) by [mvdstam](https://github.com/mvdstam))
|
||||||
|
- **[acme]** Improve Let's Encrypt documentation ([#1885](https://github.com/containous/traefik/pull/1885) by [nmengin](https://github.com/nmengin))
|
||||||
|
- **[acme]** Update docs for dnsimple env vars. ([#1872](https://github.com/containous/traefik/pull/1872) by [untalpierre](https://github.com/untalpierre))
|
||||||
|
- **[api]** Add examples of proxying ping ([#2102](https://github.com/containous/traefik/pull/2102) by [deitch](https://github.com/deitch))
|
||||||
|
- **[authentication,k8s]** traefik controller access to secrets ([#1707](https://github.com/containous/traefik/pull/1707) by [spinto](https://github.com/spinto))
|
||||||
|
- **[consul,tls]** doc change regarding consul SSL ([#1774](https://github.com/containous/traefik/pull/1774) by [bitsofinfo](https://github.com/bitsofinfo))
|
||||||
|
- **[consulcatalog,docker,ecs,k8s,marathon,rancher,sticky-session]** Stickiness documentation ([#2238](https://github.com/containous/traefik/pull/2238) by [ldez](https://github.com/ldez))
|
||||||
|
- **[consul]** added consul acl token note ([#1720](https://github.com/containous/traefik/pull/1720) by [bitsofinfo](https://github.com/bitsofinfo))
|
||||||
|
- **[docker]** Updating Docker output and curl for sticky sessions ([#2150](https://github.com/containous/traefik/pull/2150) by [jtyr](https://github.com/jtyr))
|
||||||
|
- **[docker]** Add more visibility to docker stack deploy label issue ([#1984](https://github.com/containous/traefik/pull/1984) by [jmaitrehenry](https://github.com/jmaitrehenry))
|
||||||
|
- **[ecs]** Fix IAM policy sid. ([#2066](https://github.com/containous/traefik/pull/2066) by [charlieoleary](https://github.com/charlieoleary))
|
||||||
|
- **[k8s,marathon]** Mark Marathon and Kubernetes as constraint-supporting. ([#1964](https://github.com/containous/traefik/pull/1964) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[k8s]** Add guide section on production advice, esp. CPU. ([#2113](https://github.com/containous/traefik/pull/2113) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[k8s]** Document ways to partition Ingresses in the k8s guide. ([#2223](https://github.com/containous/traefik/pull/2223) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[k8s]** Remove pod from RBAC rules. ([#2229](https://github.com/containous/traefik/pull/2229) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[k8s]** Quote priority values in annotation examples. ([#2230](https://github.com/containous/traefik/pull/2230) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[k8s]** Fix invalid service yaml example ([#2059](https://github.com/containous/traefik/pull/2059) by [kairen](https://github.com/kairen))
|
||||||
|
- **[k8s]** Update usage of `.local` with `.minikube` in k8s docs ([#1551](https://github.com/containous/traefik/pull/1551) by [errm](https://github.com/errm))
|
||||||
|
- **[k8s]** Update the documentation to use DaemonSet or Deployment ([#1735](https://github.com/containous/traefik/pull/1735) by [saschagrunert](https://github.com/saschagrunert))
|
||||||
|
- **[k8s]** Fix docs about default namespaces. ([#1961](https://github.com/containous/traefik/pull/1961) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[k8s]** Moved namespace to correct place ([#1911](https://github.com/containous/traefik/pull/1911) by [markround](https://github.com/markround))
|
||||||
|
- **[k8s]** examples/k8s: fix ui ingress port out of sync with deployment ([#1943](https://github.com/containous/traefik/pull/1943) by [borancar](https://github.com/borancar))
|
||||||
|
- **[k8s]** Add secrets resource to in-line RBAC spec. ([#1890](https://github.com/containous/traefik/pull/1890) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[k8s]** Improve documentation. ([#1831](https://github.com/containous/traefik/pull/1831) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[marathon]** Fix documentation glitches. ([#1996](https://github.com/containous/traefik/pull/1996) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[metrics]** Enhance web backend documentation ([#2122](https://github.com/containous/traefik/pull/2122) by [ldez](https://github.com/ldez))
|
||||||
|
- **[mesos]** fix: documentation Mesos. ([#2029](https://github.com/containous/traefik/pull/2029) by [ldez](https://github.com/ldez))
|
||||||
|
- **[middleware]** Improve compression documentation ([#2184](https://github.com/containous/traefik/pull/2184) by [errm](https://github.com/errm))
|
||||||
|
- **[provider]** Clarify that provider-enabling argument parameters set all defaults. ([#1830](https://github.com/containous/traefik/pull/1830) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[rancher]** Update Rancher documentation. ([#1776](https://github.com/containous/traefik/pull/1776) by [ldez](https://github.com/ldez))
|
||||||
|
- **[webui]** Document yarnpkg. ([#1558](https://github.com/containous/traefik/pull/1558) by [Stibbons](https://github.com/Stibbons))
|
||||||
|
- Add forward auth documentation. ([#2110](https://github.com/containous/traefik/pull/2110) by [ldez](https://github.com/ldez))
|
||||||
|
- User guide gRPC ([#2108](https://github.com/containous/traefik/pull/2108) by [Juliens](https://github.com/Juliens))
|
||||||
|
- Document custom error page restrictions. ([#2104](https://github.com/containous/traefik/pull/2104) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- Prepare release v1.4.0-rc3 ([#2135](https://github.com/containous/traefik/pull/2135) by [Juliens](https://github.com/Juliens))
|
||||||
|
- Update gRPC example ([#2191](https://github.com/containous/traefik/pull/2191) by [jsenon](https://github.com/jsenon))
|
||||||
|
- Prepare release v1.4.0-rc2 ([#2091](https://github.com/containous/traefik/pull/2091) by [ldez](https://github.com/ldez))
|
||||||
|
- Fix grammar mistake in the kv-config docs ([#2197](https://github.com/containous/traefik/pull/2197) by [chr4](https://github.com/chr4))
|
||||||
|
- Update cluster.md ([#2073](https://github.com/containous/traefik/pull/2073) by [kmbremner](https://github.com/kmbremner))
|
||||||
|
- Prepare release v1.4.0-rc4 ([#2201](https://github.com/containous/traefik/pull/2201) by [nmengin](https://github.com/nmengin))
|
||||||
|
- Prepare release v1.4.0-rc5 ([#2241](https://github.com/containous/traefik/pull/2241) by [ldez](https://github.com/ldez))
|
||||||
|
- Enhance documentation. ([#2048](https://github.com/containous/traefik/pull/2048) by [ldez](https://github.com/ldez))
|
||||||
|
- doc: add notes on server urls with path ([#2045](https://github.com/containous/traefik/pull/2045) by [chulkilee](https://github.com/chulkilee))
|
||||||
|
- Enhance security headers doc. ([#2042](https://github.com/containous/traefik/pull/2042) by [ldez](https://github.com/ldez))
|
||||||
|
- HTTPS for images, video and links in docs. ([#2041](https://github.com/containous/traefik/pull/2041) by [ldez](https://github.com/ldez))
|
||||||
|
- Fix error pages configuration. ([#2038](https://github.com/containous/traefik/pull/2038) by [ldez](https://github.com/ldez))
|
||||||
|
- Fix Proxy Protocol documentation ([#2253](https://github.com/containous/traefik/pull/2253) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- Update GraceTimeOut documentation ([#1875](https://github.com/containous/traefik/pull/1875) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- Release cycle. ([#1812](https://github.com/containous/traefik/pull/1812) by [ldez](https://github.com/ldez))
|
||||||
|
- Update contributing guide build steps ([#1801](https://github.com/containous/traefik/pull/1801) by [jsturtevant](https://github.com/jsturtevant))
|
||||||
|
- Add Nicolas Mengin to maintainers ([#1792](https://github.com/containous/traefik/pull/1792) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- Add Julien Salleyron to maintainers ([#1790](https://github.com/containous/traefik/pull/1790) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- Change to a more flexible PR review process ([#1781](https://github.com/containous/traefik/pull/1781) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- Traefik "bug" command documentation ([#1811](https://github.com/containous/traefik/pull/1811) by [ldez](https://github.com/ldez))
|
||||||
|
- Change Traefik intro video ([#1893](https://github.com/containous/traefik/pull/1893) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- Prepare release v1.4.0-rc1 ([#2021](https://github.com/containous/traefik/pull/2021) by [ldez](https://github.com/ldez))
|
||||||
|
- Add play-with-docker example ([#1726](https://github.com/containous/traefik/pull/1726) by [marcosnils](https://github.com/marcosnils))
|
||||||
|
- Add Marco Jantke to maintainers ([#1980](https://github.com/containous/traefik/pull/1980) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- Remove Russel from maintainers ([#1614](https://github.com/containous/traefik/pull/1614) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- Update CONTRIBUTING.md. ([#1667](https://github.com/containous/traefik/pull/1667) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- drop "slave" wording for "worker" ([#1645](https://github.com/containous/traefik/pull/1645) by [djalal](https://github.com/djalal))
|
||||||
|
- Use more inclusive language in README.md {guys => folks} ([#1640](https://github.com/containous/traefik/pull/1640) by [igorwwwwwwwwwwwwwwwwwwww](https://github.com/igorwwwwwwwwwwwwwwwwwwww))
|
||||||
|
- Remove Thomas Recloux from maintainers ([#1616](https://github.com/containous/traefik/pull/1616) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- Update documentation for 1.4 release ([#2011](https://github.com/containous/traefik/pull/2011) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- Small toml documentation update ([#1603](https://github.com/containous/traefik/pull/1603) by [antoine-aumjaud](https://github.com/antoine-aumjaud))
|
||||||
|
- Add @ldez to maintainers ([#1589](https://github.com/containous/traefik/pull/1589) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- doc: add labels documentation. ([#1582](https://github.com/containous/traefik/pull/1582) by [ldez](https://github.com/ldez))
|
||||||
|
- Update golang version in contributing guide ([#2018](https://github.com/containous/traefik/pull/2018) by [ArikaChen](https://github.com/ArikaChen))
|
||||||
|
- toml page - replace li by table ([#1995](https://github.com/containous/traefik/pull/1995) by [jmaitrehenry](https://github.com/jmaitrehenry))
|
||||||
|
|
||||||
|
**Misc:**
|
||||||
|
- Merge v1.3.7 ([#2013](https://github.com/containous/traefik/pull/2013) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge 1.3.6 ([#1992](https://github.com/containous/traefik/pull/1992) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge 1.3.5 ([#1909](https://github.com/containous/traefik/pull/1909) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge 1.3.3 ([#1836](https://github.com/containous/traefik/pull/1836) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge v1.3.2 to master ([#1809](https://github.com/containous/traefik/pull/1809) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge current v1.3 ([#1797](https://github.com/containous/traefik/pull/1797) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge current v1.3 ([#1786](https://github.com/containous/traefik/pull/1786) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge v1.3.1 to master ([#1763](https://github.com/containous/traefik/pull/1763) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge current v1.3 ([#1753](https://github.com/containous/traefik/pull/1753) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge current v1.3 ([#1705](https://github.com/containous/traefik/pull/1705) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge current v1.3 to master ([#1697](https://github.com/containous/traefik/pull/1697) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge v1 3 0 ([#1692](https://github.com/containous/traefik/pull/1692) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge current v1.3 to master (rc3) ([#1666](https://github.com/containous/traefik/pull/1666) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge current v1.3 to master ([#1643](https://github.com/containous/traefik/pull/1643) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge v1.3.0-rc2 master ([#1613](https://github.com/containous/traefik/pull/1613) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- Merge v1.3 branch into master [2017-05-11] ([#1548](https://github.com/containous/traefik/pull/1548) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
|
||||||
|
## [v1.4.0-rc5](https://github.com/containous/traefik/tree/v1.4.0-rc5) (2017-10-10)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v1.4.0-rc4...v1.4.0-rc5)
|
||||||
|
|
||||||
|
**Enhancements:**
|
||||||
|
- **[middleware]** Add trusted whitelist proxy protocol ([#2234](https://github.com/containous/traefik/pull/2234) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[consul,docker,ecs,k8s,marathon,rancher,sticky-session]** Stickiness cookie name ([#2232](https://github.com/containous/traefik/pull/2232) by [ldez](https://github.com/ldez))
|
||||||
|
- **[logs]** Fix flakiness in log rotation test ([#2213](https://github.com/containous/traefik/pull/2213) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[middleware]** Enable prefix matching within slash boundaries ([#2214](https://github.com/containous/traefik/pull/2214) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[sticky-session]** Sanitize cookie names. ([#2216](https://github.com/containous/traefik/pull/2216) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- Move http2 configure transport ([#2231](https://github.com/containous/traefik/pull/2231) by [Juliens](https://github.com/Juliens))
|
||||||
|
- Delay first version check ([#2215](https://github.com/containous/traefik/pull/2215) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- **[acme]** Fix grammar ([#2208](https://github.com/containous/traefik/pull/2208) by [mvasin](https://github.com/mvasin))
|
||||||
|
- **[docker,ecs,k8s,marathon,rancher]** Stickiness documentation ([#2238](https://github.com/containous/traefik/pull/2238) by [ldez](https://github.com/ldez))
|
||||||
|
- **[k8s]** Quote priority values in annotation examples. ([#2230](https://github.com/containous/traefik/pull/2230) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[k8s]** Remove pod from RBAC rules. ([#2229](https://github.com/containous/traefik/pull/2229) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[k8s]** Document ways to partition Ingresses in the k8s guide. ([#2223](https://github.com/containous/traefik/pull/2223) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
|
||||||
|
## [v1.4.0-rc4](https://github.com/containous/traefik/tree/v1.4.0-rc4) (2017-10-02)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v1.4.0-rc3...v1.4.0-rc4)
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[cluster,kv]** Be certain to clear our marshalled representation before reloading it ([#2165](https://github.com/containous/traefik/pull/2165) by [gozer](https://github.com/gozer))
|
||||||
|
- **[consulcatalog]** Consul catalog failed to remove service ([#2157](https://github.com/containous/traefik/pull/2157) by [Juliens](https://github.com/Juliens))
|
||||||
|
- **[consulcatalog]** Flaky tests and refresh problem in consul catalog ([#2148](https://github.com/containous/traefik/pull/2148) by [Juliens](https://github.com/Juliens))
|
||||||
|
- **[ecs]** Handle empty ECS Clusters properly ([#2170](https://github.com/containous/traefik/pull/2170) by [jeffreykoetsier](https://github.com/jeffreykoetsier))
|
||||||
|
- **[middleware]** Fix SSE subscriptions when retries are enabled ([#2145](https://github.com/containous/traefik/pull/2145) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[websocket]** Forward upgrade error from backend ([#2187](https://github.com/containous/traefik/pull/2187) by [Juliens](https://github.com/Juliens))
|
||||||
|
- `bug` command. ([#2178](https://github.com/containous/traefik/pull/2178) by [ldez](https://github.com/ldez))
|
||||||
|
- Fix deprecated IdleTimeout config ([#2143](https://github.com/containous/traefik/pull/2143) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- **[docker]** Updating Docker output and curl for sticky sessions ([#2150](https://github.com/containous/traefik/pull/2150) by [jtyr](https://github.com/jtyr))
|
||||||
|
- **[middleware]** Improve compression documentation ([#2184](https://github.com/containous/traefik/pull/2184) by [errm](https://github.com/errm))
|
||||||
|
- Fix grammar mistake in the kv-config docs ([#2197](https://github.com/containous/traefik/pull/2197) by [chr4](https://github.com/chr4))
|
||||||
|
- Update gRPC example ([#2191](https://github.com/containous/traefik/pull/2191) by [jsenon](https://github.com/jsenon))
|
||||||
|
|
||||||
|
**Misc:**
|
||||||
|
- **[websocket]** Add tests for urlencoded part in url ([#2199](https://github.com/containous/traefik/pull/2199) by [Juliens](https://github.com/Juliens))
|
||||||
|
|
||||||
|
## [v1.4.0-rc3](https://github.com/containous/traefik/tree/v1.4.0-rc3) (2017-09-18)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v1.4.0-rc2...v1.4.0-rc3)
|
||||||
|
|
||||||
|
**Enhancements:**
|
||||||
|
- **[acme]** Display Traefik logs in integration tests ([#2114](https://github.com/containous/traefik/pull/2114) by [ldez](https://github.com/ldez))
|
||||||
|
- **[authentication]** Manage Headers for the Authentication forwarding. ([#2132](https://github.com/containous/traefik/pull/2132) by [ldez](https://github.com/ldez))
|
||||||
|
- Fail fast in IT and fix some flaky tests ([#2126](https://github.com/containous/traefik/pull/2126) by [ldez](https://github.com/ldez))
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[consul]** Fix Consul Catalog refresh ([#2089](https://github.com/containous/traefik/pull/2089) by [Juliens](https://github.com/Juliens))
|
||||||
|
- **[healthcheck]** Fix healthcheck port ([#2131](https://github.com/containous/traefik/pull/2131) by [fredix](https://github.com/fredix))
|
||||||
|
- **[logs,middleware]** Enable loss less rotation of log files ([#2062](https://github.com/containous/traefik/pull/2062) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[rancher]** Add stack name to backend name generation to fix rancher metadata backend ([#2107](https://github.com/containous/traefik/pull/2107) by [SantoDE](https://github.com/SantoDE))
|
||||||
|
- **[rancher]** Rancher host IP address ([#2101](https://github.com/containous/traefik/pull/2101) by [matq007](https://github.com/matq007))
|
||||||
|
- Fixes entry points configuration. ([#2120](https://github.com/containous/traefik/pull/2120) by [ldez](https://github.com/ldez))
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- **[acme,provider]** Enhance documentation readability. ([#2095](https://github.com/containous/traefik/pull/2095) by [ldez](https://github.com/ldez))
|
||||||
|
- **[api]** Add examples of proxying ping ([#2102](https://github.com/containous/traefik/pull/2102) by [deitch](https://github.com/deitch))
|
||||||
|
- **[k8s]** Add guide section on production advice, esp. CPU. ([#2113](https://github.com/containous/traefik/pull/2113) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[metrics]** Enhance web backend documentation ([#2122](https://github.com/containous/traefik/pull/2122) by [ldez](https://github.com/ldez))
|
||||||
|
- Add forward auth documentation. ([#2110](https://github.com/containous/traefik/pull/2110) by [ldez](https://github.com/ldez))
|
||||||
|
- User guide gRPC ([#2108](https://github.com/containous/traefik/pull/2108) by [Juliens](https://github.com/Juliens))
|
||||||
|
- Document custom error page restrictions. ([#2104](https://github.com/containous/traefik/pull/2104) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
|
||||||
|
## [v1.4.0-rc2](https://github.com/containous/traefik/tree/v1.4.0-rc2) (2017-09-08)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v1.4.0-rc1...v1.4.0-rc2)
|
||||||
|
|
||||||
|
**Enhancements:**
|
||||||
|
- **[authentication,consul]** Add Basic auth for consul catalog ([#2027](https://github.com/containous/traefik/pull/2027) by [mmatur](https://github.com/mmatur))
|
||||||
|
- **[authentication,ecs]** Add basic auth for ecs ([#2026](https://github.com/containous/traefik/pull/2026) by [mmatur](https://github.com/mmatur))
|
||||||
|
- **[logs]** Send traefik logs to stdout instead stderr ([#2054](https://github.com/containous/traefik/pull/2054) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[websocket]** Add test for SSL TERMINATION in Websocket IT ([#2063](https://github.com/containous/traefik/pull/2063) by [Juliens](https://github.com/Juliens))
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[consul]** Fix consul catalog refresh problems ([#2089](https://github.com/containous/traefik/pull/2089) by [Juliens](https://github.com/Juliens))
|
||||||
|
- **[logs,middleware]** Access log default values ([#2061](https://github.com/containous/traefik/pull/2061) by [ldez](https://github.com/ldez))
|
||||||
|
- **[metrics]** prometheus, HTTP method and utf8 ([#2081](https://github.com/containous/traefik/pull/2081) by [ldez](https://github.com/ldez))
|
||||||
|
- **[rancher]** fix rancher api environment get ([#2053](https://github.com/containous/traefik/pull/2053) by [SantoDE](https://github.com/SantoDE))
|
||||||
|
- **[websocket]** RawPath and Transfer TLSConfig in websocket ([#2088](https://github.com/containous/traefik/pull/2088) by [Juliens](https://github.com/Juliens))
|
||||||
|
- Fix error in prepareServer ([#2076](https://github.com/containous/traefik/pull/2076) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- **[acme,provider]** Fix whitespaces ([#2075](https://github.com/containous/traefik/pull/2075) by [chulkilee](https://github.com/chulkilee))
|
||||||
|
- **[ecs]** Fix IAM policy sid. ([#2066](https://github.com/containous/traefik/pull/2066) by [charlieoleary](https://github.com/charlieoleary))
|
||||||
|
- **[k8s]** Fix invalid service yaml example ([#2059](https://github.com/containous/traefik/pull/2059) by [kairen](https://github.com/kairen))
|
||||||
|
- **[mesos]** fix: documentation Mesos. ([#2029](https://github.com/containous/traefik/pull/2029) by [ldez](https://github.com/ldez))
|
||||||
|
- Update cluster.md ([#2073](https://github.com/containous/traefik/pull/2073) by [kmbremner](https://github.com/kmbremner))
|
||||||
|
- Enhance documentation. ([#2048](https://github.com/containous/traefik/pull/2048) by [ldez](https://github.com/ldez))
|
||||||
|
- doc: add notes on server urls with path ([#2045](https://github.com/containous/traefik/pull/2045) by [chulkilee](https://github.com/chulkilee))
|
||||||
|
- Enhance security headers doc. ([#2042](https://github.com/containous/traefik/pull/2042) by [ldez](https://github.com/ldez))
|
||||||
|
- HTTPS for images, video and links in docs. ([#2041](https://github.com/containous/traefik/pull/2041) by [ldez](https://github.com/ldez))
|
||||||
|
- Fix error pages configuration. ([#2038](https://github.com/containous/traefik/pull/2038) by [ldez](https://github.com/ldez))
|
||||||
|
|
||||||
|
## [v1.4.0-rc1](https://github.com/containous/traefik/tree/v1.4.0-rc1) (2017-08-28)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v1.3.0-rc1...v1.4.0-rc1)
|
||||||
|
|
||||||
|
**Enhancements:**
|
||||||
|
- **[acme]** Make the ACME developments testing easier ([#1769](https://github.com/containous/traefik/pull/1769) by [nmengin](https://github.com/nmengin))
|
||||||
|
- **[acme]** contrib: Dump keys/certs from acme.json to files ([#1484](https://github.com/containous/traefik/pull/1484) by [brianredbeard](https://github.com/brianredbeard))
|
||||||
|
- **[api]** Add HTTP HEAD handling to /ping endpoint ([#1768](https://github.com/containous/traefik/pull/1768) by [martinbaillie](https://github.com/martinbaillie))
|
||||||
|
- **[authentication,marathon]** Add marathon label to configure basic auth ([#1799](https://github.com/containous/traefik/pull/1799) by [nikore](https://github.com/nikore))
|
||||||
|
- **[authentication,middleware]** Add forward authentication option ([#1972](https://github.com/containous/traefik/pull/1972) by [drampelt](https://github.com/drampelt))
|
||||||
|
- **[consul,sticky-session]** Enable loadbalancer.sticky for Consul Catalog ([#1917](https://github.com/containous/traefik/pull/1917) by [nbonneval](https://github.com/nbonneval))
|
||||||
|
- **[consul]** Enhanced flexibility in Consul Catalog configuration ([#1565](https://github.com/containous/traefik/pull/1565) by [aantono](https://github.com/aantono))
|
||||||
|
- **[consul]** Exposed by default feature in Consul Catalog ([#2006](https://github.com/containous/traefik/pull/2006) by [mmatur](https://github.com/mmatur))
|
||||||
|
- **[consul]** Speeding up consul catalog health change detection ([#1694](https://github.com/containous/traefik/pull/1694) by [vholovko](https://github.com/vholovko))
|
||||||
|
- **[docker,k8s]** IP Whitelists for Frontend (with Docker- & Kubernetes-Provider Support) ([#1332](https://github.com/containous/traefik/pull/1332) by [MaZderMind](https://github.com/MaZderMind))
|
||||||
|
- **[ecs,sticky-session]** Enable loadbalancer.sticky for ECS ([#1925](https://github.com/containous/traefik/pull/1925) by [mmatur](https://github.com/mmatur))
|
||||||
|
- **[ecs]** Add support for several ECS backends ([#1913](https://github.com/containous/traefik/pull/1913) by [mmatur](https://github.com/mmatur))
|
||||||
|
- **[healthcheck]** Add healthcheck command ([#1982](https://github.com/containous/traefik/pull/1982) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- **[healthcheck]** Allow overriding the port used for healthchecks ([#1567](https://github.com/containous/traefik/pull/1567) by [bakins](https://github.com/bakins))
|
||||||
|
- **[k8s,rules]** kubernetes ingress rewrite-target implementation ([#1723](https://github.com/containous/traefik/pull/1723) by [mlaccetti](https://github.com/mlaccetti))
|
||||||
|
- **[k8s]** Added ability to override frontend priority for k8s ingress router ([#1874](https://github.com/containous/traefik/pull/1874) by [DiverOfDark](https://github.com/DiverOfDark))
|
||||||
|
- **[kv]** Adds definitions to backend kv template for health checking ([#1644](https://github.com/containous/traefik/pull/1644) by [zachomedia](https://github.com/zachomedia))
|
||||||
|
- **[logs,dynamodb,ecs,marathon]** Link some providers logs to Traefik ([#1746](https://github.com/containous/traefik/pull/1746) by [ldez](https://github.com/ldez))
|
||||||
|
- **[logs,marathon]** remove confusing go-marathon log message ([#1810](https://github.com/containous/traefik/pull/1810) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[logs]** enable logging to stdout for access logs ([#1683](https://github.com/containous/traefik/pull/1683) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[logs]** Logs & errors review ([#1673](https://github.com/containous/traefik/pull/1673) by [ldez](https://github.com/ldez))
|
||||||
|
- **[logs]** log X-Forwarded-For as ClientHost if present ([#1946](https://github.com/containous/traefik/pull/1946) by [mildis](https://github.com/mildis))
|
||||||
|
- **[logs]** Switch access logging to logrus ([#1647](https://github.com/containous/traefik/pull/1647) by [rjshep](https://github.com/rjshep))
|
||||||
|
- **[logs]** add RetryAttempts to AccessLog in JSON format ([#1793](https://github.com/containous/traefik/pull/1793) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[logs]** Restore: First stage of access logging middleware. ([#1571](https://github.com/containous/traefik/pull/1571) by [ldez](https://github.com/ldez))
|
||||||
|
- **[logs]** Add log file close and reopen on receipt of SIGUSR1 ([#1761](https://github.com/containous/traefik/pull/1761) by [rjshep](https://github.com/rjshep))
|
||||||
|
- **[logs]** Add JSON as access logging format ([#1669](https://github.com/containous/traefik/pull/1669) by [rjshep](https://github.com/rjshep))
|
||||||
|
- **[marathon]** Add support for readiness checks. ([#1883](https://github.com/containous/traefik/pull/1883) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[marathon]** Exported getSubDomain function from Marathon provider ([#1693](https://github.com/containous/traefik/pull/1693) by [aantono](https://github.com/aantono))
|
||||||
|
- **[marathon]** Improve Marathon integration tests. ([#1406](https://github.com/containous/traefik/pull/1406) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[marathon]** Use single API call to fetch Marathon resources. ([#1815](https://github.com/containous/traefik/pull/1815) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[marathon]** Move marathon mock ([#1732](https://github.com/containous/traefik/pull/1732) by [ldez](https://github.com/ldez))
|
||||||
|
- **[marathon]** Support multi-port service routing for containers running on Marathon ([#1742](https://github.com/containous/traefik/pull/1742) by [aantono](https://github.com/aantono))
|
||||||
|
- **[marathon]** Use test builder. ([#1871](https://github.com/containous/traefik/pull/1871) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[metrics]** DataDog and StatsD Metrics Support ([#1701](https://github.com/containous/traefik/pull/1701) by [aantono](https://github.com/aantono))
|
||||||
|
- **[metrics]** Add status code to request duration metric ([#1755](https://github.com/containous/traefik/pull/1755) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[metrics]** Add metrics for backend_retries_total ([#1504](https://github.com/containous/traefik/pull/1504) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[metrics]** Extract metrics to own package and refactor implementations ([#1968](https://github.com/containous/traefik/pull/1968) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[metrics]** Added RetryMetrics to DataDog and StatsD providers ([#1884](https://github.com/containous/traefik/pull/1884) by [aantono](https://github.com/aantono))
|
||||||
|
- **[middleware]** Return 503 on empty backend ([#1748](https://github.com/containous/traefik/pull/1748) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[middleware]** Add configurable timeouts and curate default timeout settings ([#1873](https://github.com/containous/traefik/pull/1873) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[middleware]** Custom Error Pages ([#1675](https://github.com/containous/traefik/pull/1675) by [bparli](https://github.com/bparli))
|
||||||
|
- **[middleware]** Retry only on real network errors ([#1549](https://github.com/containous/traefik/pull/1549) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[middleware]** Fix command bug content. ([#2002](https://github.com/containous/traefik/pull/2002) by [ldez](https://github.com/ldez))
|
||||||
|
- **[middleware]** Create Header Middleware ([#1236](https://github.com/containous/traefik/pull/1236) by [dtomcej](https://github.com/dtomcej))
|
||||||
|
- **[oxy]** Support X-Forwarded-Port. ([#1960](https://github.com/containous/traefik/pull/1960) by [ldez](https://github.com/ldez))
|
||||||
|
- **[provider,tls]** Added a check to ensure clientTLS configuration contains either a cert or a key ([#1932](https://github.com/containous/traefik/pull/1932) by [aantono](https://github.com/aantono))
|
||||||
|
- **[provider]** Factorize labels ([#1843](https://github.com/containous/traefik/pull/1843) by [ldez](https://github.com/ldez))
|
||||||
|
- **[provider]** Replace go routine by Safe.Go ([#1879](https://github.com/containous/traefik/pull/1879) by [ldez](https://github.com/ldez))
|
||||||
|
- **[provider]** Deflake integration tests ([#1599](https://github.com/containous/traefik/pull/1599) by [ldez](https://github.com/ldez))
|
||||||
|
- **[rancher]** Refactor into dual Rancher API/Metadata providers ([#1563](https://github.com/containous/traefik/pull/1563) by [martinbaillie](https://github.com/martinbaillie))
|
||||||
|
- **[rules]** Simplify stripPrefix and stripPrefixRegex tests ([#1699](https://github.com/containous/traefik/pull/1699) by [ldez](https://github.com/ldez))
|
||||||
|
- **[rules]** Add support for Query String filtering ([#1934](https://github.com/containous/traefik/pull/1934) by [driverpt](https://github.com/driverpt))
|
||||||
|
- **[rules]** Enhance rules tests. ([#1679](https://github.com/containous/traefik/pull/1679) by [ldez](https://github.com/ldez))
|
||||||
|
- **[sticky-session]** make the cookie name unique to the backend being served ([#1716](https://github.com/containous/traefik/pull/1716) by [richardjq](https://github.com/richardjq))
|
||||||
|
- **[tls]** Handle RootCAs certificate ([#1789](https://github.com/containous/traefik/pull/1789) by [Juliens](https://github.com/Juliens))
|
||||||
|
- **[tls]** enable TLS client forwarding ([#1446](https://github.com/containous/traefik/pull/1446) by [drewwells](https://github.com/drewwells))
|
||||||
|
- **[webui]** Minor Health UI fixes ([#1651](https://github.com/containous/traefik/pull/1651) by [mihaitodor](https://github.com/mihaitodor))
|
||||||
|
- **[webui]** Proxy in dev mode ([#1544](https://github.com/containous/traefik/pull/1544) by [maxwo](https://github.com/maxwo))
|
||||||
|
- extract lb configuration steps into method ([#1841](https://github.com/containous/traefik/pull/1841) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- Allow file provider to load service config from files in a directory. ([#1672](https://github.com/containous/traefik/pull/1672) by [rjshep](https://github.com/rjshep))
|
||||||
|
- Add whitelist configuration option for entrypoints ([#1702](https://github.com/containous/traefik/pull/1702) by [christopherobin](https://github.com/christopherobin))
|
||||||
|
- Enhance integration tests ([#1842](https://github.com/containous/traefik/pull/1842) by [ldez](https://github.com/ldez))
|
||||||
|
- Add helloworld tests with gRPC ([#1845](https://github.com/containous/traefik/pull/1845) by [Juliens](https://github.com/Juliens))
|
||||||
|
- Add the sprig functions in the template engine ([#1891](https://github.com/containous/traefik/pull/1891) by [thomasbach76](https://github.com/thomasbach76))
|
||||||
|
- Refactor globalConfiguration / WebProvider ([#1938](https://github.com/containous/traefik/pull/1938) by [Juliens](https://github.com/Juliens))
|
||||||
|
- Code cleaning. ([#1956](https://github.com/containous/traefik/pull/1956) by [ldez](https://github.com/ldez))
|
||||||
|
- Add proxy protocol ([#2004](https://github.com/containous/traefik/pull/2004) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- Bump gorilla/mux version. ([#1954](https://github.com/containous/traefik/pull/1954) by [ldez](https://github.com/ldez))
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[docker]** Error handling for docker swarm mode ([#1533](https://github.com/containous/traefik/pull/1533) by [tanyadegurechaff](https://github.com/tanyadegurechaff))
|
||||||
|
- **[healthcheck]** Bind healthcheck to backend by entryPointName ([#1868](https://github.com/containous/traefik/pull/1868) by [chrigl](https://github.com/chrigl))
|
||||||
|
- **[k8s]** Use default frontend priority of zero. ([#1906](https://github.com/containous/traefik/pull/1906) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[marathon]** Assign filtered tasks to apps contained in slice. ([#1881](https://github.com/containous/traefik/pull/1881) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[marathon]** Fix fallback to other nodes for Marathon ([#1740](https://github.com/containous/traefik/pull/1740) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
- **[middleware]** compress: preserve status code ([#1948](https://github.com/containous/traefik/pull/1948) by [ldez](https://github.com/ldez))
|
||||||
|
- **[sticky-session]** Setting the Cookie Path explicitly to root ([#1950](https://github.com/containous/traefik/pull/1950) by [marcopaga](https://github.com/marcopaga))
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- **[acme,provider]** Re-organize documentation ([#2012](https://github.com/containous/traefik/pull/2012) by [jmaitrehenry](https://github.com/jmaitrehenry))
|
||||||
|
- **[acme]** Add guide for Docker, Traefik & Letsencrypt ([#1923](https://github.com/containous/traefik/pull/1923) by [mvdstam](https://github.com/mvdstam))
|
||||||
|
- **[acme]** Update docs for dnsimple env vars. ([#1872](https://github.com/containous/traefik/pull/1872) by [klud1](https://github.com/klud1))
|
||||||
|
- **[acme]** Improve Let's Encrypt documentation ([#1885](https://github.com/containous/traefik/pull/1885) by [nmengin](https://github.com/nmengin))
|
||||||
|
- **[authentication,k8s]** traefik controller access to secrets ([#1707](https://github.com/containous/traefik/pull/1707) by [spinto](https://github.com/spinto))
|
||||||
|
- **[consul,tls]** doc change regarding consul SSL ([#1774](https://github.com/containous/traefik/pull/1774) by [bitsofinfo](https://github.com/bitsofinfo))
|
||||||
|
- **[consul]** added consul acl token note ([#1720](https://github.com/containous/traefik/pull/1720) by [bitsofinfo](https://github.com/bitsofinfo))
|
||||||
|
- **[docker]** Add more visibility to docker stack deploy label issue ([#1984](https://github.com/containous/traefik/pull/1984) by [jmaitrehenry](https://github.com/jmaitrehenry))
|
||||||
|
- **[k8s,marathon]** Mark Marathon and Kubernetes as constraint-supporting. ([#1964](https://github.com/containous/traefik/pull/1964) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[k8s]** examples/k8s: fix ui ingress port out of sync with deployment ([#1943](https://github.com/containous/traefik/pull/1943) by [borancar](https://github.com/borancar))
|
||||||
|
- **[k8s]** Update the documentation to use DaemonSet or Deployment ([#1735](https://github.com/containous/traefik/pull/1735) by [saschagrunert](https://github.com/saschagrunert))
|
||||||
|
- **[k8s]** Moved namespace to correct place ([#1911](https://github.com/containous/traefik/pull/1911) by [markround](https://github.com/markround))
|
||||||
|
- **[k8s]** Improve documentation. ([#1831](https://github.com/containous/traefik/pull/1831) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[k8s]** Add secrets resource to in-line RBAC spec. ([#1890](https://github.com/containous/traefik/pull/1890) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[k8s]** Fix docs about default namespaces. ([#1961](https://github.com/containous/traefik/pull/1961) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[k8s]** Update usage of `.local` with `.minikube` in k8s docs ([#1551](https://github.com/containous/traefik/pull/1551) by [errm](https://github.com/errm))
|
||||||
|
- **[marathon]** Fix documentation glitches. ([#1996](https://github.com/containous/traefik/pull/1996) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[provider]** Clarify that provider-enabling argument parameters set all defaults. ([#1830](https://github.com/containous/traefik/pull/1830) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- **[rancher]** Update Rancher documentation. ([#1776](https://github.com/containous/traefik/pull/1776) by [ldez](https://github.com/ldez))
|
||||||
|
- **[webui]** Document yarnpkg. ([#1558](https://github.com/containous/traefik/pull/1558) by [Stibbons](https://github.com/Stibbons))
|
||||||
|
- Add play-with-docker example ([#1726](https://github.com/containous/traefik/pull/1726) by [marcosnils](https://github.com/marcosnils))
|
||||||
|
- Update contributing guide build steps ([#1801](https://github.com/containous/traefik/pull/1801) by [jsturtevant](https://github.com/jsturtevant))
|
||||||
|
- Add Nicolas Mengin to maintainers ([#1792](https://github.com/containous/traefik/pull/1792) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- Add Julien Salleyron to maintainers ([#1790](https://github.com/containous/traefik/pull/1790) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- Change to a more flexible PR review process ([#1781](https://github.com/containous/traefik/pull/1781) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- Traefik "bug" command documentation ([#1811](https://github.com/containous/traefik/pull/1811) by [ldez](https://github.com/ldez))
|
||||||
|
- Add Marco Jantke to maintainers ([#1980](https://github.com/containous/traefik/pull/1980) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- toml page - replace li by table ([#1995](https://github.com/containous/traefik/pull/1995) by [jmaitrehenry](https://github.com/jmaitrehenry))
|
||||||
|
- Update golang version in contributing guide ([#2018](https://github.com/containous/traefik/pull/2018) by [ArikaChen](https://github.com/ArikaChen))
|
||||||
|
- Release cycle. ([#1812](https://github.com/containous/traefik/pull/1812) by [ldez](https://github.com/ldez))
|
||||||
|
- Remove Russel from maintainers ([#1614](https://github.com/containous/traefik/pull/1614) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- Update CONTRIBUTING.md. ([#1667](https://github.com/containous/traefik/pull/1667) by [timoreimann](https://github.com/timoreimann))
|
||||||
|
- drop "slave" wording for "worker" ([#1645](https://github.com/containous/traefik/pull/1645) by [djalal](https://github.com/djalal))
|
||||||
|
- Use more inclusive language in README.md {guys => folks} ([#1640](https://github.com/containous/traefik/pull/1640) by [igorwwwwwwwwwwwwwwwwwwww](https://github.com/igorwwwwwwwwwwwwwwwwwwww))
|
||||||
|
- Remove Thomas Recloux from maintainers ([#1616](https://github.com/containous/traefik/pull/1616) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- Update documentation for 1.4 release ([#2011](https://github.com/containous/traefik/pull/2011) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- Small toml documentation update ([#1603](https://github.com/containous/traefik/pull/1603) by [antoine-aumjaud](https://github.com/antoine-aumjaud))
|
||||||
|
- Add @ldez to maintainers ([#1589](https://github.com/containous/traefik/pull/1589) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- doc: add labels documentation. ([#1582](https://github.com/containous/traefik/pull/1582) by [ldez](https://github.com/ldez))
|
||||||
|
- Change Traefik intro video ([#1893](https://github.com/containous/traefik/pull/1893) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
- Update GraceTimeOut documentation ([#1875](https://github.com/containous/traefik/pull/1875) by [marco-jantke](https://github.com/marco-jantke))
|
||||||
|
|
||||||
|
**Misc:**
|
||||||
|
- Merge v1.3.7 ([#2013](https://github.com/containous/traefik/pull/2013) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge 1.3.6 ([#1992](https://github.com/containous/traefik/pull/1992) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge 1.3.5 ([#1909](https://github.com/containous/traefik/pull/1909) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge 1.3.3 ([#1836](https://github.com/containous/traefik/pull/1836) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge v1.3.2 to master ([#1809](https://github.com/containous/traefik/pull/1809) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge current v1.3 ([#1797](https://github.com/containous/traefik/pull/1797) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge current v1.3 ([#1786](https://github.com/containous/traefik/pull/1786) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge v1.3.1 to master ([#1763](https://github.com/containous/traefik/pull/1763) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge current v1.3 ([#1753](https://github.com/containous/traefik/pull/1753) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge current v1.3 ([#1705](https://github.com/containous/traefik/pull/1705) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge current v1.3 to master ([#1697](https://github.com/containous/traefik/pull/1697) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge v1 3 0 ([#1692](https://github.com/containous/traefik/pull/1692) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge current v1.3 to master (rc3) ([#1666](https://github.com/containous/traefik/pull/1666) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge current v1.3 to master ([#1643](https://github.com/containous/traefik/pull/1643) by [ldez](https://github.com/ldez))
|
||||||
|
- Merge v1.3.0-rc2 master ([#1613](https://github.com/containous/traefik/pull/1613) by [emilevauge](https://github.com/emilevauge))
|
||||||
|
|
||||||
|
## [v1.3.8](https://github.com/containous/traefik/tree/v1.3.8) (2017-09-07)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v1.3.7...v1.3.8)
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[middleware]** Compress and Webscocket ([#2079](https://github.com/containous/traefik/pull/2079) by [ldez](https://github.com/ldez))
|
||||||
|
|
||||||
|
## [v1.3.7](https://github.com/containous/traefik/tree/v1.3.7) (2017-08-25)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v1.3.6...v1.3.7)
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[oxy]** Only forward X-Forwarded-Port. ([#2007](https://github.com/containous/traefik/pull/2007) by [ldez](https://github.com/ldez))
|
||||||
|
|
||||||
## [v1.3.6](https://github.com/containous/traefik/tree/v1.3.6) (2017-08-20)
|
## [v1.3.6](https://github.com/containous/traefik/tree/v1.3.6) (2017-08-20)
|
||||||
[All Commits](https://github.com/containous/traefik/compare/v1.3.5...v1.3.6)
|
[All Commits](https://github.com/containous/traefik/compare/v1.3.5...v1.3.6)
|
||||||
|
|
||||||
@@ -329,7 +1012,7 @@
|
|||||||
- Chunk taskArns into groups of 100 [\#1209](https://github.com/containous/traefik/pull/1209) ([owen](https://github.com/owen))
|
- Chunk taskArns into groups of 100 [\#1209](https://github.com/containous/traefik/pull/1209) ([owen](https://github.com/owen))
|
||||||
- Prepare release v1.2.0 rc2 [\#1204](https://github.com/containous/traefik/pull/1204) ([emilevauge](https://github.com/emilevauge))
|
- Prepare release v1.2.0 rc2 [\#1204](https://github.com/containous/traefik/pull/1204) ([emilevauge](https://github.com/emilevauge))
|
||||||
- Revert "Ensure that we don't add balancees with no health check runs … [\#1198](https://github.com/containous/traefik/pull/1198) ([jangie](https://github.com/jangie))
|
- Revert "Ensure that we don't add balancees with no health check runs … [\#1198](https://github.com/containous/traefik/pull/1198) ([jangie](https://github.com/jangie))
|
||||||
- Small fixes and improvments [\#1173](https://github.com/containous/traefik/pull/1173) ([SantoDE](https://github.com/SantoDE))
|
- Small fixes and improvements [\#1173](https://github.com/containous/traefik/pull/1173) ([SantoDE](https://github.com/SantoDE))
|
||||||
- Fix docker issues with global and dead tasks [\#1167](https://github.com/containous/traefik/pull/1167) ([christopherobin](https://github.com/christopherobin))
|
- Fix docker issues with global and dead tasks [\#1167](https://github.com/containous/traefik/pull/1167) ([christopherobin](https://github.com/christopherobin))
|
||||||
- Better ECS error checking [\#1143](https://github.com/containous/traefik/pull/1143) ([lpetre](https://github.com/lpetre))
|
- Better ECS error checking [\#1143](https://github.com/containous/traefik/pull/1143) ([lpetre](https://github.com/lpetre))
|
||||||
- Fix stats race condition [\#1141](https://github.com/containous/traefik/pull/1141) ([emilevauge](https://github.com/emilevauge))
|
- Fix stats race condition [\#1141](https://github.com/containous/traefik/pull/1141) ([emilevauge](https://github.com/emilevauge))
|
||||||
@@ -429,7 +1112,7 @@
|
|||||||
**Merged pull requests:**
|
**Merged pull requests:**
|
||||||
|
|
||||||
- Revert "Ensure that we don't add balancees with no health check runs … [\#1198](https://github.com/containous/traefik/pull/1198) ([jangie](https://github.com/jangie))
|
- Revert "Ensure that we don't add balancees with no health check runs … [\#1198](https://github.com/containous/traefik/pull/1198) ([jangie](https://github.com/jangie))
|
||||||
- Small fixes and improvments [\#1173](https://github.com/containous/traefik/pull/1173) ([SantoDE](https://github.com/SantoDE))
|
- Small fixes and improvements [\#1173](https://github.com/containous/traefik/pull/1173) ([SantoDE](https://github.com/SantoDE))
|
||||||
- Fix docker issues with global and dead tasks [\#1167](https://github.com/containous/traefik/pull/1167) ([christopherobin](https://github.com/christopherobin))
|
- Fix docker issues with global and dead tasks [\#1167](https://github.com/containous/traefik/pull/1167) ([christopherobin](https://github.com/christopherobin))
|
||||||
- Better ECS error checking [\#1143](https://github.com/containous/traefik/pull/1143) ([lpetre](https://github.com/lpetre))
|
- Better ECS error checking [\#1143](https://github.com/containous/traefik/pull/1143) ([lpetre](https://github.com/lpetre))
|
||||||
- Fix stats race condition [\#1141](https://github.com/containous/traefik/pull/1141) ([emilevauge](https://github.com/emilevauge))
|
- Fix stats race condition [\#1141](https://github.com/containous/traefik/pull/1141) ([emilevauge](https://github.com/emilevauge))
|
||||||
@@ -792,7 +1475,7 @@
|
|||||||
- \#504 Initial support for Docker 1.12 Swarm Mode [\#602](https://github.com/containous/traefik/pull/602) ([diegofernandes](https://github.com/diegofernandes))
|
- \#504 Initial support for Docker 1.12 Swarm Mode [\#602](https://github.com/containous/traefik/pull/602) ([diegofernandes](https://github.com/diegofernandes))
|
||||||
- Add Host cert ACME generation [\#601](https://github.com/containous/traefik/pull/601) ([emilevauge](https://github.com/emilevauge))
|
- Add Host cert ACME generation [\#601](https://github.com/containous/traefik/pull/601) ([emilevauge](https://github.com/emilevauge))
|
||||||
- Fixed binary script so traefik version command doesn't just print default values [\#598](https://github.com/containous/traefik/pull/598) ([keiths-osc](https://github.com/keiths-osc))
|
- Fixed binary script so traefik version command doesn't just print default values [\#598](https://github.com/containous/traefik/pull/598) ([keiths-osc](https://github.com/keiths-osc))
|
||||||
- Name servers after thier pods [\#596](https://github.com/containous/traefik/pull/596) ([errm](https://github.com/errm))
|
- Name servers after their pods [\#596](https://github.com/containous/traefik/pull/596) ([errm](https://github.com/errm))
|
||||||
- Fix Consul prefix [\#589](https://github.com/containous/traefik/pull/589) ([jippi](https://github.com/jippi))
|
- Fix Consul prefix [\#589](https://github.com/containous/traefik/pull/589) ([jippi](https://github.com/jippi))
|
||||||
- Prioritize kubernetes routes by path length [\#588](https://github.com/containous/traefik/pull/588) ([philk](https://github.com/philk))
|
- Prioritize kubernetes routes by path length [\#588](https://github.com/containous/traefik/pull/588) ([philk](https://github.com/philk))
|
||||||
- beautify help [\#580](https://github.com/containous/traefik/pull/580) ([cocap10](https://github.com/cocap10))
|
- beautify help [\#580](https://github.com/containous/traefik/pull/580) ([cocap10](https://github.com/cocap10))
|
||||||
@@ -1015,7 +1698,7 @@
|
|||||||
- \#504 Initial support for Docker 1.12 Swarm Mode [\#602](https://github.com/containous/traefik/pull/602) ([diegofernandes](https://github.com/diegofernandes))
|
- \#504 Initial support for Docker 1.12 Swarm Mode [\#602](https://github.com/containous/traefik/pull/602) ([diegofernandes](https://github.com/diegofernandes))
|
||||||
- Add Host cert ACME generation [\#601](https://github.com/containous/traefik/pull/601) ([emilevauge](https://github.com/emilevauge))
|
- Add Host cert ACME generation [\#601](https://github.com/containous/traefik/pull/601) ([emilevauge](https://github.com/emilevauge))
|
||||||
- Fixed binary script so traefik version command doesn't just print default values [\#598](https://github.com/containous/traefik/pull/598) ([keiths-osc](https://github.com/keiths-osc))
|
- Fixed binary script so traefik version command doesn't just print default values [\#598](https://github.com/containous/traefik/pull/598) ([keiths-osc](https://github.com/keiths-osc))
|
||||||
- Name servers after thier pods [\#596](https://github.com/containous/traefik/pull/596) ([errm](https://github.com/errm))
|
- Name servers after their pods [\#596](https://github.com/containous/traefik/pull/596) ([errm](https://github.com/errm))
|
||||||
- Fix Consul prefix [\#589](https://github.com/containous/traefik/pull/589) ([jippi](https://github.com/jippi))
|
- Fix Consul prefix [\#589](https://github.com/containous/traefik/pull/589) ([jippi](https://github.com/jippi))
|
||||||
- Prioritize kubernetes routes by path length [\#588](https://github.com/containous/traefik/pull/588) ([philk](https://github.com/philk))
|
- Prioritize kubernetes routes by path length [\#588](https://github.com/containous/traefik/pull/588) ([philk](https://github.com/philk))
|
||||||
- beautify help [\#580](https://github.com/containous/traefik/pull/580) ([cocap10](https://github.com/cocap10))
|
- beautify help [\#580](https://github.com/containous/traefik/pull/580) ([cocap10](https://github.com/cocap10))
|
||||||
|
|||||||
256
CONTRIBUTING.md
Normal file
256
CONTRIBUTING.md
Normal file
@@ -0,0 +1,256 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
You need either [Docker](https://github.com/docker/docker) and `make` (Method 1), or `go` (Method 2) in order to build Traefik. For changes to its dependencies, the `glide` dependency management tool and `glide-vc` plugin are required.
|
||||||
|
|
||||||
|
### Method 1: Using `Docker` and `Makefile`
|
||||||
|
|
||||||
|
You need to run the `binary` target. This will create binaries for Linux platform in the `dist` folder.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ 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
|
||||||
|
---> 8c6473912976
|
||||||
|
Step 1 : RUN go get github.com/Masterminds/glide
|
||||||
|
[...]
|
||||||
|
docker run --rm -v "/var/run/docker.sock:/var/run/docker.sock" -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -v "/home/user/go/src/github.com/containous/traefik/"dist":/go/src/github.com/containous/traefik/"dist"" "traefik-dev:no-more-godep-ever" ./script/make.sh generate binary
|
||||||
|
---> Making bundle: generate (in .)
|
||||||
|
removed 'gen.go'
|
||||||
|
|
||||||
|
---> Making bundle: binary (in .)
|
||||||
|
|
||||||
|
$ ls dist/
|
||||||
|
traefik*
|
||||||
|
```
|
||||||
|
|
||||||
|
### Method 2: Using `go`
|
||||||
|
|
||||||
|
##### Setting up your `go` environment
|
||||||
|
|
||||||
|
- You need `go` v1.9+
|
||||||
|
- It is recommended you clone Træfik into a directory like `~/go/src/github.com/containous/traefik` (This is the official golang workspace hierarchy, and will allow dependencies to resolve properly)
|
||||||
|
- Set your `GOPATH` and `PATH` variable to be set to `~/go` via:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export GOPATH=~/go
|
||||||
|
export PATH=$PATH:$GOPATH/bin
|
||||||
|
```
|
||||||
|
|
||||||
|
> Note: You will want to add those 2 export lines to your `.bashrc` or `.bash_profile`
|
||||||
|
|
||||||
|
- Verify your environment is setup properly by running `$ go env`. Depending on your OS and environment you should see output similar to:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
GOARCH="amd64"
|
||||||
|
GOBIN=""
|
||||||
|
GOEXE=""
|
||||||
|
GOHOSTARCH="amd64"
|
||||||
|
GOHOSTOS="linux"
|
||||||
|
GOOS="linux"
|
||||||
|
GOPATH="/home/<yourusername>/go"
|
||||||
|
GORACE=""
|
||||||
|
## more go env's will be listed
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Build Træfik
|
||||||
|
|
||||||
|
Once your environment is set up and the Træfik repository cloned you can build Træfik. You need get `go-bindata` once to be able to use `go generate` command as part of the build. The steps to build are:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/go/src/github.com/containous/traefik
|
||||||
|
|
||||||
|
# Get go-bindata. Please note, the ellipses are required
|
||||||
|
go get github.com/jteeuwen/go-bindata/...
|
||||||
|
|
||||||
|
# Start build
|
||||||
|
|
||||||
|
# generate
|
||||||
|
# (required to merge non-code components into the final binary, such as the web dashboard and provider's Go templates)
|
||||||
|
go generate
|
||||||
|
|
||||||
|
# Standard go build
|
||||||
|
go build ./cmd/traefik
|
||||||
|
# run other commands like tests
|
||||||
|
```
|
||||||
|
|
||||||
|
You will find the Træfik executable in the `~/go/src/github.com/containous/traefik` folder as `traefik`.
|
||||||
|
|
||||||
|
### Updating the templates
|
||||||
|
|
||||||
|
If you happen to update the provider templates (in `/templates`), you need to run `go generate` to update the `autogen` package.
|
||||||
|
|
||||||
|
### Setting up `glide` and `glide-vc` for dependency management
|
||||||
|
|
||||||
|
- Glide is not required for building; however, it is necessary to modify dependencies (i.e., add, update, or remove third-party packages)
|
||||||
|
- Glide can be installed either via homebrew: `$ brew install glide` or via the official glide script: `$ curl https://glide.sh/get | sh`
|
||||||
|
- The glide plugin `glide-vc` must be installed from source: `go get github.com/sgotti/glide-vc`
|
||||||
|
|
||||||
|
If you want to add a dependency, use `$ glide get` to have glide put it into the vendor folder and update the glide manifest/lock files (`glide.yaml` and `glide.lock`, respectively). A following `glide-vc` run should be triggered to trim down the size of the vendor folder. The final result must be committed into VCS.
|
||||||
|
|
||||||
|
Care must be taken to choose the right arguments to `glide` when dealing with dependencies, or otherwise risk ending up with a broken build. For that reason, the helper script `script/glide.sh` encapsulates the gory details and conveniently calls `glide-vc` as well. Call it without parameters for basic usage instructions.
|
||||||
|
|
||||||
|
Here's a full example using glide to add a new dependency:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# install the new main dependency github.com/foo/bar and minimize vendor size
|
||||||
|
$ ./script/glide.sh get github.com/foo/bar
|
||||||
|
# generate (Only required to integrate other components such as web dashboard)
|
||||||
|
$ go generate
|
||||||
|
# Standard go build
|
||||||
|
$ go build ./cmd/traefik
|
||||||
|
# run other commands like tests
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tests
|
||||||
|
|
||||||
|
#### Method 1: `Docker` and `make`
|
||||||
|
|
||||||
|
You can run unit tests using the `test-unit` target and the
|
||||||
|
integration test using the `test-integration` target.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ make test-unit
|
||||||
|
docker build -t "traefik-dev:your-feature-branch" -f build.Dockerfile .
|
||||||
|
# […]
|
||||||
|
docker run --rm -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -v "/home/user/go/src/github/containous/traefik/dist:/go/src/github.com/containous/traefik/dist" "traefik-dev:your-feature-branch" ./script/make.sh generate test-unit
|
||||||
|
---> Making bundle: generate (in .)
|
||||||
|
removed 'gen.go'
|
||||||
|
|
||||||
|
---> Making bundle: test-unit (in .)
|
||||||
|
+ go test -cover -coverprofile=cover.out .
|
||||||
|
ok github.com/containous/traefik 0.005s coverage: 4.1% of statements
|
||||||
|
|
||||||
|
Test success
|
||||||
|
```
|
||||||
|
|
||||||
|
For development purposes, you can specify which tests to run by using:
|
||||||
|
```bash
|
||||||
|
# Run every tests in the MyTest suite
|
||||||
|
TESTFLAGS="-check.f MyTestSuite" make test-integration
|
||||||
|
|
||||||
|
# Run the test "MyTest" in the MyTest suite
|
||||||
|
TESTFLAGS="-check.f MyTestSuite.MyTest" make test-integration
|
||||||
|
|
||||||
|
# Run every tests starting with "My", in the MyTest suite
|
||||||
|
TESTFLAGS="-check.f MyTestSuite.My" make test-integration
|
||||||
|
|
||||||
|
# Run every tests ending with "Test", in the MyTest suite
|
||||||
|
TESTFLAGS="-check.f MyTestSuite.*Test" make test-integration
|
||||||
|
```
|
||||||
|
|
||||||
|
More: https://labix.org/gocheck
|
||||||
|
|
||||||
|
#### Method 2: `go`
|
||||||
|
|
||||||
|
Unit tests can be run from the cloned directory by `$ go test ./...` which should return `ok` similar to:
|
||||||
|
```
|
||||||
|
ok _/home/user/go/src/github/containous/traefik 0.004s
|
||||||
|
```
|
||||||
|
|
||||||
|
Integration tests must be run from the `integration/` directory and require the `-integration` switch to be passed like this: `$ cd integration && go test -integration ./...`.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
The [documentation site](http://docs.traefik.io/) is built with [mkdocs](http://mkdocs.org/)
|
||||||
|
|
||||||
|
### Method 1: `Docker` and `make`
|
||||||
|
|
||||||
|
You can test documentation using the `docs` target.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ make docs
|
||||||
|
docker build -t traefik-docs -f docs.Dockerfile .
|
||||||
|
# […]
|
||||||
|
docker run --rm -v /home/user/go/github/containous/traefik:/mkdocs -p 8000:8000 traefik-docs mkdocs serve
|
||||||
|
# […]
|
||||||
|
[I 170828 20:47:48 server:283] Serving on http://0.0.0.0:8000
|
||||||
|
[I 170828 20:47:48 handlers:60] Start watching changes
|
||||||
|
[I 170828 20:47:48 handlers:62] Start detecting changes
|
||||||
|
```
|
||||||
|
|
||||||
|
And go to [http://127.0.0.1:8000](http://127.0.0.1:8000).
|
||||||
|
|
||||||
|
### Method 2: `mkdocs`
|
||||||
|
|
||||||
|
First make sure you have python and pip installed
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ python --version
|
||||||
|
Python 2.7.2
|
||||||
|
$ pip --version
|
||||||
|
pip 1.5.2
|
||||||
|
```
|
||||||
|
|
||||||
|
Then install mkdocs with pip
|
||||||
|
|
||||||
|
```shell
|
||||||
|
pip install --user -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
To test documentation locally run `mkdocs serve` in the root directory, this should start a server locally to preview your changes.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ mkdocs serve
|
||||||
|
INFO - Building documentation...
|
||||||
|
WARNING - Config value: 'theme'. Warning: The theme 'united' will be removed in an upcoming MkDocs release. See http://www.mkdocs.org/about/release-notes/ for more details
|
||||||
|
INFO - Cleaning site directory
|
||||||
|
[I 160505 22:31:24 server:281] Serving on http://127.0.0.1:8000
|
||||||
|
[I 160505 22:31:24 handlers:59] Start watching changes
|
||||||
|
[I 160505 22:31:24 handlers:61] Start detecting changes
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## How to Write a Good Issue
|
||||||
|
|
||||||
|
Please keep in mind that the GitHub issue tracker is not intended as a general support forum, but for reporting bugs and feature requests.
|
||||||
|
|
||||||
|
For end-user related support questions, refer to one of the following:
|
||||||
|
- the Traefik community Slack channel: [](https://traefik.herokuapp.com)
|
||||||
|
- [Stack Overflow](https://stackoverflow.com/questions/tagged/traefik) (using the `traefik` tag)
|
||||||
|
|
||||||
|
### Title
|
||||||
|
|
||||||
|
The title must be short and descriptive. (~60 characters)
|
||||||
|
|
||||||
|
### Description
|
||||||
|
|
||||||
|
- Respect the issue template as much as possible. [template](.github/ISSUE_TEMPLATE.md)
|
||||||
|
- If it's possible use the command `traefik bug`. See https://www.youtube.com/watch?v=Lyz62L8m93I.
|
||||||
|
- Explain the conditions which led you to write this issue: the context.
|
||||||
|
- The context should lead to something, an idea or a problem that you’re facing.
|
||||||
|
- Remain clear and concise.
|
||||||
|
- Format your messages to help the reader focus on what matters and understand the structure of your message, use [Markdown syntax](https://help.github.com/articles/github-flavored-markdown)
|
||||||
|
|
||||||
|
|
||||||
|
## How to Write a Good Pull Request
|
||||||
|
|
||||||
|
### Title
|
||||||
|
|
||||||
|
The title must be short and descriptive. (~60 characters)
|
||||||
|
|
||||||
|
### Description
|
||||||
|
|
||||||
|
- Respect the pull request template as much as possible. [template](.github/PULL_REQUEST_TEMPLATE.md)
|
||||||
|
- Explain the conditions which led you to write this PR: the context.
|
||||||
|
- The context should lead to something, an idea or a problem that you’re facing.
|
||||||
|
- Remain clear and concise.
|
||||||
|
- Format your messages to help the reader focus on what matters and understand the structure of your message, use [Markdown syntax](https://help.github.com/articles/github-flavored-markdown)
|
||||||
|
|
||||||
|
### Content
|
||||||
|
|
||||||
|
- Make it small.
|
||||||
|
- Do only one thing.
|
||||||
|
- Write useful descriptions and titles.
|
||||||
|
- Avoid re-formatting.
|
||||||
|
- Make sure the code builds.
|
||||||
|
- Make sure all tests pass.
|
||||||
|
- Add tests.
|
||||||
|
- Address review comments in terms of additional commits.
|
||||||
|
- Do not amend/squash existing ones unless the PR is trivial.
|
||||||
|
- If a PR involves changes to third-party dependencies, the commits pertaining to the vendor folder and the manifest/lock file(s) should be committed separated.
|
||||||
|
|
||||||
|
|
||||||
|
Read [10 tips for better pull requests](http://blog.ploeh.dk/2015/01/15/10-tips-for-better-pull-requests/).
|
||||||
154
MAINTAINER.md
Normal file
154
MAINTAINER.md
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
# Maintainers
|
||||||
|
|
||||||
|
## The team
|
||||||
|
|
||||||
|
* Emile Vauge [@emilevauge](https://github.com/emilevauge)
|
||||||
|
* Vincent Demeester [@vdemeester](https://github.com/vdemeester)
|
||||||
|
* Ed Robinson [@errm](https://github.com/errm)
|
||||||
|
* Daniel Tomcej [@dtomcej](https://github.com/dtomcej)
|
||||||
|
* Manuel Zapf [@SantoDE](https://github.com/SantoDE)
|
||||||
|
* Timo Reimann [@timoreimann](https://github.com/timoreimann)
|
||||||
|
* Ludovic Fernandez [@ldez](https://github.com/ldez)
|
||||||
|
* Julien Salleyron [@juliens](https://github.com/juliens)
|
||||||
|
* Nicolas Mengin [@nmengin](https://github.com/nmengin)
|
||||||
|
* Marco Jantke [@marco-jantke](https://github.com/marco-jantke)
|
||||||
|
* Michaël Matur [@mmatur](https://github.com/mmatur)
|
||||||
|
|
||||||
|
|
||||||
|
## PR review process:
|
||||||
|
|
||||||
|
* The status `needs-design-review` is only used in complex/heavy/tricky PRs.
|
||||||
|
* From `1` to `2`: 1 design LGTM in comment, by a senior maintainer, if needed.
|
||||||
|
* From `2` to `3`: 3 LGTM by any maintainer.
|
||||||
|
* If needed, a specific maintainer familiar with a particular domain can be requested for the review.
|
||||||
|
|
||||||
|
We use [PRM](https://github.com/ldez/prm) to manage locally pull requests.
|
||||||
|
|
||||||
|
|
||||||
|
## Bots
|
||||||
|
|
||||||
|
### [Myrmica Lobicornis](https://github.com/containous/lobicornis/)
|
||||||
|
|
||||||
|
**Update and Merge Pull Request**
|
||||||
|
|
||||||
|
The maintainer giving the final LGTM must add the `status/3-needs-merge` label to trigger the merge bot.
|
||||||
|
|
||||||
|
By default, a squash-rebase merge will be carried out.
|
||||||
|
If you want to preserve commits you must add `bot/merge-method-rebase` before `status/3-needs-merge`.
|
||||||
|
|
||||||
|
The status `status/4-merge-in-progress` is only for the bot.
|
||||||
|
|
||||||
|
If the bot is not able to perform the merge, the label `bot/need-human-merge` is added.
|
||||||
|
In this case you must solve conflicts/CI/... and after you only need to remove `bot/need-human-merge`.
|
||||||
|
|
||||||
|
A maintainer can add `bot/no-merge` on a PR if he want (temporarily) prevent a merge by the bot.
|
||||||
|
|
||||||
|
`bot/light-review` can be used to decrease required LGTM from 3 to 1 when:
|
||||||
|
|
||||||
|
- vendor updates from previously reviewed PRs
|
||||||
|
- merges branches into master
|
||||||
|
- prepare release
|
||||||
|
|
||||||
|
|
||||||
|
### [Myrmica Bibikoffi](https://github.com/containous/bibikoffi/)
|
||||||
|
|
||||||
|
* closes stale issues [cron]
|
||||||
|
* use some criterion as number of days between creation, last update, labels, ...
|
||||||
|
|
||||||
|
|
||||||
|
### [Myrmica Aloba](https://github.com/containous/aloba)
|
||||||
|
|
||||||
|
**Manage GitHub labels**
|
||||||
|
|
||||||
|
* Add labels on new PR [GitHub WebHook]
|
||||||
|
* Add milestone to a new PR based on a branch version (1.4, 1.3, ...) [GitHub WebHook]
|
||||||
|
* Add and remove `contributor/waiting-for-corrections` label when a review request changes [GitHub WebHook]
|
||||||
|
* Weekly report of PR status on Slack (CaptainPR) [cron]
|
||||||
|
|
||||||
|
|
||||||
|
## Labels
|
||||||
|
|
||||||
|
If we open/look an issue/PR, we must add a `kind/*`, an `area/*` and a `status/*`.
|
||||||
|
|
||||||
|
### Contributor
|
||||||
|
|
||||||
|
* `contributor/need-more-information`: we need more information from the contributor in order to analyze a problem.
|
||||||
|
* `contributor/waiting-for-feedback`: we need the contributor to give us feedback.
|
||||||
|
* `contributor/waiting-for-corrections`: we need the contributor to take actions in order to move forward with a PR. **(only for PR)** _[bot, humans]_
|
||||||
|
* `contributor/needs-resolve-conflicts`: use it only when there is some conflicts (and an automatic rebase is not possible). **(only for PR)** _[bot, humans]_
|
||||||
|
|
||||||
|
### Kind
|
||||||
|
|
||||||
|
* `kind/enhancement`: a new or improved feature.
|
||||||
|
* `kind/question`: It's a question. **(only for issue)**
|
||||||
|
* `kind/proposal`: proposal PR/issues need a public debate.
|
||||||
|
* _Proposal issues_ are design proposal that need to be refined with multiple contributors.
|
||||||
|
* _Proposal PRs_ are technical prototypes that need to be refined with multiple contributors.
|
||||||
|
|
||||||
|
* `kind/bug/possible`: if we need to analyze to understand if it's a bug or not. **(only for issues)**
|
||||||
|
* `kind/bug/confirmed`: we are sure, it's a bug. **(only for issues)**
|
||||||
|
* `kind/bug/fix`: it's a bug fix. **(only for PR)**
|
||||||
|
|
||||||
|
### Resolution
|
||||||
|
|
||||||
|
* `resolution/duplicate`: it's a duplicate issue/PR.
|
||||||
|
* `resolution/declined`: Rule #1 of open-source: no is temporary, yes is forever.
|
||||||
|
* `WIP`: Work In Progress. **(only for PR)**
|
||||||
|
|
||||||
|
### Platform
|
||||||
|
|
||||||
|
* `platform/windows`: Windows related.
|
||||||
|
|
||||||
|
### Area
|
||||||
|
|
||||||
|
* `area/acme`: ACME related.
|
||||||
|
* `area/api`: Traefik API related.
|
||||||
|
* `area/authentication`: Authentication related.
|
||||||
|
* `area/cluster`: Traefik clustering related.
|
||||||
|
* `area/documentation`: regards improving/adding documentation.
|
||||||
|
* `area/infrastructure`: related to CI or Traefik building scripts.
|
||||||
|
* `area/healthcheck`: Health-check related.
|
||||||
|
* `area/logs`: Traefik logs related.
|
||||||
|
* `area/middleware`: Middleware related.
|
||||||
|
* `area/middleware/metrics`: Metrics related. (Prometheus, StatsD, ...)
|
||||||
|
* `area/oxy`: Oxy related.
|
||||||
|
* `area/provider`: related to all providers.
|
||||||
|
* `area/provider/boltdb`: Boltd DB related.
|
||||||
|
* `area/provider/consul`: Consul related.
|
||||||
|
* `area/provider/docker`: Docker and Swarm related.
|
||||||
|
* `area/provider/ecs`: ECS related.
|
||||||
|
* `area/provider/etcd`: Etcd related.
|
||||||
|
* `area/provider/eureka`: Eureka related.
|
||||||
|
* `area/provider/file`: file provider related.
|
||||||
|
* `area/provider/k8s`: Kubernetes related.
|
||||||
|
* `area/provider/marathon`: Marathon related.
|
||||||
|
* `area/provider/mesos`: Mesos related.
|
||||||
|
* `area/provider/rancher`: Rancher related.
|
||||||
|
* `area/provider/zk`: Zoo Keeper related.
|
||||||
|
* `area/sticky-session`: Sticky session related.
|
||||||
|
* `area/tls`: TLS related.
|
||||||
|
* `area/websocket`: WebSocket related.
|
||||||
|
* `area/webui`: Web UI related.
|
||||||
|
|
||||||
|
### Priority
|
||||||
|
|
||||||
|
* `priority/P0`: needs hot fix. **(only for issue)**
|
||||||
|
* `priority/P1`: need to be fixed in next release. **(only for issue)**
|
||||||
|
* `priority/P2`: need to be fixed in the future. **(only for issue)**
|
||||||
|
* `priority/P3`: maybe. **(only for issue)**
|
||||||
|
|
||||||
|
### PR size
|
||||||
|
|
||||||
|
* `size/S`: small PR. **(only for PR)** _[bot only]_
|
||||||
|
* `size/M`: medium PR. **(only for PR)** _[bot only]_
|
||||||
|
* `size/L`: Large PR. **(only for PR)** _[bot only]_
|
||||||
|
|
||||||
|
### Status - Workflow
|
||||||
|
|
||||||
|
The `status/*` labels represent the desired state in the workflow.
|
||||||
|
|
||||||
|
* `status/0-needs-triage`: all new issue or PR have this status. _[bot only]_
|
||||||
|
* `status/1-needs-design-review`: need a design review. **(only for PR)**
|
||||||
|
* `status/2-needs-review`: need a code/documentation review. **(only for PR)**
|
||||||
|
* `status/3-needs-merge`: ready to merge. **(only for PR)**
|
||||||
|
* `status/4-merge-in-progress`: merge in progress. _[bot only]_
|
||||||
37
Makefile
37
Makefile
@@ -7,23 +7,29 @@ TRAEFIK_ENVS := \
|
|||||||
-e VERBOSE \
|
-e VERBOSE \
|
||||||
-e VERSION \
|
-e VERSION \
|
||||||
-e CODENAME \
|
-e CODENAME \
|
||||||
-e TESTDIRS
|
-e TESTDIRS \
|
||||||
|
-e CI \
|
||||||
|
-e CONTAINER=DOCKER # Indicator for integration tests that we are running inside a container.
|
||||||
|
|
||||||
SRCS = $(shell git ls-files '*.go' | grep -v '^vendor/' | grep -v '^integration/vendor/')
|
SRCS = $(shell git ls-files '*.go' | grep -v '^vendor/')
|
||||||
|
|
||||||
BIND_DIR := "dist"
|
BIND_DIR := "dist"
|
||||||
TRAEFIK_MOUNT := -v "$(CURDIR)/$(BIND_DIR):/go/src/github.com/containous/traefik/$(BIND_DIR)"
|
TRAEFIK_MOUNT := -v "$(CURDIR)/$(BIND_DIR):/go/src/github.com/containous/traefik/$(BIND_DIR)"
|
||||||
|
|
||||||
GIT_BRANCH := $(subst heads/,,$(shell git rev-parse --abbrev-ref HEAD 2>/dev/null))
|
GIT_BRANCH := $(subst heads/,,$(shell git rev-parse --abbrev-ref HEAD 2>/dev/null))
|
||||||
TRAEFIK_DEV_IMAGE := traefik-dev$(if $(GIT_BRANCH),:$(GIT_BRANCH))
|
TRAEFIK_DEV_IMAGE := traefik-dev$(if $(GIT_BRANCH),:$(subst /,-,$(GIT_BRANCH)))
|
||||||
REPONAME := $(shell echo $(REPO) | tr '[:upper:]' '[:lower:]')
|
REPONAME := $(shell echo $(REPO) | tr '[:upper:]' '[:lower:]')
|
||||||
TRAEFIK_IMAGE := $(if $(REPONAME),$(REPONAME),"containous/traefik")
|
TRAEFIK_IMAGE := $(if $(REPONAME),$(REPONAME),"containous/traefik")
|
||||||
INTEGRATION_OPTS := $(if $(MAKE_DOCKER_HOST),-e "DOCKER_HOST=$(MAKE_DOCKER_HOST)", -v "/var/run/docker.sock:/var/run/docker.sock")
|
INTEGRATION_OPTS := $(if $(MAKE_DOCKER_HOST),-e "DOCKER_HOST=$(MAKE_DOCKER_HOST)", -e "TEST_CONTAINER=1" -v "/var/run/docker.sock:/var/run/docker.sock")
|
||||||
|
TRAEFIK_DOC_IMAGE := traefik-docs
|
||||||
|
|
||||||
DOCKER_BUILD_ARGS := $(if $(DOCKER_VERSION), "--build-arg=DOCKER_VERSION=$(DOCKER_VERSION)",)
|
DOCKER_BUILD_ARGS := $(if $(DOCKER_VERSION), "--build-arg=DOCKER_VERSION=$(DOCKER_VERSION)",)
|
||||||
DOCKER_RUN_OPTS := $(TRAEFIK_ENVS) $(TRAEFIK_MOUNT) "$(TRAEFIK_DEV_IMAGE)"
|
DOCKER_RUN_OPTS := $(TRAEFIK_ENVS) $(TRAEFIK_MOUNT) "$(TRAEFIK_DEV_IMAGE)"
|
||||||
DOCKER_RUN_TRAEFIK := docker run $(INTEGRATION_OPTS) -it $(DOCKER_RUN_OPTS)
|
DOCKER_RUN_TRAEFIK := docker run $(INTEGRATION_OPTS) -it $(DOCKER_RUN_OPTS)
|
||||||
DOCKER_RUN_TRAEFIK_NOTTY := docker run $(INTEGRATION_OPTS) -i $(DOCKER_RUN_OPTS)
|
DOCKER_RUN_TRAEFIK_NOTTY := docker run $(INTEGRATION_OPTS) -i $(DOCKER_RUN_OPTS)
|
||||||
|
DOCKER_RUN_DOC_PORT := 8000
|
||||||
|
DOCKER_RUN_DOC_MOUNT := -v $(CURDIR):/mkdocs
|
||||||
|
DOCKER_RUN_DOC_OPTS := --rm $(DOCKER_RUN_DOC_MOUNT) -p $(DOCKER_RUN_DOC_PORT):8000
|
||||||
|
|
||||||
|
|
||||||
print-%: ; @echo $*=$($*)
|
print-%: ; @echo $*=$($*)
|
||||||
@@ -65,9 +71,10 @@ test-unit: build ## run the unit tests
|
|||||||
|
|
||||||
test-integration: build ## run the integration tests
|
test-integration: build ## run the integration tests
|
||||||
$(DOCKER_RUN_TRAEFIK) ./script/make.sh generate binary test-integration
|
$(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 gofmt, golint and go vet
|
||||||
$(DOCKER_RUN_TRAEFIK) ./script/make.sh validate-glide validate-gofmt validate-govet validate-golint validate-misspell validate-vendor
|
$(DOCKER_RUN_TRAEFIK) ./script/make.sh validate-glide validate-gofmt validate-govet validate-golint validate-misspell validate-vendor validate-autogen
|
||||||
|
|
||||||
build: dist
|
build: dist
|
||||||
docker build $(DOCKER_BUILD_ARGS) -t "$(TRAEFIK_DEV_IMAGE)" -f build.Dockerfile .
|
docker build $(DOCKER_BUILD_ARGS) -t "$(TRAEFIK_DEV_IMAGE)" -f build.Dockerfile .
|
||||||
@@ -81,15 +88,27 @@ build-no-cache: dist
|
|||||||
shell: build ## start a shell inside the build env
|
shell: build ## start a shell inside the build env
|
||||||
$(DOCKER_RUN_TRAEFIK) /bin/bash
|
$(DOCKER_RUN_TRAEFIK) /bin/bash
|
||||||
|
|
||||||
image: binary ## build a docker traefik image
|
image-dirty: binary ## build a docker traefik image
|
||||||
docker build -t $(TRAEFIK_IMAGE) .
|
docker build -t $(TRAEFIK_IMAGE) .
|
||||||
|
|
||||||
|
image: clear-static binary ## clean up static directory and build a docker traefik image
|
||||||
|
docker build -t $(TRAEFIK_IMAGE) .
|
||||||
|
|
||||||
|
docs: docs-image
|
||||||
|
docker run $(DOCKER_RUN_DOC_OPTS) $(TRAEFIK_DOC_IMAGE) mkdocs serve
|
||||||
|
|
||||||
|
docs-image:
|
||||||
|
docker build -t $(TRAEFIK_DOC_IMAGE) -f docs.Dockerfile .
|
||||||
|
|
||||||
|
clear-static:
|
||||||
|
rm -rf static
|
||||||
|
|
||||||
dist:
|
dist:
|
||||||
mkdir dist
|
mkdir dist
|
||||||
|
|
||||||
run-dev:
|
run-dev:
|
||||||
go generate
|
go generate
|
||||||
go build
|
go build ./cmd/traefik
|
||||||
./traefik
|
./traefik
|
||||||
|
|
||||||
generate-webui: build-webui
|
generate-webui: build-webui
|
||||||
@@ -106,9 +125,7 @@ fmt:
|
|||||||
gofmt -s -l -w $(SRCS)
|
gofmt -s -l -w $(SRCS)
|
||||||
|
|
||||||
pull-images:
|
pull-images:
|
||||||
for f in $(shell find ./integration/resources/compose/ -type f); do \
|
grep --no-filename -E '^\s+image:' ./integration/resources/compose/*.yml | awk '{print $$2}' | sort | uniq | xargs -P 6 -n 1 docker pull
|
||||||
docker-compose -f $$f pull; \
|
|
||||||
done
|
|
||||||
|
|
||||||
help: ## this help
|
help: ## this help
|
||||||
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {sub("\\\\n",sprintf("\n%22c"," "), $$2);printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {sub("\\\\n",sprintf("\n%22c"," "), $$2);printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
||||||
|
|||||||
147
README.md
147
README.md
@@ -3,7 +3,7 @@
|
|||||||
<img src="docs/img/traefik.logo.png" alt="Træfik" title="Træfik" />
|
<img src="docs/img/traefik.logo.png" alt="Træfik" title="Træfik" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
[](https://travis-ci.org/containous/traefik)
|
[](https://semaphoreci.com/containous/traefik)
|
||||||
[](https://docs.traefik.io)
|
[](https://docs.traefik.io)
|
||||||
[](http://goreportcard.com/report/containous/traefik)
|
[](http://goreportcard.com/report/containous/traefik)
|
||||||
[](https://microbadger.com/images/traefik)
|
[](https://microbadger.com/images/traefik)
|
||||||
@@ -13,7 +13,26 @@
|
|||||||
|
|
||||||
|
|
||||||
Træfik (pronounced like [traffic](https://speak-ipa.bearbin.net/speak.cgi?speak=%CB%88tr%C3%A6f%C9%AAk)) is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
|
Træfik (pronounced like [traffic](https://speak-ipa.bearbin.net/speak.cgi?speak=%CB%88tr%C3%A6f%C9%AAk)) is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
|
||||||
It supports several backends ([Docker](https://www.docker.com/), [Swarm](https://docs.docker.com/swarm), [Kubernetes](http://kubernetes.io), [Marathon](https://mesosphere.github.io/marathon/), [Mesos](https://github.com/apache/mesos), [Consul](https://www.consul.io/), [Etcd](https://coreos.com/etcd/), [Zookeeper](https://zookeeper.apache.org), [BoltDB](https://github.com/boltdb/bolt), [Eureka](https://github.com/Netflix/eureka), [Amazon DynamoDB](https://aws.amazon.com/dynamodb/), Rest API, file...) to manage its configuration automatically and dynamically.
|
It supports several backends ([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 a lot more) to manage its configuration automatically and dynamically.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
. **[Overview](#overview)** .
|
||||||
|
**[Features](#features)** .
|
||||||
|
**[Supported backends](#supported-backends)** .
|
||||||
|
**[Quickstart](#quickstart)** .
|
||||||
|
**[Web UI](#web-ui)** .
|
||||||
|
**[Test it](#test-it)** .
|
||||||
|
**[Documentation](#documentation)** .
|
||||||
|
|
||||||
|
. **[Support](#support)** .
|
||||||
|
**[Release cycle](#release-cycle)** .
|
||||||
|
**[Contributing](#contributing)** .
|
||||||
|
**[Maintainers](#maintainers)** .
|
||||||
|
**[Plumbing](#plumbing)** .
|
||||||
|
**[Credits](#credits)** .
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@@ -24,7 +43,7 @@ If you want your users to access some of your microservices from the Internet, y
|
|||||||
- path `domain.com/web` will point the microservice `web` in your private network
|
- path `domain.com/web` will point the microservice `web` in your private network
|
||||||
- domain `backoffice.domain.com` will point the microservices `backoffice` in your private network, load-balancing between your multiple instances
|
- domain `backoffice.domain.com` will point the microservices `backoffice` in your private network, load-balancing between your multiple instances
|
||||||
|
|
||||||
But a microservices architecture is dynamic... Services are added, removed, killed or upgraded often, eventually several times a day.
|
Microservices are often deployed in dynamic environments where services are added, removed, killed, upgraded or scaled many times a day.
|
||||||
|
|
||||||
Traditional reverse-proxies are not natively dynamic. You can't change their configuration and hot-reload easily.
|
Traditional reverse-proxies are not natively dynamic. You can't change their configuration and hot-reload easily.
|
||||||
|
|
||||||
@@ -36,45 +55,52 @@ Træfik can listen to your service registry/orchestrator API, and knows each tim
|
|||||||
Routes to your services will be created instantly.
|
Routes to your services will be created instantly.
|
||||||
|
|
||||||
Run it and forget it!
|
Run it and forget it!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- [It's fast](http://docs.traefik.io/benchmarks)
|
- [It's fast](https://docs.traefik.io/benchmarks)
|
||||||
- No dependency hell, single binary made with go
|
- No dependency hell, single binary made with go
|
||||||
|
- [Tiny](https://microbadger.com/images/traefik) [official](https://hub.docker.com/r/_/traefik/) docker image
|
||||||
- Rest API
|
- Rest API
|
||||||
- Multiple backends supported: Docker, Swarm, Kubernetes, Marathon, Mesos, Consul, Etcd, and more to come
|
|
||||||
- Watchers for backends, can listen for changes in backends to apply a new configuration automatically
|
|
||||||
- Hot-reloading of configuration. No need to restart the process
|
- Hot-reloading of configuration. No need to restart the process
|
||||||
- Graceful shutdown http connections
|
- Circuit breakers, retry
|
||||||
- Circuit breakers on backends
|
|
||||||
- Round Robin, rebalancer load-balancers
|
- Round Robin, rebalancer load-balancers
|
||||||
- Rest Metrics
|
- Metrics (Rest, Prometheus, Datadog, Statsd, InfluxDB)
|
||||||
- [Tiny](https://microbadger.com/images/traefik) [official](https://hub.docker.com/r/_/traefik/) docker image included
|
|
||||||
- SSL backends support
|
|
||||||
- SSL frontend support (with SNI)
|
|
||||||
- Clean AngularJS Web UI
|
- Clean AngularJS Web UI
|
||||||
- Websocket support
|
- Websocket, HTTP/2, GRPC ready
|
||||||
- HTTP/2 support
|
- Access Logs (JSON, CLF)
|
||||||
- Retry request if network error
|
|
||||||
- [Let's Encrypt](https://letsencrypt.org) support (Automatic HTTPS with renewal)
|
- [Let's Encrypt](https://letsencrypt.org) support (Automatic HTTPS with renewal)
|
||||||
- High Availability with cluster mode
|
- [Proxy Protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) support
|
||||||
|
- High Availability with cluster mode (beta)
|
||||||
|
|
||||||
|
## Supported backends
|
||||||
|
|
||||||
|
- [Docker](https://www.docker.com/) / [Swarm mode](https://docs.docker.com/engine/swarm/)
|
||||||
|
- [Kubernetes](https://kubernetes.io)
|
||||||
|
- [Mesos](https://github.com/apache/mesos) / [Marathon](https://mesosphere.github.io/marathon/)
|
||||||
|
- [Rancher](https://rancher.com) (API, Metadata)
|
||||||
|
- [Consul](https://www.consul.io/) / [Etcd](https://coreos.com/etcd/) / [Zookeeper](https://zookeeper.apache.org) / [BoltDB](https://github.com/boltdb/bolt)
|
||||||
|
- [Eureka](https://github.com/Netflix/eureka)
|
||||||
|
- [Amazon ECS](https://aws.amazon.com/ecs)
|
||||||
|
- [Amazon DynamoDB](https://aws.amazon.com/dynamodb)
|
||||||
|
- File
|
||||||
|
- Rest API
|
||||||
|
|
||||||
## Quickstart
|
## Quickstart
|
||||||
|
|
||||||
You can have a quick look at Træfik in this [Katacoda tutorial](https://www.katacoda.com/courses/traefik/deploy-load-balancer) that shows how to load balance requests between multiple Docker containers.
|
You can have a quick look at Træfik in this [Katacoda tutorial](https://www.katacoda.com/courses/traefik/deploy-load-balancer) that shows how to load balance requests between multiple Docker containers. If you are looking for a more comprehensive and real use-case example, you can also check [Play-With-Docker](http://training.play-with-docker.com/traefik-load-balancing/) to see how to load balance between multiple nodes.
|
||||||
|
|
||||||
Here is a talk given by [Ed Robinson](https://github.com/errm) at the [ContainerCamp UK](https://container.camp) conference.
|
Here is a talk given by [Emile Vauge](https://github.com/emilevauge) at [GopherCon 2017](https://gophercon.com/).
|
||||||
|
You will learn Træfik basics in less than 10 minutes.
|
||||||
|
|
||||||
|
[](https://www.youtube.com/watch?v=RgudiksfL-k)
|
||||||
|
|
||||||
|
Here is a talk given by [Ed Robinson](https://github.com/errm) at [ContainerCamp UK](https://container.camp) conference.
|
||||||
You will learn fundamental Træfik features and see some demos with Kubernetes.
|
You will learn fundamental Træfik features and see some demos with Kubernetes.
|
||||||
|
|
||||||
[](https://www.youtube.com/watch?v=aFtpIShV60I)
|
[](https://www.youtube.com/watch?v=aFtpIShV60I)
|
||||||
|
|
||||||
Here is a talk (in French) given by [Emile Vauge](https://github.com/emilevauge) at the [Devoxx France 2016](http://www.devoxx.fr) conference.
|
|
||||||
You will learn fundamental Træfik features and see some demos with Docker, Mesos/Marathon and Let's Encrypt.
|
|
||||||
|
|
||||||
[](http://www.youtube.com/watch?v=QvAz9mVx5TI)
|
|
||||||
|
|
||||||
## Web UI
|
## Web UI
|
||||||
|
|
||||||
@@ -83,12 +109,6 @@ You can access the simple HTML frontend of Træfik.
|
|||||||

|

|
||||||

|

|
||||||
|
|
||||||
## Plumbing
|
|
||||||
|
|
||||||
- [Oxy](https://github.com/vulcand/oxy): an awesome proxy library made by Mailgun guys
|
|
||||||
- [Gorilla mux](https://github.com/gorilla/mux): famous request router
|
|
||||||
- [Negroni](https://github.com/codegangsta/negroni): web middlewares made simple
|
|
||||||
- [Lego](https://github.com/xenolf/lego): the best [Let's Encrypt](https://letsencrypt.org) library in go
|
|
||||||
|
|
||||||
## Test it
|
## Test it
|
||||||
|
|
||||||
@@ -98,7 +118,7 @@ You can access the simple HTML frontend of Træfik.
|
|||||||
./traefik --configFile=traefik.toml
|
./traefik --configFile=traefik.toml
|
||||||
```
|
```
|
||||||
|
|
||||||
- Use the tiny Docker image:
|
- Use the tiny Docker image and just run it with the [sample configuration file](https://raw.githubusercontent.com/containous/traefik/master/traefik.sample.toml):
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/etc/traefik/traefik.toml traefik
|
docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/etc/traefik/traefik.toml traefik
|
||||||
@@ -110,33 +130,60 @@ docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/etc/traefik/traefik.to
|
|||||||
git clone https://github.com/containous/traefik
|
git clone https://github.com/containous/traefik
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
You can find the complete documentation [here](https://docs.traefik.io).
|
You can find the complete documentation at [https://docs.traefik.io](https://docs.traefik.io).
|
||||||
|
A collection of contributions around Træfik can be found at [https://awesome.traefik.io](https://awesome.traefik.io).
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
Please refer to [this section](.github/CONTRIBUTING.md).
|
|
||||||
|
|
||||||
## Code Of Conduct
|
|
||||||
|
|
||||||
Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
|
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
You can join [](https://traefik.herokuapp.com) to get basic support.
|
To get basic support, you can:
|
||||||
|
- join the Træfik community Slack channel: [](https://traefik.herokuapp.com)
|
||||||
|
- use [Stack Overflow](https://stackoverflow.com/questions/tagged/traefik) (using the `traefik` tag)
|
||||||
|
|
||||||
If you prefer commercial support, please contact [containo.us](https://containo.us) by mail: <mailto:support@containo.us>.
|
If you prefer commercial support, please contact [containo.us](https://containo.us) by mail: <mailto:support@containo.us>.
|
||||||
|
|
||||||
|
|
||||||
|
## Release cycle
|
||||||
|
|
||||||
|
- Release: We try to release a new version every 2 months
|
||||||
|
- i.e.: 1.3.0, 1.4.0, 1.5.0
|
||||||
|
- Release candidate: we do RC (1.**x**.0-rc**y**) before the final release (1.**x**.0)
|
||||||
|
- i.e.: 1.1.0-rc1 -> 1.1.0-rc2 -> 1.1.0-rc3 -> 1.1.0-rc4 -> 1.1.0
|
||||||
|
- Bug-fixes: For each version we release bug fixes
|
||||||
|
- i.e.: 1.1.1, 1.1.2, 1.1.3
|
||||||
|
- those versions contain only bug-fixes
|
||||||
|
- no additional features are delivered in those versions
|
||||||
|
- Each version is supported until the next one is released
|
||||||
|
- i.e.: 1.1.x will be supported until 1.2.0 is out
|
||||||
|
- We use [Semantic Versioning](http://semver.org/)
|
||||||
|
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Please refer to [contributing documentation](CONTRIBUTING.md).
|
||||||
|
|
||||||
|
|
||||||
|
### Code of Conduct
|
||||||
|
|
||||||
|
Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md).
|
||||||
|
By participating in this project you agree to abide by its terms.
|
||||||
|
|
||||||
|
|
||||||
## Maintainers
|
## Maintainers
|
||||||
|
|
||||||
- Emile Vauge [@emilevauge](https://github.com/emilevauge)
|
[Information about process and maintainers](MAINTAINER.md)
|
||||||
- Vincent Demeester [@vdemeester](https://github.com/vdemeester)
|
|
||||||
- Russell Clare [@Russell-IO](https://github.com/Russell-IO)
|
|
||||||
- Ed Robinson [@errm](https://github.com/errm)
|
## Plumbing
|
||||||
- Daniel Tomcej [@dtomcej](https://github.com/dtomcej)
|
|
||||||
- Manuel Laufenberg [@SantoDE](https://github.com/SantoDE)
|
- [Oxy](https://github.com/vulcand/oxy): an awesome proxy library made by Mailgun folks
|
||||||
- Thomas Recloux [@trecloux](https://github.com/trecloux)
|
- [Gorilla mux](https://github.com/gorilla/mux): famous request router
|
||||||
- Timo Reimann [@timoreimann](https://github.com/timoreimann)
|
- [Negroni](https://github.com/urfave/negroni): web middlewares made simple
|
||||||
|
- [Lego](https://github.com/xenolf/lego): the best [Let's Encrypt](https://letsencrypt.org) library in go
|
||||||
|
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
@@ -144,4 +191,4 @@ Kudos to [Peka](http://peka.byethost11.com/photoblog/) for his awesome work on t
|
|||||||
Traefik's logo licensed under the Creative Commons 3.0 Attributions license.
|
Traefik's logo licensed under the Creative Commons 3.0 Attributions license.
|
||||||
|
|
||||||
Traefik's logo was inspired by the gopher stickers made by Takuya Ueda (https://twitter.com/tenntenn).
|
Traefik's logo was inspired by the gopher stickers made by Takuya Ueda (https://twitter.com/tenntenn).
|
||||||
The original Go gopher was designed by Renee French (http://reneefrench.blogspot.com/).
|
The original Go gopher was designed by Renee French (http://reneefrench.blogspot.com/).
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"errors"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -33,7 +33,7 @@ type ChallengeCert struct {
|
|||||||
certificate *tls.Certificate
|
certificate *tls.Certificate
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init inits acccount struct
|
// Init inits account struct
|
||||||
func (a *Account) Init() error {
|
func (a *Account) Init() error {
|
||||||
err := a.DomainsCertificate.Init()
|
err := a.DomainsCertificate.Init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -178,7 +178,7 @@ func (dc *DomainsCertificates) renewCertificates(acmeCert *Certificate, domain D
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return errors.New("Certificate to renew not found for domain " + domain.Main)
|
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 Domain) (*DomainsCertificate, error) {
|
||||||
|
|||||||
152
acme/acme.go
152
acme/acme.go
@@ -18,6 +18,8 @@ import (
|
|||||||
"github.com/containous/traefik/cluster"
|
"github.com/containous/traefik/cluster"
|
||||||
"github.com/containous/traefik/log"
|
"github.com/containous/traefik/log"
|
||||||
"github.com/containous/traefik/safe"
|
"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/types"
|
||||||
"github.com/eapache/channels"
|
"github.com/eapache/channels"
|
||||||
"github.com/xenolf/lego/acme"
|
"github.com/xenolf/lego/acme"
|
||||||
@@ -49,6 +51,7 @@ type ACME struct {
|
|||||||
checkOnDemandDomain func(domain string) bool
|
checkOnDemandDomain func(domain string) bool
|
||||||
jobs *channels.InfiniteChannel
|
jobs *channels.InfiniteChannel
|
||||||
TLSConfig *tls.Config `description:"TLS config in case wildcard certs are used"`
|
TLSConfig *tls.Config `description:"TLS config in case wildcard certs are used"`
|
||||||
|
dynamicCerts *safe.Safe
|
||||||
}
|
}
|
||||||
|
|
||||||
//Domains parse []Domain
|
//Domains parse []Domain
|
||||||
@@ -99,14 +102,14 @@ func (a *ACME) init() error {
|
|||||||
acme.Logger = fmtlog.New(ioutil.Discard, "", 0)
|
acme.Logger = fmtlog.New(ioutil.Discard, "", 0)
|
||||||
}
|
}
|
||||||
// no certificates in TLS config, so we add a default one
|
// no certificates in TLS config, so we add a default one
|
||||||
cert, err := generateDefaultCertificate()
|
cert, err := generate.DefaultCertificate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
a.defaultCertificate = cert
|
a.defaultCertificate = cert
|
||||||
// TODO: to remove in the futurs
|
// TODO: to remove in the futurs
|
||||||
if len(a.StorageFile) > 0 && len(a.Storage) == 0 {
|
if len(a.StorageFile) > 0 && len(a.Storage) == 0 {
|
||||||
log.Warnf("ACME.StorageFile is deprecated, use ACME.Storage instead")
|
log.Warn("ACME.StorageFile is deprecated, use ACME.Storage instead")
|
||||||
a.Storage = a.StorageFile
|
a.Storage = a.StorageFile
|
||||||
}
|
}
|
||||||
a.jobs = channels.NewInfiniteChannel()
|
a.jobs = channels.NewInfiniteChannel()
|
||||||
@@ -114,7 +117,7 @@ func (a *ACME) init() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateClusterConfig creates a tls.config using ACME configuration in cluster mode
|
// CreateClusterConfig creates a tls.config using ACME configuration in cluster mode
|
||||||
func (a *ACME) CreateClusterConfig(leadership *cluster.Leadership, tlsConfig *tls.Config, checkOnDemandDomain func(domain string) bool) error {
|
func (a *ACME) CreateClusterConfig(leadership *cluster.Leadership, tlsConfig *tls.Config, certs *safe.Safe, checkOnDemandDomain func(domain string) bool) error {
|
||||||
err := a.init()
|
err := a.init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -123,6 +126,7 @@ func (a *ACME) CreateClusterConfig(leadership *cluster.Leadership, tlsConfig *tl
|
|||||||
return errors.New("Empty Store, please provide a key for certs storage")
|
return errors.New("Empty Store, please provide a key for certs storage")
|
||||||
}
|
}
|
||||||
a.checkOnDemandDomain = checkOnDemandDomain
|
a.checkOnDemandDomain = checkOnDemandDomain
|
||||||
|
a.dynamicCerts = certs
|
||||||
tlsConfig.Certificates = append(tlsConfig.Certificates, *a.defaultCertificate)
|
tlsConfig.Certificates = append(tlsConfig.Certificates, *a.defaultCertificate)
|
||||||
tlsConfig.GetCertificate = a.getCertificate
|
tlsConfig.GetCertificate = a.getCertificate
|
||||||
a.TLSConfig = tlsConfig
|
a.TLSConfig = tlsConfig
|
||||||
@@ -155,8 +159,8 @@ func (a *ACME) CreateClusterConfig(leadership *cluster.Leadership, tlsConfig *tl
|
|||||||
|
|
||||||
ticker := time.NewTicker(24 * time.Hour)
|
ticker := time.NewTicker(24 * time.Hour)
|
||||||
leadership.Pool.AddGoCtx(func(ctx context.Context) {
|
leadership.Pool.AddGoCtx(func(ctx context.Context) {
|
||||||
log.Infof("Starting ACME renew job...")
|
log.Info("Starting ACME renew job...")
|
||||||
defer log.Infof("Stopped ACME renew job...")
|
defer log.Info("Stopped ACME renew job...")
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
@@ -169,7 +173,7 @@ func (a *ACME) CreateClusterConfig(leadership *cluster.Leadership, tlsConfig *tl
|
|||||||
|
|
||||||
leadership.AddListener(func(elected bool) error {
|
leadership.AddListener(func(elected bool) error {
|
||||||
if elected {
|
if elected {
|
||||||
object, err := a.store.Load()
|
_, err := a.store.Load()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -196,7 +200,7 @@ func (a *ACME) CreateClusterConfig(leadership *cluster.Leadership, tlsConfig *tl
|
|||||||
}
|
}
|
||||||
if needRegister {
|
if needRegister {
|
||||||
// New users will need to register; be sure to save it
|
// New users will need to register; be sure to save it
|
||||||
log.Debugf("Register...")
|
log.Debug("Register...")
|
||||||
reg, err := a.client.Register()
|
reg, err := a.client.Register()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -205,7 +209,7 @@ func (a *ACME) CreateClusterConfig(leadership *cluster.Leadership, tlsConfig *tl
|
|||||||
}
|
}
|
||||||
// The client has a URL to the current Let's Encrypt Subscriber
|
// The client has a URL to the current Let's Encrypt Subscriber
|
||||||
// Agreement. The user will need to agree to it.
|
// Agreement. The user will need to agree to it.
|
||||||
log.Debugf("AgreeToTOS...")
|
log.Debug("AgreeToTOS...")
|
||||||
err = a.client.AgreeToTOS()
|
err = a.client.AgreeToTOS()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Let's Encrypt Subscriber Agreement renew ?
|
// Let's Encrypt Subscriber Agreement renew ?
|
||||||
@@ -234,7 +238,7 @@ func (a *ACME) CreateClusterConfig(leadership *cluster.Leadership, tlsConfig *tl
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateLocalConfig creates a tls.config using local ACME configuration
|
// CreateLocalConfig creates a tls.config using local ACME configuration
|
||||||
func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, checkOnDemandDomain func(domain string) bool) error {
|
func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, certs *safe.Safe, checkOnDemandDomain func(domain string) bool) error {
|
||||||
err := a.init()
|
err := a.init()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -243,6 +247,7 @@ func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, checkOnDemandDomain func
|
|||||||
return errors.New("Empty Store, please provide a filename for certs storage")
|
return errors.New("Empty Store, please provide a filename for certs storage")
|
||||||
}
|
}
|
||||||
a.checkOnDemandDomain = checkOnDemandDomain
|
a.checkOnDemandDomain = checkOnDemandDomain
|
||||||
|
a.dynamicCerts = certs
|
||||||
tlsConfig.Certificates = append(tlsConfig.Certificates, *a.defaultCertificate)
|
tlsConfig.Certificates = append(tlsConfig.Certificates, *a.defaultCertificate)
|
||||||
tlsConfig.GetCertificate = a.getCertificate
|
tlsConfig.GetCertificate = a.getCertificate
|
||||||
a.TLSConfig = tlsConfig
|
a.TLSConfig = tlsConfig
|
||||||
@@ -254,7 +259,7 @@ func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, checkOnDemandDomain func
|
|||||||
var account *Account
|
var account *Account
|
||||||
|
|
||||||
if fileInfo, fileErr := os.Stat(a.Storage); fileErr == nil && fileInfo.Size() != 0 {
|
if fileInfo, fileErr := os.Stat(a.Storage); fileErr == nil && fileInfo.Size() != 0 {
|
||||||
log.Infof("Loading ACME Account...")
|
log.Info("Loading ACME Account...")
|
||||||
// load account
|
// load account
|
||||||
object, err := localStore.Load()
|
object, err := localStore.Load()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -262,7 +267,7 @@ func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, checkOnDemandDomain func
|
|||||||
}
|
}
|
||||||
account = object.(*Account)
|
account = object.(*Account)
|
||||||
} else {
|
} else {
|
||||||
log.Infof("Generating ACME Account...")
|
log.Info("Generating ACME Account...")
|
||||||
account, err = NewAccount(a.Email)
|
account, err = NewAccount(a.Email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -277,7 +282,7 @@ func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, checkOnDemandDomain func
|
|||||||
|
|
||||||
if needRegister {
|
if needRegister {
|
||||||
// New users will need to register; be sure to save it
|
// New users will need to register; be sure to save it
|
||||||
log.Infof("Register...")
|
log.Info("Register...")
|
||||||
reg, err := a.client.Register()
|
reg, err := a.client.Register()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -287,7 +292,7 @@ func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, checkOnDemandDomain func
|
|||||||
|
|
||||||
// The client has a URL to the current Let's Encrypt Subscriber
|
// The client has a URL to the current Let's Encrypt Subscriber
|
||||||
// Agreement. The user will need to agree to it.
|
// Agreement. The user will need to agree to it.
|
||||||
log.Debugf("AgreeToTOS...")
|
log.Debug("AgreeToTOS...")
|
||||||
err = a.client.AgreeToTOS()
|
err = a.client.AgreeToTOS()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Let's Encrypt Subscriber Agreement renew ?
|
// Let's Encrypt Subscriber Agreement renew ?
|
||||||
@@ -320,7 +325,6 @@ func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, checkOnDemandDomain func
|
|||||||
for range ticker.C {
|
for range ticker.C {
|
||||||
a.renewCertificates()
|
a.renewCertificates()
|
||||||
}
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -353,7 +357,7 @@ func (a *ACME) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificat
|
|||||||
|
|
||||||
func (a *ACME) retrieveCertificates() {
|
func (a *ACME) retrieveCertificates() {
|
||||||
a.jobs.In() <- func() {
|
a.jobs.In() <- func() {
|
||||||
log.Infof("Retrieving ACME certificates...")
|
log.Info("Retrieving ACME certificates...")
|
||||||
for _, domain := range a.Domains {
|
for _, domain := range a.Domains {
|
||||||
// check if cert isn't already loaded
|
// check if cert isn't already loaded
|
||||||
account := a.store.Get().(*Account)
|
account := a.store.Get().(*Account)
|
||||||
@@ -384,50 +388,33 @@ func (a *ACME) retrieveCertificates() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Infof("Retrieved ACME certificates")
|
log.Info("Retrieved ACME certificates")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *ACME) renewCertificates() {
|
func (a *ACME) renewCertificates() {
|
||||||
a.jobs.In() <- func() {
|
a.jobs.In() <- func() {
|
||||||
log.Debugf("Testing certificate renew...")
|
log.Info("Testing certificate renew...")
|
||||||
account := a.store.Get().(*Account)
|
account := a.store.Get().(*Account)
|
||||||
for _, certificateResource := range account.DomainsCertificate.Certs {
|
for _, certificateResource := range account.DomainsCertificate.Certs {
|
||||||
if certificateResource.needRenew() {
|
if certificateResource.needRenew() {
|
||||||
log.Debugf("Renewing certificate %+v", certificateResource.Domains)
|
log.Infof("Renewing certificate from LE : %+v", certificateResource.Domains)
|
||||||
renewedCert, err := a.client.RenewCertificate(acme.CertificateResource{
|
renewedACMECert, err := a.renewACMECertificate(certificateResource)
|
||||||
Domain: certificateResource.Certificate.Domain,
|
|
||||||
CertURL: certificateResource.Certificate.CertURL,
|
|
||||||
CertStableURL: certificateResource.Certificate.CertStableURL,
|
|
||||||
PrivateKey: certificateResource.Certificate.PrivateKey,
|
|
||||||
Certificate: certificateResource.Certificate.Certificate,
|
|
||||||
}, true, OSCPMustStaple)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error renewing certificate: %v", err)
|
log.Errorf("Error renewing certificate from LE: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
log.Debugf("Renewed certificate %+v", certificateResource.Domains)
|
operation := func() error {
|
||||||
renewedACMECert := &Certificate{
|
return a.storeRenewedCertificate(account, certificateResource, renewedACMECert)
|
||||||
Domain: renewedCert.Domain,
|
|
||||||
CertURL: renewedCert.CertURL,
|
|
||||||
CertStableURL: renewedCert.CertStableURL,
|
|
||||||
PrivateKey: renewedCert.PrivateKey,
|
|
||||||
Certificate: renewedCert.Certificate,
|
|
||||||
}
|
}
|
||||||
transaction, object, err := a.store.Begin()
|
notify := func(err error, time time.Duration) {
|
||||||
|
log.Warnf("Renewed certificate storage error: %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 {
|
if err != nil {
|
||||||
log.Errorf("Error renewing certificate: %v", err)
|
log.Errorf("Datastore cannot sync: %v", err)
|
||||||
continue
|
|
||||||
}
|
|
||||||
account = object.(*Account)
|
|
||||||
err = account.DomainsCertificate.renewCertificates(renewedACMECert, certificateResource.Domains)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Error renewing certificate: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = transaction.Commit(account); err != nil {
|
|
||||||
log.Errorf("Error Saving ACME account %+v: %s", account, err.Error())
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -435,6 +422,56 @@ func (a *ACME) renewCertificates() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *ACME) renewACMECertificate(certificateResource *DomainsCertificate) (*Certificate, error) {
|
||||||
|
renewedCert, err := a.client.RenewCertificate(acme.CertificateResource{
|
||||||
|
Domain: certificateResource.Certificate.Domain,
|
||||||
|
CertURL: certificateResource.Certificate.CertURL,
|
||||||
|
CertStableURL: certificateResource.Certificate.CertStableURL,
|
||||||
|
PrivateKey: certificateResource.Certificate.PrivateKey,
|
||||||
|
Certificate: certificateResource.Certificate.Certificate,
|
||||||
|
}, true, OSCPMustStaple)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Infof("Renewed certificate from LE: %+v", certificateResource.Domains)
|
||||||
|
return &Certificate{
|
||||||
|
Domain: renewedCert.Domain,
|
||||||
|
CertURL: renewedCert.CertURL,
|
||||||
|
CertStableURL: renewedCert.CertStableURL,
|
||||||
|
PrivateKey: renewedCert.PrivateKey,
|
||||||
|
Certificate: renewedCert.Certificate,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ACME) storeRenewedCertificate(account *Account, 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)
|
||||||
|
err = account.DomainsCertificate.renewCertificates(renewedACMECert, certificateResource.Domains)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error renewing certificate in datastore: %v ", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("Commit certificate renewed in data store : %+v", certificateResource.Domains)
|
||||||
|
if err = transaction.Commit(account); err != nil {
|
||||||
|
return fmt.Errorf("error saving ACME account %+v: %v", account, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
oldAccount := a.store.Get().(*Account)
|
||||||
|
for _, oldCertificateResource := range oldAccount.DomainsCertificate.Certs {
|
||||||
|
if oldCertificateResource.Domains.Main == certificateResource.Domains.Main && strings.Join(oldCertificateResource.Domains.SANs, ",") == strings.Join(certificateResource.Domains.SANs, ",") && certificateResource.Certificate != renewedACMECert {
|
||||||
|
return fmt.Errorf("renewed certificate not stored: %+v", certificateResource.Domains)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("Certificate successfully renewed in data store: %+v", certificateResource.Domains)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func dnsOverrideDelay(delay int) error {
|
func dnsOverrideDelay(delay int) error {
|
||||||
var err error
|
var err error
|
||||||
if delay > 0 {
|
if delay > 0 {
|
||||||
@@ -444,13 +481,13 @@ func dnsOverrideDelay(delay int) error {
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
} else if delay < 0 {
|
} else if delay < 0 {
|
||||||
err = fmt.Errorf("Invalid negative DelayDontCheckDNS: %d", delay)
|
err = fmt.Errorf("invalid negative DelayDontCheckDNS: %d", delay)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *ACME) buildACMEClient(account *Account) (*acme.Client, error) {
|
func (a *ACME) buildACMEClient(account *Account) (*acme.Client, error) {
|
||||||
log.Debugf("Building ACME client...")
|
log.Debug("Building ACME client...")
|
||||||
caServer := "https://acme-v01.api.letsencrypt.org/directory"
|
caServer := "https://acme-v01.api.letsencrypt.org/directory"
|
||||||
if len(a.CAServer) > 0 {
|
if len(a.CAServer) > 0 {
|
||||||
caServer = a.CAServer
|
caServer = a.CAServer
|
||||||
@@ -533,7 +570,7 @@ func (a *ACME) LoadCertificateForDomains(domains []string) {
|
|||||||
|
|
||||||
operation := func() error {
|
operation := func() error {
|
||||||
if a.client == nil {
|
if a.client == nil {
|
||||||
return fmt.Errorf("ACME client still not built")
|
return errors.New("ACME client still not built")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -584,11 +621,21 @@ func (a *ACME) LoadCertificateForDomains(domains []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get provided certificate which check a domains list (Main and SANs)
|
// Get provided certificate which check a domains list (Main and SANs)
|
||||||
|
// from static and dynamic provided certificates
|
||||||
func (a *ACME) getProvidedCertificate(domains []string) *tls.Certificate {
|
func (a *ACME) getProvidedCertificate(domains []string) *tls.Certificate {
|
||||||
|
log.Debugf("Look 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))
|
||||||
|
}
|
||||||
|
log.Debugf("No provided certificate found for domains %s, get ACME certificate.", domains)
|
||||||
|
return cert
|
||||||
|
}
|
||||||
|
|
||||||
|
func searchProvidedCertificateForDomains(domains []string, certs map[string]*tls.Certificate) *tls.Certificate {
|
||||||
// Use regex to test for provided certs that might have been added into TLSConfig
|
// Use regex to test for provided certs that might have been added into TLSConfig
|
||||||
providedCertMatch := false
|
providedCertMatch := false
|
||||||
log.Debugf("Look for provided certificate to validate %s...", domains)
|
for k := range certs {
|
||||||
for k := range a.TLSConfig.NameToCertificate {
|
|
||||||
selector := "^" + strings.Replace(k, "*.", "[^\\.]*\\.?", -1) + "$"
|
selector := "^" + strings.Replace(k, "*.", "[^\\.]*\\.?", -1) + "$"
|
||||||
for _, domainToCheck := range domains {
|
for _, domainToCheck := range domains {
|
||||||
providedCertMatch, _ = regexp.MatchString(selector, domainToCheck)
|
providedCertMatch, _ = regexp.MatchString(selector, domainToCheck)
|
||||||
@@ -598,11 +645,10 @@ func (a *ACME) getProvidedCertificate(domains []string) *tls.Certificate {
|
|||||||
}
|
}
|
||||||
if providedCertMatch {
|
if providedCertMatch {
|
||||||
log.Debugf("Got provided certificate for domains %s", domains)
|
log.Debugf("Got provided certificate for domains %s", domains)
|
||||||
return a.TLSConfig.NameToCertificate[k]
|
return certs[k]
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Debugf("No provided certificate found for domains %s, get ACME certificate.", domains)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/containous/traefik/tls/generate"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/xenolf/lego/acme"
|
"github.com/xenolf/lego/acme"
|
||||||
)
|
)
|
||||||
@@ -70,8 +71,8 @@ func TestDomainsSetAppend(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCertificatesRenew(t *testing.T) {
|
func TestCertificatesRenew(t *testing.T) {
|
||||||
foo1Cert, foo1Key, _ := generateKeyPair("foo1.com", time.Now())
|
foo1Cert, foo1Key, _ := generate.KeyPair("foo1.com", time.Now())
|
||||||
foo2Cert, foo2Key, _ := generateKeyPair("foo2.com", time.Now())
|
foo2Cert, foo2Key, _ := generate.KeyPair("foo2.com", time.Now())
|
||||||
domainsCertificates := DomainsCertificates{
|
domainsCertificates := DomainsCertificates{
|
||||||
lock: sync.RWMutex{},
|
lock: sync.RWMutex{},
|
||||||
Certs: []*DomainsCertificate{
|
Certs: []*DomainsCertificate{
|
||||||
@@ -101,7 +102,7 @@ func TestCertificatesRenew(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
foo1Cert, foo1Key, _ = generateKeyPair("foo1.com", time.Now())
|
foo1Cert, foo1Key, _ = generate.KeyPair("foo1.com", time.Now())
|
||||||
newCertificate := &Certificate{
|
newCertificate := &Certificate{
|
||||||
Domain: "foo1.com",
|
Domain: "foo1.com",
|
||||||
CertURL: "url",
|
CertURL: "url",
|
||||||
@@ -128,10 +129,10 @@ func TestCertificatesRenew(t *testing.T) {
|
|||||||
|
|
||||||
func TestRemoveDuplicates(t *testing.T) {
|
func TestRemoveDuplicates(t *testing.T) {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
fooCert, fooKey, _ := generateKeyPair("foo.com", now)
|
fooCert, fooKey, _ := generate.KeyPair("foo.com", now)
|
||||||
foo24Cert, foo24Key, _ := generateKeyPair("foo.com", now.Add(24*time.Hour))
|
foo24Cert, foo24Key, _ := generate.KeyPair("foo.com", now.Add(24*time.Hour))
|
||||||
foo48Cert, foo48Key, _ := generateKeyPair("foo.com", now.Add(48*time.Hour))
|
foo48Cert, foo48Key, _ := generate.KeyPair("foo.com", now.Add(48*time.Hour))
|
||||||
barCert, barKey, _ := generateKeyPair("bar.com", now)
|
barCert, barKey, _ := generate.KeyPair("bar.com", now)
|
||||||
domainsCertificates := DomainsCertificates{
|
domainsCertificates := DomainsCertificates{
|
||||||
lock: sync.RWMutex{},
|
lock: sync.RWMutex{},
|
||||||
Certs: []*DomainsCertificate{
|
Certs: []*DomainsCertificate{
|
||||||
@@ -224,14 +225,14 @@ func TestNoPreCheckOverride(t *testing.T) {
|
|||||||
t.Errorf("Error in dnsOverrideDelay :%v", err)
|
t.Errorf("Error in dnsOverrideDelay :%v", err)
|
||||||
}
|
}
|
||||||
if acme.PreCheckDNS != nil {
|
if acme.PreCheckDNS != nil {
|
||||||
t.Errorf("Unexpected change to acme.PreCheckDNS when leaving DNS verification as is.")
|
t.Error("Unexpected change to acme.PreCheckDNS when leaving DNS verification as is.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSillyPreCheckOverride(t *testing.T) {
|
func TestSillyPreCheckOverride(t *testing.T) {
|
||||||
err := dnsOverrideDelay(-5)
|
err := dnsOverrideDelay(-5)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("Missing expected error in dnsOverrideDelay!")
|
t.Error("Missing expected error in dnsOverrideDelay!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,7 +243,7 @@ func TestPreCheckOverride(t *testing.T) {
|
|||||||
t.Errorf("Error in dnsOverrideDelay :%v", err)
|
t.Errorf("Error in dnsOverrideDelay :%v", err)
|
||||||
}
|
}
|
||||||
if acme.PreCheckDNS == nil {
|
if acme.PreCheckDNS == nil {
|
||||||
t.Errorf("No change to acme.PreCheckDNS when meant to be adding enforcing override function.")
|
t.Error("No change to acme.PreCheckDNS when meant to be adding enforcing override function.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,10 +274,10 @@ cijFkALeQp/qyeXdFld2v9gUN3eCgljgcl0QweRoIc=---`)
|
|||||||
t.Errorf("Error in buildACMEClient: %v", err)
|
t.Errorf("Error in buildACMEClient: %v", err)
|
||||||
}
|
}
|
||||||
if client == nil {
|
if client == nil {
|
||||||
t.Errorf("No client from buildACMEClient!")
|
t.Error("No client from buildACMEClient!")
|
||||||
}
|
}
|
||||||
if acme.PreCheckDNS == nil {
|
if acme.PreCheckDNS == nil {
|
||||||
t.Errorf("No change to acme.PreCheckDNS when meant to be adding enforcing override function.")
|
t.Error("No change to acme.PreCheckDNS when meant to be adding enforcing override function.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
package acme
|
package acme
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha256"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -11,6 +19,7 @@ import (
|
|||||||
"github.com/containous/traefik/cluster"
|
"github.com/containous/traefik/cluster"
|
||||||
"github.com/containous/traefik/log"
|
"github.com/containous/traefik/log"
|
||||||
"github.com/containous/traefik/safe"
|
"github.com/containous/traefik/safe"
|
||||||
|
"github.com/containous/traefik/tls/generate"
|
||||||
"github.com/xenolf/lego/acme"
|
"github.com/xenolf/lego/acme"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -43,7 +52,7 @@ func (c *challengeProvider) getCertificate(domain string) (cert *tls.Certificate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fmt.Errorf("Cannot find challenge cert for domain %s", domain)
|
return fmt.Errorf("cannot find challenge cert for domain %s", domain)
|
||||||
}
|
}
|
||||||
notify := func(err error, time time.Duration) {
|
notify := func(err error, time time.Duration) {
|
||||||
log.Errorf("Error getting cert: %v, retrying in %s", err, time)
|
log.Errorf("Error getting cert: %v, retrying in %s", err, time)
|
||||||
@@ -60,7 +69,7 @@ func (c *challengeProvider) getCertificate(domain string) (cert *tls.Certificate
|
|||||||
|
|
||||||
func (c *challengeProvider) Present(domain, token, keyAuth string) error {
|
func (c *challengeProvider) Present(domain, token, keyAuth string) error {
|
||||||
log.Debugf("Challenge Present %s", domain)
|
log.Debugf("Challenge Present %s", domain)
|
||||||
cert, _, err := TLSSNI01ChallengeCert(keyAuth)
|
cert, _, err := tlsSNI01ChallengeCert(keyAuth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -95,3 +104,47 @@ func (c *challengeProvider) CleanUp(domain, token, keyAuth string) error {
|
|||||||
func (c *challengeProvider) Timeout() (timeout, interval time.Duration) {
|
func (c *challengeProvider) Timeout() (timeout, interval time.Duration) {
|
||||||
return 60 * time.Second, 5 * time.Second
|
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)
|
||||||
|
}
|
||||||
|
|||||||
135
acme/crypto.go
135
acme/crypto.go
@@ -1,135 +0,0 @@
|
|||||||
package acme
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto"
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/tls"
|
|
||||||
"crypto/x509"
|
|
||||||
"crypto/x509/pkix"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/pem"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func generateDefaultCertificate() (*tls.Certificate, error) {
|
|
||||||
randomBytes := make([]byte, 100)
|
|
||||||
_, err := rand.Read(randomBytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
zBytes := sha256.Sum256(randomBytes)
|
|
||||||
z := hex.EncodeToString(zBytes[:sha256.Size])
|
|
||||||
domain := fmt.Sprintf("%s.%s.traefik.default", z[:32], z[32:])
|
|
||||||
|
|
||||||
certPEM, keyPEM, err := generateKeyPair(domain, time.Time{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
certificate, err := tls.X509KeyPair(certPEM, keyPEM)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &certificate, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateKeyPair(domain string, expiration time.Time) ([]byte, []byte, error) {
|
|
||||||
rsaPrivKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(rsaPrivKey)})
|
|
||||||
|
|
||||||
certPEM, err := generatePemCert(rsaPrivKey, domain, expiration)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
return certPEM, keyPEM, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func generatePemCert(privKey *rsa.PrivateKey, domain string, expiration time.Time) ([]byte, error) {
|
|
||||||
derBytes, err := generateDerCert(privKey, expiration, domain)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func generateDerCert(privKey *rsa.PrivateKey, expiration time.Time, domain string) ([]byte, error) {
|
|
||||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
|
||||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if expiration.IsZero() {
|
|
||||||
expiration = time.Now().Add(365)
|
|
||||||
}
|
|
||||||
|
|
||||||
template := x509.Certificate{
|
|
||||||
SerialNumber: serialNumber,
|
|
||||||
Subject: pkix.Name{
|
|
||||||
CommonName: "TRAEFIK DEFAULT CERT",
|
|
||||||
},
|
|
||||||
NotBefore: time.Now(),
|
|
||||||
NotAfter: expiration,
|
|
||||||
|
|
||||||
KeyUsage: x509.KeyUsageKeyEncipherment,
|
|
||||||
BasicConstraintsValid: true,
|
|
||||||
DNSNames: []string{domain},
|
|
||||||
}
|
|
||||||
|
|
||||||
return x509.CreateCertificate(rand.Reader, &template, &template, &privKey.PublicKey, privKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 := generatePemCert(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)}
|
|
||||||
break
|
|
||||||
case *x509.CertificateRequest:
|
|
||||||
pemBlock = &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: key.Raw}
|
|
||||||
break
|
|
||||||
case []byte:
|
|
||||||
pemBlock = &pem.Block{Type: "CERTIFICATE", Bytes: []byte(data.([]byte))}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pem.EncodeToMemory(pemBlock)
|
|
||||||
}
|
|
||||||
22
api/dashboard.go
Normal file
22
api/dashboard.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/containous/mux"
|
||||||
|
"github.com/containous/traefik/autogen/genstatic"
|
||||||
|
"github.com/elazarl/go-bindata-assetfs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DashboardHandler expose dashboard routes
|
||||||
|
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, "/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"})))
|
||||||
|
}
|
||||||
46
api/debug.go
Normal file
46
api/debug.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"expvar"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/pprof"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/containous/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
expvar.Publish("Goroutines", expvar.Func(goroutines))
|
||||||
|
}
|
||||||
|
|
||||||
|
func goroutines() interface{} {
|
||||||
|
return runtime.NumGoroutine()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DebugHandler expose debug routes
|
||||||
|
type DebugHandler struct{}
|
||||||
|
|
||||||
|
// AddRoutes add debug routes on a router
|
||||||
|
func (g DebugHandler) AddRoutes(router *mux.Router) {
|
||||||
|
router.Methods(http.MethodGet).Path("/debug/vars").
|
||||||
|
HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
fmt.Fprint(w, "{\n")
|
||||||
|
first := true
|
||||||
|
expvar.Do(func(kv expvar.KeyValue) {
|
||||||
|
if !first {
|
||||||
|
fmt.Fprint(w, ",\n")
|
||||||
|
}
|
||||||
|
first = false
|
||||||
|
fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
|
||||||
|
})
|
||||||
|
fmt.Fprint(w, "\n}\n")
|
||||||
|
})
|
||||||
|
|
||||||
|
router.Methods(http.MethodGet).PathPrefix("/debug/pprof/cmdline").HandlerFunc(pprof.Cmdline)
|
||||||
|
router.Methods(http.MethodGet).PathPrefix("/debug/pprof/profile").HandlerFunc(pprof.Profile)
|
||||||
|
router.Methods(http.MethodGet).PathPrefix("/debug/pprof/symbol").HandlerFunc(pprof.Symbol)
|
||||||
|
router.Methods(http.MethodGet).PathPrefix("/debug/pprof/trace").HandlerFunc(pprof.Trace)
|
||||||
|
router.Methods(http.MethodGet).PathPrefix("/debug/pprof/").HandlerFunc(pprof.Index)
|
||||||
|
}
|
||||||
250
api/handler.go
Normal file
250
api/handler.go
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/containous/mux"
|
||||||
|
"github.com/containous/traefik/log"
|
||||||
|
"github.com/containous/traefik/middlewares"
|
||||||
|
"github.com/containous/traefik/safe"
|
||||||
|
"github.com/containous/traefik/types"
|
||||||
|
"github.com/containous/traefik/version"
|
||||||
|
thoas_stats "github.com/thoas/stats"
|
||||||
|
"github.com/unrolled/render"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Handler expose api routes
|
||||||
|
type Handler struct {
|
||||||
|
EntryPoint string `description:"EntryPoint" export:"true"`
|
||||||
|
Dashboard bool `description:"Activate dashboard" export:"true"`
|
||||||
|
Debug bool `export:"true"`
|
||||||
|
CurrentConfigurations *safe.Safe
|
||||||
|
Statistics *types.Statistics `description:"Enable more detailed statistics" export:"true"`
|
||||||
|
Stats *thoas_stats.Stats
|
||||||
|
StatsRecorder *middlewares.StatsRecorder
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
templatesRenderer = render.New(render.Options{
|
||||||
|
Directory: "nowhere",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddRoutes add api routes on a router
|
||||||
|
func (p Handler) AddRoutes(router *mux.Router) {
|
||||||
|
if p.Debug {
|
||||||
|
DebugHandler{}.AddRoutes(router)
|
||||||
|
}
|
||||||
|
|
||||||
|
router.Methods(http.MethodGet).Path("/api").HandlerFunc(p.getConfigHandler)
|
||||||
|
router.Methods(http.MethodGet).Path("/api/providers").HandlerFunc(p.getConfigHandler)
|
||||||
|
router.Methods(http.MethodGet).Path("/api/providers/{provider}").HandlerFunc(p.getProviderHandler)
|
||||||
|
router.Methods(http.MethodGet).Path("/api/providers/{provider}/backends").HandlerFunc(p.getBackendsHandler)
|
||||||
|
router.Methods(http.MethodGet).Path("/api/providers/{provider}/backends/{backend}").HandlerFunc(p.getBackendHandler)
|
||||||
|
router.Methods(http.MethodGet).Path("/api/providers/{provider}/backends/{backend}/servers").HandlerFunc(p.getServersHandler)
|
||||||
|
router.Methods(http.MethodGet).Path("/api/providers/{provider}/backends/{backend}/servers/{server}").HandlerFunc(p.getServerHandler)
|
||||||
|
router.Methods(http.MethodGet).Path("/api/providers/{provider}/frontends").HandlerFunc(p.getFrontendsHandler)
|
||||||
|
router.Methods(http.MethodGet).Path("/api/providers/{provider}/frontends/{frontend}").HandlerFunc(p.getFrontendHandler)
|
||||||
|
router.Methods(http.MethodGet).Path("/api/providers/{provider}/frontends/{frontend}/routes").HandlerFunc(p.getRoutesHandler)
|
||||||
|
router.Methods(http.MethodGet).Path("/api/providers/{provider}/frontends/{frontend}/routes/{route}").HandlerFunc(p.getRouteHandler)
|
||||||
|
|
||||||
|
// health route
|
||||||
|
router.Methods(http.MethodGet).Path("/health").HandlerFunc(p.getHealthHandler)
|
||||||
|
|
||||||
|
version.Handler{}.AddRoutes(router)
|
||||||
|
|
||||||
|
if p.Dashboard {
|
||||||
|
DashboardHandler{}.AddRoutes(router)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getProviderIDFromVars(vars map[string]string) string {
|
||||||
|
providerID := vars["provider"]
|
||||||
|
// TODO: Deprecated
|
||||||
|
if providerID == "rest" {
|
||||||
|
providerID = "web"
|
||||||
|
}
|
||||||
|
return providerID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Handler) getConfigHandler(response http.ResponseWriter, request *http.Request) {
|
||||||
|
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
|
||||||
|
err := templatesRenderer.JSON(response, http.StatusOK, currentConfigurations)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Handler) getProviderHandler(response http.ResponseWriter, request *http.Request) {
|
||||||
|
providerID := getProviderIDFromVars(mux.Vars(request))
|
||||||
|
|
||||||
|
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
|
||||||
|
if provider, ok := currentConfigurations[providerID]; ok {
|
||||||
|
err := templatesRenderer.JSON(response, http.StatusOK, provider)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
http.NotFound(response, request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Handler) getBackendsHandler(response http.ResponseWriter, request *http.Request) {
|
||||||
|
providerID := getProviderIDFromVars(mux.Vars(request))
|
||||||
|
|
||||||
|
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
|
||||||
|
if provider, ok := currentConfigurations[providerID]; ok {
|
||||||
|
err := templatesRenderer.JSON(response, http.StatusOK, provider.Backends)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
http.NotFound(response, request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Handler) getBackendHandler(response http.ResponseWriter, request *http.Request) {
|
||||||
|
vars := mux.Vars(request)
|
||||||
|
providerID := getProviderIDFromVars(vars)
|
||||||
|
backendID := vars["backend"]
|
||||||
|
|
||||||
|
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
|
||||||
|
if provider, ok := currentConfigurations[providerID]; ok {
|
||||||
|
if backend, ok := provider.Backends[backendID]; ok {
|
||||||
|
err := templatesRenderer.JSON(response, http.StatusOK, backend)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
http.NotFound(response, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Handler) getServersHandler(response http.ResponseWriter, request *http.Request) {
|
||||||
|
vars := mux.Vars(request)
|
||||||
|
providerID := getProviderIDFromVars(vars)
|
||||||
|
backendID := vars["backend"]
|
||||||
|
|
||||||
|
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
|
||||||
|
if provider, ok := currentConfigurations[providerID]; ok {
|
||||||
|
if backend, ok := provider.Backends[backendID]; ok {
|
||||||
|
err := templatesRenderer.JSON(response, http.StatusOK, backend.Servers)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
http.NotFound(response, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Handler) getServerHandler(response http.ResponseWriter, request *http.Request) {
|
||||||
|
vars := mux.Vars(request)
|
||||||
|
providerID := getProviderIDFromVars(vars)
|
||||||
|
backendID := vars["backend"]
|
||||||
|
serverID := vars["server"]
|
||||||
|
|
||||||
|
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
|
||||||
|
if provider, ok := currentConfigurations[providerID]; ok {
|
||||||
|
if backend, ok := provider.Backends[backendID]; ok {
|
||||||
|
if server, ok := backend.Servers[serverID]; ok {
|
||||||
|
err := templatesRenderer.JSON(response, http.StatusOK, server)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
http.NotFound(response, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Handler) getFrontendsHandler(response http.ResponseWriter, request *http.Request) {
|
||||||
|
providerID := getProviderIDFromVars(mux.Vars(request))
|
||||||
|
|
||||||
|
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
|
||||||
|
if provider, ok := currentConfigurations[providerID]; ok {
|
||||||
|
err := templatesRenderer.JSON(response, http.StatusOK, provider.Frontends)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
http.NotFound(response, request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Handler) getFrontendHandler(response http.ResponseWriter, request *http.Request) {
|
||||||
|
vars := mux.Vars(request)
|
||||||
|
providerID := getProviderIDFromVars(vars)
|
||||||
|
frontendID := vars["frontend"]
|
||||||
|
|
||||||
|
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
|
||||||
|
if provider, ok := currentConfigurations[providerID]; ok {
|
||||||
|
if frontend, ok := provider.Frontends[frontendID]; ok {
|
||||||
|
err := templatesRenderer.JSON(response, http.StatusOK, frontend)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
http.NotFound(response, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Handler) getRoutesHandler(response http.ResponseWriter, request *http.Request) {
|
||||||
|
vars := mux.Vars(request)
|
||||||
|
providerID := getProviderIDFromVars(vars)
|
||||||
|
frontendID := vars["frontend"]
|
||||||
|
|
||||||
|
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
|
||||||
|
if provider, ok := currentConfigurations[providerID]; ok {
|
||||||
|
if frontend, ok := provider.Frontends[frontendID]; ok {
|
||||||
|
err := templatesRenderer.JSON(response, http.StatusOK, frontend.Routes)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
http.NotFound(response, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Handler) getRouteHandler(response http.ResponseWriter, request *http.Request) {
|
||||||
|
vars := mux.Vars(request)
|
||||||
|
providerID := getProviderIDFromVars(vars)
|
||||||
|
frontendID := vars["frontend"]
|
||||||
|
routeID := vars["route"]
|
||||||
|
|
||||||
|
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
|
||||||
|
if provider, ok := currentConfigurations[providerID]; ok {
|
||||||
|
if frontend, ok := provider.Frontends[frontendID]; ok {
|
||||||
|
if route, ok := frontend.Routes[routeID]; ok {
|
||||||
|
err := templatesRenderer.JSON(response, http.StatusOK, route)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
http.NotFound(response, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
// healthResponse combines data returned by thoas/stats with statistics (if
|
||||||
|
// they are enabled).
|
||||||
|
type healthResponse struct {
|
||||||
|
*thoas_stats.Data
|
||||||
|
*middlewares.Stats
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Handler) getHealthHandler(response http.ResponseWriter, request *http.Request) {
|
||||||
|
health := &healthResponse{Data: p.Stats.Data()}
|
||||||
|
if p.StatsRecorder != nil {
|
||||||
|
health.Stats = p.StatsRecorder.Data()
|
||||||
|
}
|
||||||
|
err := templatesRenderer.JSON(response, http.StatusOK, health)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
977
autogen/gentemplates/gen.go
Normal file
977
autogen/gentemplates/gen.go
Normal file
@@ -0,0 +1,977 @@
|
|||||||
|
// Code generated by go-bindata.
|
||||||
|
// sources:
|
||||||
|
// templates/consul_catalog.tmpl
|
||||||
|
// templates/docker.tmpl
|
||||||
|
// templates/ecs.tmpl
|
||||||
|
// templates/eureka.tmpl
|
||||||
|
// templates/kubernetes.tmpl
|
||||||
|
// templates/kv.tmpl
|
||||||
|
// templates/marathon.tmpl
|
||||||
|
// templates/mesos.tmpl
|
||||||
|
// templates/notFound.tmpl
|
||||||
|
// templates/rancher.tmpl
|
||||||
|
// DO NOT EDIT!
|
||||||
|
|
||||||
|
package gentemplates
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type asset struct {
|
||||||
|
bytes []byte
|
||||||
|
info os.FileInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type bindataFileInfo struct {
|
||||||
|
name string
|
||||||
|
size int64
|
||||||
|
mode os.FileMode
|
||||||
|
modTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fi bindataFileInfo) Name() string {
|
||||||
|
return fi.name
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) Size() int64 {
|
||||||
|
return fi.size
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) Mode() os.FileMode {
|
||||||
|
return fi.mode
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) ModTime() time.Time {
|
||||||
|
return fi.modTime
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) IsDir() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func (fi bindataFileInfo) Sys() interface{} {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _templatesConsul_catalogTmpl = []byte(`[backends]
|
||||||
|
{{range $index, $node := .Nodes}}
|
||||||
|
[backends."backend-{{getBackend $node}}".servers."{{getBackendName $node $index}}"]
|
||||||
|
url = "{{getAttribute "protocol" $node.Service.Tags "http"}}://{{getBackendAddress $node}}:{{$node.Service.Port}}"
|
||||||
|
{{$weight := getAttribute "backend.weight" $node.Service.Tags "0"}}
|
||||||
|
{{with $weight}}
|
||||||
|
weight = {{$weight}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{range .Services}}
|
||||||
|
{{$service := .ServiceName}}
|
||||||
|
{{$circuitBreaker := getAttribute "backend.circuitbreaker" .Attributes ""}}
|
||||||
|
{{with $circuitBreaker}}
|
||||||
|
[backends."backend-{{$service}}".circuitbreaker]
|
||||||
|
expression = "{{$circuitBreaker}}"
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
[backends."backend-{{$service}}".loadbalancer]
|
||||||
|
method = "{{getAttribute "backend.loadbalancer" .Attributes "wrr"}}"
|
||||||
|
sticky = {{getSticky .Attributes}}
|
||||||
|
{{if hasStickinessLabel .Attributes}}
|
||||||
|
[backends."backend-{{$service}}".loadbalancer.stickiness]
|
||||||
|
cookieName = "{{getStickinessCookieName .Attributes}}"
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if hasMaxconnAttributes .Attributes}}
|
||||||
|
[backends."backend-{{$service}}".maxconn]
|
||||||
|
amount = {{getAttribute "backend.maxconn.amount" .Attributes "" }}
|
||||||
|
extractorfunc = "{{getAttribute "backend.maxconn.extractorfunc" .Attributes "" }}"
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
[frontends]
|
||||||
|
{{range .Services}}
|
||||||
|
[frontends."frontend-{{.ServiceName}}"]
|
||||||
|
backend = "backend-{{.ServiceName}}"
|
||||||
|
passHostHeader = {{getAttribute "frontend.passHostHeader" .Attributes "true"}}
|
||||||
|
priority = {{getAttribute "frontend.priority" .Attributes "0"}}
|
||||||
|
{{$entryPoints := getAttribute "frontend.entrypoints" .Attributes ""}}
|
||||||
|
{{with $entryPoints}}
|
||||||
|
entrypoints = [{{range getEntryPoints $entryPoints}}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
{{end}}
|
||||||
|
basicAuth = [{{range getBasicAuth .Attributes}}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
[frontends."frontend-{{.ServiceName}}".routes."route-host-{{.ServiceName}}"]
|
||||||
|
rule = "{{getFrontendRule .}}"
|
||||||
|
{{end}}
|
||||||
|
`)
|
||||||
|
|
||||||
|
func templatesConsul_catalogTmplBytes() ([]byte, error) {
|
||||||
|
return _templatesConsul_catalogTmpl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func templatesConsul_catalogTmpl() (*asset, error) {
|
||||||
|
bytes, err := templatesConsul_catalogTmplBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := bindataFileInfo{name: "templates/consul_catalog.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||||
|
a := &asset{bytes: bytes, info: info}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
|
||||||
|
[backends]{{range $backendName, $backend := .Backends}}
|
||||||
|
{{if hasCircuitBreakerLabel $backend}}
|
||||||
|
[backends.backend-{{$backendName}}.circuitbreaker]
|
||||||
|
expression = "{{getCircuitBreakerExpression $backend}}"
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if hasLoadBalancerLabel $backend}}
|
||||||
|
[backends.backend-{{$backendName}}.loadbalancer]
|
||||||
|
method = "{{getLoadBalancerMethod $backend}}"
|
||||||
|
sticky = {{getSticky $backend}}
|
||||||
|
{{if hasStickinessLabel $backend}}
|
||||||
|
[backends.backend-{{$backendName}}.loadBalancer.stickiness]
|
||||||
|
cookieName = "{{getStickinessCookieName $backend}}"
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if hasMaxConnLabels $backend}}
|
||||||
|
[backends.backend-{{$backendName}}.maxconn]
|
||||||
|
amount = {{getMaxConnAmount $backend}}
|
||||||
|
extractorfunc = "{{getMaxConnExtractorFunc $backend}}"
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{$servers := index $backendServers $backendName}}
|
||||||
|
{{range $serverName, $server := $servers}}
|
||||||
|
{{if hasServices $server}}
|
||||||
|
{{$services := getServiceNames $server}}
|
||||||
|
{{range $serviceIndex, $serviceName := $services}}
|
||||||
|
[backends.backend-{{getServiceBackend $server $serviceName}}.servers.service-{{$serverName}}]
|
||||||
|
url = "{{getServiceProtocol $server $serviceName}}://{{getIPAddress $server}}:{{getServicePort $server $serviceName}}"
|
||||||
|
weight = {{getServiceWeight $server $serviceName}}
|
||||||
|
{{end}}
|
||||||
|
{{else}}
|
||||||
|
[backends.backend-{{$backendName}}.servers.server-{{$server.Name | replace "/" "" | replace "." "-"}}]
|
||||||
|
url = "{{getProtocol $server}}://{{getIPAddress $server}}:{{getPort $server}}"
|
||||||
|
weight = {{getWeight $server}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
[frontends]{{range $frontend, $containers := .Frontends}}
|
||||||
|
{{$container := index $containers 0}}
|
||||||
|
{{if hasServices $container}}
|
||||||
|
{{$services := getServiceNames $container}}
|
||||||
|
{{range $serviceIndex, $serviceName := $services}}
|
||||||
|
[frontends."frontend-{{getServiceBackend $container $serviceName}}"]
|
||||||
|
backend = "backend-{{getServiceBackend $container $serviceName}}"
|
||||||
|
passHostHeader = {{getServicePassHostHeader $container $serviceName}}
|
||||||
|
{{if getWhitelistSourceRange $container}}
|
||||||
|
whitelistSourceRange = [{{range getWhitelistSourceRange $container}}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
{{end}}
|
||||||
|
priority = {{getServicePriority $container $serviceName}}
|
||||||
|
entryPoints = [{{range getServiceEntryPoints $container $serviceName}}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
basicAuth = [{{range getServiceBasicAuth $container $serviceName}}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
|
||||||
|
{{if hasServiceRedirect $container $serviceName}}
|
||||||
|
[frontends."frontend-{{getServiceBackend $container $serviceName}}".redirect]
|
||||||
|
entryPoint = "{{getServiceRedirectEntryPoint $container $serviceName}}"
|
||||||
|
regex = "{{getServiceRedirectRegex $container $serviceName}}"
|
||||||
|
replacement = "{{getServiceRedirectReplacement $container $serviceName}}"
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
[frontends."frontend-{{getServiceBackend $container $serviceName}}".routes."service-{{$serviceName | replace "/" "" | replace "." "-"}}"]
|
||||||
|
rule = "{{getServiceFrontendRule $container $serviceName}}"
|
||||||
|
{{end}}
|
||||||
|
{{else}}
|
||||||
|
[frontends."frontend-{{$frontend}}"]
|
||||||
|
backend = "backend-{{getBackend $container}}"
|
||||||
|
passHostHeader = {{getPassHostHeader $container}}
|
||||||
|
{{if getWhitelistSourceRange $container}}
|
||||||
|
whitelistSourceRange = [{{range getWhitelistSourceRange $container}}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
{{end}}
|
||||||
|
priority = {{getPriority $container}}
|
||||||
|
entryPoints = [{{range getEntryPoints $container}}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
basicAuth = [{{range getBasicAuth $container}}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
|
||||||
|
{{if hasRedirect $container}}
|
||||||
|
[frontends."frontend-{{$frontend}}".redirect]
|
||||||
|
entryPoint = "{{getRedirectEntryPoint $container}}"
|
||||||
|
regex = "{{getRedirectRegex $container}}"
|
||||||
|
replacement = "{{getRedirectReplacement $container}}"
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
[frontends."frontend-{{$frontend}}".headers]
|
||||||
|
{{if hasSSLRedirectHeaders $container}}
|
||||||
|
SSLRedirect = {{getSSLRedirectHeaders $container}}
|
||||||
|
{{end}}
|
||||||
|
{{if hasSSLTemporaryRedirectHeaders $container}}
|
||||||
|
SSLTemporaryRedirect = {{getSSLTemporaryRedirectHeaders $container}}
|
||||||
|
{{end}}
|
||||||
|
{{if hasSSLHostHeaders $container}}
|
||||||
|
SSLHost = "{{getSSLHostHeaders $container}}"
|
||||||
|
{{end}}
|
||||||
|
{{if hasSTSSecondsHeaders $container}}
|
||||||
|
STSSeconds = {{getSTSSecondsHeaders $container}}
|
||||||
|
{{end}}
|
||||||
|
{{if hasSTSIncludeSubdomainsHeaders $container}}
|
||||||
|
STSIncludeSubdomains = {{getSTSIncludeSubdomainsHeaders $container}}
|
||||||
|
{{end}}
|
||||||
|
{{if hasSTSPreloadHeaders $container}}
|
||||||
|
STSPreload = {{getSTSPreloadHeaders $container}}
|
||||||
|
{{end}}
|
||||||
|
{{if hasForceSTSHeaderHeaders $container}}
|
||||||
|
ForceSTSHeader = {{getForceSTSHeaderHeaders $container}}
|
||||||
|
{{end}}
|
||||||
|
{{if hasFrameDenyHeaders $container}}
|
||||||
|
FrameDeny = {{getFrameDenyHeaders $container}}
|
||||||
|
{{end}}
|
||||||
|
{{if hasCustomFrameOptionsValueHeaders $container}}
|
||||||
|
CustomFrameOptionsValue = "{{getCustomFrameOptionsValueHeaders $container}}"
|
||||||
|
{{end}}
|
||||||
|
{{if hasContentTypeNosniffHeaders $container}}
|
||||||
|
ContentTypeNosniff = {{getContentTypeNosniffHeaders $container}}
|
||||||
|
{{end}}
|
||||||
|
{{if hasBrowserXSSFilterHeaders $container}}
|
||||||
|
BrowserXSSFilter = {{getBrowserXSSFilterHeaders $container}}
|
||||||
|
{{end}}
|
||||||
|
{{if hasContentSecurityPolicyHeaders $container}}
|
||||||
|
ContentSecurityPolicy = "{{getContentSecurityPolicyHeaders $container}}"
|
||||||
|
{{end}}
|
||||||
|
{{if hasPublicKeyHeaders $container}}
|
||||||
|
PublicKey = "{{getPublicKeyHeaders $container}}"
|
||||||
|
{{end}}
|
||||||
|
{{if hasReferrerPolicyHeaders $container}}
|
||||||
|
ReferrerPolicy = "{{getReferrerPolicyHeaders $container}}"
|
||||||
|
{{end}}
|
||||||
|
{{if hasIsDevelopmentHeaders $container}}
|
||||||
|
IsDevelopment = {{getIsDevelopmentHeaders $container}}
|
||||||
|
{{end}}
|
||||||
|
{{if hasRequestHeaders $container}}
|
||||||
|
[frontends."frontend-{{$frontend}}".headers.customrequestheaders]
|
||||||
|
{{range $k, $v := getRequestHeaders $container}}
|
||||||
|
{{$k}} = "{{$v}}"
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{if hasResponseHeaders $container}}
|
||||||
|
[frontends."frontend-{{$frontend}}".headers.customresponseheaders]
|
||||||
|
{{range $k, $v := getResponseHeaders $container}}
|
||||||
|
{{$k}} = "{{$v}}"
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{if hasAllowedHostsHeaders $container}}
|
||||||
|
[frontends."frontend-{{$frontend}}".headers.AllowedHosts]
|
||||||
|
{{range getAllowedHostsHeaders $container}}
|
||||||
|
"{{.}}"
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{if hasHostsProxyHeaders $container}}
|
||||||
|
[frontends."frontend-{{$frontend}}".headers.HostsProxyHeaders]
|
||||||
|
{{range getHostsProxyHeaders $container}}
|
||||||
|
"{{.}}"
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{if hasSSLProxyHeaders $container}}
|
||||||
|
[frontends."frontend-{{$frontend}}".headers.SSLProxyHeaders]
|
||||||
|
{{range $k, $v := getSSLProxyHeaders $container}}
|
||||||
|
{{$k}} = "{{$v}}"
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
[frontends."frontend-{{$frontend}}".routes."route-frontend-{{$frontend}}"]
|
||||||
|
rule = "{{getFrontendRule $container}}"
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
`)
|
||||||
|
|
||||||
|
func templatesDockerTmplBytes() ([]byte, error) {
|
||||||
|
return _templatesDockerTmpl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func templatesDockerTmpl() (*asset, error) {
|
||||||
|
bytes, err := templatesDockerTmplBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := bindataFileInfo{name: "templates/docker.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||||
|
a := &asset{bytes: bytes, info: info}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _templatesEcsTmpl = []byte(`[backends]{{range $serviceName, $instances := .Services}}
|
||||||
|
[backends.backend-{{ $serviceName }}.loadbalancer]
|
||||||
|
method = "{{ getLoadBalancerMethod $instances}}"
|
||||||
|
sticky = {{ getLoadBalancerSticky $instances}}
|
||||||
|
{{if hasStickinessLabel $instances}}
|
||||||
|
[backends.backend-{{ $serviceName }}.loadbalancer.stickiness]
|
||||||
|
cookieName = "{{getStickinessCookieName $instances}}"
|
||||||
|
{{end}}
|
||||||
|
{{ if hasHealthCheckLabels $instances }}
|
||||||
|
[backends.backend-{{ $serviceName }}.healthcheck]
|
||||||
|
path = "{{getHealthCheckPath $instances }}"
|
||||||
|
interval = "{{getHealthCheckInterval $instances }}"
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{range $index, $i := $instances}}
|
||||||
|
[backends.backend-{{ $i.Name }}.servers.server-{{ $i.Name }}{{ $i.ID }}]
|
||||||
|
url = "{{ getProtocol $i }}://{{ getHost $i }}:{{ getPort $i }}"
|
||||||
|
weight = {{ getWeight $i}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
[frontends]{{range $serviceName, $instances := .Services}}
|
||||||
|
{{range filterFrontends $instances}}
|
||||||
|
[frontends.frontend-{{ $serviceName }}]
|
||||||
|
backend = "backend-{{ $serviceName }}"
|
||||||
|
passHostHeader = {{ getPassHostHeader .}}
|
||||||
|
priority = {{ getPriority .}}
|
||||||
|
entryPoints = [{{range getEntryPoints .}}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
basicAuth = [{{range getBasicAuth .}}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
[frontends.frontend-{{ $serviceName }}.routes.route-frontend-{{ $serviceName }}]
|
||||||
|
rule = "{{getFrontendRule .}}"
|
||||||
|
{{end}}
|
||||||
|
{{end}}`)
|
||||||
|
|
||||||
|
func templatesEcsTmplBytes() ([]byte, error) {
|
||||||
|
return _templatesEcsTmpl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func templatesEcsTmpl() (*asset, error) {
|
||||||
|
bytes, err := templatesEcsTmplBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := bindataFileInfo{name: "templates/ecs.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||||
|
a := &asset{bytes: bytes, info: info}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _templatesEurekaTmpl = []byte(`[backends]{{range .Applications}}
|
||||||
|
{{ $app := .}}
|
||||||
|
{{range .Instances}}
|
||||||
|
[backends.backend{{$app.Name}}.servers.server-{{ getInstanceID . }}]
|
||||||
|
url = "{{ getProtocol . }}://{{ .IpAddr }}:{{ getPort . }}"
|
||||||
|
weight = {{ getWeight . }}
|
||||||
|
{{end}}{{end}}
|
||||||
|
|
||||||
|
[frontends]{{range .Applications}}
|
||||||
|
[frontends.frontend{{.Name}}]
|
||||||
|
backend = "backend{{.Name}}"
|
||||||
|
entryPoints = ["http"]
|
||||||
|
[frontends.frontend{{.Name }}.routes.route-host{{.Name}}]
|
||||||
|
rule = "Host:{{ .Name | tolower }}"
|
||||||
|
{{end}}
|
||||||
|
`)
|
||||||
|
|
||||||
|
func templatesEurekaTmplBytes() ([]byte, error) {
|
||||||
|
return _templatesEurekaTmpl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func templatesEurekaTmpl() (*asset, error) {
|
||||||
|
bytes, err := templatesEurekaTmplBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := bindataFileInfo{name: "templates/eureka.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||||
|
a := &asset{bytes: bytes, info: info}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _templatesKubernetesTmpl = []byte(`[backends]{{range $backendName, $backend := .Backends}}
|
||||||
|
[backends."{{$backendName}}"]
|
||||||
|
{{if $backend.CircuitBreaker}}
|
||||||
|
[backends."{{$backendName}}".circuitbreaker]
|
||||||
|
expression = "{{$backend.CircuitBreaker.Expression}}"
|
||||||
|
{{end}}
|
||||||
|
[backends."{{$backendName}}".loadbalancer]
|
||||||
|
method = "{{$backend.LoadBalancer.Method}}"
|
||||||
|
{{if $backend.LoadBalancer.Sticky}}
|
||||||
|
sticky = true
|
||||||
|
{{end}}
|
||||||
|
{{if $backend.LoadBalancer.Stickiness}}
|
||||||
|
[backends."{{$backendName}}".loadbalancer.stickiness]
|
||||||
|
cookieName = "{{$backend.LoadBalancer.Stickiness.CookieName}}"
|
||||||
|
{{end}}
|
||||||
|
{{range $serverName, $server := $backend.Servers}}
|
||||||
|
[backends."{{$backendName}}".servers."{{$serverName}}"]
|
||||||
|
url = "{{$server.URL}}"
|
||||||
|
weight = {{$server.Weight}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
[frontends]{{range $frontendName, $frontend := .Frontends}}
|
||||||
|
[frontends."{{$frontendName}}"]
|
||||||
|
backend = "{{$frontend.Backend}}"
|
||||||
|
priority = {{$frontend.Priority}}
|
||||||
|
passHostHeader = {{$frontend.PassHostHeader}}
|
||||||
|
entryPoints = [{{range $frontend.EntryPoints}}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
basicAuth = [{{range $frontend.BasicAuth}}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
whitelistSourceRange = [{{range $frontend.WhitelistSourceRange}}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
|
||||||
|
{{if $frontend.Redirect}}
|
||||||
|
[frontends."{{$frontendName}}".redirect]
|
||||||
|
entryPoint = "{{$frontend.RedirectEntryPoint}}"
|
||||||
|
regex = "{{$frontend.RedirectRegex}}"
|
||||||
|
replacement = "{{$frontend.RedirectReplacement}}"
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
[frontends."{{$frontendName}}".headers]
|
||||||
|
SSLRedirect = {{$frontend.Headers.SSLRedirect}}
|
||||||
|
SSLTemporaryRedirect = {{$frontend.Headers.SSLTemporaryRedirect}}
|
||||||
|
SSLHost = "{{$frontend.Headers.SSLHost}}"
|
||||||
|
STSSeconds = {{$frontend.Headers.STSSeconds}}
|
||||||
|
STSIncludeSubdomains = {{$frontend.Headers.STSIncludeSubdomains}}
|
||||||
|
STSPreload = {{$frontend.Headers.STSPreload}}
|
||||||
|
ForceSTSHeader = {{$frontend.Headers.ForceSTSHeader}}
|
||||||
|
FrameDeny = {{$frontend.Headers.FrameDeny}}
|
||||||
|
CustomFrameOptionsValue = "{{$frontend.Headers.CustomFrameOptionsValue}}"
|
||||||
|
ContentTypeNosniff = {{$frontend.Headers.ContentTypeNosniff}}
|
||||||
|
BrowserXSSFilter = {{$frontend.Headers.BrowserXSSFilter}}
|
||||||
|
ContentSecurityPolicy = "{{$frontend.Headers.ContentSecurityPolicy}}"
|
||||||
|
PublicKey = "{{$frontend.Headers.PublicKey}}"
|
||||||
|
ReferrerPolicy = "{{$frontend.Headers.ReferrerPolicy}}"
|
||||||
|
IsDevelopment = {{$frontend.Headers.IsDevelopment}}
|
||||||
|
{{if $frontend.Headers.CustomRequestHeaders}}
|
||||||
|
[frontends."{{$frontendName}}".headers.customrequestheaders]
|
||||||
|
{{range $k, $v := $frontend.Headers.CustomRequestHeaders}}
|
||||||
|
{{$k}} = "{{$v}}"
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{if $frontend.Headers.CustomResponseHeaders}}
|
||||||
|
[frontends."{{$frontendName}}".headers.customresponseheaders]
|
||||||
|
{{range $k, $v := $frontend.Headers.CustomResponseHeaders}}
|
||||||
|
{{$k}} = "{{$v}}"
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{if $frontend.Headers.AllowedHosts}}
|
||||||
|
[frontends."{{$frontendName}}".headers.AllowedHosts]
|
||||||
|
{{range $frontend.Headers.AllowedHosts}}
|
||||||
|
"{{.}}"
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{if $frontend.Headers.HostsProxyHeaders}}
|
||||||
|
[frontends."{{$frontendName}}".headers.HostsProxyHeaders]
|
||||||
|
{{range $frontend.Headers.HostsProxyHeaders}}
|
||||||
|
"{{.}}"
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{if $frontend.Headers.SSLProxyHeaders}}
|
||||||
|
[frontends."{{$frontendName}}".headers.SSLProxyHeaders]
|
||||||
|
{{range $k, $v := $frontend.Headers.SSLProxyHeaders}}
|
||||||
|
{{$k}} = "{{$v}}"
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{range $routeName, $route := $frontend.Routes}}
|
||||||
|
[frontends."{{$frontendName}}".routes."{{$routeName}}"]
|
||||||
|
rule = "{{$route.Rule}}"
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
`)
|
||||||
|
|
||||||
|
func templatesKubernetesTmplBytes() ([]byte, error) {
|
||||||
|
return _templatesKubernetesTmpl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func templatesKubernetesTmpl() (*asset, error) {
|
||||||
|
bytes, err := templatesKubernetesTmplBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := bindataFileInfo{name: "templates/kubernetes.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||||
|
a := &asset{bytes: bytes, info: info}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _templatesKvTmpl = []byte(`{{$frontends := List .Prefix "/frontends/" }}
|
||||||
|
{{$backends := List .Prefix "/backends/"}}
|
||||||
|
{{$tlsconfiguration := List .Prefix "/tlsconfiguration/"}}
|
||||||
|
|
||||||
|
[backends]{{range $backends}}
|
||||||
|
{{$backend := .}}
|
||||||
|
{{$backendName := Last $backend}}
|
||||||
|
{{$servers := ListServers $backend }}
|
||||||
|
|
||||||
|
{{$circuitBreaker := Get "" . "/circuitbreaker/" "expression"}}
|
||||||
|
{{with $circuitBreaker}}
|
||||||
|
[backends."{{$backendName}}".circuitBreaker]
|
||||||
|
expression = "{{$circuitBreaker}}"
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{$loadBalancer := Get "" . "/loadbalancer/" "method"}}
|
||||||
|
{{with $loadBalancer}}
|
||||||
|
[backends."{{$backendName}}".loadBalancer]
|
||||||
|
method = "{{$loadBalancer}}"
|
||||||
|
sticky = {{ getSticky . }}
|
||||||
|
{{if hasStickinessLabel $backend}}
|
||||||
|
[backends."{{$backendName}}".loadBalancer.stickiness]
|
||||||
|
cookieName = "{{getStickinessCookieName $backend}}"
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{$healthCheck := Get "" . "/healthcheck/" "path"}}
|
||||||
|
{{with $healthCheck}}
|
||||||
|
[backends."{{$backendName}}".healthCheck]
|
||||||
|
path = "{{$healthCheck}}"
|
||||||
|
interval = "{{ Get "30s" $backend "/healthcheck/" "interval" }}"
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{$maxConnAmt := Get "" . "/maxconn/" "amount"}}
|
||||||
|
{{$maxConnExtractorFunc := Get "" . "/maxconn/" "extractorfunc"}}
|
||||||
|
{{with $maxConnAmt}}
|
||||||
|
{{with $maxConnExtractorFunc}}
|
||||||
|
[backends."{{$backendName}}".maxConn]
|
||||||
|
amount = {{$maxConnAmt}}
|
||||||
|
extractorFunc = "{{$maxConnExtractorFunc}}"
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{range $servers}}
|
||||||
|
[backends."{{$backendName}}".servers."{{Last .}}"]
|
||||||
|
url = "{{Get "" . "/url"}}"
|
||||||
|
weight = {{Get "0" . "/weight"}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
[frontends]{{range $frontends}}
|
||||||
|
{{$frontend := Last .}}
|
||||||
|
{{$entryPoints := SplitGet . "/entrypoints"}}
|
||||||
|
[frontends."{{$frontend}}"]
|
||||||
|
backend = "{{Get "" . "/backend"}}"
|
||||||
|
passHostHeader = {{Get "true" . "/passHostHeader"}}
|
||||||
|
priority = {{Get "0" . "/priority"}}
|
||||||
|
entryPoints = [{{range $entryPoints}}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
{{$routes := List . "/routes/"}}
|
||||||
|
{{range $routes}}
|
||||||
|
[frontends."{{$frontend}}".routes."{{Last .}}"]
|
||||||
|
rule = "{{Get "" . "/rule"}}"
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{range $tlsconfiguration}}
|
||||||
|
{{$entryPoints := SplitGet . "/entrypoints"}}
|
||||||
|
[[tlsConfiguration]]
|
||||||
|
entryPoints = [{{range $entryPoints}}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
[tlsConfiguration.certificate]
|
||||||
|
certFile = """{{Get "" . "/certificate" "/certfile"}}"""
|
||||||
|
keyFile = """{{Get "" . "/certificate" "/keyfile"}}"""
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
`)
|
||||||
|
|
||||||
|
func templatesKvTmplBytes() ([]byte, error) {
|
||||||
|
return _templatesKvTmpl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func templatesKvTmpl() (*asset, error) {
|
||||||
|
bytes, err := templatesKvTmplBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := bindataFileInfo{name: "templates/kv.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||||
|
a := &asset{bytes: bytes, info: info}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _templatesMarathonTmpl = []byte(`{{$apps := .Applications}}
|
||||||
|
|
||||||
|
{{range $app := $apps}}
|
||||||
|
{{range $task := $app.Tasks}}
|
||||||
|
{{range $serviceIndex, $serviceName := getServiceNames $app}}
|
||||||
|
[backends."backend{{getBackend $app $serviceName}}".servers."server-{{$task.ID | replace "." "-"}}{{getServiceNameSuffix $serviceName }}"]
|
||||||
|
url = "{{getProtocol $app $serviceName}}://{{getBackendServer $task $app}}:{{getPort $task $app $serviceName}}"
|
||||||
|
weight = {{getWeight $app $serviceName}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{range $app := $apps}}
|
||||||
|
{{range $serviceIndex, $serviceName := getServiceNames $app}}
|
||||||
|
[backends."backend{{getBackend $app $serviceName }}"]
|
||||||
|
{{ if hasMaxConnLabels $app }}
|
||||||
|
[backends."backend{{getBackend $app $serviceName }}".maxconn]
|
||||||
|
amount = {{getMaxConnAmount $app }}
|
||||||
|
extractorfunc = "{{getMaxConnExtractorFunc $app }}"
|
||||||
|
{{end}}
|
||||||
|
{{ if hasLoadBalancerLabels $app }}
|
||||||
|
[backends."backend{{getBackend $app $serviceName }}".loadbalancer]
|
||||||
|
method = "{{getLoadBalancerMethod $app }}"
|
||||||
|
sticky = {{getSticky $app}}
|
||||||
|
{{if hasStickinessLabel $app}}
|
||||||
|
[backends."backend{{getBackend $app $serviceName }}".loadbalancer.stickiness]
|
||||||
|
cookieName = "{{getStickinessCookieName $app}}"
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{ if hasCircuitBreakerLabels $app }}
|
||||||
|
[backends."backend{{getBackend $app $serviceName }}".circuitbreaker]
|
||||||
|
expression = "{{getCircuitBreakerExpression $app }}"
|
||||||
|
{{end}}
|
||||||
|
{{ if hasHealthCheckLabels $app }}
|
||||||
|
[backends."backend{{getBackend $app $serviceName }}".healthcheck]
|
||||||
|
path = "{{getHealthCheckPath $app }}"
|
||||||
|
interval = "{{getHealthCheckInterval $app }}"
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
[frontends]{{range $app := $apps}}{{range $serviceIndex, $serviceName := getServiceNames .}}
|
||||||
|
[frontends."{{ getFrontendName $app $serviceName }}"]
|
||||||
|
backend = "backend{{getBackend $app $serviceName}}"
|
||||||
|
passHostHeader = {{getPassHostHeader $app $serviceName}}
|
||||||
|
priority = {{getPriority $app $serviceName}}
|
||||||
|
entryPoints = [{{range getEntryPoints $app $serviceName}}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
basicAuth = [{{range getBasicAuth $app $serviceName}}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
[frontends."{{ getFrontendName $app $serviceName }}".routes."route-host{{$app.ID | replace "/" "-"}}{{getServiceNameSuffix $serviceName }}"]
|
||||||
|
rule = "{{getFrontendRule $app $serviceName}}"
|
||||||
|
{{end}}{{end}}
|
||||||
|
`)
|
||||||
|
|
||||||
|
func templatesMarathonTmplBytes() ([]byte, error) {
|
||||||
|
return _templatesMarathonTmpl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func templatesMarathonTmpl() (*asset, error) {
|
||||||
|
bytes, err := templatesMarathonTmplBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := bindataFileInfo{name: "templates/marathon.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||||
|
a := &asset{bytes: bytes, info: info}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _templatesMesosTmpl = []byte(`{{$apps := .Applications}}
|
||||||
|
[backends]{{range .Tasks}}
|
||||||
|
[backends.backend{{getBackend . $apps}}.servers.server-{{getID .}}]
|
||||||
|
url = "{{getProtocol . $apps}}://{{getHost .}}:{{getPort . $apps}}"
|
||||||
|
weight = {{getWeight . $apps}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
[frontends]{{range .Applications}}
|
||||||
|
[frontends.frontend-{{getFrontEndName .}}]
|
||||||
|
backend = "backend{{getFrontendBackend .}}"
|
||||||
|
passHostHeader = {{getPassHostHeader .}}
|
||||||
|
priority = {{getPriority .}}
|
||||||
|
entryPoints = [{{range getEntryPoints .}}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
[frontends.frontend-{{getFrontEndName .}}.routes.route-host{{getFrontEndName .}}]
|
||||||
|
rule = "{{getFrontendRule .}}"
|
||||||
|
{{end}}
|
||||||
|
`)
|
||||||
|
|
||||||
|
func templatesMesosTmplBytes() ([]byte, error) {
|
||||||
|
return _templatesMesosTmpl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func templatesMesosTmpl() (*asset, error) {
|
||||||
|
bytes, err := templatesMesosTmplBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := bindataFileInfo{name: "templates/mesos.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||||
|
a := &asset{bytes: bytes, info: info}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _templatesNotfoundTmpl = []byte(`<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Traefik</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
Ohhhh man, this is bad...
|
||||||
|
</body>
|
||||||
|
</html>`)
|
||||||
|
|
||||||
|
func templatesNotfoundTmplBytes() ([]byte, error) {
|
||||||
|
return _templatesNotfoundTmpl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func templatesNotfoundTmpl() (*asset, error) {
|
||||||
|
bytes, err := templatesNotfoundTmplBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := bindataFileInfo{name: "templates/notFound.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||||
|
a := &asset{bytes: bytes, info: info}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _templatesRancherTmpl = []byte(`{{$backendServers := .Backends}}
|
||||||
|
[backends]{{range $backendName, $backend := .Backends}}
|
||||||
|
{{if hasCircuitBreakerLabel $backend}}
|
||||||
|
[backends.backend-{{$backendName}}.circuitbreaker]
|
||||||
|
expression = "{{getCircuitBreakerExpression $backend}}"
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if hasLoadBalancerLabel $backend}}
|
||||||
|
[backends.backend-{{$backendName}}.loadbalancer]
|
||||||
|
method = "{{getLoadBalancerMethod $backend}}"
|
||||||
|
sticky = {{getSticky $backend}}
|
||||||
|
{{if hasStickinessLabel $backend}}
|
||||||
|
[backends.backend-{{$backendName}}.loadbalancer.stickiness]
|
||||||
|
cookieName = "{{getStickinessCookieName $backend}}"
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if hasMaxConnLabels $backend}}
|
||||||
|
[backends.backend-{{$backendName}}.maxconn]
|
||||||
|
amount = {{getMaxConnAmount $backend}}
|
||||||
|
extractorfunc = "{{getMaxConnExtractorFunc $backend}}"
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{range $index, $ip := $backend.Containers}}
|
||||||
|
[backends.backend-{{$backendName}}.servers.server-{{$index}}]
|
||||||
|
url = "{{getProtocol $backend}}://{{$ip}}:{{getPort $backend}}"
|
||||||
|
weight = {{getWeight $backend}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
[frontends]{{range $frontendName, $service := .Frontends}}
|
||||||
|
[frontends."frontend-{{$frontendName}}"]
|
||||||
|
backend = "backend-{{getBackend $service}}"
|
||||||
|
passHostHeader = {{getPassHostHeader $service}}
|
||||||
|
priority = {{getPriority $service}}
|
||||||
|
entryPoints = [{{range getEntryPoints $service}}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
basicAuth = [{{range getBasicAuth $service}}
|
||||||
|
"{{.}}",
|
||||||
|
{{end}}]
|
||||||
|
|
||||||
|
{{if hasRedirect $service}}
|
||||||
|
[frontends."frontend-{{$frontendName}}".redirect]
|
||||||
|
entryPoint = "{{getRedirectEntryPoint $service}}"
|
||||||
|
regex = "{{getRedirectRegex $service}}"
|
||||||
|
replacement = "{{getRedirectReplacement $service}}"
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
[frontends."frontend-{{$frontendName}}".routes."route-frontend-{{$frontendName}}"]
|
||||||
|
rule = "{{getFrontendRule $service}}"
|
||||||
|
{{end}}
|
||||||
|
`)
|
||||||
|
|
||||||
|
func templatesRancherTmplBytes() ([]byte, error) {
|
||||||
|
return _templatesRancherTmpl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func templatesRancherTmpl() (*asset, error) {
|
||||||
|
bytes, err := templatesRancherTmplBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info := bindataFileInfo{name: "templates/rancher.tmpl", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
|
||||||
|
a := &asset{bytes: bytes, info: info}
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Asset loads and returns the asset for the given name.
|
||||||
|
// It returns an error if the asset could not be found or
|
||||||
|
// could not be loaded.
|
||||||
|
func Asset(name string) ([]byte, error) {
|
||||||
|
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
if f, ok := _bindata[cannonicalName]; ok {
|
||||||
|
a, err := f()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
|
||||||
|
}
|
||||||
|
return a.bytes, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("Asset %s not found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustAsset is like Asset but panics when Asset would return an error.
|
||||||
|
// It simplifies safe initialization of global variables.
|
||||||
|
func MustAsset(name string) []byte {
|
||||||
|
a, err := Asset(name)
|
||||||
|
if err != nil {
|
||||||
|
panic("asset: Asset(" + name + "): " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetInfo loads and returns the asset info for the given name.
|
||||||
|
// It returns an error if the asset could not be found or
|
||||||
|
// could not be loaded.
|
||||||
|
func AssetInfo(name string) (os.FileInfo, error) {
|
||||||
|
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
if f, ok := _bindata[cannonicalName]; ok {
|
||||||
|
a, err := f()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
|
||||||
|
}
|
||||||
|
return a.info, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("AssetInfo %s not found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetNames returns the names of the assets.
|
||||||
|
func AssetNames() []string {
|
||||||
|
names := make([]string, 0, len(_bindata))
|
||||||
|
for name := range _bindata {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
// _bindata is a table, holding each asset generator, mapped to its name.
|
||||||
|
var _bindata = map[string]func() (*asset, error){
|
||||||
|
"templates/consul_catalog.tmpl": templatesConsul_catalogTmpl,
|
||||||
|
"templates/docker.tmpl": templatesDockerTmpl,
|
||||||
|
"templates/ecs.tmpl": templatesEcsTmpl,
|
||||||
|
"templates/eureka.tmpl": templatesEurekaTmpl,
|
||||||
|
"templates/kubernetes.tmpl": templatesKubernetesTmpl,
|
||||||
|
"templates/kv.tmpl": templatesKvTmpl,
|
||||||
|
"templates/marathon.tmpl": templatesMarathonTmpl,
|
||||||
|
"templates/mesos.tmpl": templatesMesosTmpl,
|
||||||
|
"templates/notFound.tmpl": templatesNotfoundTmpl,
|
||||||
|
"templates/rancher.tmpl": templatesRancherTmpl,
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetDir returns the file names below a certain
|
||||||
|
// directory embedded in the file by go-bindata.
|
||||||
|
// For example if you run go-bindata on data/... and data contains the
|
||||||
|
// following hierarchy:
|
||||||
|
// data/
|
||||||
|
// foo.txt
|
||||||
|
// img/
|
||||||
|
// a.png
|
||||||
|
// b.png
|
||||||
|
// then AssetDir("data") would return []string{"foo.txt", "img"}
|
||||||
|
// AssetDir("data/img") would return []string{"a.png", "b.png"}
|
||||||
|
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
|
||||||
|
// AssetDir("") will return []string{"data"}.
|
||||||
|
func AssetDir(name string) ([]string, error) {
|
||||||
|
node := _bintree
|
||||||
|
if len(name) != 0 {
|
||||||
|
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
pathList := strings.Split(cannonicalName, "/")
|
||||||
|
for _, p := range pathList {
|
||||||
|
node = node.Children[p]
|
||||||
|
if node == nil {
|
||||||
|
return nil, fmt.Errorf("Asset %s not found", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if node.Func != nil {
|
||||||
|
return nil, fmt.Errorf("Asset %s not found", name)
|
||||||
|
}
|
||||||
|
rv := make([]string, 0, len(node.Children))
|
||||||
|
for childName := range node.Children {
|
||||||
|
rv = append(rv, childName)
|
||||||
|
}
|
||||||
|
return rv, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type bintree struct {
|
||||||
|
Func func() (*asset, error)
|
||||||
|
Children map[string]*bintree
|
||||||
|
}
|
||||||
|
|
||||||
|
var _bintree = &bintree{nil, map[string]*bintree{
|
||||||
|
"templates": {nil, map[string]*bintree{
|
||||||
|
"consul_catalog.tmpl": {templatesConsul_catalogTmpl, map[string]*bintree{}},
|
||||||
|
"docker.tmpl": {templatesDockerTmpl, map[string]*bintree{}},
|
||||||
|
"ecs.tmpl": {templatesEcsTmpl, map[string]*bintree{}},
|
||||||
|
"eureka.tmpl": {templatesEurekaTmpl, map[string]*bintree{}},
|
||||||
|
"kubernetes.tmpl": {templatesKubernetesTmpl, map[string]*bintree{}},
|
||||||
|
"kv.tmpl": {templatesKvTmpl, map[string]*bintree{}},
|
||||||
|
"marathon.tmpl": {templatesMarathonTmpl, map[string]*bintree{}},
|
||||||
|
"mesos.tmpl": {templatesMesosTmpl, map[string]*bintree{}},
|
||||||
|
"notFound.tmpl": {templatesNotfoundTmpl, map[string]*bintree{}},
|
||||||
|
"rancher.tmpl": {templatesRancherTmpl, map[string]*bintree{}},
|
||||||
|
}},
|
||||||
|
}}
|
||||||
|
|
||||||
|
// RestoreAsset restores an asset under the given directory
|
||||||
|
func RestoreAsset(dir, name string) error {
|
||||||
|
data, err := Asset(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
info, err := AssetInfo(name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestoreAssets restores an asset under the given directory recursively
|
||||||
|
func RestoreAssets(dir, name string) error {
|
||||||
|
children, err := AssetDir(name)
|
||||||
|
// File
|
||||||
|
if err != nil {
|
||||||
|
return RestoreAsset(dir, name)
|
||||||
|
}
|
||||||
|
// Dir
|
||||||
|
for _, child := range children {
|
||||||
|
err = RestoreAssets(dir, filepath.Join(name, child))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func _filePath(dir, name string) string {
|
||||||
|
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||||
|
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
|
||||||
|
}
|
||||||
@@ -1,11 +1,8 @@
|
|||||||
FROM golang:1.8
|
FROM golang:1.9-alpine
|
||||||
|
|
||||||
# Install a more recent version of mercurial to avoid mismatching results
|
RUN apk --update upgrade \
|
||||||
# between glide run on a decently updated host system and the build container.
|
&& apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar \
|
||||||
RUN awk '$1 ~ "^deb" { $3 = $3 "-backports"; print; exit }' /etc/apt/sources.list > /etc/apt/sources.list.d/backports.list && \
|
&& rm -rf /var/cache/apk/*
|
||||||
DEBIAN_FRONTEND=noninteractive apt-get update && \
|
|
||||||
DEBIAN_FRONTEND=noninteractive apt-get install -t jessie-backports --yes --no-install-recommends mercurial=3.9.1-1~bpo8+1 && \
|
|
||||||
rm -fr /var/lib/apt/lists/
|
|
||||||
|
|
||||||
RUN go get github.com/jteeuwen/go-bindata/... \
|
RUN go get github.com/jteeuwen/go-bindata/... \
|
||||||
&& go get github.com/golang/lint/golint \
|
&& go get github.com/golang/lint/golint \
|
||||||
@@ -15,8 +12,7 @@ RUN go get github.com/jteeuwen/go-bindata/... \
|
|||||||
&& go get github.com/sgotti/glide-vc
|
&& go get github.com/sgotti/glide-vc
|
||||||
|
|
||||||
# Which docker version to test on
|
# Which docker version to test on
|
||||||
ARG DOCKER_VERSION=17.03.1
|
ARG DOCKER_VERSION=17.03.2
|
||||||
|
|
||||||
|
|
||||||
# Which glide version to test on
|
# Which glide version to test on
|
||||||
ARG GLIDE_VERSION=v0.12.3
|
ARG GLIDE_VERSION=v0.12.3
|
||||||
@@ -28,7 +24,7 @@ RUN mkdir -p /usr/local/bin \
|
|||||||
|
|
||||||
# Download docker
|
# Download docker
|
||||||
RUN mkdir -p /usr/local/bin \
|
RUN mkdir -p /usr/local/bin \
|
||||||
&& curl -fL https://get.docker.com/builds/Linux/x86_64/docker-${DOCKER_VERSION}-ce.tgz \
|
&& curl -fL https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}-ce.tgz \
|
||||||
| tar -xzC /usr/local/bin --transform 's#^.+/##x'
|
| tar -xzC /usr/local/bin --transform 's#^.+/##x'
|
||||||
|
|
||||||
WORKDIR /go/src/github.com/containous/traefik
|
WORKDIR /go/src/github.com/containous/traefik
|
||||||
|
|||||||
@@ -76,11 +76,11 @@ func NewDataStore(ctx context.Context, kvSource staert.KvSource, object Object,
|
|||||||
|
|
||||||
func (d *Datastore) watchChanges() error {
|
func (d *Datastore) watchChanges() error {
|
||||||
stopCh := make(chan struct{})
|
stopCh := make(chan struct{})
|
||||||
kvCh, err := d.kv.Watch(d.lockKey, stopCh)
|
kvCh, err := d.kv.Watch(d.lockKey, stopCh, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
go func() {
|
safe.Go(func() {
|
||||||
ctx, cancel := context.WithCancel(d.ctx)
|
ctx, cancel := context.WithCancel(d.ctx)
|
||||||
operation := func() error {
|
operation := func() error {
|
||||||
for {
|
for {
|
||||||
@@ -97,7 +97,6 @@ func (d *Datastore) watchChanges() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// log.Debugf("Datastore object change received: %+v", d.meta)
|
|
||||||
if d.listener != nil {
|
if d.listener != nil {
|
||||||
err := d.listener(d.meta.object)
|
err := d.listener(d.meta.object)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -114,25 +113,14 @@ func (d *Datastore) watchChanges() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("Error in watch datastore: %v", err)
|
log.Errorf("Error in watch datastore: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Datastore) reload() error {
|
func (d *Datastore) reload() error {
|
||||||
log.Debugf("Datastore reload")
|
log.Debug("Datastore reload")
|
||||||
d.localLock.Lock()
|
_, err := d.Load()
|
||||||
err := d.kv.LoadConfig(d.meta)
|
return err
|
||||||
if err != nil {
|
|
||||||
d.localLock.Unlock()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = d.meta.unmarshall()
|
|
||||||
if err != nil {
|
|
||||||
d.localLock.Unlock()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
d.localLock.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Begin creates a transaction with the KV store.
|
// Begin creates a transaction with the KV store.
|
||||||
@@ -200,6 +188,10 @@ func (d *Datastore) get() *Metadata {
|
|||||||
func (d *Datastore) Load() (Object, error) {
|
func (d *Datastore) Load() (Object, error) {
|
||||||
d.localLock.Lock()
|
d.localLock.Lock()
|
||||||
defer d.localLock.Unlock()
|
defer d.localLock.Unlock()
|
||||||
|
|
||||||
|
// clear Object first, as mapstructure's decoder doesn't have ZeroFields set to true for merging purposes
|
||||||
|
d.meta.Object = d.meta.Object[:0]
|
||||||
|
|
||||||
err := d.kv.LoadConfig(d.meta)
|
err := d.kv.LoadConfig(d.meta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ func (l *Leadership) Participate(pool *safe.Pool) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddListener adds a leadership listerner
|
// AddListener adds a leadership listener
|
||||||
func (l *Leadership) AddListener(listener LeaderListener) {
|
func (l *Leadership) AddListener(listener LeaderListener) {
|
||||||
l.listeners = append(l.listeners, listener)
|
l.listeners = append(l.listeners, listener)
|
||||||
}
|
}
|
||||||
@@ -86,7 +86,7 @@ func (l *Leadership) onElection(elected bool) {
|
|||||||
l.leader.Set(true)
|
l.leader.Set(true)
|
||||||
l.Start()
|
l.Start()
|
||||||
} else {
|
} else {
|
||||||
log.Infof("Node %s elected slave ♝", l.Cluster.Node)
|
log.Infof("Node %s elected worker ♝", l.Cluster.Node)
|
||||||
l.leader.Set(false)
|
l.leader.Set(false)
|
||||||
l.Stop()
|
l.Stop()
|
||||||
}
|
}
|
||||||
|
|||||||
136
cmd/traefik/anonymize/anonymize.go
Normal file
136
cmd/traefik/anonymize/anonymize.go
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
package anonymize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/mitchellh/copystructure"
|
||||||
|
"github.com/mvdan/xurls"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maskShort = "xxxx"
|
||||||
|
maskLarge = maskShort + maskShort + maskShort + maskShort + maskShort + maskShort + maskShort + maskShort
|
||||||
|
)
|
||||||
|
|
||||||
|
// Do configuration.
|
||||||
|
func Do(baseConfig interface{}, indent bool) (string, error) {
|
||||||
|
anomConfig, err := copystructure.Copy(baseConfig)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
val := reflect.ValueOf(anomConfig)
|
||||||
|
|
||||||
|
err = doOnStruct(val)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
configJSON, err := marshal(anomConfig, indent)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return doOnJSON(string(configJSON)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func doOnJSON(input string) string {
|
||||||
|
mailExp := regexp.MustCompile(`\w[-._\w]*\w@\w[-._\w]*\w\.\w{2,3}"`)
|
||||||
|
return xurls.Relaxed.ReplaceAllString(mailExp.ReplaceAllString(input, maskLarge+"\""), maskLarge)
|
||||||
|
}
|
||||||
|
|
||||||
|
func doOnStruct(field reflect.Value) error {
|
||||||
|
switch field.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
if !field.IsNil() {
|
||||||
|
if err := doOnStruct(field.Elem()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Struct:
|
||||||
|
for i := 0; i < field.NumField(); i++ {
|
||||||
|
fld := field.Field(i)
|
||||||
|
stField := field.Type().Field(i)
|
||||||
|
if !isExported(stField) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if stField.Tag.Get("export") == "true" {
|
||||||
|
if err := doOnStruct(fld); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := reset(fld, stField.Name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
for _, key := range field.MapKeys() {
|
||||||
|
if err := doOnStruct(field.MapIndex(key)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Slice:
|
||||||
|
for j := 0; j < field.Len(); j++ {
|
||||||
|
if err := doOnStruct(field.Index(j)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func reset(field reflect.Value, name string) error {
|
||||||
|
if !field.CanSet() {
|
||||||
|
return fmt.Errorf("cannot reset field %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch field.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
if !field.IsNil() {
|
||||||
|
field.Set(reflect.Zero(field.Type()))
|
||||||
|
}
|
||||||
|
case reflect.Struct:
|
||||||
|
if field.IsValid() {
|
||||||
|
field.Set(reflect.Zero(field.Type()))
|
||||||
|
}
|
||||||
|
case reflect.String:
|
||||||
|
if field.String() != "" {
|
||||||
|
field.Set(reflect.ValueOf(maskShort))
|
||||||
|
}
|
||||||
|
case reflect.Map:
|
||||||
|
if field.Len() > 0 {
|
||||||
|
field.Set(reflect.MakeMap(field.Type()))
|
||||||
|
}
|
||||||
|
case reflect.Slice:
|
||||||
|
if field.Len() > 0 {
|
||||||
|
field.Set(reflect.MakeSlice(field.Type(), 0, 0))
|
||||||
|
}
|
||||||
|
case reflect.Interface:
|
||||||
|
if !field.IsNil() {
|
||||||
|
return reset(field.Elem(), "")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// Primitive type
|
||||||
|
field.Set(reflect.Zero(field.Type()))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// isExported return true is a struct field is exported, else false
|
||||||
|
func isExported(f reflect.StructField) bool {
|
||||||
|
if f.PkgPath != "" && !f.Anonymous {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshal(anomConfig interface{}, indent bool) ([]byte, error) {
|
||||||
|
if indent {
|
||||||
|
return json.MarshalIndent(anomConfig, "", " ")
|
||||||
|
}
|
||||||
|
return json.Marshal(anomConfig)
|
||||||
|
}
|
||||||
664
cmd/traefik/anonymize/anonymize_config_test.go
Normal file
664
cmd/traefik/anonymize/anonymize_config_test.go
Normal file
@@ -0,0 +1,664 @@
|
|||||||
|
package anonymize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containous/flaeg"
|
||||||
|
"github.com/containous/traefik/acme"
|
||||||
|
"github.com/containous/traefik/configuration"
|
||||||
|
"github.com/containous/traefik/provider"
|
||||||
|
"github.com/containous/traefik/provider/boltdb"
|
||||||
|
"github.com/containous/traefik/provider/consul"
|
||||||
|
"github.com/containous/traefik/provider/docker"
|
||||||
|
"github.com/containous/traefik/provider/dynamodb"
|
||||||
|
"github.com/containous/traefik/provider/ecs"
|
||||||
|
"github.com/containous/traefik/provider/etcd"
|
||||||
|
"github.com/containous/traefik/provider/eureka"
|
||||||
|
"github.com/containous/traefik/provider/file"
|
||||||
|
"github.com/containous/traefik/provider/kubernetes"
|
||||||
|
"github.com/containous/traefik/provider/kv"
|
||||||
|
"github.com/containous/traefik/provider/marathon"
|
||||||
|
"github.com/containous/traefik/provider/mesos"
|
||||||
|
"github.com/containous/traefik/provider/rancher"
|
||||||
|
"github.com/containous/traefik/provider/zk"
|
||||||
|
traefikTls "github.com/containous/traefik/tls"
|
||||||
|
"github.com/containous/traefik/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDo_globalConfiguration(t *testing.T) {
|
||||||
|
|
||||||
|
config := &configuration.GlobalConfiguration{}
|
||||||
|
|
||||||
|
config.GraceTimeOut = flaeg.Duration(666 * time.Second)
|
||||||
|
config.Debug = true
|
||||||
|
config.CheckNewVersion = true
|
||||||
|
config.AccessLogsFile = "AccessLogsFile"
|
||||||
|
config.AccessLog = &types.AccessLog{
|
||||||
|
FilePath: "AccessLog FilePath",
|
||||||
|
Format: "AccessLog Format",
|
||||||
|
}
|
||||||
|
config.TraefikLogsFile = "TraefikLogsFile"
|
||||||
|
config.LogLevel = "LogLevel"
|
||||||
|
config.EntryPoints = configuration.EntryPoints{
|
||||||
|
"foo": {
|
||||||
|
Network: "foo Network",
|
||||||
|
Address: "foo Address",
|
||||||
|
TLS: &traefikTls.TLS{
|
||||||
|
MinVersion: "foo MinVersion",
|
||||||
|
CipherSuites: []string{"foo CipherSuites 1", "foo CipherSuites 2", "foo CipherSuites 3"},
|
||||||
|
Certificates: traefikTls.Certificates{
|
||||||
|
{CertFile: "CertFile 1", KeyFile: "KeyFile 1"},
|
||||||
|
{CertFile: "CertFile 2", KeyFile: "KeyFile 2"},
|
||||||
|
},
|
||||||
|
ClientCA: traefikTls.ClientCA{
|
||||||
|
Files: []string{"foo ClientCAFiles 1", "foo ClientCAFiles 2", "foo ClientCAFiles 3"},
|
||||||
|
Optional: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Redirect: &types.Redirect{
|
||||||
|
Replacement: "foo Replacement",
|
||||||
|
Regex: "foo Regex",
|
||||||
|
EntryPoint: "foo EntryPoint",
|
||||||
|
},
|
||||||
|
Auth: &types.Auth{
|
||||||
|
Basic: &types.Basic{
|
||||||
|
UsersFile: "foo Basic UsersFile",
|
||||||
|
Users: types.Users{"foo Basic Users 1", "foo Basic Users 2", "foo Basic Users 3"},
|
||||||
|
},
|
||||||
|
Digest: &types.Digest{
|
||||||
|
UsersFile: "foo Digest UsersFile",
|
||||||
|
Users: types.Users{"foo Digest Users 1", "foo Digest Users 2", "foo Digest Users 3"},
|
||||||
|
},
|
||||||
|
Forward: &types.Forward{
|
||||||
|
Address: "foo Address",
|
||||||
|
TLS: &types.ClientTLS{
|
||||||
|
CA: "foo CA",
|
||||||
|
Cert: "foo Cert",
|
||||||
|
Key: "foo Key",
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
TrustForwardHeader: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
WhitelistSourceRange: []string{"foo WhitelistSourceRange 1", "foo WhitelistSourceRange 2", "foo WhitelistSourceRange 3"},
|
||||||
|
Compress: true,
|
||||||
|
ProxyProtocol: &configuration.ProxyProtocol{
|
||||||
|
TrustedIPs: []string{"127.0.0.1/32", "192.168.0.1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"fii": {
|
||||||
|
Network: "fii Network",
|
||||||
|
Address: "fii Address",
|
||||||
|
TLS: &traefikTls.TLS{
|
||||||
|
MinVersion: "fii MinVersion",
|
||||||
|
CipherSuites: []string{"fii CipherSuites 1", "fii CipherSuites 2", "fii CipherSuites 3"},
|
||||||
|
Certificates: traefikTls.Certificates{
|
||||||
|
{CertFile: "CertFile 1", KeyFile: "KeyFile 1"},
|
||||||
|
{CertFile: "CertFile 2", KeyFile: "KeyFile 2"},
|
||||||
|
},
|
||||||
|
ClientCA: traefikTls.ClientCA{
|
||||||
|
Files: []string{"fii ClientCAFiles 1", "fii ClientCAFiles 2", "fii ClientCAFiles 3"},
|
||||||
|
Optional: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Redirect: &types.Redirect{
|
||||||
|
Replacement: "fii Replacement",
|
||||||
|
Regex: "fii Regex",
|
||||||
|
EntryPoint: "fii EntryPoint",
|
||||||
|
},
|
||||||
|
Auth: &types.Auth{
|
||||||
|
Basic: &types.Basic{
|
||||||
|
UsersFile: "fii Basic UsersFile",
|
||||||
|
Users: types.Users{"fii Basic Users 1", "fii Basic Users 2", "fii Basic Users 3"},
|
||||||
|
},
|
||||||
|
Digest: &types.Digest{
|
||||||
|
UsersFile: "fii Digest UsersFile",
|
||||||
|
Users: types.Users{"fii Digest Users 1", "fii Digest Users 2", "fii Digest Users 3"},
|
||||||
|
},
|
||||||
|
Forward: &types.Forward{
|
||||||
|
Address: "fii Address",
|
||||||
|
TLS: &types.ClientTLS{
|
||||||
|
CA: "fii CA",
|
||||||
|
Cert: "fii Cert",
|
||||||
|
Key: "fii Key",
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
TrustForwardHeader: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
WhitelistSourceRange: []string{"fii WhitelistSourceRange 1", "fii WhitelistSourceRange 2", "fii WhitelistSourceRange 3"},
|
||||||
|
Compress: true,
|
||||||
|
ProxyProtocol: &configuration.ProxyProtocol{
|
||||||
|
TrustedIPs: []string{"127.0.0.1/32", "192.168.0.1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
config.Cluster = &types.Cluster{
|
||||||
|
Node: "Cluster Node",
|
||||||
|
Store: &types.Store{
|
||||||
|
Prefix: "Cluster Store Prefix",
|
||||||
|
// ...
|
||||||
|
},
|
||||||
|
}
|
||||||
|
config.Constraints = types.Constraints{
|
||||||
|
{
|
||||||
|
Key: "Constraints Key 1",
|
||||||
|
Regex: "Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "Constraints Key 1",
|
||||||
|
Regex: "Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
config.ACME = &acme.ACME{
|
||||||
|
Email: "acme Email",
|
||||||
|
Domains: []acme.Domain{
|
||||||
|
{
|
||||||
|
Main: "Domains Main",
|
||||||
|
SANs: []string{"Domains acme SANs 1", "Domains acme SANs 2", "Domains acme SANs 3"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Storage: "Storage",
|
||||||
|
StorageFile: "StorageFile",
|
||||||
|
OnDemand: true,
|
||||||
|
OnHostRule: true,
|
||||||
|
CAServer: "CAServer",
|
||||||
|
EntryPoint: "EntryPoint",
|
||||||
|
DNSProvider: "DNSProvider",
|
||||||
|
DelayDontCheckDNS: 666,
|
||||||
|
ACMELogging: true,
|
||||||
|
TLSConfig: &tls.Config{
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
// ...
|
||||||
|
},
|
||||||
|
}
|
||||||
|
config.DefaultEntryPoints = configuration.DefaultEntryPoints{"DefaultEntryPoints 1", "DefaultEntryPoints 2", "DefaultEntryPoints 3"}
|
||||||
|
config.ProvidersThrottleDuration = flaeg.Duration(666 * time.Second)
|
||||||
|
config.MaxIdleConnsPerHost = 666
|
||||||
|
config.IdleTimeout = flaeg.Duration(666 * time.Second)
|
||||||
|
config.InsecureSkipVerify = true
|
||||||
|
config.RootCAs = traefikTls.RootCAs{"RootCAs 1", "RootCAs 2", "RootCAs 3"}
|
||||||
|
config.Retry = &configuration.Retry{
|
||||||
|
Attempts: 666,
|
||||||
|
}
|
||||||
|
config.HealthCheck = &configuration.HealthCheckConfig{
|
||||||
|
Interval: flaeg.Duration(666 * time.Second),
|
||||||
|
}
|
||||||
|
config.RespondingTimeouts = &configuration.RespondingTimeouts{
|
||||||
|
ReadTimeout: flaeg.Duration(666 * time.Second),
|
||||||
|
WriteTimeout: flaeg.Duration(666 * time.Second),
|
||||||
|
IdleTimeout: flaeg.Duration(666 * time.Second),
|
||||||
|
}
|
||||||
|
config.ForwardingTimeouts = &configuration.ForwardingTimeouts{
|
||||||
|
DialTimeout: flaeg.Duration(666 * time.Second),
|
||||||
|
ResponseHeaderTimeout: flaeg.Duration(666 * time.Second),
|
||||||
|
}
|
||||||
|
config.Docker = &docker.Provider{
|
||||||
|
BaseProvider: provider.BaseProvider{
|
||||||
|
Watch: true,
|
||||||
|
Filename: "docker Filename",
|
||||||
|
Constraints: types.Constraints{
|
||||||
|
{
|
||||||
|
Key: "docker Constraints Key 1",
|
||||||
|
Regex: "docker Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "docker Constraints Key 1",
|
||||||
|
Regex: "docker Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Trace: true,
|
||||||
|
DebugLogGeneratedTemplate: true,
|
||||||
|
},
|
||||||
|
Endpoint: "docker Endpoint",
|
||||||
|
Domain: "docker Domain",
|
||||||
|
TLS: &types.ClientTLS{
|
||||||
|
CA: "docker CA",
|
||||||
|
Cert: "docker Cert",
|
||||||
|
Key: "docker Key",
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
ExposedByDefault: true,
|
||||||
|
UseBindPortIP: true,
|
||||||
|
SwarmMode: true,
|
||||||
|
}
|
||||||
|
config.File = &file.Provider{
|
||||||
|
BaseProvider: provider.BaseProvider{
|
||||||
|
Watch: true,
|
||||||
|
Filename: "file Filename",
|
||||||
|
Constraints: types.Constraints{
|
||||||
|
{
|
||||||
|
Key: "file Constraints Key 1",
|
||||||
|
Regex: "file Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "file Constraints Key 1",
|
||||||
|
Regex: "file Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Trace: true,
|
||||||
|
DebugLogGeneratedTemplate: true,
|
||||||
|
},
|
||||||
|
Directory: "file Directory",
|
||||||
|
}
|
||||||
|
config.Web = &configuration.WebCompatibility{
|
||||||
|
Address: "web Address",
|
||||||
|
CertFile: "web CertFile",
|
||||||
|
KeyFile: "web KeyFile",
|
||||||
|
ReadOnly: true,
|
||||||
|
Statistics: &types.Statistics{
|
||||||
|
RecentErrors: 666,
|
||||||
|
},
|
||||||
|
Metrics: &types.Metrics{
|
||||||
|
Prometheus: &types.Prometheus{
|
||||||
|
Buckets: types.Buckets{6.5, 6.6, 6.7},
|
||||||
|
},
|
||||||
|
Datadog: &types.Datadog{
|
||||||
|
Address: "Datadog Address",
|
||||||
|
PushInterval: "Datadog PushInterval",
|
||||||
|
},
|
||||||
|
StatsD: &types.Statsd{
|
||||||
|
Address: "StatsD Address",
|
||||||
|
PushInterval: "StatsD PushInterval",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Path: "web Path",
|
||||||
|
Auth: &types.Auth{
|
||||||
|
Basic: &types.Basic{
|
||||||
|
UsersFile: "web Basic UsersFile",
|
||||||
|
Users: types.Users{"web Basic Users 1", "web Basic Users 2", "web Basic Users 3"},
|
||||||
|
},
|
||||||
|
Digest: &types.Digest{
|
||||||
|
UsersFile: "web Digest UsersFile",
|
||||||
|
Users: types.Users{"web Digest Users 1", "web Digest Users 2", "web Digest Users 3"},
|
||||||
|
},
|
||||||
|
Forward: &types.Forward{
|
||||||
|
Address: "web Address",
|
||||||
|
TLS: &types.ClientTLS{
|
||||||
|
CA: "web CA",
|
||||||
|
Cert: "web Cert",
|
||||||
|
Key: "web Key",
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
TrustForwardHeader: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Debug: true,
|
||||||
|
}
|
||||||
|
config.Marathon = &marathon.Provider{
|
||||||
|
BaseProvider: provider.BaseProvider{
|
||||||
|
Watch: true,
|
||||||
|
Filename: "marathon Filename",
|
||||||
|
Constraints: types.Constraints{
|
||||||
|
{
|
||||||
|
Key: "marathon Constraints Key 1",
|
||||||
|
Regex: "marathon Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "marathon Constraints Key 1",
|
||||||
|
Regex: "marathon Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Trace: true,
|
||||||
|
DebugLogGeneratedTemplate: true,
|
||||||
|
},
|
||||||
|
Endpoint: "",
|
||||||
|
Domain: "",
|
||||||
|
ExposedByDefault: true,
|
||||||
|
GroupsAsSubDomains: true,
|
||||||
|
DCOSToken: "",
|
||||||
|
MarathonLBCompatibility: true,
|
||||||
|
TLS: &types.ClientTLS{
|
||||||
|
CA: "marathon CA",
|
||||||
|
Cert: "marathon Cert",
|
||||||
|
Key: "marathon Key",
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
DialerTimeout: flaeg.Duration(666 * time.Second),
|
||||||
|
KeepAlive: flaeg.Duration(666 * time.Second),
|
||||||
|
ForceTaskHostname: true,
|
||||||
|
Basic: &marathon.Basic{
|
||||||
|
HTTPBasicAuthUser: "marathon HTTPBasicAuthUser",
|
||||||
|
HTTPBasicPassword: "marathon HTTPBasicPassword",
|
||||||
|
},
|
||||||
|
RespectReadinessChecks: true,
|
||||||
|
}
|
||||||
|
config.ConsulCatalog = &consul.CatalogProvider{
|
||||||
|
BaseProvider: provider.BaseProvider{
|
||||||
|
Watch: true,
|
||||||
|
Filename: "ConsulCatalog Filename",
|
||||||
|
Constraints: types.Constraints{
|
||||||
|
{
|
||||||
|
Key: "ConsulCatalog Constraints Key 1",
|
||||||
|
Regex: "ConsulCatalog Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "ConsulCatalog Constraints Key 1",
|
||||||
|
Regex: "ConsulCatalog Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Trace: true,
|
||||||
|
DebugLogGeneratedTemplate: true,
|
||||||
|
},
|
||||||
|
Endpoint: "ConsulCatalog Endpoint",
|
||||||
|
Domain: "ConsulCatalog Domain",
|
||||||
|
ExposedByDefault: true,
|
||||||
|
Prefix: "ConsulCatalog Prefix",
|
||||||
|
FrontEndRule: "ConsulCatalog FrontEndRule",
|
||||||
|
}
|
||||||
|
config.Kubernetes = &kubernetes.Provider{
|
||||||
|
BaseProvider: provider.BaseProvider{
|
||||||
|
Watch: true,
|
||||||
|
Filename: "k8s Filename",
|
||||||
|
Constraints: types.Constraints{
|
||||||
|
{
|
||||||
|
Key: "k8s Constraints Key 1",
|
||||||
|
Regex: "k8s Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "k8s Constraints Key 1",
|
||||||
|
Regex: "k8s Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Trace: true,
|
||||||
|
DebugLogGeneratedTemplate: true,
|
||||||
|
},
|
||||||
|
Endpoint: "k8s Endpoint",
|
||||||
|
Token: "k8s Token",
|
||||||
|
CertAuthFilePath: "k8s CertAuthFilePath",
|
||||||
|
DisablePassHostHeaders: true,
|
||||||
|
Namespaces: kubernetes.Namespaces{"k8s Namespaces 1", "k8s Namespaces 2", "k8s Namespaces 3"},
|
||||||
|
LabelSelector: "k8s LabelSelector",
|
||||||
|
}
|
||||||
|
config.Mesos = &mesos.Provider{
|
||||||
|
BaseProvider: provider.BaseProvider{
|
||||||
|
Watch: true,
|
||||||
|
Filename: "mesos Filename",
|
||||||
|
Constraints: types.Constraints{
|
||||||
|
{
|
||||||
|
Key: "mesos Constraints Key 1",
|
||||||
|
Regex: "mesos Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "mesos Constraints Key 1",
|
||||||
|
Regex: "mesos Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Trace: true,
|
||||||
|
DebugLogGeneratedTemplate: true,
|
||||||
|
},
|
||||||
|
Endpoint: "mesos Endpoint",
|
||||||
|
Domain: "mesos Domain",
|
||||||
|
ExposedByDefault: true,
|
||||||
|
GroupsAsSubDomains: true,
|
||||||
|
ZkDetectionTimeout: 666,
|
||||||
|
RefreshSeconds: 666,
|
||||||
|
IPSources: "mesos IPSources",
|
||||||
|
StateTimeoutSecond: 666,
|
||||||
|
Masters: []string{"mesos Masters 1", "mesos Masters 2", "mesos Masters 3"},
|
||||||
|
}
|
||||||
|
config.Eureka = &eureka.Provider{
|
||||||
|
BaseProvider: provider.BaseProvider{
|
||||||
|
Watch: true,
|
||||||
|
Filename: "eureka Filename",
|
||||||
|
Constraints: types.Constraints{
|
||||||
|
{
|
||||||
|
Key: "eureka Constraints Key 1",
|
||||||
|
Regex: "eureka Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "eureka Constraints Key 1",
|
||||||
|
Regex: "eureka Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Trace: true,
|
||||||
|
DebugLogGeneratedTemplate: true,
|
||||||
|
},
|
||||||
|
Endpoint: "eureka Endpoint",
|
||||||
|
Delay: "eureka Delay",
|
||||||
|
}
|
||||||
|
config.ECS = &ecs.Provider{
|
||||||
|
BaseProvider: provider.BaseProvider{
|
||||||
|
Watch: true,
|
||||||
|
Filename: "ecs Filename",
|
||||||
|
Constraints: types.Constraints{
|
||||||
|
{
|
||||||
|
Key: "ecs Constraints Key 1",
|
||||||
|
Regex: "ecs Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "ecs Constraints Key 1",
|
||||||
|
Regex: "ecs Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Trace: true,
|
||||||
|
DebugLogGeneratedTemplate: true,
|
||||||
|
},
|
||||||
|
Domain: "ecs Domain",
|
||||||
|
ExposedByDefault: true,
|
||||||
|
RefreshSeconds: 666,
|
||||||
|
Clusters: ecs.Clusters{"ecs Clusters 1", "ecs Clusters 2", "ecs Clusters 3"},
|
||||||
|
Cluster: "ecs Cluster",
|
||||||
|
AutoDiscoverClusters: true,
|
||||||
|
Region: "ecs Region",
|
||||||
|
AccessKeyID: "ecs AccessKeyID",
|
||||||
|
SecretAccessKey: "ecs SecretAccessKey",
|
||||||
|
}
|
||||||
|
config.Rancher = &rancher.Provider{
|
||||||
|
BaseProvider: provider.BaseProvider{
|
||||||
|
Watch: true,
|
||||||
|
Filename: "rancher Filename",
|
||||||
|
Constraints: types.Constraints{
|
||||||
|
{
|
||||||
|
Key: "rancher Constraints Key 1",
|
||||||
|
Regex: "rancher Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "rancher Constraints Key 1",
|
||||||
|
Regex: "rancher Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Trace: true,
|
||||||
|
DebugLogGeneratedTemplate: true,
|
||||||
|
},
|
||||||
|
APIConfiguration: rancher.APIConfiguration{
|
||||||
|
Endpoint: "rancher Endpoint",
|
||||||
|
AccessKey: "rancher AccessKey",
|
||||||
|
SecretKey: "rancher SecretKey",
|
||||||
|
},
|
||||||
|
API: &rancher.APIConfiguration{
|
||||||
|
Endpoint: "rancher Endpoint",
|
||||||
|
AccessKey: "rancher AccessKey",
|
||||||
|
SecretKey: "rancher SecretKey",
|
||||||
|
},
|
||||||
|
Metadata: &rancher.MetadataConfiguration{
|
||||||
|
IntervalPoll: true,
|
||||||
|
Prefix: "rancher Metadata Prefix",
|
||||||
|
},
|
||||||
|
Domain: "rancher Domain",
|
||||||
|
RefreshSeconds: 666,
|
||||||
|
ExposedByDefault: true,
|
||||||
|
EnableServiceHealthFilter: true,
|
||||||
|
}
|
||||||
|
config.DynamoDB = &dynamodb.Provider{
|
||||||
|
BaseProvider: provider.BaseProvider{
|
||||||
|
Watch: true,
|
||||||
|
Filename: "dynamodb Filename",
|
||||||
|
Constraints: types.Constraints{
|
||||||
|
{
|
||||||
|
Key: "dynamodb Constraints Key 1",
|
||||||
|
Regex: "dynamodb Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "dynamodb Constraints Key 1",
|
||||||
|
Regex: "dynamodb Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Trace: true,
|
||||||
|
DebugLogGeneratedTemplate: true,
|
||||||
|
},
|
||||||
|
AccessKeyID: "dynamodb AccessKeyID",
|
||||||
|
RefreshSeconds: 666,
|
||||||
|
Region: "dynamodb Region",
|
||||||
|
SecretAccessKey: "dynamodb SecretAccessKey",
|
||||||
|
TableName: "dynamodb TableName",
|
||||||
|
Endpoint: "dynamodb Endpoint",
|
||||||
|
}
|
||||||
|
config.Etcd = &etcd.Provider{
|
||||||
|
Provider: kv.Provider{
|
||||||
|
BaseProvider: provider.BaseProvider{
|
||||||
|
Watch: true,
|
||||||
|
Filename: "etcd Filename",
|
||||||
|
Constraints: types.Constraints{
|
||||||
|
{
|
||||||
|
Key: "etcd Constraints Key 1",
|
||||||
|
Regex: "etcd Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "etcd Constraints Key 1",
|
||||||
|
Regex: "etcd Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Trace: true,
|
||||||
|
DebugLogGeneratedTemplate: true,
|
||||||
|
},
|
||||||
|
Endpoint: "etcd Endpoint",
|
||||||
|
Prefix: "etcd Prefix",
|
||||||
|
TLS: &types.ClientTLS{
|
||||||
|
CA: "etcd CA",
|
||||||
|
Cert: "etcd Cert",
|
||||||
|
Key: "etcd Key",
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
Username: "etcd Username",
|
||||||
|
Password: "etcd Password",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
config.Zookeeper = &zk.Provider{
|
||||||
|
Provider: kv.Provider{
|
||||||
|
BaseProvider: provider.BaseProvider{
|
||||||
|
Watch: true,
|
||||||
|
Filename: "zk Filename",
|
||||||
|
Constraints: types.Constraints{
|
||||||
|
{
|
||||||
|
Key: "zk Constraints Key 1",
|
||||||
|
Regex: "zk Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "zk Constraints Key 1",
|
||||||
|
Regex: "zk Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Trace: true,
|
||||||
|
DebugLogGeneratedTemplate: true,
|
||||||
|
},
|
||||||
|
Endpoint: "zk Endpoint",
|
||||||
|
Prefix: "zk Prefix",
|
||||||
|
TLS: &types.ClientTLS{
|
||||||
|
CA: "zk CA",
|
||||||
|
Cert: "zk Cert",
|
||||||
|
Key: "zk Key",
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
Username: "zk Username",
|
||||||
|
Password: "zk Password",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
config.Boltdb = &boltdb.Provider{
|
||||||
|
Provider: kv.Provider{
|
||||||
|
BaseProvider: provider.BaseProvider{
|
||||||
|
Watch: true,
|
||||||
|
Filename: "boltdb Filename",
|
||||||
|
Constraints: types.Constraints{
|
||||||
|
{
|
||||||
|
Key: "boltdb Constraints Key 1",
|
||||||
|
Regex: "boltdb Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "boltdb Constraints Key 1",
|
||||||
|
Regex: "boltdb Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Trace: true,
|
||||||
|
DebugLogGeneratedTemplate: true,
|
||||||
|
},
|
||||||
|
Endpoint: "boltdb Endpoint",
|
||||||
|
Prefix: "boltdb Prefix",
|
||||||
|
TLS: &types.ClientTLS{
|
||||||
|
CA: "boltdb CA",
|
||||||
|
Cert: "boltdb Cert",
|
||||||
|
Key: "boltdb Key",
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
Username: "boltdb Username",
|
||||||
|
Password: "boltdb Password",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
config.Consul = &consul.Provider{
|
||||||
|
Provider: kv.Provider{
|
||||||
|
BaseProvider: provider.BaseProvider{
|
||||||
|
Watch: true,
|
||||||
|
Filename: "consul Filename",
|
||||||
|
Constraints: types.Constraints{
|
||||||
|
{
|
||||||
|
Key: "consul Constraints Key 1",
|
||||||
|
Regex: "consul Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "consul Constraints Key 1",
|
||||||
|
Regex: "consul Constraints Regex 2",
|
||||||
|
MustMatch: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Trace: true,
|
||||||
|
DebugLogGeneratedTemplate: true,
|
||||||
|
},
|
||||||
|
Endpoint: "consul Endpoint",
|
||||||
|
Prefix: "consul Prefix",
|
||||||
|
TLS: &types.ClientTLS{
|
||||||
|
CA: "consul CA",
|
||||||
|
Cert: "consul Cert",
|
||||||
|
Key: "consul Key",
|
||||||
|
InsecureSkipVerify: true,
|
||||||
|
},
|
||||||
|
Username: "consul Username",
|
||||||
|
Password: "consul Password",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanJSON, err := Do(config, true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err, cleanJSON)
|
||||||
|
}
|
||||||
|
}
|
||||||
239
cmd/traefik/anonymize/anonymize_doOnJSON_test.go
Normal file
239
cmd/traefik/anonymize/anonymize_doOnJSON_test.go
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
package anonymize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_doOnJSON(t *testing.T) {
|
||||||
|
baseConfiguration := `
|
||||||
|
{
|
||||||
|
"GraceTimeOut": 10000000000,
|
||||||
|
"Debug": false,
|
||||||
|
"CheckNewVersion": true,
|
||||||
|
"AccessLogsFile": "",
|
||||||
|
"TraefikLogsFile": "",
|
||||||
|
"LogLevel": "ERROR",
|
||||||
|
"EntryPoints": {
|
||||||
|
"http": {
|
||||||
|
"Network": "",
|
||||||
|
"Address": ":80",
|
||||||
|
"TLS": null,
|
||||||
|
"Redirect": {
|
||||||
|
"EntryPoint": "https",
|
||||||
|
"Regex": "",
|
||||||
|
"Replacement": ""
|
||||||
|
},
|
||||||
|
"Auth": null,
|
||||||
|
"Compress": false
|
||||||
|
},
|
||||||
|
"https": {
|
||||||
|
"Network": "",
|
||||||
|
"Address": ":443",
|
||||||
|
"TLS": {
|
||||||
|
"MinVersion": "",
|
||||||
|
"CipherSuites": null,
|
||||||
|
"Certificates": null,
|
||||||
|
"ClientCAFiles": null
|
||||||
|
},
|
||||||
|
"Redirect": null,
|
||||||
|
"Auth": null,
|
||||||
|
"Compress": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Cluster": null,
|
||||||
|
"Constraints": [],
|
||||||
|
"ACME": {
|
||||||
|
"Email": "foo@bar.com",
|
||||||
|
"Domains": [
|
||||||
|
{
|
||||||
|
"Main": "foo@bar.com",
|
||||||
|
"SANs": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Main": "foo@bar.com",
|
||||||
|
"SANs": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Storage": "",
|
||||||
|
"StorageFile": "/acme/acme.json",
|
||||||
|
"OnDemand": true,
|
||||||
|
"OnHostRule": true,
|
||||||
|
"CAServer": "",
|
||||||
|
"EntryPoint": "https",
|
||||||
|
"DNSProvider": "",
|
||||||
|
"DelayDontCheckDNS": 0,
|
||||||
|
"ACMELogging": false,
|
||||||
|
"TLSConfig": null
|
||||||
|
},
|
||||||
|
"DefaultEntryPoints": [
|
||||||
|
"https",
|
||||||
|
"http"
|
||||||
|
],
|
||||||
|
"ProvidersThrottleDuration": 2000000000,
|
||||||
|
"MaxIdleConnsPerHost": 200,
|
||||||
|
"IdleTimeout": 180000000000,
|
||||||
|
"InsecureSkipVerify": false,
|
||||||
|
"Retry": null,
|
||||||
|
"HealthCheck": {
|
||||||
|
"Interval": 30000000000
|
||||||
|
},
|
||||||
|
"Docker": null,
|
||||||
|
"File": null,
|
||||||
|
"Web": null,
|
||||||
|
"Marathon": null,
|
||||||
|
"Consul": null,
|
||||||
|
"ConsulCatalog": null,
|
||||||
|
"Etcd": null,
|
||||||
|
"Zookeeper": null,
|
||||||
|
"Boltdb": null,
|
||||||
|
"Kubernetes": null,
|
||||||
|
"Mesos": null,
|
||||||
|
"Eureka": null,
|
||||||
|
"ECS": null,
|
||||||
|
"Rancher": null,
|
||||||
|
"DynamoDB": null,
|
||||||
|
"ConfigFile": "/etc/traefik/traefik.toml"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
expectedConfiguration := `
|
||||||
|
{
|
||||||
|
"GraceTimeOut": 10000000000,
|
||||||
|
"Debug": false,
|
||||||
|
"CheckNewVersion": true,
|
||||||
|
"AccessLogsFile": "",
|
||||||
|
"TraefikLogsFile": "",
|
||||||
|
"LogLevel": "ERROR",
|
||||||
|
"EntryPoints": {
|
||||||
|
"http": {
|
||||||
|
"Network": "",
|
||||||
|
"Address": ":80",
|
||||||
|
"TLS": null,
|
||||||
|
"Redirect": {
|
||||||
|
"EntryPoint": "https",
|
||||||
|
"Regex": "",
|
||||||
|
"Replacement": ""
|
||||||
|
},
|
||||||
|
"Auth": null,
|
||||||
|
"Compress": false
|
||||||
|
},
|
||||||
|
"https": {
|
||||||
|
"Network": "",
|
||||||
|
"Address": ":443",
|
||||||
|
"TLS": {
|
||||||
|
"MinVersion": "",
|
||||||
|
"CipherSuites": null,
|
||||||
|
"Certificates": null,
|
||||||
|
"ClientCAFiles": null
|
||||||
|
},
|
||||||
|
"Redirect": null,
|
||||||
|
"Auth": null,
|
||||||
|
"Compress": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Cluster": null,
|
||||||
|
"Constraints": [],
|
||||||
|
"ACME": {
|
||||||
|
"Email": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||||
|
"Domains": [
|
||||||
|
{
|
||||||
|
"Main": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||||
|
"SANs": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Main": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||||
|
"SANs": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Storage": "",
|
||||||
|
"StorageFile": "/acme/acme.json",
|
||||||
|
"OnDemand": true,
|
||||||
|
"OnHostRule": true,
|
||||||
|
"CAServer": "",
|
||||||
|
"EntryPoint": "https",
|
||||||
|
"DNSProvider": "",
|
||||||
|
"DelayDontCheckDNS": 0,
|
||||||
|
"ACMELogging": false,
|
||||||
|
"TLSConfig": null
|
||||||
|
},
|
||||||
|
"DefaultEntryPoints": [
|
||||||
|
"https",
|
||||||
|
"http"
|
||||||
|
],
|
||||||
|
"ProvidersThrottleDuration": 2000000000,
|
||||||
|
"MaxIdleConnsPerHost": 200,
|
||||||
|
"IdleTimeout": 180000000000,
|
||||||
|
"InsecureSkipVerify": false,
|
||||||
|
"Retry": null,
|
||||||
|
"HealthCheck": {
|
||||||
|
"Interval": 30000000000
|
||||||
|
},
|
||||||
|
"Docker": null,
|
||||||
|
"File": null,
|
||||||
|
"Web": null,
|
||||||
|
"Marathon": null,
|
||||||
|
"Consul": null,
|
||||||
|
"ConsulCatalog": null,
|
||||||
|
"Etcd": null,
|
||||||
|
"Zookeeper": null,
|
||||||
|
"Boltdb": null,
|
||||||
|
"Kubernetes": null,
|
||||||
|
"Mesos": null,
|
||||||
|
"Eureka": null,
|
||||||
|
"ECS": null,
|
||||||
|
"Rancher": null,
|
||||||
|
"DynamoDB": null,
|
||||||
|
"ConfigFile": "/etc/traefik/traefik.toml"
|
||||||
|
}
|
||||||
|
`
|
||||||
|
anomConfiguration := doOnJSON(baseConfiguration)
|
||||||
|
|
||||||
|
if anomConfiguration != expectedConfiguration {
|
||||||
|
t.Errorf("Got %s, want %s.", anomConfiguration, expectedConfiguration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_doOnJSON_simple(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
expectedOutput string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "email",
|
||||||
|
input: `{
|
||||||
|
"email1": "goo@example.com",
|
||||||
|
"email2": "foo.bargoo@example.com",
|
||||||
|
"email3": "foo.bargoo@example.com.us"
|
||||||
|
}`,
|
||||||
|
expectedOutput: `{
|
||||||
|
"email1": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||||
|
"email2": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
||||||
|
"email3": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "url",
|
||||||
|
input: `{
|
||||||
|
"URL": "foo domain.com foo",
|
||||||
|
"URL": "foo sub.domain.com foo",
|
||||||
|
"URL": "foo sub.sub.domain.com foo",
|
||||||
|
"URL": "foo sub.sub.sub.domain.com.us foo"
|
||||||
|
}`,
|
||||||
|
expectedOutput: `{
|
||||||
|
"URL": "foo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx foo",
|
||||||
|
"URL": "foo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx foo",
|
||||||
|
"URL": "foo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx foo",
|
||||||
|
"URL": "foo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx foo"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
output := doOnJSON(test.input)
|
||||||
|
assert.Equal(t, test.expectedOutput, output)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
176
cmd/traefik/anonymize/anonymize_doOnStruct_test.go
Normal file
176
cmd/traefik/anonymize/anonymize_doOnStruct_test.go
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
package anonymize
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Courgette struct {
|
||||||
|
Ji string
|
||||||
|
Ho string
|
||||||
|
}
|
||||||
|
type Tomate struct {
|
||||||
|
Ji string
|
||||||
|
Ho string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Carotte struct {
|
||||||
|
Name string
|
||||||
|
Value int
|
||||||
|
Courgette Courgette
|
||||||
|
ECourgette Courgette `export:"true"`
|
||||||
|
Pourgette *Courgette
|
||||||
|
EPourgette *Courgette `export:"true"`
|
||||||
|
Aubergine map[string]string
|
||||||
|
EAubergine map[string]string `export:"true"`
|
||||||
|
SAubergine map[string]Tomate
|
||||||
|
ESAubergine map[string]Tomate `export:"true"`
|
||||||
|
PSAubergine map[string]*Tomate
|
||||||
|
EPAubergine map[string]*Tomate `export:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_doOnStruct(t *testing.T) {
|
||||||
|
testCase := []struct {
|
||||||
|
name string
|
||||||
|
base *Carotte
|
||||||
|
expected *Carotte
|
||||||
|
hasError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "primitive",
|
||||||
|
base: &Carotte{
|
||||||
|
Name: "koko",
|
||||||
|
Value: 666,
|
||||||
|
},
|
||||||
|
expected: &Carotte{
|
||||||
|
Name: "xxxx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "struct",
|
||||||
|
base: &Carotte{
|
||||||
|
Name: "koko",
|
||||||
|
Courgette: Courgette{
|
||||||
|
Ji: "huu",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &Carotte{
|
||||||
|
Name: "xxxx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "pointer",
|
||||||
|
base: &Carotte{
|
||||||
|
Name: "koko",
|
||||||
|
Pourgette: &Courgette{
|
||||||
|
Ji: "hoo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &Carotte{
|
||||||
|
Name: "xxxx",
|
||||||
|
Pourgette: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "export struct",
|
||||||
|
base: &Carotte{
|
||||||
|
Name: "koko",
|
||||||
|
ECourgette: Courgette{
|
||||||
|
Ji: "huu",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &Carotte{
|
||||||
|
Name: "xxxx",
|
||||||
|
ECourgette: Courgette{
|
||||||
|
Ji: "xxxx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "export pointer struct",
|
||||||
|
base: &Carotte{
|
||||||
|
Name: "koko",
|
||||||
|
ECourgette: Courgette{
|
||||||
|
Ji: "huu",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &Carotte{
|
||||||
|
Name: "xxxx",
|
||||||
|
ECourgette: Courgette{
|
||||||
|
Ji: "xxxx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "export map string/string",
|
||||||
|
base: &Carotte{
|
||||||
|
Name: "koko",
|
||||||
|
EAubergine: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &Carotte{
|
||||||
|
Name: "xxxx",
|
||||||
|
EAubergine: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "export map string/pointer",
|
||||||
|
base: &Carotte{
|
||||||
|
Name: "koko",
|
||||||
|
EPAubergine: map[string]*Tomate{
|
||||||
|
"foo": {
|
||||||
|
Ji: "fdskljf",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &Carotte{
|
||||||
|
Name: "xxxx",
|
||||||
|
EPAubergine: map[string]*Tomate{
|
||||||
|
"foo": {
|
||||||
|
Ji: "xxxx",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "export map string/struct (UNSAFE)",
|
||||||
|
base: &Carotte{
|
||||||
|
Name: "koko",
|
||||||
|
ESAubergine: map[string]Tomate{
|
||||||
|
"foo": {
|
||||||
|
Ji: "JiJiJi",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: &Carotte{
|
||||||
|
Name: "xxxx",
|
||||||
|
ESAubergine: map[string]Tomate{
|
||||||
|
"foo": {
|
||||||
|
Ji: "JiJiJi",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
hasError: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCase {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
val := reflect.ValueOf(test.base).Elem()
|
||||||
|
err := doOnStruct(val)
|
||||||
|
if !test.hasError && err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if test.hasError && err == nil {
|
||||||
|
t.Fatal("Got no error but want an error.")
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.EqualValues(t, test.expected, test.base)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,33 +2,45 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"regexp"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"github.com/containous/flaeg"
|
"github.com/containous/flaeg"
|
||||||
"github.com/mvdan/xurls"
|
"github.com/containous/traefik/cmd/traefik/anonymize"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
const (
|
||||||
bugtracker = "https://github.com/containous/traefik/issues/new"
|
bugTracker = "https://github.com/containous/traefik/issues/new"
|
||||||
bugTemplate = `<!--
|
bugTemplate = `<!--
|
||||||
PLEASE READ THIS MESSAGE.
|
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
|
||||||
|
|
||||||
Please keep in mind that the GitHub issue tracker is not intended as a general support forum, but for reporting bugs and feature requests.
|
The issue tracker is for reporting bugs and feature requests only.
|
||||||
|
For end-user related support questions, refer to one of the following:
|
||||||
For other type of questions, consider using one of:
|
|
||||||
|
|
||||||
|
- Stack Overflow (using the "traefik" tag): https://stackoverflow.com/questions/tagged/traefik
|
||||||
- the Traefik community Slack channel: https://traefik.herokuapp.com
|
- the Traefik community Slack channel: https://traefik.herokuapp.com
|
||||||
- StackOverflow: https://stackoverflow.com/questions/tagged/traefik
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
### Do you want to request a *feature* or report a *bug*?
|
||||||
|
|
||||||
|
(If you intend to ask a support question: **DO NOT FILE AN ISSUE**.
|
||||||
|
Use [Stack Overflow](https://stackoverflow.com/questions/tagged/traefik)
|
||||||
|
or [Slack](https://traefik.herokuapp.com) instead.)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### What did you do?
|
||||||
|
|
||||||
|
<!--
|
||||||
|
|
||||||
HOW TO WRITE A GOOD ISSUE?
|
HOW TO WRITE A GOOD ISSUE?
|
||||||
|
|
||||||
- if it's possible use the command` + "`" + `traefik bug` + "`" + `. See https://www.youtube.com/watch?v=Lyz62L8m93I.
|
- Respect the issue template as more as possible.
|
||||||
|
- If it's possible use the command ` + "`" + "traefik bug" + "`" + `. See https://www.youtube.com/watch?v=Lyz62L8m93I.
|
||||||
- The title must be short and descriptive.
|
- The title must be short and descriptive.
|
||||||
- Explain the conditions which led you to write this issue: the context.
|
- Explain the conditions which led you to write this issue: the context.
|
||||||
- The context should lead to something, an idea or a problem that you’re facing.
|
- The context should lead to something, an idea or a problem that you’re facing.
|
||||||
@@ -37,12 +49,6 @@ HOW TO WRITE A GOOD ISSUE?
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
### Do you want to request a *feature* or report a *bug*?
|
|
||||||
|
|
||||||
|
|
||||||
### What did you do?
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### What did you expect to see?
|
### What did you expect to see?
|
||||||
|
|
||||||
@@ -60,7 +66,7 @@ HOW TO WRITE A GOOD ISSUE?
|
|||||||
|
|
||||||
### What is your environment & configuration (arguments, toml, provider, platform, ...)?
|
### What is your environment & configuration (arguments, toml, provider, platform, ...)?
|
||||||
|
|
||||||
` + "```" + `toml
|
` + "```" + `json
|
||||||
{{.Configuration}}
|
{{.Configuration}}
|
||||||
` + "```" + `
|
` + "```" + `
|
||||||
|
|
||||||
@@ -86,50 +92,67 @@ func newBugCmd(traefikConfiguration interface{}, traefikPointersConfiguration in
|
|||||||
Description: `Report an issue on Traefik bugtracker`,
|
Description: `Report an issue on Traefik bugtracker`,
|
||||||
Config: traefikConfiguration,
|
Config: traefikConfiguration,
|
||||||
DefaultPointersConfig: traefikPointersConfiguration,
|
DefaultPointersConfig: traefikPointersConfiguration,
|
||||||
Run: func() error {
|
Run: runBugCmd(traefikConfiguration),
|
||||||
var version bytes.Buffer
|
|
||||||
if err := getVersionPrint(&version); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpl, err := template.New("").Parse(bugTemplate)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
configJSON, err := json.MarshalIndent(traefikConfiguration, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
v := struct {
|
|
||||||
Version string
|
|
||||||
Configuration string
|
|
||||||
}{
|
|
||||||
Version: version.String(),
|
|
||||||
Configuration: anonymize(string(configJSON)),
|
|
||||||
}
|
|
||||||
|
|
||||||
var bug bytes.Buffer
|
|
||||||
if err := tmpl.Execute(&bug, v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
body := bug.String()
|
|
||||||
URL := bugtracker + "?body=" + url.QueryEscape(body)
|
|
||||||
if err := openBrowser(URL); err != nil {
|
|
||||||
fmt.Print("Please file a new issue at " + bugtracker + " using this template:\n\n")
|
|
||||||
fmt.Print(body)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Metadata: map[string]string{
|
Metadata: map[string]string{
|
||||||
"parseAllSources": "true",
|
"parseAllSources": "true",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runBugCmd(traefikConfiguration interface{}) func() error {
|
||||||
|
return func() error {
|
||||||
|
|
||||||
|
body, err := createBugReport(traefikConfiguration)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sendBugReport(body)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createBugReport(traefikConfiguration interface{}) (string, error) {
|
||||||
|
var version bytes.Buffer
|
||||||
|
if err := getVersionPrint(&version); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl, err := template.New("bug").Parse(bugTemplate)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
config, err := anonymize.Do(&traefikConfiguration, true)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
v := struct {
|
||||||
|
Version string
|
||||||
|
Configuration string
|
||||||
|
}{
|
||||||
|
Version: version.String(),
|
||||||
|
Configuration: config,
|
||||||
|
}
|
||||||
|
|
||||||
|
var bug bytes.Buffer
|
||||||
|
if err := tmpl.Execute(&bug, v); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bug.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendBugReport(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)
|
||||||
|
fmt.Print(body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func openBrowser(URL string) error {
|
func openBrowser(URL string) error {
|
||||||
var err error
|
var err error
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
@@ -144,9 +167,3 @@ func openBrowser(URL string) error {
|
|||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func anonymize(input string) string {
|
|
||||||
replace := "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
|
||||||
mailExp := regexp.MustCompile(`\w[-._\w]*\w@\w[-._\w]*\w\.\w{2,3}"`)
|
|
||||||
return xurls.Relaxed.ReplaceAllString(mailExp.ReplaceAllString(input, replace), replace)
|
|
||||||
}
|
|
||||||
|
|||||||
50
cmd/traefik/bug_test.go
Normal file
50
cmd/traefik/bug_test.go
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/containous/traefik/cmd/traefik/anonymize"
|
||||||
|
"github.com/containous/traefik/configuration"
|
||||||
|
"github.com/containous/traefik/provider/file"
|
||||||
|
"github.com/containous/traefik/tls"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_createBugReport(t *testing.T) {
|
||||||
|
traefikConfiguration := TraefikConfiguration{
|
||||||
|
ConfigFile: "FOO",
|
||||||
|
GlobalConfiguration: configuration.GlobalConfiguration{
|
||||||
|
EntryPoints: configuration.EntryPoints{
|
||||||
|
"goo": &configuration.EntryPoint{
|
||||||
|
Address: "hoo.bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
File: &file.Provider{
|
||||||
|
Directory: "BAR",
|
||||||
|
},
|
||||||
|
RootCAs: tls.RootCAs{"fllf"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
report, err := createBugReport(traefikConfiguration)
|
||||||
|
assert.NoError(t, err, report)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_anonymize_traefikConfiguration(t *testing.T) {
|
||||||
|
traefikConfiguration := &TraefikConfiguration{
|
||||||
|
ConfigFile: "FOO",
|
||||||
|
GlobalConfiguration: configuration.GlobalConfiguration{
|
||||||
|
EntryPoints: configuration.EntryPoints{
|
||||||
|
"goo": &configuration.EntryPoint{
|
||||||
|
Address: "hoo.bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
File: &file.Provider{
|
||||||
|
Directory: "BAR",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err := anonymize.Do(traefikConfiguration, true)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "hoo.bar", traefikConfiguration.GlobalConfiguration.EntryPoints["goo"].Address)
|
||||||
|
}
|
||||||
293
cmd/traefik/configuration.go
Normal file
293
cmd/traefik/configuration.go
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containous/flaeg"
|
||||||
|
"github.com/containous/traefik-extra-service-fabric"
|
||||||
|
"github.com/containous/traefik/api"
|
||||||
|
"github.com/containous/traefik/configuration"
|
||||||
|
"github.com/containous/traefik/middlewares/accesslog"
|
||||||
|
"github.com/containous/traefik/ping"
|
||||||
|
"github.com/containous/traefik/provider/boltdb"
|
||||||
|
"github.com/containous/traefik/provider/consul"
|
||||||
|
"github.com/containous/traefik/provider/docker"
|
||||||
|
"github.com/containous/traefik/provider/dynamodb"
|
||||||
|
"github.com/containous/traefik/provider/ecs"
|
||||||
|
"github.com/containous/traefik/provider/etcd"
|
||||||
|
"github.com/containous/traefik/provider/eureka"
|
||||||
|
"github.com/containous/traefik/provider/file"
|
||||||
|
"github.com/containous/traefik/provider/kubernetes"
|
||||||
|
"github.com/containous/traefik/provider/marathon"
|
||||||
|
"github.com/containous/traefik/provider/mesos"
|
||||||
|
"github.com/containous/traefik/provider/rancher"
|
||||||
|
"github.com/containous/traefik/provider/rest"
|
||||||
|
"github.com/containous/traefik/provider/zk"
|
||||||
|
"github.com/containous/traefik/types"
|
||||||
|
sf "github.com/jjcollinge/servicefabric"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TraefikConfiguration holds GlobalConfiguration and other stuff
|
||||||
|
type TraefikConfiguration struct {
|
||||||
|
configuration.GlobalConfiguration `mapstructure:",squash" export:"true"`
|
||||||
|
ConfigFile string `short:"c" description:"Configuration file to use (TOML)." export:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTraefikDefaultPointersConfiguration creates a TraefikConfiguration with pointers default values
|
||||||
|
func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||||
|
//default Docker
|
||||||
|
var defaultDocker docker.Provider
|
||||||
|
defaultDocker.Watch = true
|
||||||
|
defaultDocker.ExposedByDefault = true
|
||||||
|
defaultDocker.Endpoint = "unix:///var/run/docker.sock"
|
||||||
|
defaultDocker.SwarmMode = false
|
||||||
|
|
||||||
|
// default File
|
||||||
|
var defaultFile file.Provider
|
||||||
|
defaultFile.Watch = true
|
||||||
|
defaultFile.Filename = "" //needs equivalent to viper.ConfigFileUsed()
|
||||||
|
|
||||||
|
// default Rest
|
||||||
|
var defaultRest rest.Provider
|
||||||
|
defaultRest.EntryPoint = configuration.DefaultInternalEntryPointName
|
||||||
|
|
||||||
|
// TODO: Deprecated - Web provider, use REST provider instead
|
||||||
|
var defaultWeb configuration.WebCompatibility
|
||||||
|
defaultWeb.Address = ":8080"
|
||||||
|
defaultWeb.Statistics = &types.Statistics{
|
||||||
|
RecentErrors: 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Deprecated - default Metrics
|
||||||
|
defaultWeb.Metrics = &types.Metrics{
|
||||||
|
Prometheus: &types.Prometheus{
|
||||||
|
Buckets: types.Buckets{0.1, 0.3, 1.2, 5},
|
||||||
|
},
|
||||||
|
Datadog: &types.Datadog{
|
||||||
|
Address: "localhost:8125",
|
||||||
|
PushInterval: "10s",
|
||||||
|
},
|
||||||
|
StatsD: &types.Statsd{
|
||||||
|
Address: "localhost:8125",
|
||||||
|
PushInterval: "10s",
|
||||||
|
},
|
||||||
|
InfluxDB: &types.InfluxDB{
|
||||||
|
Address: "localhost:8089",
|
||||||
|
PushInterval: "10s",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// default Marathon
|
||||||
|
var defaultMarathon marathon.Provider
|
||||||
|
defaultMarathon.Watch = true
|
||||||
|
defaultMarathon.Endpoint = "http://127.0.0.1:8080"
|
||||||
|
defaultMarathon.ExposedByDefault = true
|
||||||
|
defaultMarathon.Constraints = types.Constraints{}
|
||||||
|
defaultMarathon.DialerTimeout = flaeg.Duration(60 * time.Second)
|
||||||
|
defaultMarathon.KeepAlive = flaeg.Duration(10 * time.Second)
|
||||||
|
|
||||||
|
// default Consul
|
||||||
|
var defaultConsul consul.Provider
|
||||||
|
defaultConsul.Watch = true
|
||||||
|
defaultConsul.Endpoint = "127.0.0.1:8500"
|
||||||
|
defaultConsul.Prefix = "traefik"
|
||||||
|
defaultConsul.Constraints = types.Constraints{}
|
||||||
|
|
||||||
|
// default CatalogProvider
|
||||||
|
var defaultConsulCatalog consul.CatalogProvider
|
||||||
|
defaultConsulCatalog.Endpoint = "127.0.0.1:8500"
|
||||||
|
defaultConsulCatalog.ExposedByDefault = true
|
||||||
|
defaultConsulCatalog.Constraints = types.Constraints{}
|
||||||
|
defaultConsulCatalog.Prefix = "traefik"
|
||||||
|
defaultConsulCatalog.FrontEndRule = "Host:{{.ServiceName}}.{{.Domain}}"
|
||||||
|
|
||||||
|
// default Etcd
|
||||||
|
var defaultEtcd etcd.Provider
|
||||||
|
defaultEtcd.Watch = true
|
||||||
|
defaultEtcd.Endpoint = "127.0.0.1:2379"
|
||||||
|
defaultEtcd.Prefix = "/traefik"
|
||||||
|
defaultEtcd.Constraints = types.Constraints{}
|
||||||
|
|
||||||
|
//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
|
||||||
|
var defaultBoltDb boltdb.Provider
|
||||||
|
defaultBoltDb.Watch = true
|
||||||
|
defaultBoltDb.Endpoint = "127.0.0.1:4001"
|
||||||
|
defaultBoltDb.Prefix = "/traefik"
|
||||||
|
defaultBoltDb.Constraints = types.Constraints{}
|
||||||
|
|
||||||
|
//default Kubernetes
|
||||||
|
var defaultKubernetes kubernetes.Provider
|
||||||
|
defaultKubernetes.Watch = true
|
||||||
|
defaultKubernetes.Endpoint = ""
|
||||||
|
defaultKubernetes.LabelSelector = ""
|
||||||
|
defaultKubernetes.Constraints = types.Constraints{}
|
||||||
|
|
||||||
|
// default Mesos
|
||||||
|
var defaultMesos mesos.Provider
|
||||||
|
defaultMesos.Watch = true
|
||||||
|
defaultMesos.Endpoint = "http://127.0.0.1:5050"
|
||||||
|
defaultMesos.ExposedByDefault = true
|
||||||
|
defaultMesos.Constraints = types.Constraints{}
|
||||||
|
defaultMesos.RefreshSeconds = 30
|
||||||
|
defaultMesos.ZkDetectionTimeout = 30
|
||||||
|
defaultMesos.StateTimeoutSecond = 30
|
||||||
|
|
||||||
|
//default ECS
|
||||||
|
var defaultECS ecs.Provider
|
||||||
|
defaultECS.Watch = true
|
||||||
|
defaultECS.ExposedByDefault = true
|
||||||
|
defaultECS.AutoDiscoverClusters = false
|
||||||
|
defaultECS.Clusters = ecs.Clusters{"default"}
|
||||||
|
defaultECS.RefreshSeconds = 15
|
||||||
|
defaultECS.Constraints = types.Constraints{}
|
||||||
|
|
||||||
|
//default Rancher
|
||||||
|
var defaultRancher rancher.Provider
|
||||||
|
defaultRancher.Watch = true
|
||||||
|
defaultRancher.ExposedByDefault = true
|
||||||
|
defaultRancher.RefreshSeconds = 15
|
||||||
|
|
||||||
|
// default DynamoDB
|
||||||
|
var defaultDynamoDB dynamodb.Provider
|
||||||
|
defaultDynamoDB.Constraints = types.Constraints{}
|
||||||
|
defaultDynamoDB.RefreshSeconds = 15
|
||||||
|
defaultDynamoDB.TableName = "traefik"
|
||||||
|
defaultDynamoDB.Watch = true
|
||||||
|
|
||||||
|
// default Eureka
|
||||||
|
var defaultEureka eureka.Provider
|
||||||
|
defaultEureka.Delay = "30s"
|
||||||
|
|
||||||
|
// default ServiceFabric
|
||||||
|
var defaultServiceFabric servicefabric.Provider
|
||||||
|
defaultServiceFabric.APIVersion = sf.DefaultAPIVersion
|
||||||
|
defaultServiceFabric.RefreshSeconds = 10
|
||||||
|
|
||||||
|
// default Ping
|
||||||
|
var defaultPing = ping.Handler{
|
||||||
|
EntryPoint: "traefik",
|
||||||
|
}
|
||||||
|
|
||||||
|
// default TraefikLog
|
||||||
|
defaultTraefikLog := types.TraefikLog{
|
||||||
|
Format: "common",
|
||||||
|
FilePath: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
// default AccessLog
|
||||||
|
defaultAccessLog := types.AccessLog{
|
||||||
|
Format: accesslog.CommonFormat,
|
||||||
|
FilePath: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
// default HealthCheckConfig
|
||||||
|
healthCheck := configuration.HealthCheckConfig{
|
||||||
|
Interval: flaeg.Duration(configuration.DefaultHealthCheckInterval),
|
||||||
|
}
|
||||||
|
|
||||||
|
// default RespondingTimeouts
|
||||||
|
respondingTimeouts := configuration.RespondingTimeouts{
|
||||||
|
IdleTimeout: flaeg.Duration(configuration.DefaultIdleTimeout),
|
||||||
|
}
|
||||||
|
|
||||||
|
// default ForwardingTimeouts
|
||||||
|
forwardingTimeouts := configuration.ForwardingTimeouts{
|
||||||
|
DialTimeout: flaeg.Duration(configuration.DefaultDialTimeout),
|
||||||
|
}
|
||||||
|
|
||||||
|
// default LifeCycle
|
||||||
|
defaultLifeCycle := configuration.LifeCycle{
|
||||||
|
GraceTimeOut: flaeg.Duration(configuration.DefaultGraceTimeout),
|
||||||
|
}
|
||||||
|
|
||||||
|
// default ApiConfiguration
|
||||||
|
defaultAPI := api.Handler{
|
||||||
|
EntryPoint: "traefik",
|
||||||
|
Dashboard: true,
|
||||||
|
}
|
||||||
|
defaultAPI.Statistics = &types.Statistics{
|
||||||
|
RecentErrors: 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
// default Metrics
|
||||||
|
defaultMetrics := types.Metrics{
|
||||||
|
Prometheus: &types.Prometheus{
|
||||||
|
Buckets: types.Buckets{0.1, 0.3, 1.2, 5},
|
||||||
|
EntryPoint: "traefik",
|
||||||
|
},
|
||||||
|
Datadog: &types.Datadog{
|
||||||
|
Address: "localhost:8125",
|
||||||
|
PushInterval: "10s",
|
||||||
|
},
|
||||||
|
StatsD: &types.Statsd{
|
||||||
|
Address: "localhost:8125",
|
||||||
|
PushInterval: "10s",
|
||||||
|
},
|
||||||
|
InfluxDB: &types.InfluxDB{
|
||||||
|
Address: "localhost:8089",
|
||||||
|
PushInterval: "10s",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultConfiguration := configuration.GlobalConfiguration{
|
||||||
|
Docker: &defaultDocker,
|
||||||
|
File: &defaultFile,
|
||||||
|
Web: &defaultWeb,
|
||||||
|
Rest: &defaultRest,
|
||||||
|
Marathon: &defaultMarathon,
|
||||||
|
Consul: &defaultConsul,
|
||||||
|
ConsulCatalog: &defaultConsulCatalog,
|
||||||
|
Etcd: &defaultEtcd,
|
||||||
|
Zookeeper: &defaultZookeeper,
|
||||||
|
Boltdb: &defaultBoltDb,
|
||||||
|
Kubernetes: &defaultKubernetes,
|
||||||
|
Mesos: &defaultMesos,
|
||||||
|
ECS: &defaultECS,
|
||||||
|
Rancher: &defaultRancher,
|
||||||
|
Eureka: &defaultEureka,
|
||||||
|
DynamoDB: &defaultDynamoDB,
|
||||||
|
Retry: &configuration.Retry{},
|
||||||
|
HealthCheck: &healthCheck,
|
||||||
|
RespondingTimeouts: &respondingTimeouts,
|
||||||
|
ForwardingTimeouts: &forwardingTimeouts,
|
||||||
|
TraefikLog: &defaultTraefikLog,
|
||||||
|
AccessLog: &defaultAccessLog,
|
||||||
|
LifeCycle: &defaultLifeCycle,
|
||||||
|
Ping: &defaultPing,
|
||||||
|
API: &defaultAPI,
|
||||||
|
Metrics: &defaultMetrics,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &TraefikConfiguration{
|
||||||
|
GlobalConfiguration: defaultConfiguration,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTraefikConfiguration creates a TraefikConfiguration with default values
|
||||||
|
func NewTraefikConfiguration() *TraefikConfiguration {
|
||||||
|
return &TraefikConfiguration{
|
||||||
|
GlobalConfiguration: configuration.GlobalConfiguration{
|
||||||
|
AccessLogsFile: "",
|
||||||
|
TraefikLogsFile: "",
|
||||||
|
LogLevel: "ERROR",
|
||||||
|
EntryPoints: map[string]*configuration.EntryPoint{},
|
||||||
|
Constraints: types.Constraints{},
|
||||||
|
DefaultEntryPoints: []string{"http"},
|
||||||
|
ProvidersThrottleDuration: flaeg.Duration(2 * time.Second),
|
||||||
|
MaxIdleConnsPerHost: 200,
|
||||||
|
IdleTimeout: flaeg.Duration(0),
|
||||||
|
HealthCheck: &configuration.HealthCheckConfig{
|
||||||
|
Interval: flaeg.Duration(configuration.DefaultHealthCheckInterval),
|
||||||
|
},
|
||||||
|
CheckNewVersion: true,
|
||||||
|
},
|
||||||
|
ConfigFile: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
72
cmd/traefik/healthcheck.go
Normal file
72
cmd/traefik/healthcheck.go
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containous/flaeg"
|
||||||
|
"github.com/containous/traefik/configuration"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newHealthCheckCmd(traefikConfiguration *TraefikConfiguration, traefikPointersConfiguration *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),
|
||||||
|
Metadata: map[string]string{
|
||||||
|
"parseAllSources": "true",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runHealthCheck(traefikConfiguration *TraefikConfiguration) func() error {
|
||||||
|
return func() error {
|
||||||
|
traefikConfiguration.GlobalConfiguration.SetEffectiveConfiguration(traefikConfiguration.ConfigFile)
|
||||||
|
|
||||||
|
if traefikConfiguration.Ping == nil {
|
||||||
|
fmt.Println("Please enable `ping` to use healtcheck.")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, errPing := healthCheck(traefikConfiguration.GlobalConfiguration)
|
||||||
|
if errPing != nil {
|
||||||
|
fmt.Printf("Error calling healthcheck: %s\n", errPing)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
fmt.Printf("Bad healthcheck status: %s\n", resp.Status)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Printf("OK: %s\n", resp.Request.URL)
|
||||||
|
os.Exit(0)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func healthCheck(globalConfiguration configuration.GlobalConfiguration) (*http.Response, error) {
|
||||||
|
pingEntryPoint, ok := globalConfiguration.EntryPoints[globalConfiguration.Ping.EntryPoint]
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("missing ping entrypoint")
|
||||||
|
}
|
||||||
|
|
||||||
|
client := &http.Client{Timeout: 5 * time.Second}
|
||||||
|
protocol := "http"
|
||||||
|
if pingEntryPoint.TLS != nil {
|
||||||
|
protocol = "https"
|
||||||
|
tr := &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||||
|
}
|
||||||
|
client.Transport = tr
|
||||||
|
}
|
||||||
|
path := "/"
|
||||||
|
if globalConfiguration.Web != nil {
|
||||||
|
path = globalConfiguration.Web.Path
|
||||||
|
}
|
||||||
|
return client.Head(protocol + "://" + pingEntryPoint.Address + path + "ping")
|
||||||
|
}
|
||||||
145
cmd/traefik/storeconfig.go
Normal file
145
cmd/traefik/storeconfig.go
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
stdlog "log"
|
||||||
|
|
||||||
|
"github.com/containous/flaeg"
|
||||||
|
"github.com/containous/staert"
|
||||||
|
"github.com/containous/traefik/acme"
|
||||||
|
"github.com/containous/traefik/cluster"
|
||||||
|
"github.com/docker/libkv/store"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newStoreConfigCmd(traefikConfiguration *TraefikConfiguration, traefikPointersConfiguration *TraefikConfiguration) *flaeg.Command {
|
||||||
|
return &flaeg.Command{
|
||||||
|
Name: "storeconfig",
|
||||||
|
Description: `Store the static traefik configuration into a Key-value stores. Traefik will not start.`,
|
||||||
|
Config: traefikConfiguration,
|
||||||
|
DefaultPointersConfig: traefikPointersConfiguration,
|
||||||
|
Metadata: map[string]string{
|
||||||
|
"parseAllSources": "true",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func runStoreConfig(kv *staert.KvSource, traefikConfiguration *TraefikConfiguration) func() error {
|
||||||
|
return func() error {
|
||||||
|
if kv == nil {
|
||||||
|
return fmt.Errorf("error using command storeconfig, no Key-value store defined")
|
||||||
|
}
|
||||||
|
|
||||||
|
fileConfig := traefikConfiguration.GlobalConfiguration.File
|
||||||
|
if fileConfig != nil {
|
||||||
|
traefikConfiguration.GlobalConfiguration.File = nil
|
||||||
|
if len(fileConfig.Filename) == 0 && len(fileConfig.Directory) == 0 {
|
||||||
|
fileConfig.Filename = traefikConfiguration.ConfigFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonConf, err := json.Marshal(traefikConfiguration.GlobalConfiguration)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
stdlog.Printf("Storing configuration: %s\n", jsonConf)
|
||||||
|
|
||||||
|
err = kv.StoreConfig(traefikConfiguration.GlobalConfiguration)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if fileConfig != nil {
|
||||||
|
jsonConf, err = json.Marshal(fileConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
stdlog.Printf("Storing file configuration: %s\n", jsonConf)
|
||||||
|
config, err := fileConfig.LoadConfig()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
stdlog.Print("Writing config to KV")
|
||||||
|
err = kv.StoreConfig(config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if traefikConfiguration.GlobalConfiguration.ACME != nil {
|
||||||
|
var object cluster.Object
|
||||||
|
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()
|
||||||
|
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)
|
||||||
|
err = meta.Marshall()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
source := staert.KvSource{
|
||||||
|
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 nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// createKvSource creates KvSource
|
||||||
|
// TLS support is enable for Consul and Etcd backends
|
||||||
|
func createKvSource(traefikConfiguration *TraefikConfiguration) (*staert.KvSource, error) {
|
||||||
|
var kv *staert.KvSource
|
||||||
|
var kvStore store.Store
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case traefikConfiguration.Consul != nil:
|
||||||
|
kvStore, err = traefikConfiguration.Consul.CreateStore()
|
||||||
|
kv = &staert.KvSource{
|
||||||
|
Store: kvStore,
|
||||||
|
Prefix: traefikConfiguration.Consul.Prefix,
|
||||||
|
}
|
||||||
|
case traefikConfiguration.Etcd != nil:
|
||||||
|
kvStore, err = traefikConfiguration.Etcd.CreateStore()
|
||||||
|
kv = &staert.KvSource{
|
||||||
|
Store: kvStore,
|
||||||
|
Prefix: traefikConfiguration.Etcd.Prefix,
|
||||||
|
}
|
||||||
|
case traefikConfiguration.Zookeeper != nil:
|
||||||
|
kvStore, err = traefikConfiguration.Zookeeper.CreateStore()
|
||||||
|
kv = &staert.KvSource{
|
||||||
|
Store: kvStore,
|
||||||
|
Prefix: traefikConfiguration.Zookeeper.Prefix,
|
||||||
|
}
|
||||||
|
case traefikConfiguration.Boltdb != nil:
|
||||||
|
kvStore, err = traefikConfiguration.Boltdb.CreateStore()
|
||||||
|
kv = &staert.KvSource{
|
||||||
|
Store: kvStore,
|
||||||
|
Prefix: traefikConfiguration.Boltdb.Prefix,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return kv, err
|
||||||
|
}
|
||||||
@@ -1,41 +1,39 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
fmtlog "log"
|
fmtlog "log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/cenk/backoff"
|
||||||
"github.com/containous/flaeg"
|
"github.com/containous/flaeg"
|
||||||
"github.com/containous/staert"
|
"github.com/containous/staert"
|
||||||
"github.com/containous/traefik/acme"
|
"github.com/containous/traefik/acme"
|
||||||
"github.com/containous/traefik/cluster"
|
"github.com/containous/traefik/collector"
|
||||||
|
"github.com/containous/traefik/configuration"
|
||||||
|
"github.com/containous/traefik/job"
|
||||||
"github.com/containous/traefik/log"
|
"github.com/containous/traefik/log"
|
||||||
"github.com/containous/traefik/middlewares"
|
"github.com/containous/traefik/provider/ecs"
|
||||||
"github.com/containous/traefik/provider/kubernetes"
|
"github.com/containous/traefik/provider/kubernetes"
|
||||||
"github.com/containous/traefik/safe"
|
"github.com/containous/traefik/safe"
|
||||||
"github.com/containous/traefik/server"
|
"github.com/containous/traefik/server"
|
||||||
|
"github.com/containous/traefik/server/uuid"
|
||||||
|
traefikTls "github.com/containous/traefik/tls"
|
||||||
"github.com/containous/traefik/types"
|
"github.com/containous/traefik/types"
|
||||||
"github.com/containous/traefik/version"
|
"github.com/containous/traefik/version"
|
||||||
"github.com/coreos/go-systemd/daemon"
|
"github.com/coreos/go-systemd/daemon"
|
||||||
"github.com/docker/libkv/store"
|
|
||||||
"github.com/satori/go.uuid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
|
||||||
|
|
||||||
//traefik config inits
|
//traefik config inits
|
||||||
traefikConfiguration := server.NewTraefikConfiguration()
|
traefikConfiguration := NewTraefikConfiguration()
|
||||||
traefikPointersConfiguration := server.NewTraefikDefaultPointersConfiguration()
|
traefikPointersConfiguration := NewTraefikDefaultPointersConfiguration()
|
||||||
//traefik Command init
|
//traefik Command init
|
||||||
traefikCmd := &flaeg.Command{
|
traefikCmd := &flaeg.Command{
|
||||||
Name: "traefik",
|
Name: "traefik",
|
||||||
@@ -44,75 +42,31 @@ Complete documentation is available at https://traefik.io`,
|
|||||||
Config: traefikConfiguration,
|
Config: traefikConfiguration,
|
||||||
DefaultPointersConfig: traefikPointersConfiguration,
|
DefaultPointersConfig: traefikPointersConfiguration,
|
||||||
Run: func() error {
|
Run: func() error {
|
||||||
run(traefikConfiguration)
|
run(&traefikConfiguration.GlobalConfiguration, traefikConfiguration.ConfigFile)
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
//storeconfig Command init
|
//storeconfig Command init
|
||||||
var kv *staert.KvSource
|
storeConfigCmd := newStoreConfigCmd(traefikConfiguration, traefikPointersConfiguration)
|
||||||
var err error
|
|
||||||
|
|
||||||
storeconfigCmd := &flaeg.Command{
|
|
||||||
Name: "storeconfig",
|
|
||||||
Description: `Store the static traefik configuration into a Key-value stores. Traefik will not start.`,
|
|
||||||
Config: traefikConfiguration,
|
|
||||||
DefaultPointersConfig: traefikPointersConfiguration,
|
|
||||||
Run: func() error {
|
|
||||||
if kv == nil {
|
|
||||||
return fmt.Errorf("Error using command storeconfig, no Key-value store defined")
|
|
||||||
}
|
|
||||||
jsonConf, err := json.Marshal(traefikConfiguration.GlobalConfiguration)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmtlog.Printf("Storing configuration: %s\n", jsonConf)
|
|
||||||
err = kv.StoreConfig(traefikConfiguration.GlobalConfiguration)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if traefikConfiguration.GlobalConfiguration.ACME != nil && len(traefikConfiguration.GlobalConfiguration.ACME.StorageFile) > 0 {
|
|
||||||
// convert ACME json file to KV store
|
|
||||||
store := acme.NewLocalStore(traefikConfiguration.GlobalConfiguration.ACME.StorageFile)
|
|
||||||
object, err := store.Load()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
meta := cluster.NewMetadata(object)
|
|
||||||
err = meta.Marshall()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
source := staert.KvSource{
|
|
||||||
Store: kv,
|
|
||||||
Prefix: traefikConfiguration.GlobalConfiguration.ACME.Storage,
|
|
||||||
}
|
|
||||||
err = source.StoreConfig(meta)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Metadata: map[string]string{
|
|
||||||
"parseAllSources": "true",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
//init flaeg source
|
//init flaeg source
|
||||||
f := flaeg.New(traefikCmd, os.Args[1:])
|
f := flaeg.New(traefikCmd, os.Args[1:])
|
||||||
//add custom parsers
|
//add custom parsers
|
||||||
f.AddParser(reflect.TypeOf(server.EntryPoints{}), &server.EntryPoints{})
|
f.AddParser(reflect.TypeOf(configuration.EntryPoints{}), &configuration.EntryPoints{})
|
||||||
f.AddParser(reflect.TypeOf(server.DefaultEntryPoints{}), &server.DefaultEntryPoints{})
|
f.AddParser(reflect.TypeOf(configuration.DefaultEntryPoints{}), &configuration.DefaultEntryPoints{})
|
||||||
|
f.AddParser(reflect.TypeOf(traefikTls.RootCAs{}), &traefikTls.RootCAs{})
|
||||||
f.AddParser(reflect.TypeOf(types.Constraints{}), &types.Constraints{})
|
f.AddParser(reflect.TypeOf(types.Constraints{}), &types.Constraints{})
|
||||||
f.AddParser(reflect.TypeOf(kubernetes.Namespaces{}), &kubernetes.Namespaces{})
|
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([]acme.Domain{}), &acme.Domains{})
|
||||||
f.AddParser(reflect.TypeOf(types.Buckets{}), &types.Buckets{})
|
f.AddParser(reflect.TypeOf(types.Buckets{}), &types.Buckets{})
|
||||||
|
|
||||||
//add commands
|
//add commands
|
||||||
f.AddCommand(newVersionCmd())
|
f.AddCommand(newVersionCmd())
|
||||||
f.AddCommand(newBugCmd(traefikConfiguration, traefikPointersConfiguration))
|
f.AddCommand(newBugCmd(traefikConfiguration, traefikPointersConfiguration))
|
||||||
f.AddCommand(storeconfigCmd)
|
f.AddCommand(storeConfigCmd)
|
||||||
|
f.AddCommand(newHealthCheckCmd(traefikConfiguration, traefikPointersConfiguration))
|
||||||
|
|
||||||
usedCmd, err := f.GetCommand()
|
usedCmd, err := f.GetCommand()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -134,28 +88,37 @@ Complete documentation is available at https://traefik.io`,
|
|||||||
s.AddSource(toml)
|
s.AddSource(toml)
|
||||||
s.AddSource(f)
|
s.AddSource(f)
|
||||||
if _, err := s.LoadConfig(); err != nil {
|
if _, err := s.LoadConfig(); err != nil {
|
||||||
fmtlog.Println(fmt.Errorf("Error reading TOML config file %s : %s", toml.ConfigFileUsed(), err))
|
fmtlog.Printf("Error reading TOML config file %s : %s\n", toml.ConfigFileUsed(), err)
|
||||||
os.Exit(-1)
|
os.Exit(-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
traefikConfiguration.ConfigFile = toml.ConfigFileUsed()
|
traefikConfiguration.ConfigFile = toml.ConfigFileUsed()
|
||||||
|
|
||||||
kv, err = CreateKvSource(traefikConfiguration)
|
kv, err := createKvSource(traefikConfiguration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmtlog.Printf("Error creating kv store: %s\n", err)
|
fmtlog.Printf("Error creating kv store: %s\n", err)
|
||||||
os.Exit(-1)
|
os.Exit(-1)
|
||||||
}
|
}
|
||||||
|
storeConfigCmd.Run = runStoreConfig(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 kv != nil && usedCmd == traefikCmd {
|
||||||
if traefikConfiguration.Cluster == nil {
|
if traefikConfiguration.Cluster == nil {
|
||||||
traefikConfiguration.Cluster = &types.Cluster{Node: uuid.NewV4().String()}
|
traefikConfiguration.Cluster = &types.Cluster{Node: uuid.Get()}
|
||||||
}
|
}
|
||||||
if traefikConfiguration.Cluster.Store == nil {
|
if traefikConfiguration.Cluster.Store == nil {
|
||||||
traefikConfiguration.Cluster.Store = &types.Store{Prefix: kv.Prefix, Store: kv.Store}
|
traefikConfiguration.Cluster.Store = &types.Store{Prefix: kv.Prefix, Store: kv.Store}
|
||||||
}
|
}
|
||||||
s.AddSource(kv)
|
s.AddSource(kv)
|
||||||
if _, err := s.LoadConfig(); err != nil {
|
operation := func() error {
|
||||||
|
_, err := s.LoadConfig()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
notify := func(err error, time time.Duration) {
|
||||||
|
log.Errorf("Load config error: %+v, retrying in %s", err, time)
|
||||||
|
}
|
||||||
|
err := backoff.RetryNotify(safe.OperationWithRecover(operation), job.NewBackOff(backoff.NewExponentialBackOff()), notify)
|
||||||
|
if err != nil {
|
||||||
fmtlog.Printf("Error loading configuration: %s\n", err)
|
fmtlog.Printf("Error loading configuration: %s\n", err)
|
||||||
os.Exit(-1)
|
os.Exit(-1)
|
||||||
}
|
}
|
||||||
@@ -169,93 +132,36 @@ Complete documentation is available at https://traefik.io`,
|
|||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func run(traefikConfiguration *server.TraefikConfiguration) {
|
func run(globalConfiguration *configuration.GlobalConfiguration, configFile string) {
|
||||||
fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags)
|
configureLogging(globalConfiguration)
|
||||||
|
|
||||||
// load global configuration
|
if len(configFile) > 0 {
|
||||||
globalConfiguration := traefikConfiguration.GlobalConfiguration
|
log.Infof("Using TOML configuration file %s", configFile)
|
||||||
|
|
||||||
http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = globalConfiguration.MaxIdleConnsPerHost
|
|
||||||
if globalConfiguration.InsecureSkipVerify {
|
|
||||||
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
|
||||||
}
|
|
||||||
loggerMiddleware := middlewares.NewLogger(globalConfiguration.AccessLogsFile)
|
|
||||||
defer loggerMiddleware.Close()
|
|
||||||
|
|
||||||
if globalConfiguration.File != nil && len(globalConfiguration.File.Filename) == 0 {
|
|
||||||
// no filename, setting to global config file
|
|
||||||
if len(traefikConfiguration.ConfigFile) != 0 {
|
|
||||||
globalConfiguration.File.Filename = traefikConfiguration.ConfigFile
|
|
||||||
} else {
|
|
||||||
log.Errorln("Error using file configuration backend, no filename defined")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(globalConfiguration.EntryPoints) == 0 {
|
http.DefaultTransport.(*http.Transport).Proxy = http.ProxyFromEnvironment
|
||||||
globalConfiguration.EntryPoints = map[string]*server.EntryPoint{"http": {Address: ":80"}}
|
|
||||||
globalConfiguration.DefaultEntryPoints = []string{"http"}
|
|
||||||
}
|
|
||||||
|
|
||||||
if globalConfiguration.Debug {
|
globalConfiguration.SetEffectiveConfiguration(configFile)
|
||||||
globalConfiguration.LogLevel = "DEBUG"
|
|
||||||
}
|
|
||||||
|
|
||||||
// logging
|
|
||||||
level, err := logrus.ParseLevel(strings.ToLower(globalConfiguration.LogLevel))
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Error getting level", err)
|
|
||||||
}
|
|
||||||
log.SetLevel(level)
|
|
||||||
if len(globalConfiguration.TraefikLogsFile) > 0 {
|
|
||||||
dir := filepath.Dir(globalConfiguration.TraefikLogsFile)
|
|
||||||
|
|
||||||
err := os.MkdirAll(dir, 0755)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Failed to create log path %s: %s", dir, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fi, err := os.OpenFile(globalConfiguration.TraefikLogsFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
|
||||||
defer func() {
|
|
||||||
if err := fi.Close(); err != nil {
|
|
||||||
log.Error("Error closing file", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
if err != nil {
|
|
||||||
log.Error("Error opening file", err)
|
|
||||||
} else {
|
|
||||||
log.SetOutput(fi)
|
|
||||||
log.SetFormatter(&logrus.TextFormatter{DisableColors: true, FullTimestamp: true, DisableSorting: true})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.SetFormatter(&logrus.TextFormatter{FullTimestamp: true, DisableSorting: true})
|
|
||||||
}
|
|
||||||
jsonConf, _ := json.Marshal(globalConfiguration)
|
jsonConf, _ := json.Marshal(globalConfiguration)
|
||||||
log.Infof("Traefik version %s built on %s", version.Version, version.BuildDate)
|
log.Infof("Traefik version %s built on %s", version.Version, version.BuildDate)
|
||||||
|
|
||||||
if globalConfiguration.CheckNewVersion {
|
if globalConfiguration.CheckNewVersion {
|
||||||
ticker := time.NewTicker(24 * time.Hour)
|
checkNewVersion()
|
||||||
safe.Go(func() {
|
|
||||||
version.CheckNewVersion()
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ticker.C:
|
|
||||||
version.CheckNewVersion()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(traefikConfiguration.ConfigFile) != 0 {
|
stats(globalConfiguration)
|
||||||
log.Infof("Using TOML configuration file %s", traefikConfiguration.ConfigFile)
|
|
||||||
}
|
|
||||||
log.Debugf("Global configuration loaded %s", string(jsonConf))
|
log.Debugf("Global configuration loaded %s", string(jsonConf))
|
||||||
svr := server.NewServer(globalConfiguration)
|
svr := server.NewServer(*globalConfiguration)
|
||||||
svr.Start()
|
svr.Start()
|
||||||
defer svr.Close()
|
defer svr.Close()
|
||||||
|
|
||||||
sent, err := daemon.SdNotify(false, "READY=1")
|
sent, err := daemon.SdNotify(false, "READY=1")
|
||||||
if !sent && err != nil {
|
if !sent && err != nil {
|
||||||
log.Error("Fail to notify", err)
|
log.Error("Fail to notify", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
t, err := daemon.SdWatchdogEnabled(false)
|
t, err := daemon.SdWatchdogEnabled(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Problem with watchdog", err)
|
log.Error("Problem with watchdog", err)
|
||||||
@@ -266,48 +172,114 @@ func run(traefikConfiguration *server.TraefikConfiguration) {
|
|||||||
safe.Go(func() {
|
safe.Go(func() {
|
||||||
tick := time.Tick(t)
|
tick := time.Tick(t)
|
||||||
for range tick {
|
for range tick {
|
||||||
if ok, _ := daemon.SdNotify(false, "WATCHDOG=1"); !ok {
|
_, errHealthCheck := healthCheck(*globalConfiguration)
|
||||||
log.Error("Fail to tick watchdog")
|
if globalConfiguration.Ping == nil || errHealthCheck == nil {
|
||||||
|
if ok, _ := daemon.SdNotify(false, "WATCHDOG=1"); !ok {
|
||||||
|
log.Error("Fail to tick watchdog")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Error(errHealthCheck)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
svr.Wait()
|
svr.Wait()
|
||||||
log.Info("Shutting down")
|
log.Info("Shutting down")
|
||||||
|
logrus.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateKvSource creates KvSource
|
func configureLogging(globalConfiguration *configuration.GlobalConfiguration) {
|
||||||
// TLS support is enable for Consul and Etcd backends
|
// configure default log flags
|
||||||
func CreateKvSource(traefikConfiguration *server.TraefikConfiguration) (*staert.KvSource, error) {
|
fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags)
|
||||||
var kv *staert.KvSource
|
|
||||||
var store store.Store
|
|
||||||
var err error
|
|
||||||
|
|
||||||
switch {
|
if globalConfiguration.Debug {
|
||||||
case traefikConfiguration.Consul != nil:
|
globalConfiguration.LogLevel = "DEBUG"
|
||||||
store, err = traefikConfiguration.Consul.CreateStore()
|
}
|
||||||
kv = &staert.KvSource{
|
|
||||||
Store: store,
|
// configure log level
|
||||||
Prefix: traefikConfiguration.Consul.Prefix,
|
level, err := logrus.ParseLevel(strings.ToLower(globalConfiguration.LogLevel))
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error getting level", err)
|
||||||
|
}
|
||||||
|
log.SetLevel(level)
|
||||||
|
|
||||||
|
// configure log output file
|
||||||
|
logFile := globalConfiguration.TraefikLogsFile
|
||||||
|
if len(logFile) > 0 {
|
||||||
|
log.Warn("top-level traefikLogsFile has been deprecated -- please use traefiklog.filepath")
|
||||||
|
}
|
||||||
|
if globalConfiguration.TraefikLog != nil && len(globalConfiguration.TraefikLog.FilePath) > 0 {
|
||||||
|
logFile = globalConfiguration.TraefikLog.FilePath
|
||||||
|
}
|
||||||
|
|
||||||
|
// configure log format
|
||||||
|
var formatter logrus.Formatter
|
||||||
|
if globalConfiguration.TraefikLog != nil && globalConfiguration.TraefikLog.Format == "json" {
|
||||||
|
formatter = &logrus.JSONFormatter{}
|
||||||
|
} else {
|
||||||
|
disableColors := false
|
||||||
|
if len(logFile) > 0 {
|
||||||
|
disableColors = true
|
||||||
}
|
}
|
||||||
case traefikConfiguration.Etcd != nil:
|
formatter = &logrus.TextFormatter{DisableColors: disableColors, FullTimestamp: true, DisableSorting: true}
|
||||||
store, err = traefikConfiguration.Etcd.CreateStore()
|
}
|
||||||
kv = &staert.KvSource{
|
log.SetFormatter(formatter)
|
||||||
Store: store,
|
|
||||||
Prefix: traefikConfiguration.Etcd.Prefix,
|
if len(logFile) > 0 {
|
||||||
|
dir := filepath.Dir(logFile)
|
||||||
|
|
||||||
|
err := os.MkdirAll(dir, 0755)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Failed to create log path %s: %s", dir, err)
|
||||||
}
|
}
|
||||||
case traefikConfiguration.Zookeeper != nil:
|
|
||||||
store, err = traefikConfiguration.Zookeeper.CreateStore()
|
err = log.OpenFile(logFile)
|
||||||
kv = &staert.KvSource{
|
logrus.RegisterExitHandler(func() {
|
||||||
Store: store,
|
if err := log.CloseFile(); err != nil {
|
||||||
Prefix: traefikConfiguration.Zookeeper.Prefix,
|
log.Error("Error closing log", err)
|
||||||
}
|
}
|
||||||
case traefikConfiguration.Boltdb != nil:
|
})
|
||||||
store, err = traefikConfiguration.Boltdb.CreateStore()
|
if err != nil {
|
||||||
kv = &staert.KvSource{
|
log.Error("Error opening file", err)
|
||||||
Store: store,
|
|
||||||
Prefix: traefikConfiguration.Boltdb.Prefix,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return kv, err
|
}
|
||||||
|
|
||||||
|
func checkNewVersion() {
|
||||||
|
ticker := time.Tick(24 * time.Hour)
|
||||||
|
safe.Go(func() {
|
||||||
|
for time.Sleep(10 * time.Minute); ; <-ticker {
|
||||||
|
version.CheckNewVersion()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func stats(globalConfiguration *configuration.GlobalConfiguration) {
|
||||||
|
if globalConfiguration.SendAnonymousUsage {
|
||||||
|
log.Info(`
|
||||||
|
Stats collection is enabled.
|
||||||
|
Many thanks for contributing to Traefik's improvement by allowing us to receive anonymous information from your configuration.
|
||||||
|
Help us improve Traefik by leaving this feature on :)
|
||||||
|
More details on: https://docs.traefik.io/basic/#collected-data
|
||||||
|
`)
|
||||||
|
collect(globalConfiguration)
|
||||||
|
} else {
|
||||||
|
log.Info(`
|
||||||
|
Stats collection is disabled.
|
||||||
|
Help us improve Traefik by turning this feature on :)
|
||||||
|
More details on: https://docs.traefik.io/basic/#collected-data
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func collect(globalConfiguration *configuration.GlobalConfiguration) {
|
||||||
|
ticker := time.Tick(24 * time.Hour)
|
||||||
|
safe.Go(func() {
|
||||||
|
for time.Sleep(10 * time.Minute); ; <-ticker {
|
||||||
|
if err := collector.Collect(globalConfiguration); err != nil {
|
||||||
|
log.Debug(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func newVersionCmd() *flaeg.Command {
|
|||||||
if err := getVersionPrint(os.Stdout); err != nil {
|
if err := getVersionPrint(os.Stdout); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Printf("\n")
|
fmt.Print("\n")
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|||||||
79
collector/collector.go
Normal file
79
collector/collector.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package collector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containous/traefik/cmd/traefik/anonymize"
|
||||||
|
"github.com/containous/traefik/configuration"
|
||||||
|
"github.com/containous/traefik/log"
|
||||||
|
"github.com/containous/traefik/version"
|
||||||
|
"github.com/mitchellh/hashstructure"
|
||||||
|
)
|
||||||
|
|
||||||
|
// collectorURL URL where the stats are send
|
||||||
|
const collectorURL = "https://collect.traefik.io/619df80498b60f985d766ce62f912b7c"
|
||||||
|
|
||||||
|
// Collected data
|
||||||
|
type data struct {
|
||||||
|
Version string
|
||||||
|
Codename string
|
||||||
|
BuildDate string
|
||||||
|
Configuration string
|
||||||
|
Hash string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect anonymous data.
|
||||||
|
func Collect(globalConfiguration *configuration.GlobalConfiguration) error {
|
||||||
|
anonConfig, err := anonymize.Do(globalConfiguration, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("Anonymous stats sent to %s: %s", collectorURL, anonConfig)
|
||||||
|
|
||||||
|
hashConf, err := hashstructure.Hash(globalConfiguration, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
data := &data{
|
||||||
|
Version: version.Version,
|
||||||
|
Codename: version.Codename,
|
||||||
|
BuildDate: version.BuildDate,
|
||||||
|
Hash: strconv.FormatUint(hashConf, 10),
|
||||||
|
Configuration: base64.StdEncoding.EncodeToString([]byte(anonConfig)),
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
err = json.NewEncoder(buf).Encode(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = makeHTTPClient().Post(collectorURL, "application/json; charset=utf-8", buf)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeHTTPClient() *http.Client {
|
||||||
|
dialer := &net.Dialer{
|
||||||
|
Timeout: configuration.DefaultDialTimeout,
|
||||||
|
KeepAlive: 30 * time.Second,
|
||||||
|
DualStack: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
transport := &http.Transport{
|
||||||
|
Proxy: http.ProxyFromEnvironment,
|
||||||
|
DialContext: dialer.DialContext,
|
||||||
|
IdleConnTimeout: 90 * time.Second,
|
||||||
|
TLSHandshakeTimeout: 10 * time.Second,
|
||||||
|
ExpectContinueTimeout: 1 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &http.Client{Transport: transport}
|
||||||
|
}
|
||||||
474
configuration/configuration.go
Normal file
474
configuration/configuration.go
Normal file
@@ -0,0 +1,474 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containous/flaeg"
|
||||||
|
"github.com/containous/traefik-extra-service-fabric"
|
||||||
|
"github.com/containous/traefik/acme"
|
||||||
|
"github.com/containous/traefik/api"
|
||||||
|
"github.com/containous/traefik/log"
|
||||||
|
"github.com/containous/traefik/ping"
|
||||||
|
"github.com/containous/traefik/provider/boltdb"
|
||||||
|
"github.com/containous/traefik/provider/consul"
|
||||||
|
"github.com/containous/traefik/provider/docker"
|
||||||
|
"github.com/containous/traefik/provider/dynamodb"
|
||||||
|
"github.com/containous/traefik/provider/ecs"
|
||||||
|
"github.com/containous/traefik/provider/etcd"
|
||||||
|
"github.com/containous/traefik/provider/eureka"
|
||||||
|
"github.com/containous/traefik/provider/file"
|
||||||
|
"github.com/containous/traefik/provider/kubernetes"
|
||||||
|
"github.com/containous/traefik/provider/marathon"
|
||||||
|
"github.com/containous/traefik/provider/mesos"
|
||||||
|
"github.com/containous/traefik/provider/rancher"
|
||||||
|
"github.com/containous/traefik/provider/rest"
|
||||||
|
"github.com/containous/traefik/provider/zk"
|
||||||
|
"github.com/containous/traefik/tls"
|
||||||
|
"github.com/containous/traefik/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DefaultInternalEntryPointName the name of the default internal entry point
|
||||||
|
DefaultInternalEntryPointName = "traefik"
|
||||||
|
|
||||||
|
// DefaultHealthCheckInterval is the default health check interval.
|
||||||
|
DefaultHealthCheckInterval = 30 * time.Second
|
||||||
|
|
||||||
|
// DefaultDialTimeout when connecting to a backend server.
|
||||||
|
DefaultDialTimeout = 30 * time.Second
|
||||||
|
|
||||||
|
// DefaultIdleTimeout before closing an idle connection.
|
||||||
|
DefaultIdleTimeout = 180 * time.Second
|
||||||
|
|
||||||
|
// DefaultGraceTimeout controls how long Traefik serves pending requests
|
||||||
|
// prior to shutting down.
|
||||||
|
DefaultGraceTimeout = 10 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
// GlobalConfiguration holds global configuration (with providers, etc.).
|
||||||
|
// It's populated from the traefik configuration file passed as an argument to the binary.
|
||||||
|
type GlobalConfiguration struct {
|
||||||
|
LifeCycle *LifeCycle `description:"Timeouts influencing the server life cycle" export:"true"`
|
||||||
|
GraceTimeOut flaeg.Duration `short:"g" description:"(Deprecated) Duration to give active requests a chance to finish before Traefik stops" export:"true"` // Deprecated
|
||||||
|
Debug bool `short:"d" description:"Enable debug mode" export:"true"`
|
||||||
|
CheckNewVersion bool `description:"Periodically check if a new version has been released" export:"true"`
|
||||||
|
SendAnonymousUsage bool `description:"send periodically anonymous usage statistics" export:"true"`
|
||||||
|
AccessLogsFile string `description:"(Deprecated) Access logs file" export:"true"` // Deprecated
|
||||||
|
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"`
|
||||||
|
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"`
|
||||||
|
Constraints types.Constraints `description:"Filter services by constraint, matching with service tags" export:"true"`
|
||||||
|
ACME *acme.ACME `description:"Enable ACME (Let's Encrypt): automatic SSL" export:"true"`
|
||||||
|
DefaultEntryPoints DefaultEntryPoints `description:"Entrypoints to be used by frontends that do not specify any entrypoint" export:"true"`
|
||||||
|
ProvidersThrottleDuration flaeg.Duration `description:"Backends throttle duration: minimum duration 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." export:"true"`
|
||||||
|
MaxIdleConnsPerHost int `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used" export:"true"`
|
||||||
|
IdleTimeout flaeg.Duration `description:"(Deprecated) maximum amount of time an idle (keep-alive) connection will remain idle before closing itself." export:"true"` // Deprecated
|
||||||
|
InsecureSkipVerify bool `description:"Disable SSL certificate verification" export:"true"`
|
||||||
|
RootCAs tls.RootCAs `description:"Add cert file for self-signed certificate"`
|
||||||
|
Retry *Retry `description:"Enable retry sending request if network error" export:"true"`
|
||||||
|
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"`
|
||||||
|
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"`
|
||||||
|
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"`
|
||||||
|
Kubernetes *kubernetes.Provider `description:"Enable Kubernetes backend with default settings" export:"true"`
|
||||||
|
Mesos *mesos.Provider `description:"Enable Mesos backend with default settings" export:"true"`
|
||||||
|
Eureka *eureka.Provider `description:"Enable Eureka backend with default settings" export:"true"`
|
||||||
|
ECS *ecs.Provider `description:"Enable ECS backend with default settings" export:"true"`
|
||||||
|
Rancher *rancher.Provider `description:"Enable Rancher backend with default settings" export:"true"`
|
||||||
|
DynamoDB *dynamodb.Provider `description:"Enable DynamoDB backend with default settings" export:"true"`
|
||||||
|
ServiceFabric *servicefabric.Provider `description:"Enable Service Fabric backend with default settings" export:"true"`
|
||||||
|
Rest *rest.Provider `description:"Enable Rest backend with default settings" export:"true"`
|
||||||
|
API *api.Handler `description:"Enable api/dashboard" export:"true"`
|
||||||
|
Metrics *types.Metrics `description:"Enable a metrics exporter" export:"true"`
|
||||||
|
Ping *ping.Handler `description:"Enable ping" export:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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"`
|
||||||
|
Auth *types.Auth `export:"true"`
|
||||||
|
Debug bool `export:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gc *GlobalConfiguration) handleWebDeprecation() {
|
||||||
|
if gc.Web != nil {
|
||||||
|
log.Warn("web provider configuration is deprecated, you should use these options : api, rest provider, ping and metrics")
|
||||||
|
|
||||||
|
if gc.API != nil || gc.Metrics != nil || gc.Ping != nil || gc.Rest != nil {
|
||||||
|
log.Warn("web option is ignored if you use it with one of these options : api, rest provider, ping or metrics")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
gc.EntryPoints[DefaultInternalEntryPointName] = &EntryPoint{
|
||||||
|
Address: gc.Web.Address,
|
||||||
|
Auth: gc.Web.Auth,
|
||||||
|
}
|
||||||
|
if gc.Web.CertFile != "" {
|
||||||
|
gc.EntryPoints[DefaultInternalEntryPointName].TLS = &tls.TLS{
|
||||||
|
Certificates: []tls.Certificate{
|
||||||
|
{
|
||||||
|
CertFile: tls.FileOrContent(gc.Web.CertFile),
|
||||||
|
KeyFile: tls.FileOrContent(gc.Web.KeyFile),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if gc.API == nil {
|
||||||
|
gc.API = &api.Handler{
|
||||||
|
EntryPoint: DefaultInternalEntryPointName,
|
||||||
|
Statistics: gc.Web.Statistics,
|
||||||
|
Dashboard: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if gc.Ping == nil {
|
||||||
|
gc.Ping = &ping.Handler{
|
||||||
|
EntryPoint: DefaultInternalEntryPointName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if gc.Metrics == nil {
|
||||||
|
gc.Metrics = gc.Web.Metrics
|
||||||
|
}
|
||||||
|
|
||||||
|
if !gc.Debug {
|
||||||
|
gc.Debug = gc.Web.Debug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetEffectiveConfiguration adds missing configuration parameters derived from existing ones.
|
||||||
|
// It also takes care of maintaining backwards compatibility.
|
||||||
|
func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
|
||||||
|
if len(gc.EntryPoints) == 0 {
|
||||||
|
gc.EntryPoints = map[string]*EntryPoint{"http": {
|
||||||
|
Address: ":80",
|
||||||
|
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||||
|
}}
|
||||||
|
gc.DefaultEntryPoints = []string{"http"}
|
||||||
|
}
|
||||||
|
|
||||||
|
gc.handleWebDeprecation()
|
||||||
|
|
||||||
|
if (gc.API != nil && gc.API.EntryPoint == DefaultInternalEntryPointName) ||
|
||||||
|
(gc.Ping != nil && gc.Ping.EntryPoint == DefaultInternalEntryPointName) ||
|
||||||
|
(gc.Metrics != nil && gc.Metrics.Prometheus != nil && gc.Metrics.Prometheus.EntryPoint == DefaultInternalEntryPointName) ||
|
||||||
|
(gc.Rest != nil && gc.Rest.EntryPoint == DefaultInternalEntryPointName) {
|
||||||
|
if _, ok := gc.EntryPoints[DefaultInternalEntryPointName]; !ok {
|
||||||
|
gc.EntryPoints[DefaultInternalEntryPointName] = &EntryPoint{Address: ":8080"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForwardedHeaders must be remove in the next breaking version
|
||||||
|
for entryPointName := range gc.EntryPoints {
|
||||||
|
entryPoint := gc.EntryPoints[entryPointName]
|
||||||
|
if entryPoint.ForwardedHeaders == nil {
|
||||||
|
entryPoint.ForwardedHeaders = &ForwardedHeaders{Insecure: true}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure LifeCycle isn't nil to spare nil checks elsewhere.
|
||||||
|
if gc.LifeCycle == nil {
|
||||||
|
gc.LifeCycle = &LifeCycle{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefer legacy grace timeout parameter for backwards compatibility reasons.
|
||||||
|
if gc.GraceTimeOut > 0 {
|
||||||
|
log.Warn("top-level grace period configuration has been deprecated -- please use lifecycle grace period")
|
||||||
|
gc.LifeCycle.GraceTimeOut = gc.GraceTimeOut
|
||||||
|
}
|
||||||
|
|
||||||
|
if gc.Rancher != nil {
|
||||||
|
// Ensure backwards compatibility for now
|
||||||
|
if len(gc.Rancher.AccessKey) > 0 ||
|
||||||
|
len(gc.Rancher.Endpoint) > 0 ||
|
||||||
|
len(gc.Rancher.SecretKey) > 0 {
|
||||||
|
|
||||||
|
if gc.Rancher.API == nil {
|
||||||
|
gc.Rancher.API = &rancher.APIConfiguration{
|
||||||
|
AccessKey: gc.Rancher.AccessKey,
|
||||||
|
SecretKey: gc.Rancher.SecretKey,
|
||||||
|
Endpoint: gc.Rancher.Endpoint,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Warn("Deprecated configuration found: rancher.[accesskey|secretkey|endpoint]. " +
|
||||||
|
"Please use rancher.api.[accesskey|secretkey|endpoint] instead.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if gc.Rancher.Metadata != nil && len(gc.Rancher.Metadata.Prefix) == 0 {
|
||||||
|
gc.Rancher.Metadata.Prefix = "latest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if gc.API != nil {
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultEntryPoints holds default entry points
|
||||||
|
type DefaultEntryPoints []string
|
||||||
|
|
||||||
|
// 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 (dep *DefaultEntryPoints) String() string {
|
||||||
|
return strings.Join(*dep, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 (dep *DefaultEntryPoints) Set(value string) error {
|
||||||
|
entrypoints := strings.Split(value, ",")
|
||||||
|
if len(entrypoints) == 0 {
|
||||||
|
return fmt.Errorf("bad DefaultEntryPoints format: %s", value)
|
||||||
|
}
|
||||||
|
for _, entrypoint := range entrypoints {
|
||||||
|
*dep = append(*dep, entrypoint)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get return the EntryPoints map
|
||||||
|
func (dep *DefaultEntryPoints) Get() interface{} {
|
||||||
|
return DefaultEntryPoints(*dep)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetValue sets the EntryPoints map with val
|
||||||
|
func (dep *DefaultEntryPoints) SetValue(val interface{}) {
|
||||||
|
*dep = DefaultEntryPoints(val.(DefaultEntryPoints))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type is type of the struct
|
||||||
|
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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HealthCheckConfig contains health check configuration parameters.
|
||||||
|
type HealthCheckConfig struct {
|
||||||
|
Interval flaeg.Duration `description:"Default periodicity of enabled health checks" export:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RespondingTimeouts contains timeout configurations for incoming requests to the Traefik instance.
|
||||||
|
type RespondingTimeouts struct {
|
||||||
|
ReadTimeout flaeg.Duration `description:"ReadTimeout is the maximum duration for reading the entire request, including the body. If zero, no timeout is set" export:"true"`
|
||||||
|
WriteTimeout flaeg.Duration `description:"WriteTimeout is the maximum duration before timing out writes of the response. If zero, no timeout is set" export:"true"`
|
||||||
|
IdleTimeout flaeg.Duration `description:"IdleTimeout is the maximum amount duration an idle (keep-alive) connection will remain idle before closing itself. Defaults to 180 seconds. If zero, no timeout is set" export:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ForwardingTimeouts contains timeout configurations for forwarding requests to the backend servers.
|
||||||
|
type ForwardingTimeouts struct {
|
||||||
|
DialTimeout flaeg.Duration `description:"The amount of time to wait until a connection to a backend server can be established. Defaults to 30 seconds. If zero, no timeout exists" export:"true"`
|
||||||
|
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 {
|
||||||
|
RequestAcceptGraceTimeout flaeg.Duration `description:"Duration to keep accepting requests before Traefik initiates the graceful shutdown procedure"`
|
||||||
|
GraceTimeOut flaeg.Duration `description:"Duration to give active requests a chance to finish before Traefik stops"`
|
||||||
|
}
|
||||||
393
configuration/configuration_test.go
Normal file
393
configuration/configuration_test.go
Normal file
@@ -0,0 +1,393 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containous/flaeg"
|
||||||
|
"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 {
|
||||||
|
desc string
|
||||||
|
legacyGraceTimeout time.Duration
|
||||||
|
lifeCycleGraceTimeout time.Duration
|
||||||
|
wantGraceTimeout time.Duration
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "legacy grace timeout given only",
|
||||||
|
legacyGraceTimeout: 5 * time.Second,
|
||||||
|
wantGraceTimeout: 5 * time.Second,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "legacy and life cycle grace timeouts given",
|
||||||
|
legacyGraceTimeout: 5 * time.Second,
|
||||||
|
lifeCycleGraceTimeout: 12 * time.Second,
|
||||||
|
wantGraceTimeout: 5 * time.Second,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "legacy grace timeout omitted",
|
||||||
|
legacyGraceTimeout: 0,
|
||||||
|
lifeCycleGraceTimeout: 12 * time.Second,
|
||||||
|
wantGraceTimeout: 12 * time.Second,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
gc := &GlobalConfiguration{
|
||||||
|
GraceTimeOut: flaeg.Duration(test.legacyGraceTimeout),
|
||||||
|
}
|
||||||
|
if test.lifeCycleGraceTimeout > 0 {
|
||||||
|
gc.LifeCycle = &LifeCycle{
|
||||||
|
GraceTimeOut: flaeg.Duration(test.lifeCycleGraceTimeout),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetEffectiveConfigurationFileProviderFilename(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
desc string
|
||||||
|
fileProvider *file.Provider
|
||||||
|
wantFileProviderFilename string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "no filename for file provider given",
|
||||||
|
fileProvider: &file.Provider{},
|
||||||
|
wantFileProviderFilename: defaultConfigFile,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "filename for file provider given",
|
||||||
|
fileProvider: &file.Provider{BaseProvider: provider.BaseProvider{Filename: "other.toml"}},
|
||||||
|
wantFileProviderFilename: "other.toml",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
170
contrib/scripts/dumpcerts.sh
Executable file
170
contrib/scripts/dumpcerts.sh
Executable file
@@ -0,0 +1,170 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Copyright (c) 2017 Brian 'redbeard' Harrington <redbeard@dead-city.org>
|
||||||
|
#
|
||||||
|
# dumpcerts.sh - A simple utility to explode a Traefik acme.json file into a
|
||||||
|
# directory of certificates and a private key
|
||||||
|
#
|
||||||
|
# Usage - dumpcerts.sh /etc/traefik/acme.json /etc/ssl/
|
||||||
|
#
|
||||||
|
# Dependencies -
|
||||||
|
# util-linux
|
||||||
|
# openssl
|
||||||
|
# jq
|
||||||
|
# The MIT License (MIT)
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
# THE SOFTWARE.
|
||||||
|
|
||||||
|
# Exit codes:
|
||||||
|
# 1 - A component is missing or could not be read
|
||||||
|
# 2 - There was a problem reading acme.json
|
||||||
|
# 4 - The destination certificate directory does not exist
|
||||||
|
# 8 - Missing private key
|
||||||
|
|
||||||
|
set -o errexit
|
||||||
|
set -o pipefail
|
||||||
|
set -o nounset
|
||||||
|
|
||||||
|
USAGE="$(basename "$0") <path to acme> <destination cert directory>"
|
||||||
|
|
||||||
|
# Platform variations
|
||||||
|
case "$(uname)" in
|
||||||
|
'Linux')
|
||||||
|
# On Linux, -d should always work. --decode does not work with Alpine's busybox-binary
|
||||||
|
CMD_DECODE_BASE64="base64 -d"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# Max OS-X supports --decode and -D, but --decode may be supported by other platforms as well.
|
||||||
|
CMD_DECODE_BASE64="base64 --decode"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Allow us to exit on a missing jq binary
|
||||||
|
exit_jq() {
|
||||||
|
echo "
|
||||||
|
You must have the binary 'jq' to use this.
|
||||||
|
jq is available at: https://stedolan.github.io/jq/download/
|
||||||
|
|
||||||
|
${USAGE}" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
bad_acme() {
|
||||||
|
echo "
|
||||||
|
There was a problem parsing your acme.json file.
|
||||||
|
|
||||||
|
${USAGE}" >&2
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ $# -ne 2 ]; then
|
||||||
|
echo "
|
||||||
|
Insufficient number of parameters.
|
||||||
|
|
||||||
|
${USAGE}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
readonly acmefile="${1}"
|
||||||
|
readonly certdir="${2%/}"
|
||||||
|
|
||||||
|
if [ ! -r "${acmefile}" ]; then
|
||||||
|
echo "
|
||||||
|
There was a problem reading from '${acmefile}'
|
||||||
|
We need to read this file to explode the JSON bundle... exiting.
|
||||||
|
|
||||||
|
${USAGE}" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
if [ ! -d "${certdir}" ]; then
|
||||||
|
echo "
|
||||||
|
Path ${certdir} does not seem to be a directory
|
||||||
|
We need a directory in which to explode the JSON bundle... exiting.
|
||||||
|
|
||||||
|
${USAGE}" >&2
|
||||||
|
exit 4
|
||||||
|
fi
|
||||||
|
|
||||||
|
jq=$(command -v jq) || exit_jq
|
||||||
|
|
||||||
|
priv=$(${jq} -e -r '.PrivateKey' "${acmefile}") || bad_acme
|
||||||
|
|
||||||
|
if [ ! -n "${priv}" ]; then
|
||||||
|
echo "
|
||||||
|
There didn't seem to be a private key in ${acmefile}.
|
||||||
|
Please ensure that there is a key in this file and try again." >&2
|
||||||
|
exit 8
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If they do not exist, create the needed subdirectories for our assets
|
||||||
|
# and place each in a variable for later use, normalizing the path
|
||||||
|
mkdir -p "${certdir}"/{certs,private}
|
||||||
|
|
||||||
|
pdir="${certdir}/private/"
|
||||||
|
cdir="${certdir}/certs/"
|
||||||
|
|
||||||
|
# Save the existing umask, change the default mode to 600, then
|
||||||
|
# after writing the private key switch it back to the default
|
||||||
|
oldumask=$(umask)
|
||||||
|
umask 177
|
||||||
|
trap 'umask ${oldumask}' EXIT
|
||||||
|
|
||||||
|
# traefik stores the private key in stripped base64 format but the certificates
|
||||||
|
# bundled as a base64 object without stripping headers. This normalizes the
|
||||||
|
# headers and formatting.
|
||||||
|
#
|
||||||
|
# In testing this out it was a balance between the following mechanisms:
|
||||||
|
# gawk:
|
||||||
|
# echo ${priv} | awk 'BEGIN {print "-----BEGIN RSA PRIVATE KEY-----"}
|
||||||
|
# {gsub(/.{64}/,"&\n")}1
|
||||||
|
# END {print "-----END RSA PRIVATE KEY-----"}' > "${pdir}/letsencrypt.key"
|
||||||
|
#
|
||||||
|
# openssl:
|
||||||
|
# echo -e "-----BEGIN RSA PRIVATE KEY-----\n${priv}\n-----END RSA PRIVATE KEY-----" \
|
||||||
|
# | openssl rsa -inform pem -out "${pdir}/letsencrypt.key"
|
||||||
|
#
|
||||||
|
# and sed:
|
||||||
|
# echo "-----BEGIN RSA PRIVATE KEY-----" > "${pdir}/letsencrypt.key"
|
||||||
|
# echo ${priv} | sed -E 's/(.{64})/\1\n/g' >> "${pdir}/letsencrypt.key"
|
||||||
|
# sed -i '$ d' "${pdir}/letsencrypt.key"
|
||||||
|
# echo "-----END RSA PRIVATE KEY-----" >> "${pdir}/letsencrypt.key"
|
||||||
|
# openssl rsa -noout -in "${pdir}/letsencrypt.key" -check # To check if the key is valid
|
||||||
|
|
||||||
|
# In the end, openssl was chosen because most users will need this script
|
||||||
|
# *because* of openssl combined with the fact that it will refuse to write the
|
||||||
|
# key if it does not parse out correctly. The other mechanisms were left as
|
||||||
|
# comments so that the user can choose the mechanism most appropriate to them.
|
||||||
|
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
|
||||||
|
# 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
|
||||||
|
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
|
||||||
|
echo "${key}" | ${CMD_DECODE_BASE64} > "${pdir}/${domain}.key"
|
||||||
|
done
|
||||||
11
docs.Dockerfile
Normal file
11
docs.Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
FROM alpine
|
||||||
|
|
||||||
|
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.local/bin
|
||||||
|
|
||||||
|
COPY requirements.txt /mkdocs/
|
||||||
|
WORKDIR /mkdocs
|
||||||
|
|
||||||
|
RUN apk --update upgrade \
|
||||||
|
&& apk --no-cache --no-progress add py-pip \
|
||||||
|
&& rm -rf /var/cache/apk/* \
|
||||||
|
&& pip install --user -r requirements.txt
|
||||||
23
docs/archive.md
Normal file
23
docs/archive.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
## Current versions documentation
|
||||||
|
|
||||||
|
- [Latest stable](https://docs.traefik.io)
|
||||||
|
|
||||||
|
## Future version documentation
|
||||||
|
|
||||||
|
- [Experimental](https://master--traefik-docs.netlify.com/)
|
||||||
|
|
||||||
|
## Previous versions documentation
|
||||||
|
|
||||||
|
- [v1.5 aka Cancoillotte](http://v1-5.archive.docs.traefik.io/)
|
||||||
|
|
||||||
|
- [v1.4 aka Roquefort](http://v1-4.archive.docs.traefik.io/)
|
||||||
|
|
||||||
|
- [v1.3 aka Raclette](http://v1-3.archive.docs.traefik.io/)
|
||||||
|
|
||||||
|
- [v1.2 aka Morbier](http://v1-2.archive.docs.traefik.io/)
|
||||||
|
|
||||||
|
- [v1.1 aka Camembert](http://v1-1.archive.docs.traefik.io/)
|
||||||
|
|
||||||
|
## More
|
||||||
|
|
||||||
|
[Change log](https://github.com/containous/traefik/blob/master/CHANGELOG.md)
|
||||||
525
docs/basics.md
525
docs/basics.md
@@ -1,7 +1,8 @@
|
|||||||
|
# Basics
|
||||||
|
|
||||||
# Concepts
|
## Concepts
|
||||||
|
|
||||||
Let's take our example from the [overview](https://docs.traefik.io/#overview) again:
|
Let's take our example from the [overview](/#overview) again:
|
||||||
|
|
||||||
|
|
||||||
> Imagine that you have deployed a bunch of microservices on your infrastructure. You probably used a service registry (like etcd or consul) and/or an orchestrator (swarm, Mesos/Marathon) to manage all these services.
|
> Imagine that you have deployed a bunch of microservices on your infrastructure. You probably used a service registry (like etcd or consul) and/or an orchestrator (swarm, Mesos/Marathon) to manage all these services.
|
||||||
@@ -24,7 +25,7 @@ Routes are created using requests fields (`Host`, `Path`, `Headers`...) and can
|
|||||||
- The [frontend](#frontends) will then send the request to a [backend](#backends). A backend can be composed by one or more [servers](#servers), and by a load-balancing strategy.
|
- The [frontend](#frontends) will then send the request to a [backend](#backends). A backend can be composed by one or more [servers](#servers), and by a load-balancing strategy.
|
||||||
- Finally, the [server](#servers) will forward the request to the corresponding microservice in the private network.
|
- Finally, the [server](#servers) will forward the request to the corresponding microservice in the private network.
|
||||||
|
|
||||||
## Entrypoints
|
### Entrypoints
|
||||||
|
|
||||||
Entrypoints are the network entry points into Træfik.
|
Entrypoints are the network entry points into Træfik.
|
||||||
They can be defined using:
|
They can be defined using:
|
||||||
@@ -61,23 +62,26 @@ And here is another example with client certificate authentication:
|
|||||||
[entryPoints.https]
|
[entryPoints.https]
|
||||||
address = ":443"
|
address = ":443"
|
||||||
[entryPoints.https.tls]
|
[entryPoints.https.tls]
|
||||||
clientCAFiles = ["tests/clientca1.crt", "tests/clientca2.crt"]
|
[entryPoints.https.tls]
|
||||||
[[entryPoints.https.tls.certificates]]
|
[entryPoints.https.tls.ClientCA]
|
||||||
certFile = "tests/traefik.crt"
|
files = ["tests/clientca1.crt", "tests/clientca2.crt"]
|
||||||
keyFile = "tests/traefik.key"
|
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.
|
- We enable SSL on `https` by giving a certificate and a key.
|
||||||
- One or several files containing Certificate Authorities in PEM format are added.
|
- One or several files containing Certificate Authorities in PEM format are added.
|
||||||
- It is possible to have multiple CA:s in the same file or keep them in separate files.
|
- It is possible to have multiple CA:s in the same file or keep them in separate files.
|
||||||
|
|
||||||
## Frontends
|
### Frontends
|
||||||
|
|
||||||
A frontend consists of a set of rules that determine how incoming requests are forwarded from an entrypoint to a backend.
|
A frontend consists of a set of rules that determine how incoming requests are forwarded from an entrypoint to a backend.
|
||||||
|
|
||||||
Rules may be classified in one of two groups: Modifiers and matchers.
|
Rules may be classified in one of two groups: Modifiers and matchers.
|
||||||
|
|
||||||
### Modifiers
|
#### Modifiers
|
||||||
|
|
||||||
Modifier rules only modify the request. They do not have any impact on routing decisions being made.
|
Modifier rules only modify the request. They do not have any impact on routing decisions being made.
|
||||||
|
|
||||||
@@ -85,49 +89,63 @@ Following is the list of existing modifier rules:
|
|||||||
|
|
||||||
- `AddPrefix: /products`: Add path prefix to the existing request path prior to forwarding the request to the backend.
|
- `AddPrefix: /products`: Add path prefix to the existing request path prior to forwarding the request to the backend.
|
||||||
- `ReplacePath: /serverless-path`: Replaces the path and adds the old path to the `X-Replaced-Path` header. Useful for mapping to AWS Lambda or Google Cloud Functions.
|
- `ReplacePath: /serverless-path`: Replaces the path and adds the old path to the `X-Replaced-Path` header. Useful for mapping to AWS Lambda or Google Cloud Functions.
|
||||||
|
- `ReplacePathRegex: ^/api/v2/(.*) /api/$1`: Replaces the path with a regular expression and adds the old path to the `X-Replaced-Path` header. Separate the regular expression and the replacement by a space.
|
||||||
|
|
||||||
### Matchers
|
#### Matchers
|
||||||
|
|
||||||
Matcher rules determine if a particular request should be forwarded to a backend.
|
Matcher rules determine if a particular request should be forwarded to a backend.
|
||||||
|
|
||||||
Separate multiple rule values by `,` (comma) in order to enable ANY semantics (i.e., forward a request if any rule matches). Does not work for `Headers` and `HeadersRegexp`.
|
Separate multiple rule values by `,` (comma) in order to enable ANY semantics (i.e., forward a request if any rule matches).
|
||||||
|
Does not work for `Headers` and `HeadersRegexp`.
|
||||||
|
|
||||||
Separate multiple rule values by `;` (semicolon) in order to enable ALL semantics (i.e., forward a request if all rules match).
|
Separate multiple rule values by `;` (semicolon) in order to enable ALL semantics (i.e., forward a request if all rules match).
|
||||||
|
|
||||||
You can optionally enable `passHostHeader` to forward client `Host` header to the backend.
|
|
||||||
|
|
||||||
Following is the list of existing matcher rules along with examples:
|
Following is the list of existing matcher rules along with examples:
|
||||||
|
|
||||||
- `Headers: Content-Type, application/json`: Match HTTP header. It accepts a comma-separated key/value pair where both key and value must be literals.
|
| Matcher | Description |
|
||||||
- `HeadersRegexp: Content-Type, application/(text|json)`: Match HTTP header. It accepts a comma-separated key/value pair where the key must be a literal and the value may be a literal or a regular expression.
|
|------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
- `Host: traefik.io, www.traefik.io`: Match request host. It accepts a sequence of literal hosts.
|
| `Headers: Content-Type, application/json` | Match HTTP header. It accepts a comma-separated key/value pair where both key and value must be literals. |
|
||||||
- `HostRegexp: traefik.io, {subdomain:[a-z]+}.traefik.io`: Match request host. It accepts a sequence of literal and regular expression hosts.
|
| `HeadersRegexp: Content-Type, application/(text/json)` | Match HTTP header. It accepts a comma-separated key/value pair where the key must be a literal and the value may be a literal or a regular expression. |
|
||||||
- `Method: GET, POST, PUT`: Match request HTTP method. It accepts a sequence of HTTP methods.
|
| `Host: traefik.io, www.traefik.io` | Match request host. It accepts a sequence of literal hosts. |
|
||||||
- `Path: /products/, /articles/{category}/{id:[0-9]+}`: Match exact request path. It accepts a sequence of literal and regular expression paths.
|
| `HostRegexp: traefik.io, {subdomain:[a-z]+}.traefik.io` | Match request host. It accepts a sequence of literal and regular expression hosts. |
|
||||||
- `PathStrip: /products/`: Match exact path and strip off the path prior to forwarding the request to the backend. It accepts a sequence of literal paths.
|
| `Method: GET, POST, PUT` | Match request HTTP method. It accepts a sequence of HTTP methods. |
|
||||||
- `PathStripRegex: /articles/{category}/{id:[0-9]+}`: Match exact path and strip off the path prior to forwarding the request to the backend. It accepts a sequence of literal and regular expression paths.
|
| `Path: /products/, /articles/{category}/{id:[0-9]+}` | Match exact request path. It accepts a sequence of literal and regular expression paths. |
|
||||||
- `PathPrefix: /products/, /articles/{category}/{id:[0-9]+}`: Match request prefix path. It accepts a sequence of literal and regular expression prefix paths.
|
| `PathStrip: /products/` | Match exact path and strip off the path prior to forwarding the request to the backend. It accepts a sequence of literal paths. |
|
||||||
- `PathPrefixStrip: /products/`: Match request prefix path and strip off the path prefix prior to forwarding the request to the backend. It accepts a sequence of literal prefix paths. Starting with Traefik 1.3, the stripped prefix path will be available in the `X-Forwarded-Prefix` header.
|
| `PathStripRegex: /articles/{category}/{id:[0-9]+}` | Match exact path and strip off the path prior to forwarding the request to the backend. It accepts a sequence of literal and regular expression paths. |
|
||||||
- `PathPrefixStripRegex: /articles/{category}/{id:[0-9]+}`: Match request prefix path and strip off the path prefix prior to forwarding the request to the backend. It accepts a sequence of literal and regular expression prefix paths. Starting with Traefik 1.3, the stripped prefix path will be available in the `X-Forwarded-Prefix` header.
|
| `PathPrefix: /products/, /articles/{category}/{id:[0-9]+}` | Match request prefix path. It accepts a sequence of literal and regular expression prefix paths. |
|
||||||
|
| `PathPrefixStrip: /products/` | Match request prefix path and strip off the path prefix prior to forwarding the request to the backend. It accepts a sequence of literal prefix paths. Starting with Traefik 1.3, the stripped prefix path will be available in the `X-Forwarded-Prefix` header. |
|
||||||
|
| `PathPrefixStripRegex: /articles/{category}/{id:[0-9]+}` | Match request prefix path and strip off the path prefix prior to forwarding the request to the backend. It accepts a sequence of literal and regular expression prefix paths. Starting with Traefik 1.3, the stripped prefix path will be available in the `X-Forwarded-Prefix` header. |
|
||||||
|
| `Query: foo=bar, bar=baz` | Match Query String parameters. It accepts a sequence of key=value pairs. |
|
||||||
|
|
||||||
In order to use regular expressions with Host and Path matchers, you must declare an arbitrarily named variable followed by the colon-separated regular expression, all enclosed in curly braces. Any pattern supported by [Go's regexp package](https://golang.org/pkg/regexp/) may be used. Example: `/posts/{id:[0-9]+}`.
|
In order to use regular expressions with Host and Path matchers, you must declare an arbitrarily named variable followed by the colon-separated regular expression, all enclosed in curly braces. Any pattern supported by [Go's regexp package](https://golang.org/pkg/regexp/) may be used (example: `/posts/{id:[0-9]+}`).
|
||||||
|
|
||||||
(Note that the variable has no special meaning; however, it is required by the gorilla/mux dependency which embeds the regular expression and defines the syntax.)
|
!!! note
|
||||||
|
The variable has no special meaning; however, it is required by the [gorilla/mux](https://github.com/gorilla/mux) dependency which embeds the regular expression and defines the syntax.
|
||||||
|
|
||||||
#### Path Matcher Usage Guidelines
|
You can optionally enable `passHostHeader` to forward client `Host` header to the backend.
|
||||||
|
You can also optionally enable `passTLSCert` to forward TLS Client certificates to the backend.
|
||||||
|
|
||||||
|
##### Path Matcher Usage Guidelines
|
||||||
|
|
||||||
This section explains when to use the various path matchers.
|
This section explains when to use the various path matchers.
|
||||||
|
|
||||||
Use `Path` if your backend listens on the exact path only. For instance, `Path: /products` would match `/products` but not `/products/shoes`.
|
Use `Path` if your backend listens on the exact path only. For instance, `Path: /products` would match `/products` but not `/products/shoes`.
|
||||||
|
|
||||||
Use a `*Prefix*` matcher if your backend listens on a particular base path but also serves requests on sub-paths. For instance, `PathPrefix: /products` would match `/products` but also `/products/shoes` and `/products/shirts`. Since the path is forwarded as-is, your backend is expected to listen on `/products`.
|
Use a `*Prefix*` matcher if your backend listens on a particular base path but also serves requests on sub-paths.
|
||||||
|
For instance, `PathPrefix: /products` would match `/products` but also `/products/shoes` and `/products/shirts`.
|
||||||
|
Since the path is forwarded as-is, your backend is expected to listen on `/products`.
|
||||||
|
|
||||||
Use a `*Strip` matcher if your backend listens on the root path (`/`) but should be routeable on a specific prefix. For instance, `PathPrefixStrip: /products` would match `/products` but also `/products/shoes` and `/products/shirts`. Since the path is stripped prior to forwarding, your backend is expected to listen on `/`.
|
Use a `*Strip` matcher if your backend listens on the root path (`/`) but should be routeable on a specific prefix.
|
||||||
If your backend is serving assets (e.g., images or Javascript files), chances are it must return properly constructed relative URLs. Continuing on the example, the backend should return `/products/shoes/image.png` (and not `/images.png` which Traefik would likely not be able to associate with the same backend). The `X-Forwarded-Prefix` header (available since Traefik 1.3) can be queried to build such URLs dynamically.
|
For instance, `PathPrefixStrip: /products` would match `/products` but also `/products/shoes` and `/products/shirts`.
|
||||||
|
Since the path is stripped prior to forwarding, your backend is expected to listen on `/`.
|
||||||
|
If your backend is serving assets (e.g., images or Javascript files), chances are it must return properly constructed relative URLs.
|
||||||
|
Continuing on the example, the backend should return `/products/shoes/image.png` (and not `/images.png` which Traefik would likely not be able to associate with the same backend).
|
||||||
|
The `X-Forwarded-Prefix` header (available since Traefik 1.3) can be queried to build such URLs dynamically.
|
||||||
|
|
||||||
Instead of distinguishing your backends by path only, you can add a Host matcher to the mix. That way, namespacing of your backends happens on the basis of hosts in addition to paths.
|
Instead of distinguishing your backends by path only, you can add a Host matcher to the mix.
|
||||||
|
That way, namespacing of your backends happens on the basis of hosts in addition to paths.
|
||||||
|
|
||||||
### Examples
|
#### Examples
|
||||||
|
|
||||||
Here is an example of frontends definition:
|
Here is an example of frontends definition:
|
||||||
|
|
||||||
@@ -140,6 +158,7 @@ Here is an example of frontends definition:
|
|||||||
[frontends.frontend2]
|
[frontends.frontend2]
|
||||||
backend = "backend1"
|
backend = "backend1"
|
||||||
passHostHeader = true
|
passHostHeader = true
|
||||||
|
passTLSCert = true
|
||||||
priority = 10
|
priority = 10
|
||||||
entrypoints = ["https"] # overrides defaultEntryPoints
|
entrypoints = ["https"] # overrides defaultEntryPoints
|
||||||
[frontends.frontend2.routes.test_1]
|
[frontends.frontend2.routes.test_1]
|
||||||
@@ -155,45 +174,47 @@ Here is an example of frontends definition:
|
|||||||
- `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 `Host: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
|
- `frontend3` will forward the traffic to the `backend2` if the rules `Host:test3.localhost` **AND** `Path:/test` are matched
|
||||||
|
|
||||||
### Combining multiple rules
|
#### Combining multiple rules
|
||||||
|
|
||||||
As seen in the previous example, you can combine multiple rules.
|
As seen in the previous example, you can combine multiple rules.
|
||||||
In TOML file, you can use multiple routes:
|
In TOML file, you can use multiple routes:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[frontends.frontend3]
|
[frontends.frontend3]
|
||||||
backend = "backend2"
|
backend = "backend2"
|
||||||
[frontends.frontend3.routes.test_1]
|
[frontends.frontend3.routes.test_1]
|
||||||
rule = "Host:test3.localhost"
|
rule = "Host:test3.localhost"
|
||||||
[frontends.frontend3.routes.test_2]
|
[frontends.frontend3.routes.test_2]
|
||||||
rule = "Path:/test"
|
rule = "Path:/test"
|
||||||
```
|
```
|
||||||
|
|
||||||
Here `frontend3` will forward the traffic to the `backend2` if the rules `Host:test3.localhost` **AND** `Path:/test` are matched.
|
Here `frontend3` will forward the traffic to the `backend2` if the rules `Host:test3.localhost` **AND** `Path:/test` are matched.
|
||||||
|
|
||||||
You can also use the notation using a `;` separator, same result:
|
You can also use the notation using a `;` separator, same result:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[frontends.frontend3]
|
[frontends.frontend3]
|
||||||
backend = "backend2"
|
backend = "backend2"
|
||||||
[frontends.frontend3.routes.test_1]
|
[frontends.frontend3.routes.test_1]
|
||||||
rule = "Host:test3.localhost;Path:/test"
|
rule = "Host:test3.localhost;Path:/test"
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally, you can create a rule to bind multiple domains or Path to a frontend, using the `,` separator:
|
Finally, you can create a rule to bind multiple domains or Path to a frontend, using the `,` separator:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[frontends.frontend2]
|
[frontends.frontend2]
|
||||||
[frontends.frontend2.routes.test_1]
|
[frontends.frontend2.routes.test_1]
|
||||||
rule = "Host:test1.localhost,test2.localhost"
|
rule = "Host:test1.localhost,test2.localhost"
|
||||||
[frontends.frontend3]
|
[frontends.frontend3]
|
||||||
backend = "backend2"
|
backend = "backend2"
|
||||||
[frontends.frontend3.routes.test_1]
|
[frontends.frontend3.routes.test_1]
|
||||||
rule = "Path:/test1,/test2"
|
rule = "Path:/test1,/test2"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Rules Order
|
#### Rules Order
|
||||||
|
|
||||||
|
When combining `Modifier` rules with `Matcher` rules, it is important to remember that `Modifier` rules **ALWAYS** apply after the `Matcher` rules.
|
||||||
|
|
||||||
When combining `Modifier` rules with `Matcher` rules, it is important to remember that `Modifier` rules **ALWAYS** apply after the `Matcher` rules.
|
|
||||||
The following rules are both `Matchers` and `Modifiers`, so the `Matcher` portion of the rule will apply first, and the `Modifier` will apply later.
|
The following rules are both `Matchers` and `Modifiers`, so the `Matcher` portion of the rule will apply first, and the `Modifier` will apply later.
|
||||||
|
|
||||||
- `PathStrip`
|
- `PathStrip`
|
||||||
@@ -208,9 +229,9 @@ The following rules are both `Matchers` and `Modifiers`, so the `Matcher` portio
|
|||||||
3. `PathStripRegex`
|
3. `PathStripRegex`
|
||||||
4. `PathPrefixStripRegex`
|
4. `PathPrefixStripRegex`
|
||||||
5. `AddPrefix`
|
5. `AddPrefix`
|
||||||
6. `ReplacePath`
|
6. `ReplacePath`
|
||||||
|
|
||||||
### Priorities
|
#### Priorities
|
||||||
|
|
||||||
By default, routes will be sorted (in descending order) using rules length (to avoid path overlap):
|
By default, routes will be sorted (in descending order) using rules length (to avoid path overlap):
|
||||||
`PathPrefix:/12345` will be matched before `PathPrefix:/1234` that will be matched before `PathPrefix:/1`.
|
`PathPrefix:/12345` will be matched before `PathPrefix:/1234` that will be matched before `PathPrefix:/1`.
|
||||||
@@ -218,30 +239,97 @@ By default, routes will be sorted (in descending order) using rules length (to a
|
|||||||
You can customize priority by frontend:
|
You can customize priority by frontend:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[frontends]
|
[frontends]
|
||||||
[frontends.frontend1]
|
[frontends.frontend1]
|
||||||
backend = "backend1"
|
backend = "backend1"
|
||||||
priority = 10
|
priority = 10
|
||||||
passHostHeader = true
|
passHostHeader = true
|
||||||
[frontends.frontend1.routes.test_1]
|
[frontends.frontend1.routes.test_1]
|
||||||
rule = "PathPrefix:/to"
|
rule = "PathPrefix:/to"
|
||||||
[frontends.frontend2]
|
[frontends.frontend2]
|
||||||
priority = 5
|
priority = 5
|
||||||
backend = "backend2"
|
backend = "backend2"
|
||||||
passHostHeader = true
|
passHostHeader = true
|
||||||
[frontends.frontend2.routes.test_1]
|
[frontends.frontend2.routes.test_1]
|
||||||
rule = "PathPrefix:/toto"
|
rule = "PathPrefix:/toto"
|
||||||
```
|
```
|
||||||
|
|
||||||
Here, `frontend1` will be matched before `frontend2` (`10 > 5`).
|
Here, `frontend1` will be matched before `frontend2` (`10 > 5`).
|
||||||
|
|
||||||
## Backends
|
#### Custom headers
|
||||||
|
|
||||||
|
Custom headers can be configured through the frontends, to add headers to either requests or responses that match the frontend's rules.
|
||||||
|
This allows for setting headers such as `X-Script-Name` to be added to the request, or custom headers to be added to the response.
|
||||||
|
|
||||||
|
!!! 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.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[frontends]
|
||||||
|
[frontends.frontend1]
|
||||||
|
backend = "backend1"
|
||||||
|
[frontends.frontend1.headers.customresponseheaders]
|
||||||
|
X-Custom-Response-Header = "True"
|
||||||
|
[frontends.frontend1.headers.customrequestheaders]
|
||||||
|
X-Script-Name = "test"
|
||||||
|
[frontends.frontend1.routes.test_1]
|
||||||
|
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.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[frontends]
|
||||||
|
[frontends.frontend1]
|
||||||
|
backend = "backend1"
|
||||||
|
[frontends.frontend1.headers.customresponseheaders]
|
||||||
|
X-Custom-Response-Header = ""
|
||||||
|
[frontends.frontend1.headers.customrequestheaders]
|
||||||
|
X-Script-Name = "test"
|
||||||
|
X-Custom-Request-Header = ""
|
||||||
|
[frontends.frontend1.routes.test_1]
|
||||||
|
rule = "PathPrefixStrip:/cheese"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Security headers
|
||||||
|
|
||||||
|
Security related headers (HSTS headers, SSL redirection, Browser XSS filter, etc) can be added and configured per frontend in a similar manner to the custom headers above.
|
||||||
|
This functionality allows for some easy security features to quickly be set.
|
||||||
|
|
||||||
|
An example of some of the security headers:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[frontends]
|
||||||
|
[frontends.frontend1]
|
||||||
|
backend = "backend1"
|
||||||
|
[frontends.frontend1.headers]
|
||||||
|
FrameDeny = true
|
||||||
|
[frontends.frontend1.routes.test_1]
|
||||||
|
rule = "PathPrefixStrip:/cheddar"
|
||||||
|
[frontends.frontend2]
|
||||||
|
backend = "backend2"
|
||||||
|
[frontends.frontend2.headers]
|
||||||
|
SSLRedirect = true
|
||||||
|
[frontends.frontend2.routes.test_1]
|
||||||
|
rule = "PathPrefixStrip:/stilton"
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, traffic routed through the first frontend will have the `X-Frame-Options` header set to `DENY`, and the second will only allow HTTPS request through, otherwise will return a 301 HTTPS redirect.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
The detailed documentation for those security headers can be found in [unrolled/secure](https://github.com/unrolled/secure#available-options).
|
||||||
|
|
||||||
|
### Backends
|
||||||
|
|
||||||
A backend is responsible to load-balance the traffic coming from one or more frontends to a set of http servers.
|
A backend is responsible to load-balance the traffic coming from one or more frontends to a set of http servers.
|
||||||
|
|
||||||
Various methods of load-balancing are supported:
|
Various methods of load-balancing are supported:
|
||||||
|
|
||||||
- `wrr`: Weighted Round Robin
|
- `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.
|
- `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.
|
||||||
|
|
||||||
A circuit breaker can also be applied to a backend, preventing high loads on failing servers.
|
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.
|
Initial state is Standby. CB observes the statistics and does not modify the request.
|
||||||
@@ -256,16 +344,13 @@ It can be configured using:
|
|||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
- `NetworkErrorRatio() > 0.5`: watch error ratio over 10 second sliding window for a frontend
|
- `NetworkErrorRatio() > 0.5`: watch error ratio over 10 second sliding window for a frontend.
|
||||||
- `LatencyAtQuantileMS(50.0) > 50`: watch latency at quantile in milliseconds.
|
- `LatencyAtQuantileMS(50.0) > 50`: watch latency at quantile in milliseconds.
|
||||||
- `ResponseCodeRatio(500, 600, 0, 600) > 0.5`: ratio of response codes in range [500-600) to [0-600)
|
- `ResponseCodeRatio(500, 600, 0, 600) > 0.5`: ratio of response codes in ranges [500-600) and [0-600).
|
||||||
|
|
||||||
To proactively prevent backends from being overwhelmed with high load, a maximum connection limit can
|
To proactively prevent backends from being overwhelmed with high load, a maximum connection limit can also be applied to each backend.
|
||||||
also be applied to each backend.
|
|
||||||
|
|
||||||
Maximum connections can be configured by specifying an integer value for `maxconn.amount` and
|
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.
|
||||||
`maxconn.extractorfunc` which is a strategy used to determine how to categorize requests in order to
|
|
||||||
evaluate the maximum connections.
|
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
```toml
|
```toml
|
||||||
@@ -280,11 +365,31 @@ For example:
|
|||||||
- Another possible value for `extractorfunc` is `client.ip` which will categorize requests based on client source ip.
|
- 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.
|
- Lastly `extractorfunc` can take the value of `request.header.ANY_HEADER` which will categorize requests based on `ANY_HEADER` that you provide.
|
||||||
|
|
||||||
Sticky sessions are supported with both load balancers. When sticky sessions are enabled, a cookie called `_TRAEFIK_BACKEND` is set on the initial
|
### Sticky sessions
|
||||||
request. 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.
|
Sticky sessions are supported with both load balancers.
|
||||||
|
When sticky sessions are enabled, a cookie is set on the initial request.
|
||||||
|
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]
|
||||||
|
# Enable sticky session
|
||||||
|
[backends.backend1.loadbalancer.stickiness]
|
||||||
|
|
||||||
|
# Customize the cookie name
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: a sha1 (6 chars)
|
||||||
|
#
|
||||||
|
# cookieName = "my_cookie"
|
||||||
|
```
|
||||||
|
|
||||||
|
The deprecated way:
|
||||||
|
|
||||||
For example:
|
|
||||||
```toml
|
```toml
|
||||||
[backends]
|
[backends]
|
||||||
[backends.backend1]
|
[backends.backend1]
|
||||||
@@ -292,12 +397,12 @@ For example:
|
|||||||
sticky = true
|
sticky = true
|
||||||
```
|
```
|
||||||
|
|
||||||
A health check can be configured in order to remove a backend from LB rotation
|
### Health Check
|
||||||
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 path
|
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.
|
||||||
appended to the backend URL and an interval (given in a format understood by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration)) specifying how
|
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).
|
||||||
often the health check should be executed (the default being 30 seconds). Each
|
Each backend must respond to the health check within 5 seconds.
|
||||||
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.
|
||||||
|
|
||||||
A recovering backend returning 200 OK responses again is being returned to the
|
A recovering backend returning 200 OK responses again is being returned to the
|
||||||
LB rotation pool.
|
LB rotation pool.
|
||||||
@@ -307,13 +412,26 @@ For example:
|
|||||||
[backends]
|
[backends]
|
||||||
[backends.backend1]
|
[backends.backend1]
|
||||||
[backends.backend1.healthcheck]
|
[backends.backend1.healthcheck]
|
||||||
path = "/health"
|
path = "/health"
|
||||||
interval = "10s"
|
interval = "10s"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Servers
|
To use a different port for the healthcheck:
|
||||||
|
```toml
|
||||||
|
[backends]
|
||||||
|
[backends.backend1]
|
||||||
|
[backends.backend1.healthcheck]
|
||||||
|
path = "/health"
|
||||||
|
interval = "10s"
|
||||||
|
port = 8080
|
||||||
|
```
|
||||||
|
|
||||||
Servers are simply defined using a `URL`. You can also apply a custom `weight` to each server (this will be used by load-balancing).
|
### 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:
|
Here is an example of backends and servers definition:
|
||||||
|
|
||||||
@@ -321,7 +439,7 @@ Here is an example of backends and servers definition:
|
|||||||
[backends]
|
[backends]
|
||||||
[backends.backend1]
|
[backends.backend1]
|
||||||
[backends.backend1.circuitbreaker]
|
[backends.backend1.circuitbreaker]
|
||||||
expression = "NetworkErrorRatio() > 0.5"
|
expression = "NetworkErrorRatio() > 0.5"
|
||||||
[backends.backend1.servers.server1]
|
[backends.backend1.servers.server1]
|
||||||
url = "http://172.17.0.2:80"
|
url = "http://172.17.0.2:80"
|
||||||
weight = 10
|
weight = 10
|
||||||
@@ -330,7 +448,7 @@ Here is an example of backends and servers definition:
|
|||||||
weight = 1
|
weight = 1
|
||||||
[backends.backend2]
|
[backends.backend2]
|
||||||
[backends.backend2.LoadBalancer]
|
[backends.backend2.LoadBalancer]
|
||||||
method = "drr"
|
method = "drr"
|
||||||
[backends.backend2.servers.server1]
|
[backends.backend2.servers.server1]
|
||||||
url = "http://172.17.0.4:80"
|
url = "http://172.17.0.4:80"
|
||||||
weight = 1
|
weight = 1
|
||||||
@@ -344,99 +462,256 @@ Here is an example of backends and servers definition:
|
|||||||
- `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.
|
- `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
|
- 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:
|
## Configuration
|
||||||
|
|
||||||
- The [static Træfik configuration](/basics#static-trfk-configuration) which is loaded only at the beginning.
|
Træfik's configuration has two parts:
|
||||||
- The [dynamic Træfik configuration](/basics#dynamic-trfk-configuration) which can be hot-reloaded (no need to restart the process).
|
|
||||||
|
|
||||||
|
- The [static Træfik configuration](/basics#static-trfik-configuration) which is loaded only at the beginning.
|
||||||
|
- The [dynamic Træfik configuration](/basics#dynamic-trfik-configuration) which can be hot-reloaded (no need to restart the process).
|
||||||
|
|
||||||
## Static Træfik configuration
|
### Static Træfik configuration
|
||||||
|
|
||||||
The static configuration is the global configuration which is setting up connections to configuration backends and entrypoints.
|
The static configuration is the global configuration which is setting up connections to configuration backends and entrypoints.
|
||||||
|
|
||||||
Træfik can be configured using many configuration sources with the following precedence order.
|
Træfik can be configured using many configuration sources with the following precedence order.
|
||||||
Each item takes precedence over the item below it:
|
Each item takes precedence over the item below it:
|
||||||
|
|
||||||
- [Key-value Store](/basics/#key-value-stores)
|
- [Key-value store](/basics/#key-value-stores)
|
||||||
- [Arguments](/basics/#arguments)
|
- [Arguments](/basics/#arguments)
|
||||||
- [Configuration file](/basics/#configuration-file)
|
- [Configuration file](/basics/#configuration-file)
|
||||||
- Default
|
- Default
|
||||||
|
|
||||||
It means that arguments override configuration file, and Key-value Store overrides arguments.
|
It means that arguments override configuration file, and key-value store overrides arguments.
|
||||||
|
|
||||||
### Configuration file
|
!!! 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.
|
||||||
|
|
||||||
|
#### Configuration file
|
||||||
|
|
||||||
By default, Træfik will try to find a `traefik.toml` in the following places:
|
By default, Træfik will try to find a `traefik.toml` in the following places:
|
||||||
|
|
||||||
- `/etc/traefik/`
|
- `/etc/traefik/`
|
||||||
- `$HOME/.traefik/`
|
- `$HOME/.traefik/`
|
||||||
- `.` *the working directory*
|
- `.` _the working directory_
|
||||||
|
|
||||||
You can override this by setting a `configFile` argument:
|
You can override this by setting a `configFile` argument:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ traefik --configFile=foo/bar/myconfigfile.toml
|
traefik --configFile=foo/bar/myconfigfile.toml
|
||||||
```
|
```
|
||||||
|
|
||||||
Please refer to the [global configuration](/toml/#global-configuration) section to get documentation on it.
|
Please refer to the [global configuration](/configuration/commons) section to get documentation on it.
|
||||||
|
|
||||||
### Arguments
|
#### Arguments
|
||||||
|
|
||||||
Each argument (and command) is described in the help section:
|
Each argument (and command) is described in the help section:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ traefik --help
|
traefik --help
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that all default values will be displayed as well.
|
Note that all default values will be displayed as well.
|
||||||
|
|
||||||
### Key-value stores
|
#### Key-value stores
|
||||||
|
|
||||||
Træfik supports several Key-value stores:
|
Træfik supports several Key-value stores:
|
||||||
|
|
||||||
- [Consul](https://consul.io)
|
- [Consul](https://consul.io)
|
||||||
- [etcd](https://coreos.com/etcd/)
|
- [etcd](https://coreos.com/etcd/)
|
||||||
- [ZooKeeper](https://zookeeper.apache.org/)
|
- [ZooKeeper](https://zookeeper.apache.org/)
|
||||||
- [boltdb](https://github.com/boltdb/bolt)
|
- [boltdb](https://github.com/boltdb/bolt)
|
||||||
|
|
||||||
Please refer to the [User Guide Key-value store configuration](/user-guide/kv-config/) section to get documentation on it.
|
Please refer to the [User Guide Key-value store configuration](/user-guide/kv-config/) section to get documentation on it.
|
||||||
|
|
||||||
## Dynamic Træfik configuration
|
### Dynamic Træfik configuration
|
||||||
|
|
||||||
The dynamic configuration concerns :
|
The dynamic configuration concerns :
|
||||||
|
|
||||||
- [Frontends](/basics/#frontends)
|
- [Frontends](/basics/#frontends)
|
||||||
- [Backends](/basics/#backends)
|
- [Backends](/basics/#backends)
|
||||||
- [Servers](/basics/#servers)
|
- [Servers](/basics/#servers)
|
||||||
|
- HTTPS Certificates
|
||||||
|
|
||||||
Træfik can hot-reload those rules which could be provided by [multiple configuration backends](/toml/#configuration-backends).
|
Træfik can hot-reload those rules which could be provided by [multiple configuration backends](/configuration/commons).
|
||||||
|
|
||||||
We only need to enable `watch` option to make Træfik watch configuration backend changes and generate its configuration automatically.
|
We only need to enable `watch` option to make Træfik watch configuration backend changes and generate its configuration automatically.
|
||||||
Routes to services will be created and updated instantly at any changes.
|
Routes to services will be created and updated instantly at any changes.
|
||||||
|
|
||||||
Please refer to the [configuration backends](/toml/#configuration-backends) section to get documentation on it.
|
Please refer to the [configuration backends](/configuration/commons) section to get documentation on it.
|
||||||
|
|
||||||
# Commands
|
## Commands
|
||||||
|
|
||||||
Usage: `traefik [command] [--flag=flag_argument]`
|
### traefik
|
||||||
|
|
||||||
List of Træfik available commands with description :
|
Usage:
|
||||||
|
```bash
|
||||||
|
traefik [command] [--flag=flag_argument]
|
||||||
|
```
|
||||||
|
|
||||||
- `version` : Print version
|
List of Træfik available commands with description :
|
||||||
- `storeconfig` : Store the static traefik configuration into a Key-value stores. Please refer to the [Store Træfik configuration](/user-guide/kv-config/#store-trfk-configuration) section to get documentation on it.
|
|
||||||
|
- `version` : Print version
|
||||||
|
- `storeconfig` : Store the static Traefik configuration into a Key-value stores. Please refer to the [Store Træfik configuration](/user-guide/kv-config/#store-configuration-in-key-value-store) section to get documentation on it.
|
||||||
|
- `bug`: The easiest way to submit a pre-filled issue.
|
||||||
|
- `healthcheck`: Calls Traefik `/ping` to check health.
|
||||||
|
|
||||||
|
Each command may have related flags.
|
||||||
|
|
||||||
Each command may have related flags.
|
|
||||||
All those related flags will be displayed with :
|
All those related flags will be displayed with :
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ traefik [command] --help
|
traefik [command] --help
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that each command is described at the beginning of the help section:
|
Each command is described at the beginning of the help section:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ traefik --help
|
traefik --help
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Command: bug
|
||||||
|
|
||||||
|
Here is the easiest way to submit a pre-filled issue on [Træfik GitHub](https://github.com/containous/traefik).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
traefik bug
|
||||||
|
```
|
||||||
|
|
||||||
|
Watch [this demo](https://www.youtube.com/watch?v=Lyz62L8m93I).
|
||||||
|
|
||||||
|
### Command: healthcheck
|
||||||
|
|
||||||
|
This command allows to check the health of Traefik. Its exit status is `0` if Traefik is healthy and `1` if it is unhealthy.
|
||||||
|
|
||||||
|
This can be used with Docker [HEALTHCHECK](https://docs.docker.com/engine/reference/builder/#healthcheck) instruction or any other health check orchestration mechanism.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
The [`ping`](/configuration/ping) must be enabled to allow the `healthcheck` command to call `/ping`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
traefik healthcheck
|
||||||
|
```
|
||||||
|
```bash
|
||||||
|
OK: http://:8082/ping
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Collected Data
|
||||||
|
|
||||||
|
**This feature is disabled by default.**
|
||||||
|
|
||||||
|
You can read the public proposal on this topic [here](https://github.com/containous/traefik/issues/2369).
|
||||||
|
|
||||||
|
### Why ?
|
||||||
|
|
||||||
|
In order to help us learn more about how Træfik is being used and improve it, we collect anonymous usage statistics from running instances.
|
||||||
|
Those data help us prioritize our developments and focus on what's more important (for example, which configuration backend is used and which is not used).
|
||||||
|
|
||||||
|
### What ?
|
||||||
|
|
||||||
|
Once a day (the first call begins 10 minutes after the start of Træfik), we collect:
|
||||||
|
- the Træfik version
|
||||||
|
- a hash of the configuration
|
||||||
|
- an **anonymous version** of the static configuration:
|
||||||
|
- token, user name, password, URL, IP, domain, email, etc, are removed
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
We do not collect the dynamic configuration (frontends & backends).
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
We do not collect data behind the scenes to run advertising programs or to sell such data to third-party.
|
||||||
|
|
||||||
|
#### Here is an example
|
||||||
|
|
||||||
|
- Source configuration:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.http]
|
||||||
|
address = ":80"
|
||||||
|
|
||||||
|
[web]
|
||||||
|
address = ":8080"
|
||||||
|
|
||||||
|
[Docker]
|
||||||
|
endpoint = "tcp://10.10.10.10:2375"
|
||||||
|
domain = "foo.bir"
|
||||||
|
exposedByDefault = true
|
||||||
|
swarmMode = true
|
||||||
|
|
||||||
|
[Docker.TLS]
|
||||||
|
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"
|
||||||
|
```
|
||||||
|
|
||||||
|
- Obfuscated and anonymous configuration:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.http]
|
||||||
|
address = ":80"
|
||||||
|
|
||||||
|
[web]
|
||||||
|
address = ":8080"
|
||||||
|
|
||||||
|
[Docker]
|
||||||
|
Endpoint = "xxxx"
|
||||||
|
Domain = "xxxx"
|
||||||
|
ExposedByDefault = true
|
||||||
|
SwarmMode = true
|
||||||
|
|
||||||
|
[Docker.TLS]
|
||||||
|
CA = "xxxx"
|
||||||
|
Cert = "xxxx"
|
||||||
|
Key = "xxxx"
|
||||||
|
InsecureSkipVerify = false
|
||||||
|
|
||||||
|
[ECS]
|
||||||
|
Domain = "xxxx"
|
||||||
|
ExposedByDefault = true
|
||||||
|
Clusters = []
|
||||||
|
Region = "us-west-2"
|
||||||
|
AccessKeyID = "xxxx"
|
||||||
|
SecretAccessKey = "xxxx"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Show me the code !
|
||||||
|
|
||||||
|
If you want to dig into more details, here is the source code of the collecting system: [collector.go](https://github.com/containous/traefik/blob/master/collector/collector.go)
|
||||||
|
|
||||||
|
By default we anonymize all configuration fields, except fields tagged with `export=true`.
|
||||||
|
|
||||||
|
You can check all fields in the [godoc](https://godoc.org/github.com/containous/traefik/configuration#GlobalConfiguration).
|
||||||
|
|
||||||
|
### How to enable this ?
|
||||||
|
|
||||||
|
You can enable the collecting system by:
|
||||||
|
|
||||||
|
- adding this line in the configuration TOML file:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Send anonymous usage data
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
sendAnonymousUsage = true
|
||||||
|
```
|
||||||
|
|
||||||
|
- adding this flag in the CLI:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./traefik --sendAnonymousUsage=true
|
||||||
|
```
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ I used 4 VMs for the tests with the following configuration:
|
|||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
1. One VM used to launch the benchmarking tool [wrk](https://github.com/wg/wrk)
|
1. One VM used to launch the benchmarking tool [wrk](https://github.com/wg/wrk)
|
||||||
2. One VM for traefik (v1.0.0-beta.416) / nginx (v1.4.6)
|
2. One VM for Traefik (v1.0.0-beta.416) / nginx (v1.4.6)
|
||||||
3. Two VMs for 2 backend servers in go [whoami](https://github.com/emilevauge/whoamI/)
|
3. Two VMs for 2 backend servers in go [whoami](https://github.com/emilevauge/whoamI/)
|
||||||
|
|
||||||
Each VM has been tuned using the following limits:
|
Each VM has been tuned using the following limits:
|
||||||
@@ -65,8 +65,8 @@ http {
|
|||||||
keepalive_requests 10000;
|
keepalive_requests 10000;
|
||||||
types_hash_max_size 2048;
|
types_hash_max_size 2048;
|
||||||
|
|
||||||
open_file_cache max=200000 inactive=300s;
|
open_file_cache max=200000 inactive=300s;
|
||||||
open_file_cache_valid 300s;
|
open_file_cache_valid 300s;
|
||||||
open_file_cache_min_uses 2;
|
open_file_cache_min_uses 2;
|
||||||
open_file_cache_errors on;
|
open_file_cache_errors on;
|
||||||
|
|
||||||
@@ -182,7 +182,8 @@ Requests/sec: 33591.67
|
|||||||
Transfer/sec: 4.97MB
|
Transfer/sec: 4.97MB
|
||||||
```
|
```
|
||||||
|
|
||||||
### traefik:
|
### Traefik:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
wrk -t20 -c1000 -d60s -H "Host: test.traefik" --latency http://IP-traefik:8000/bench
|
wrk -t20 -c1000 -d60s -H "Host: test.traefik" --latency http://IP-traefik:8000/bench
|
||||||
Running 1m test @ http://IP-traefik:8000/bench
|
Running 1m test @ http://IP-traefik:8000/bench
|
||||||
@@ -209,5 +210,5 @@ Not bad for young project :) !
|
|||||||
Some areas of possible improvements:
|
Some areas of possible improvements:
|
||||||
|
|
||||||
- Use [GO_REUSEPORT](https://github.com/kavu/go_reuseport) listener
|
- Use [GO_REUSEPORT](https://github.com/kavu/go_reuseport) listener
|
||||||
- Run a separate server instance per CPU core with `GOMAXPROCS=1` (it appears during benchmarks that there is a lot more context switches with traefik than with nginx)
|
- Run a separate server instance per CPU core with `GOMAXPROCS=1` (it appears during benchmarks that there is a lot more context switches with Traefik than with nginx)
|
||||||
|
|
||||||
|
|||||||
267
docs/configuration/acme.md
Normal file
267
docs/configuration/acme.md
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
# 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).
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Sample entrypoint configuration when using ACME.
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.https]
|
||||||
|
address = ":443"
|
||||||
|
[entryPoints.https.tls]
|
||||||
|
|
||||||
|
# Enable ACME (Let's Encrypt): automatic SSL.
|
||||||
|
[acme]
|
||||||
|
|
||||||
|
# Email address used for registration.
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
#
|
||||||
|
email = "test@traefik.io"
|
||||||
|
|
||||||
|
# File used for certificates storage.
|
||||||
|
#
|
||||||
|
# Optional (Deprecated)
|
||||||
|
#
|
||||||
|
#storageFile = "acme.json"
|
||||||
|
|
||||||
|
# File or key used for certificates storage.
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
#
|
||||||
|
storage = "acme.json"
|
||||||
|
# or `storage = "traefik/acme/account"` if using KV store.
|
||||||
|
|
||||||
|
# Entrypoint to proxy acme challenge/apply certificates to.
|
||||||
|
# WARNING, must point to an entrypoint on port 443
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
#
|
||||||
|
entryPoint = "https"
|
||||||
|
|
||||||
|
# Use a DNS based acme challenge rather than external HTTPS access
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# delayDontCheckDNS = 0
|
||||||
|
|
||||||
|
# If true, display debug log messages from the acme client library.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# acmeLogging = true
|
||||||
|
|
||||||
|
# Enable on demand certificate. (Deprecated)
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# onDemand = true
|
||||||
|
|
||||||
|
# Enable certificate generation on frontends Host rules.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# onHostRule = true
|
||||||
|
|
||||||
|
# CA server to use.
|
||||||
|
# - Uncomment the line to run on the staging let's encrypt server.
|
||||||
|
# - Leave comment to go to prod.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# caServer = "https://acme-staging.api.letsencrypt.org/directory"
|
||||||
|
|
||||||
|
# Domains list.
|
||||||
|
#
|
||||||
|
# [[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"
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
ACME entryPoint has to be relied to the port 443, otherwise ACME Challenges can not be done.
|
||||||
|
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).
|
||||||
|
|
||||||
|
### `storage`
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[acme]
|
||||||
|
# ...
|
||||||
|
storage = "acme.json"
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
File or key used for certificates storage.
|
||||||
|
|
||||||
|
**WARNING** If you use Træfik in Docker, you have 2 options:
|
||||||
|
|
||||||
|
- 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
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
`storage` replaces `storageFile` which is deprecated.
|
||||||
|
|
||||||
|
!!! 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`.
|
||||||
|
`storageFile` will contain the path to the `acme.json` file to migrate.
|
||||||
|
`storage` will contain the key where the certificates will be stored.
|
||||||
|
|
||||||
|
### `dnsProvider`
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[acme]
|
||||||
|
# ...
|
||||||
|
dnsProvider = "digitalocean"
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Use a DNS based acme challenge rather than external HTTPS access, e.g. for a firewalled server.
|
||||||
|
|
||||||
|
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` |
|
||||||
|
|
||||||
|
### `delayDontCheckDNS`
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[acme]
|
||||||
|
# ...
|
||||||
|
delayDontCheckDNS = 0
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
### `onDemand` (Deprecated)
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[acme]
|
||||||
|
# ...
|
||||||
|
onDemand = true
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Enable on demand certificate.
|
||||||
|
|
||||||
|
This will request a certificate from Let's Encrypt during the first TLS handshake for a hostname that does not yet have a certificate.
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
TLS handshakes will be slow when requesting a hostname 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).
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
This option is deprecated.
|
||||||
|
|
||||||
|
### `onHostRule`
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[acme]
|
||||||
|
# ...
|
||||||
|
onHostRule = true
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Enable certificate generation on frontends Host rules.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
### `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.
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
Take note that Let's Encrypt have [rate limiting](https://letsencrypt.org/docs/rate-limits).
|
||||||
|
|
||||||
|
Each domain & SANs will lead to a certificate request.
|
||||||
206
docs/configuration/api.md
Normal file
206
docs/configuration/api.md
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
# API Definition
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# API definition
|
||||||
|
[api]
|
||||||
|
# Name of the related entry point
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "traefik"
|
||||||
|
#
|
||||||
|
entryPoint = "traefik"
|
||||||
|
|
||||||
|
# Enabled Dashboard
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: true
|
||||||
|
#
|
||||||
|
dashboard = true
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
debug = true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Web UI
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
| Path | Method | Description |
|
||||||
|
|-----------------------------------------------------------------|------------------|-------------------------------------------|
|
||||||
|
| `/` | `GET` | Provides a simple HTML frontend of Træfik |
|
||||||
|
| `/health` | `GET` | json health metrics |
|
||||||
|
| `/api` | `GET` | Configuration for all providers |
|
||||||
|
| `/api/providers` | `GET` | Providers |
|
||||||
|
| `/api/providers/{provider}` | `GET`, `PUT` | Get or update provider |
|
||||||
|
| `/api/providers/{provider}/backends` | `GET` | List backends |
|
||||||
|
| `/api/providers/{provider}/backends/{backend}` | `GET` | Get backend |
|
||||||
|
| `/api/providers/{provider}/backends/{backend}/servers` | `GET` | List servers in backend |
|
||||||
|
| `/api/providers/{provider}/backends/{backend}/servers/{server}` | `GET` | Get a server in a backend |
|
||||||
|
| `/api/providers/{provider}/frontends` | `GET` | List frontends |
|
||||||
|
| `/api/providers/{provider}/frontends/{frontend}` | `GET` | Get a frontend |
|
||||||
|
| `/api/providers/{provider}/frontends/{frontend}/routes` | `GET` | List routes in a frontend |
|
||||||
|
| `/api/providers/{provider}/frontends/{frontend}/routes/{route}` | `GET` | Get a route in a frontend |
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
For compatibility reason, when you activate the rest provider, you can use `web` or `rest` as `provider` value.
|
||||||
|
But be careful, in the configuration for all providers the key is still `web`.
|
||||||
|
|
||||||
|
### Provider configurations
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl -s "http://localhost:8080/api" | jq .
|
||||||
|
```
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"file": {
|
||||||
|
"frontends": {
|
||||||
|
"frontend2": {
|
||||||
|
"routes": {
|
||||||
|
"test_2": {
|
||||||
|
"rule": "Path:/test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"backend": "backend1"
|
||||||
|
},
|
||||||
|
"frontend1": {
|
||||||
|
"routes": {
|
||||||
|
"test_1": {
|
||||||
|
"rule": "Host:test.localhost"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"backend": "backend2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"backends": {
|
||||||
|
"backend2": {
|
||||||
|
"loadBalancer": {
|
||||||
|
"method": "drr"
|
||||||
|
},
|
||||||
|
"servers": {
|
||||||
|
"server2": {
|
||||||
|
"weight": 2,
|
||||||
|
"URL": "http://172.17.0.5:80"
|
||||||
|
},
|
||||||
|
"server1": {
|
||||||
|
"weight": 1,
|
||||||
|
"url": "http://172.17.0.4:80"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"backend1": {
|
||||||
|
"loadBalancer": {
|
||||||
|
"method": "wrr"
|
||||||
|
},
|
||||||
|
"circuitBreaker": {
|
||||||
|
"expression": "NetworkErrorRatio() > 0.5"
|
||||||
|
},
|
||||||
|
"servers": {
|
||||||
|
"server2": {
|
||||||
|
"weight": 1,
|
||||||
|
"url": "http://172.17.0.3:80"
|
||||||
|
},
|
||||||
|
"server1": {
|
||||||
|
"weight": 10,
|
||||||
|
"url": "http://172.17.0.2:80"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Health
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl -s "http://localhost:8080/health" | jq .
|
||||||
|
```
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
// Træfik PID
|
||||||
|
"pid": 2458,
|
||||||
|
// Træfik server uptime (formated time)
|
||||||
|
"uptime": "39m6.885931127s",
|
||||||
|
// Træfik server uptime in seconds
|
||||||
|
"uptime_sec": 2346.885931127,
|
||||||
|
// current server date
|
||||||
|
"time": "2015-10-07 18:32:24.362238909 +0200 CEST",
|
||||||
|
// current server date in seconds
|
||||||
|
"unixtime": 1444235544,
|
||||||
|
// count HTTP response status code in realtime
|
||||||
|
"status_code_count": {
|
||||||
|
"502": 1
|
||||||
|
},
|
||||||
|
// count HTTP response status code since Træfik started
|
||||||
|
"total_status_code_count": {
|
||||||
|
"200": 7,
|
||||||
|
"404": 21,
|
||||||
|
"502": 13
|
||||||
|
},
|
||||||
|
// count HTTP response
|
||||||
|
"count": 1,
|
||||||
|
// count HTTP response
|
||||||
|
"total_count": 41,
|
||||||
|
// sum of all response time (formated time)
|
||||||
|
"total_response_time": "35.456865605s",
|
||||||
|
// sum of all response time in seconds
|
||||||
|
"total_response_time_sec": 35.456865605,
|
||||||
|
// average response time (formated time)
|
||||||
|
"average_response_time": "864.8016ms",
|
||||||
|
// average response time in seconds
|
||||||
|
"average_response_time_sec": 0.8648016000000001,
|
||||||
|
|
||||||
|
// request statistics [requires --web.statistics to be set]
|
||||||
|
// ten most recent requests with 4xx and 5xx status codes
|
||||||
|
"recent_errors": [
|
||||||
|
{
|
||||||
|
// status code
|
||||||
|
"status_code": 500,
|
||||||
|
// description of status code
|
||||||
|
"status": "Internal Server Error",
|
||||||
|
// request HTTP method
|
||||||
|
"method": "GET",
|
||||||
|
// request hostname
|
||||||
|
"host": "localhost",
|
||||||
|
// request path
|
||||||
|
"path": "/path",
|
||||||
|
// RFC 3339 formatted date/time
|
||||||
|
"time": "2016-10-21T16:59:15.418495872-07:00"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Metrics
|
||||||
|
|
||||||
|
You can enable Traefik to export internal metrics to different monitoring systems.
|
||||||
|
```toml
|
||||||
|
[api]
|
||||||
|
# ...
|
||||||
|
|
||||||
|
# Enable more detailed statistics.
|
||||||
|
[api.statistics]
|
||||||
|
|
||||||
|
# Number of recent errors logged.
|
||||||
|
#
|
||||||
|
# Default: 10
|
||||||
|
#
|
||||||
|
recentErrors = 10
|
||||||
|
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
| Path | Method | Description |
|
||||||
|
|------------|---------------|-------------------------|
|
||||||
|
| `/metrics` | `GET` | Export internal metrics |
|
||||||
59
docs/configuration/backends/boltdb.md
Normal file
59
docs/configuration/backends/boltdb.md
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# BoltDB Backend
|
||||||
|
|
||||||
|
Træfik can be configured to use BoltDB as a backend configuration.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
################################################################
|
||||||
|
# BoltDB configuration backend
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# Enable BoltDB configuration backend.
|
||||||
|
[boltdb]
|
||||||
|
|
||||||
|
# BoltDB file.
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
# Default: "127.0.0.1:4001"
|
||||||
|
#
|
||||||
|
endpoint = "/my.db"
|
||||||
|
|
||||||
|
# Enable watch BoltDB changes.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: true
|
||||||
|
#
|
||||||
|
watch = true
|
||||||
|
|
||||||
|
# Prefix used for KV store.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "/traefik"
|
||||||
|
#
|
||||||
|
prefix = "/traefik"
|
||||||
|
|
||||||
|
# Override default configuration template.
|
||||||
|
# For advanced users :)
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
filename = "boltdb.tmpl"
|
||||||
|
|
||||||
|
# Use BoltDB user/pass authentication.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# username = foo
|
||||||
|
# password = bar
|
||||||
|
|
||||||
|
# Enable BoltDB TLS connection.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# [boltdb.tls]
|
||||||
|
# ca = "/etc/ssl/ca.crt"
|
||||||
|
# cert = "/etc/ssl/boltdb.crt"
|
||||||
|
# key = "/etc/ssl/boltdb.key"
|
||||||
|
# insecureskipverify = true
|
||||||
|
```
|
||||||
|
|
||||||
|
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||||
157
docs/configuration/backends/consul.md
Normal file
157
docs/configuration/backends/consul.md
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
# Consul Backend
|
||||||
|
|
||||||
|
## Consul Key-Value backend
|
||||||
|
|
||||||
|
Træfik can be configured to use Consul as a backend configuration.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
################################################################
|
||||||
|
# Consul KV configuration backend
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# Enable Consul KV configuration backend.
|
||||||
|
[consul]
|
||||||
|
|
||||||
|
# Consul server endpoint.
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
# Default: "127.0.0.1:8500"
|
||||||
|
#
|
||||||
|
endpoint = "127.0.0.1:8500"
|
||||||
|
|
||||||
|
# Enable watch Consul changes.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: true
|
||||||
|
#
|
||||||
|
watch = true
|
||||||
|
|
||||||
|
# Prefix used for KV store.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: traefik
|
||||||
|
#
|
||||||
|
prefix = "traefik"
|
||||||
|
|
||||||
|
# Override default configuration template.
|
||||||
|
# For advanced users :)
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# filename = "consul.tmpl"
|
||||||
|
|
||||||
|
# Use Consul user/pass authentication.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# username = foo
|
||||||
|
# password = bar
|
||||||
|
|
||||||
|
# Enable Consul TLS connection.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# [consul.tls]
|
||||||
|
# ca = "/etc/ssl/ca.crt"
|
||||||
|
# cert = "/etc/ssl/consul.crt"
|
||||||
|
# key = "/etc/ssl/consul.key"
|
||||||
|
# insecureskipverify = true
|
||||||
|
```
|
||||||
|
|
||||||
|
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-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.
|
||||||
|
|
||||||
|
## Consul Catalog backend
|
||||||
|
|
||||||
|
Træfik can be configured to use service discovery catalog of Consul as a backend configuration.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
################################################################
|
||||||
|
# Consul Catalog configuration backend
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# Enable Consul Catalog configuration backend.
|
||||||
|
[consulCatalog]
|
||||||
|
|
||||||
|
# Consul server endpoint.
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
# Default: "127.0.0.1:8500"
|
||||||
|
#
|
||||||
|
endpoint = "127.0.0.1:8500"
|
||||||
|
|
||||||
|
# Expose Consul catalog services by default in Traefik.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: true
|
||||||
|
#
|
||||||
|
exposedByDefault = false
|
||||||
|
|
||||||
|
# Default domain used.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
domain = "consul.localhost"
|
||||||
|
|
||||||
|
# Prefix for Consul catalog tags.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "traefik"
|
||||||
|
#
|
||||||
|
prefix = "traefik"
|
||||||
|
|
||||||
|
# Default frontEnd Rule for Consul services.
|
||||||
|
#
|
||||||
|
# The format is a Go Template with:
|
||||||
|
# - ".ServiceName", ".Domain" and ".Attributes" available
|
||||||
|
# - "getTag(name, tags, defaultValue)", "hasTag(name, tags)" and "getAttribute(name, tags, defaultValue)" functions are available
|
||||||
|
# - "getAttribute(...)" function uses prefixed tag names based on "prefix" value
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "Host:{{.ServiceName}}.{{.Domain}}"
|
||||||
|
#
|
||||||
|
#frontEndRule = "Host:{{.ServiceName}}.{{Domain}}"
|
||||||
|
```
|
||||||
|
|
||||||
|
This backend 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).
|
||||||
|
|
||||||
|
### 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) |
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
If you want that Træfik uses Consul tags correctly you need to defined them like that:
|
||||||
|
```json
|
||||||
|
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
|
||||||
|
bla.enable=true
|
||||||
|
bla.tags=api
|
||||||
|
bla.tags=external
|
||||||
|
```
|
||||||
234
docs/configuration/backends/docker.md
Normal file
234
docs/configuration/backends/docker.md
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
|
||||||
|
# Docker Backend
|
||||||
|
|
||||||
|
Træfik can be configured to use Docker as a backend configuration.
|
||||||
|
|
||||||
|
## Docker
|
||||||
|
|
||||||
|
```toml
|
||||||
|
################################################################
|
||||||
|
# Docker configuration backend
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# Enable Docker configuration backend.
|
||||||
|
[docker]
|
||||||
|
|
||||||
|
# Docker server endpoint. Can be a tcp or a unix socket endpoint.
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
#
|
||||||
|
endpoint = "unix:///var/run/docker.sock"
|
||||||
|
|
||||||
|
# Default domain used.
|
||||||
|
# Can be overridden by setting the "traefik.domain" label on a container.
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
#
|
||||||
|
domain = "docker.localhost"
|
||||||
|
|
||||||
|
# Enable watch docker changes.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
watch = true
|
||||||
|
|
||||||
|
# Override default configuration template.
|
||||||
|
# For advanced users :)
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# filename = "docker.tmpl"
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# Use the IP address from the binded port instead of the inner network one.
|
||||||
|
# For specific use-case :)
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
usebindportip = true
|
||||||
|
|
||||||
|
# Use Swarm Mode services as data provider.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
swarmmode = false
|
||||||
|
|
||||||
|
# Enable docker TLS connection.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# [docker.tls]
|
||||||
|
# ca = "/etc/ssl/ca.crt"
|
||||||
|
# cert = "/etc/ssl/docker.crt"
|
||||||
|
# key = "/etc/ssl/docker.key"
|
||||||
|
# insecureskipverify = true
|
||||||
|
```
|
||||||
|
|
||||||
|
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||||
|
|
||||||
|
|
||||||
|
## Docker Swarm Mode
|
||||||
|
|
||||||
|
```toml
|
||||||
|
################################################################
|
||||||
|
# Docker Swarmmode configuration backend
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# Enable Docker configuration backend.
|
||||||
|
[docker]
|
||||||
|
|
||||||
|
# Docker server endpoint.
|
||||||
|
# Can be a tcp or a unix socket endpoint.
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
# Default: "unix:///var/run/docker.sock"
|
||||||
|
#
|
||||||
|
endpoint = "tcp://127.0.0.1:2375"
|
||||||
|
|
||||||
|
# Default domain used.
|
||||||
|
# Can be overridden by setting the "traefik.domain" label on a services.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: ""
|
||||||
|
#
|
||||||
|
domain = "docker.localhost"
|
||||||
|
|
||||||
|
# Enable watch docker changes.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: true
|
||||||
|
#
|
||||||
|
watch = true
|
||||||
|
|
||||||
|
# Use Docker Swarm Mode as data provider.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
swarmmode = true
|
||||||
|
|
||||||
|
# Override default configuration template.
|
||||||
|
# For advanced users :)
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# filename = "docker.tmpl"
|
||||||
|
|
||||||
|
# Expose services by default in Traefik.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: true
|
||||||
|
#
|
||||||
|
exposedbydefault = false
|
||||||
|
|
||||||
|
# Enable docker TLS connection.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# [docker.tls]
|
||||||
|
# ca = "/etc/ssl/ca.crt"
|
||||||
|
# cert = "/etc/ssl/docker.crt"
|
||||||
|
# key = "/etc/ssl/docker.key"
|
||||||
|
# insecureskipverify = true
|
||||||
|
```
|
||||||
|
|
||||||
|
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||||
|
|
||||||
|
## Labels: overriding default behaviour
|
||||||
|
|
||||||
|
### On Containers
|
||||||
|
|
||||||
|
Labels can be used on containers to override default behaviour.
|
||||||
|
|
||||||
|
| 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. 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. |
|
||||||
|
| `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`. |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### 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.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.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
|
||||||
|
|
||||||
|
Services labels can be used for overriding default behaviour
|
||||||
|
|
||||||
|
| 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`. |
|
||||||
|
|
||||||
|
|
||||||
|
!!! 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).
|
||||||
|
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`.
|
||||||
|
More details in this [example](/user-guide/docker-and-lets-encrypt/#labels).
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
when running inside a container, Træfik will need network access through:
|
||||||
|
|
||||||
|
`docker network connect <network> <traefik-container>`
|
||||||
71
docs/configuration/backends/dynamodb.md
Normal file
71
docs/configuration/backends/dynamodb.md
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
# DynamoDB Backend
|
||||||
|
|
||||||
|
Træfik can be configured to use Amazon DynamoDB as a backend configuration.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
```toml
|
||||||
|
################################################################
|
||||||
|
# DynamoDB configuration backend
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# Enable DynamoDB configuration backend.
|
||||||
|
[dynamodb]
|
||||||
|
|
||||||
|
# Region to use when connecting to AWS.
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
#
|
||||||
|
region = "us-west-1"
|
||||||
|
|
||||||
|
# DyanmoDB Table Name.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "traefik"
|
||||||
|
#
|
||||||
|
tableName = "traefik"
|
||||||
|
|
||||||
|
# Enable watch DynamoDB changes.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: true
|
||||||
|
#
|
||||||
|
watch = true
|
||||||
|
|
||||||
|
# Polling interval (in seconds).
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: 15
|
||||||
|
#
|
||||||
|
refreshSeconds = 15
|
||||||
|
|
||||||
|
# AccessKeyID to use when connecting to AWS.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
accessKeyID = "abc"
|
||||||
|
|
||||||
|
# SecretAccessKey to use when connecting to AWS.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
secretAccessKey = "123"
|
||||||
|
|
||||||
|
# Endpoint of local dynamodb instance for testing?
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
endpoint = "http://localhost:8080"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Table Items
|
||||||
|
|
||||||
|
Items in the `dynamodb` table must have three attributes:
|
||||||
|
|
||||||
|
- `id` (string): The id is the primary key.
|
||||||
|
- `name`(string): The name is used as the name of the frontend or backend.
|
||||||
|
- `frontend` or `backend` (map): This attribute's structure matches exactly the structure of a Frontend or Backend type in Traefik.
|
||||||
|
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.
|
||||||
|
|
||||||
143
docs/configuration/backends/ecs.md
Normal file
143
docs/configuration/backends/ecs.md
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
# ECS Backend
|
||||||
|
|
||||||
|
Træfik can be configured to use Amazon ECS as a backend configuration.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
```toml
|
||||||
|
################################################################
|
||||||
|
# ECS configuration backend
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# Enable ECS configuration backend.
|
||||||
|
[ecs]
|
||||||
|
|
||||||
|
# ECS Cluster Name.
|
||||||
|
#
|
||||||
|
# DEPRECATED - Please use `clusters`.
|
||||||
|
#
|
||||||
|
cluster = "default"
|
||||||
|
|
||||||
|
# ECS Clusters Name.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: ["default"]
|
||||||
|
#
|
||||||
|
clusters = ["default"]
|
||||||
|
|
||||||
|
# Enable watch ECS changes.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: true
|
||||||
|
#
|
||||||
|
watch = true
|
||||||
|
|
||||||
|
# Default domain used.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: ""
|
||||||
|
#
|
||||||
|
domain = "ecs.localhost"
|
||||||
|
|
||||||
|
# Enable auto discover ECS clusters.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
autoDiscoverClusters = false
|
||||||
|
|
||||||
|
# Polling interval (in seconds).
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: 15
|
||||||
|
#
|
||||||
|
refreshSeconds = 15
|
||||||
|
|
||||||
|
# Expose ECS services by default in Traefik.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: true
|
||||||
|
#
|
||||||
|
exposedByDefault = false
|
||||||
|
|
||||||
|
# Region to use when connecting to AWS.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
region = "us-east-1"
|
||||||
|
|
||||||
|
# AccessKeyID to use when connecting to AWS.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
accessKeyID = "abc"
|
||||||
|
|
||||||
|
# SecretAccessKey to use when connecting to AWS.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
secretAccessKey = "123"
|
||||||
|
|
||||||
|
# Override default configuration template.
|
||||||
|
# For advanced users :)
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# filename = "ecs.tmpl"
|
||||||
|
```
|
||||||
|
|
||||||
|
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`.
|
||||||
|
- EC2 instance role or ECS task role
|
||||||
|
|
||||||
|
## Policy
|
||||||
|
|
||||||
|
Træfik needs the following policy to read ECS information:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Version": "2012-10-17",
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Sid": "TraefikECSReadAccess",
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Action": [
|
||||||
|
"ecs:ListClusters",
|
||||||
|
"ecs:DescribeClusters",
|
||||||
|
"ecs:ListTasks",
|
||||||
|
"ecs:DescribeTasks",
|
||||||
|
"ecs:DescribeContainerInstances",
|
||||||
|
"ecs:DescribeTaskDefinition",
|
||||||
|
"ec2:DescribeInstances"
|
||||||
|
],
|
||||||
|
"Resource": [
|
||||||
|
"*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Labels: overriding default behaviour
|
||||||
|
|
||||||
|
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` |
|
||||||
75
docs/configuration/backends/etcd.md
Normal file
75
docs/configuration/backends/etcd.md
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# Etcd Backend
|
||||||
|
|
||||||
|
Træfik can be configured to use Etcd as a backend configuration.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
################################################################
|
||||||
|
# Etcd configuration backend
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# Enable Etcd configuration backend.
|
||||||
|
[etcd]
|
||||||
|
|
||||||
|
# Etcd server endpoint.
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
# Default: "127.0.0.1:2379"
|
||||||
|
#
|
||||||
|
endpoint = "127.0.0.1:2379"
|
||||||
|
|
||||||
|
# Enable watch Etcd changes.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: true
|
||||||
|
#
|
||||||
|
watch = true
|
||||||
|
|
||||||
|
# Prefix used for KV store.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "/traefik"
|
||||||
|
#
|
||||||
|
prefix = "/traefik"
|
||||||
|
|
||||||
|
# Force to use API V3 (otherwise still use API V2)
|
||||||
|
#
|
||||||
|
# Deprecated
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
useAPIV3 = true
|
||||||
|
|
||||||
|
|
||||||
|
# Override default configuration template.
|
||||||
|
# For advanced users :)
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# filename = "etcd.tmpl"
|
||||||
|
|
||||||
|
# Use etcd user/pass authentication.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# username = foo
|
||||||
|
# password = bar
|
||||||
|
|
||||||
|
# Enable etcd TLS connection.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# [etcd.tls]
|
||||||
|
# ca = "/etc/ssl/ca.crt"
|
||||||
|
# cert = "/etc/ssl/etcd.crt"
|
||||||
|
# key = "/etc/ssl/etcd.key"
|
||||||
|
# insecureskipverify = true
|
||||||
|
```
|
||||||
|
|
||||||
|
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-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.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
The option `useAPIV3` allows using Etcd API V3 only if it's set to true.
|
||||||
|
This option is **deprecated** and API V2 won't be supported in the future.
|
||||||
32
docs/configuration/backends/eureka.md
Normal file
32
docs/configuration/backends/eureka.md
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# Eureka Backend
|
||||||
|
|
||||||
|
Træfik can be configured to use Eureka as a backend configuration.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
################################################################
|
||||||
|
# Eureka configuration backend
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# Enable Eureka configuration backend.
|
||||||
|
[eureka]
|
||||||
|
|
||||||
|
# Eureka server endpoint.
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
#
|
||||||
|
endpoint = "http://my.eureka.server/eureka"
|
||||||
|
|
||||||
|
# Override default configuration time between refresh.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: 30s
|
||||||
|
#
|
||||||
|
delay = "1m"
|
||||||
|
|
||||||
|
# Override default configuration template.
|
||||||
|
# For advanced users :)
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# filename = "eureka.tmpl"
|
||||||
|
```
|
||||||
187
docs/configuration/backends/file.md
Normal file
187
docs/configuration/backends/file.md
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
# File Backends
|
||||||
|
|
||||||
|
Like any other reverse proxy, Træfik can be configured with a file.
|
||||||
|
|
||||||
|
You have three 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)
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
Add your configuration at the end of the global configuration file `traefik.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
defaultEntryPoints = ["http", "https"]
|
||||||
|
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.http]
|
||||||
|
address = ":80"
|
||||||
|
[entryPoints.http.redirect]
|
||||||
|
entryPoint = "https"
|
||||||
|
[entryPoints.https]
|
||||||
|
address = ":443"
|
||||||
|
[entryPoints.https.tls]
|
||||||
|
[[entryPoints.https.tls.certificates]]
|
||||||
|
certFile = "integration/fixtures/https/snitest.org.cert"
|
||||||
|
keyFile = "integration/fixtures/https/snitest.org.key"
|
||||||
|
|
||||||
|
[file]
|
||||||
|
|
||||||
|
# rules
|
||||||
|
[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.maxconn]
|
||||||
|
amount = 10
|
||||||
|
extractorfunc = "request.host"
|
||||||
|
[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
|
||||||
|
|
||||||
|
[frontends]
|
||||||
|
[frontends.frontend1]
|
||||||
|
backend = "backend2"
|
||||||
|
[frontends.frontend1.routes.test_1]
|
||||||
|
rule = "Host:test.localhost"
|
||||||
|
|
||||||
|
[frontends.frontend2]
|
||||||
|
backend = "backend1"
|
||||||
|
passHostHeader = true
|
||||||
|
priority = 10
|
||||||
|
|
||||||
|
# restrict access to this frontend to the specified list of IPv4/IPv6 CIDR Nets
|
||||||
|
# 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.
|
||||||
|
whitelistSourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
|
||||||
|
|
||||||
|
entrypoints = ["https"] # overrides defaultEntryPoints
|
||||||
|
[frontends.frontend2.routes.test_1]
|
||||||
|
rule = "Host:{subdomain:[a-z]+}.localhost"
|
||||||
|
|
||||||
|
[frontends.frontend3]
|
||||||
|
entrypoints = ["http", "https"] # overrides defaultEntryPoints
|
||||||
|
backend = "backend2"
|
||||||
|
rule = "Path:/test"
|
||||||
|
|
||||||
|
# HTTPS certificate
|
||||||
|
[[tlsConfiguration]]
|
||||||
|
entryPoints = ["https"]
|
||||||
|
[tlsConfiguration.certificate]
|
||||||
|
certFile = "integration/fixtures/https/snitest.com.cert"
|
||||||
|
keyFile = "integration/fixtures/https/snitest.com.key"
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! 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
|
||||||
|
|
||||||
|
Put your rules in a separate file, for example `rules.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# traefik.toml
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.http]
|
||||||
|
address = ":80"
|
||||||
|
[entryPoints.http.redirect]
|
||||||
|
entryPoint = "https"
|
||||||
|
[entryPoints.https]
|
||||||
|
address = ":443"
|
||||||
|
[entryPoints.https.tls]
|
||||||
|
|
||||||
|
[file]
|
||||||
|
filename = "rules.toml"
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# rules.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.maxconn]
|
||||||
|
amount = 10
|
||||||
|
extractorfunc = "request.host"
|
||||||
|
[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
|
||||||
|
|
||||||
|
[frontends]
|
||||||
|
[frontends.frontend1]
|
||||||
|
backend = "backend2"
|
||||||
|
[frontends.frontend1.routes.test_1]
|
||||||
|
rule = "Host:test.localhost"
|
||||||
|
[frontends.frontend2]
|
||||||
|
backend = "backend1"
|
||||||
|
passHostHeader = true
|
||||||
|
priority = 10
|
||||||
|
entrypoints = ["https"] # overrides defaultEntryPoints
|
||||||
|
[frontends.frontend2.routes.test_1]
|
||||||
|
rule = "Host:{subdomain:[a-z]+}.localhost"
|
||||||
|
[frontends.frontend3]
|
||||||
|
entrypoints = ["http", "https"] # overrides defaultEntryPoints
|
||||||
|
backend = "backend2"
|
||||||
|
rule = "Path:/test"
|
||||||
|
|
||||||
|
# HTTPS certificate
|
||||||
|
[[tlsConfiguration]]
|
||||||
|
entryPoints = ["https"]
|
||||||
|
[tlsConfiguration.certificate]
|
||||||
|
certFile = "integration/fixtures/https/snitest.com.cert"
|
||||||
|
keyFile = "integration/fixtures/https/snitest.com.key"
|
||||||
|
|
||||||
|
[[tlsConfiguration]]
|
||||||
|
entryPoints = ["https"]
|
||||||
|
[[tlsConfiguration.certificates]]
|
||||||
|
certFile = "integration/fixtures/https/snitest.org.cert"
|
||||||
|
keyFile = "integration/fixtures/https/snitest.org.key"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Multiple `.toml` Files
|
||||||
|
|
||||||
|
You could have multiple `.toml` files in a directory (and recursively in its sub-directories):
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[file]
|
||||||
|
directory = "/path/to/config/"
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want Træfik to watch file changes automatically, just add:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[file]
|
||||||
|
watch = true
|
||||||
|
```
|
||||||
185
docs/configuration/backends/kubernetes.md
Normal file
185
docs/configuration/backends/kubernetes.md
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
# Kubernetes Ingress Backend
|
||||||
|
|
||||||
|
Træfik can be configured to use Kubernetes Ingress as a backend configuration.
|
||||||
|
|
||||||
|
See also [Kubernetes user guide](/user-guide/kubernetes).
|
||||||
|
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
```toml
|
||||||
|
################################################################
|
||||||
|
# Kubernetes Ingress configuration backend
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# Enable Kubernetes Ingress configuration backend.
|
||||||
|
[kubernetes]
|
||||||
|
|
||||||
|
# Kubernetes server endpoint.
|
||||||
|
#
|
||||||
|
# Optional for in-cluster configuration, required otherwise.
|
||||||
|
# Default: empty
|
||||||
|
#
|
||||||
|
# endpoint = "http://localhost:8080"
|
||||||
|
|
||||||
|
# Bearer token used for the Kubernetes client configuration.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: empty
|
||||||
|
#
|
||||||
|
# token = "my token"
|
||||||
|
|
||||||
|
# Path to the certificate authority file.
|
||||||
|
# Used for the Kubernetes client configuration.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: empty
|
||||||
|
#
|
||||||
|
# certAuthFilePath = "/my/ca.crt"
|
||||||
|
|
||||||
|
# Array of namespaces to watch.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: all namespaces (empty array).
|
||||||
|
#
|
||||||
|
# namespaces = ["default", "production"]
|
||||||
|
|
||||||
|
# Ingress label selector to identify Ingress objects that should be processed.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: empty (process all Ingresses)
|
||||||
|
#
|
||||||
|
# labelselector = "A and not B"
|
||||||
|
|
||||||
|
# Disable PassHost Headers.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
# disablePassHostHeaders = true
|
||||||
|
|
||||||
|
# Enable PassTLSCert Headers.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
# enablePassTLSCert = true
|
||||||
|
|
||||||
|
# Override default configuration template.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: <built-in template>
|
||||||
|
#
|
||||||
|
# filename = "kubernetes.tmpl"
|
||||||
|
```
|
||||||
|
|
||||||
|
### `endpoint`
|
||||||
|
|
||||||
|
The Kubernetes server endpoint.
|
||||||
|
|
||||||
|
When deployed as a replication controller in Kubernetes, Traefik will use the environment variables `KUBERNETES_SERVICE_HOST` and `KUBERNETES_SERVICE_PORT` to construct the endpoint.
|
||||||
|
|
||||||
|
Secure token will be found in `/var/run/secrets/kubernetes.io/serviceaccount/token` and SSL CA cert in `/var/run/secrets/kubernetes.io/serviceaccount/ca.crt`
|
||||||
|
|
||||||
|
The endpoint may be given to override the environment variable values.
|
||||||
|
|
||||||
|
When the environment variables are not found, Traefik will try to connect to the Kubernetes API server with an external-cluster client.
|
||||||
|
In this case, the endpoint is required.
|
||||||
|
Specifically, it may be set to the URL used by `kubectl proxy` to connect to a Kubernetes cluster from localhost.
|
||||||
|
|
||||||
|
### `labelselector`
|
||||||
|
|
||||||
|
Ingress label selector to identify Ingress objects that should be processed.
|
||||||
|
|
||||||
|
See [label-selectors](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors) for details.
|
||||||
|
|
||||||
|
|
||||||
|
## Annotations
|
||||||
|
|
||||||
|
Annotations can be used on containers to override default behaviour for the whole Ingress resource:
|
||||||
|
|
||||||
|
- `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`.
|
||||||
|
|
||||||
|
!!! 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).
|
||||||
|
|
||||||
|
|
||||||
|
Annotations can be used on the Kubernetes service to override default behaviour:
|
||||||
|
|
||||||
|
- `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)
|
||||||
|
|
||||||
|
Additionally, an annotation can be used on Kubernetes services to set the [circuit breaker expression](/basics/#backends) for a backend.
|
||||||
|
|
||||||
|
- `traefik.backend.circuitbreaker: <expression>`
|
||||||
|
Set the circuit breaker expression for the backend. Default: `nil`.
|
||||||
|
|
||||||
|
As known from nginx when used as Kubernetes Ingress Controller, a list of IP-Ranges which are allowed to access can be configured by using an ingress annotation:
|
||||||
|
|
||||||
|
- `ingress.kubernetes.io/whitelist-source-range: "1.2.3.0/24, fe80::/16"`
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
#### Security annotations
|
||||||
|
|
||||||
|
The following security annotations can be applied to the ingress object to add security features:
|
||||||
|
|
||||||
|
| 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. |
|
||||||
|
| `ngress.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. |
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
|
||||||
|
Is possible to add additional authentication annotations in the Ingress rule.
|
||||||
|
The source of the authentication is a secret that contains usernames and passwords inside the key auth.
|
||||||
|
|
||||||
|
- `ingress.kubernetes.io/auth-type`: `basic`
|
||||||
|
- `ingress.kubernetes.io/auth-secret`: `mysecret`
|
||||||
|
Contains the usernames and passwords with access to the paths defined in the Ingress Rule.
|
||||||
|
|
||||||
|
The secret must be created in the same namespace as the Ingress rule.
|
||||||
|
|
||||||
|
Limitations:
|
||||||
|
|
||||||
|
- Basic authentication only.
|
||||||
|
- Realm not configurable; only `traefik` default.
|
||||||
|
- Secret must contain only single file.
|
||||||
198
docs/configuration/backends/marathon.md
Normal file
198
docs/configuration/backends/marathon.md
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
# Marathon Backend
|
||||||
|
|
||||||
|
Træfik can be configured to use Marathon as a backend configuration.
|
||||||
|
|
||||||
|
See also [Marathon user guide](/user-guide/marathon).
|
||||||
|
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
```toml
|
||||||
|
################################################################
|
||||||
|
# Mesos/Marathon configuration backend
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# Enable Marathon configuration backend.
|
||||||
|
[marathon]
|
||||||
|
|
||||||
|
# Marathon server endpoint.
|
||||||
|
# You can also specify multiple endpoint for Marathon:
|
||||||
|
# endpoint = "http://10.241.1.71:8080,10.241.1.72:8080,10.241.1.73:8080"
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
# Default: "http://127.0.0.1:8080"
|
||||||
|
#
|
||||||
|
endpoint = "http://127.0.0.1:8080"
|
||||||
|
|
||||||
|
# Enable watch Marathon changes.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: true
|
||||||
|
#
|
||||||
|
watch = true
|
||||||
|
|
||||||
|
# Default domain used.
|
||||||
|
# Can be overridden by setting the "traefik.domain" label on an application.
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
#
|
||||||
|
domain = "marathon.localhost"
|
||||||
|
|
||||||
|
# Override default configuration template.
|
||||||
|
# For advanced users :)
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# filename = "marathon.tmpl"
|
||||||
|
|
||||||
|
# Expose Marathon apps by default in Traefik.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: true
|
||||||
|
#
|
||||||
|
# exposedByDefault = false
|
||||||
|
|
||||||
|
# Convert Marathon groups to subdomains.
|
||||||
|
# Default behavior: /foo/bar/myapp => foo-bar-myapp.{defaultDomain}
|
||||||
|
# with groupsAsSubDomains enabled: /foo/bar/myapp => myapp.bar.foo.{defaultDomain}
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
# groupsAsSubDomains = true
|
||||||
|
|
||||||
|
# Enable compatibility with marathon-lb labels.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
# marathonLBCompatibility = true
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
# i.e. "rack_id:CLUSTER:rack-1", with all constraint groups concatenated together using ":"
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
# filterMarathonConstraints = true
|
||||||
|
|
||||||
|
# Enable Marathon basic authentication.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# [marathon.basic]
|
||||||
|
# httpBasicAuthUser = "foo"
|
||||||
|
# httpBasicPassword = "bar"
|
||||||
|
|
||||||
|
# TLS client configuration. https://golang.org/pkg/crypto/tls/#Config
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# [marathon.TLS]
|
||||||
|
# CA = "/etc/ssl/ca.crt"
|
||||||
|
# Cert = "/etc/ssl/marathon.cert"
|
||||||
|
# Key = "/etc/ssl/marathon.key"
|
||||||
|
# InsecureSkipVerify = true
|
||||||
|
|
||||||
|
# DCOSToken for DCOS environment.
|
||||||
|
# This will override the Authorization header.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# dcosToken = "xxxxxx"
|
||||||
|
|
||||||
|
# Override DialerTimeout.
|
||||||
|
# Amount of time to allow the Marathon provider to wait to open a TCP connection
|
||||||
|
# to a Marathon master.
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "60s"
|
||||||
|
#
|
||||||
|
# dialerTimeout = "60s"
|
||||||
|
|
||||||
|
# Set the TCP Keep Alive interval for the Marathon HTTP Client.
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "10s"
|
||||||
|
#
|
||||||
|
# keepAlive = "10s"
|
||||||
|
|
||||||
|
# By default, a task's IP address (as returned by the Marathon API) is used as
|
||||||
|
# backend server if an IP-per-task configuration can be found; otherwise, the
|
||||||
|
# name of the host running the task is used.
|
||||||
|
# The latter behavior can be enforced by enabling this switch.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
# forceTaskHostname = true
|
||||||
|
|
||||||
|
# Applications may define readiness checks which are probed by Marathon during
|
||||||
|
# deployments periodically and the results exposed via the API.
|
||||||
|
# Enabling the following parameter causes Traefik to filter out tasks
|
||||||
|
# whose readiness checks have not succeeded.
|
||||||
|
# Note that the checks are only valid at deployment times.
|
||||||
|
# See the Marathon guide for details.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
# respectReadinessChecks = true
|
||||||
|
```
|
||||||
|
|
||||||
|
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||||
|
|
||||||
|
|
||||||
|
## Labels: overriding default behaviour
|
||||||
|
|
||||||
|
### On Containers
|
||||||
|
|
||||||
|
Labels can be used on containers to override default behaviour:
|
||||||
|
|
||||||
|
| 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`. |
|
||||||
|
|
||||||
|
### On Services
|
||||||
|
|
||||||
|
If several ports need to be exposed from a container, the services labels can be used:
|
||||||
|
|
||||||
|
| 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`. |
|
||||||
93
docs/configuration/backends/mesos.md
Normal file
93
docs/configuration/backends/mesos.md
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
# Mesos Generic Backend
|
||||||
|
|
||||||
|
Træfik can be configured to use Mesos as a backend configuration.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
################################################################
|
||||||
|
# Mesos configuration backend
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# Enable Mesos configuration backend.
|
||||||
|
[mesos]
|
||||||
|
|
||||||
|
# Mesos server endpoint.
|
||||||
|
# You can also specify multiple endpoint for Mesos:
|
||||||
|
# endpoint = "192.168.35.40:5050,192.168.35.41:5050,192.168.35.42:5050"
|
||||||
|
# endpoint = "zk://192.168.35.20:2181,192.168.35.21:2181,192.168.35.22:2181/mesos"
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
# Default: "http://127.0.0.1:5050"
|
||||||
|
#
|
||||||
|
endpoint = "http://127.0.0.1:8080"
|
||||||
|
|
||||||
|
# Enable watch Mesos changes.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: true
|
||||||
|
#
|
||||||
|
watch = true
|
||||||
|
|
||||||
|
# Default domain used.
|
||||||
|
# Can be overridden by setting the "traefik.domain" label on an application.
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
#
|
||||||
|
domain = "mesos.localhost"
|
||||||
|
|
||||||
|
# Override default configuration template.
|
||||||
|
# For advanced users :)
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# filename = "mesos.tmpl"
|
||||||
|
|
||||||
|
# Expose Mesos apps by default in Traefik.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: true
|
||||||
|
#
|
||||||
|
# ExposedByDefault = false
|
||||||
|
|
||||||
|
# TLS client configuration. https://golang.org/pkg/crypto/tls/#Config
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# [mesos.TLS]
|
||||||
|
# InsecureSkipVerify = true
|
||||||
|
|
||||||
|
# Zookeeper timeout (in seconds).
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: 30
|
||||||
|
#
|
||||||
|
# ZkDetectionTimeout = 30
|
||||||
|
|
||||||
|
# Polling interval (in seconds).
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: 30
|
||||||
|
#
|
||||||
|
# RefreshSeconds = 30
|
||||||
|
|
||||||
|
# IP sources (e.g. host, docker, mesos, rkt).
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# IPSources = "host"
|
||||||
|
|
||||||
|
# HTTP Timeout (in seconds).
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: 30
|
||||||
|
#
|
||||||
|
# StateTimeoutSecond = "30"
|
||||||
|
|
||||||
|
# Convert groups to subdomains.
|
||||||
|
# Default behavior: /foo/bar/myapp => foo-bar-myapp.{defaultDomain}
|
||||||
|
# with groupsAsSubDomains enabled: /foo/bar/myapp => myapp.bar.foo.{defaultDomain}
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
# groupsAsSubDomains = true
|
||||||
|
```
|
||||||
140
docs/configuration/backends/rancher.md
Normal file
140
docs/configuration/backends/rancher.md
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
# Rancher Backend
|
||||||
|
|
||||||
|
Træfik can be configured to use Rancher as a backend configuration.
|
||||||
|
|
||||||
|
## Global Configuration
|
||||||
|
|
||||||
|
```toml
|
||||||
|
################################################################
|
||||||
|
# Rancher configuration backend
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# Enable Rancher configuration backend.
|
||||||
|
[rancher]
|
||||||
|
|
||||||
|
# Default domain used.
|
||||||
|
# Can be overridden by setting the "traefik.domain" label on an service.
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
#
|
||||||
|
domain = "rancher.localhost"
|
||||||
|
|
||||||
|
# Enable watch Rancher changes.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: true
|
||||||
|
#
|
||||||
|
watch = true
|
||||||
|
|
||||||
|
# Polling interval (in seconds).
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: 15
|
||||||
|
#
|
||||||
|
refreshSeconds = 15
|
||||||
|
|
||||||
|
# Expose Rancher services by default in Traefik.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: true
|
||||||
|
#
|
||||||
|
exposedByDefault = false
|
||||||
|
|
||||||
|
# Filter services with unhealthy states and inactive states.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
enableServiceHealthFilter = true
|
||||||
|
```
|
||||||
|
|
||||||
|
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
|
||||||
|
|
||||||
|
## Rancher Metadata Service
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Enable Rancher metadata service configuration backend instead of the API
|
||||||
|
# configuration backend.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
[rancher.metadata]
|
||||||
|
|
||||||
|
# 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
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
intervalPoll = true
|
||||||
|
|
||||||
|
# Prefix used for accessing the Rancher metadata service.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "/latest"
|
||||||
|
#
|
||||||
|
prefix = "/2016-07-29"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rancher API
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Enable Rancher API configuration backend.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: true
|
||||||
|
#
|
||||||
|
[rancher.api]
|
||||||
|
|
||||||
|
# Endpoint to use when connecting to the Rancher API.
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
endpoint = "http://rancherserver.example.com/v1"
|
||||||
|
|
||||||
|
# AccessKey to use when connecting to the Rancher API.
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
accessKey = "XXXXXXXXXXXXXXXXXXXX"
|
||||||
|
|
||||||
|
# SecretKey to use when connecting to the Rancher API.
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
secretKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
If Traefik needs access to the Rancher API, you need to set the `endpoint`, `accesskey` and `secretkey` parameters.
|
||||||
|
|
||||||
|
To enable Traefik to fetch information about the Environment it's deployed in only, you need to create an `Environment API Key`.
|
||||||
|
This can be found within the API Key advanced options.
|
||||||
|
|
||||||
|
Add these labels to traefik docker deployment to autogenerated these values:
|
||||||
|
```
|
||||||
|
io.rancher.container.agent.role: environment
|
||||||
|
io.rancher.container.create_agent: true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Labels: overriding default behaviour
|
||||||
|
|
||||||
|
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.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) |
|
||||||
91
docs/configuration/backends/rest.md
Normal file
91
docs/configuration/backends/rest.md
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
# Rest Backend
|
||||||
|
|
||||||
|
Træfik can be configured:
|
||||||
|
|
||||||
|
- using a RESTful api.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Enable rest backend.
|
||||||
|
[rest]
|
||||||
|
# Name of the related entry point
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "traefik"
|
||||||
|
#
|
||||||
|
entryPoint = "traefik"
|
||||||
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
| Path | Method | Description |
|
||||||
|
|------------------------------|--------|-----------------|
|
||||||
|
| `/api/providers/web` | `PUT` | update provider |
|
||||||
|
| `/api/providers/rest` | `PUT` | update provider |
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
For compatibility reason, when you activate the rest provider, you can use `web` or `rest` as `provider` value.
|
||||||
|
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl -XPUT @file "http://localhost:8080/api"
|
||||||
|
```
|
||||||
|
with `@file`
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"frontends": {
|
||||||
|
"frontend2": {
|
||||||
|
"routes": {
|
||||||
|
"test_2": {
|
||||||
|
"rule": "Path:/test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"backend": "backend1"
|
||||||
|
},
|
||||||
|
"frontend1": {
|
||||||
|
"routes": {
|
||||||
|
"test_1": {
|
||||||
|
"rule": "Host:test.localhost"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"backend": "backend2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"backends": {
|
||||||
|
"backend2": {
|
||||||
|
"loadBalancer": {
|
||||||
|
"method": "drr"
|
||||||
|
},
|
||||||
|
"servers": {
|
||||||
|
"server2": {
|
||||||
|
"weight": 2,
|
||||||
|
"URL": "http://172.17.0.5:80"
|
||||||
|
},
|
||||||
|
"server1": {
|
||||||
|
"weight": 1,
|
||||||
|
"url": "http://172.17.0.4:80"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"backend1": {
|
||||||
|
"loadBalancer": {
|
||||||
|
"method": "wrr"
|
||||||
|
},
|
||||||
|
"circuitBreaker": {
|
||||||
|
"expression": "NetworkErrorRatio() > 0.5"
|
||||||
|
},
|
||||||
|
"servers": {
|
||||||
|
"server2": {
|
||||||
|
"weight": 1,
|
||||||
|
"url": "http://172.17.0.3:80"
|
||||||
|
},
|
||||||
|
"server1": {
|
||||||
|
"weight": 10,
|
||||||
|
"url": "http://172.17.0.2:80"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
114
docs/configuration/backends/servicefabric.md
Normal file
114
docs/configuration/backends/servicefabric.md
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
# Service Fabric Backend
|
||||||
|
|
||||||
|
Træfik can be configured to use Service Fabric as a backend configuration.
|
||||||
|
|
||||||
|
See [this repository for an example deployment package and further documentation.](https://aka.ms/traefikonsf)
|
||||||
|
|
||||||
|
## Service Fabric
|
||||||
|
|
||||||
|
```toml
|
||||||
|
################################################################
|
||||||
|
# Service Fabric provider
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# Enable Service Fabric configuration backend
|
||||||
|
[serviceFabric]
|
||||||
|
|
||||||
|
# Service Fabric Management Endpoint
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
#
|
||||||
|
clusterManagementUrl = "https://localhost:19080"
|
||||||
|
|
||||||
|
# Service Fabric Management Endpoint API Version
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
# Default: "3.0"
|
||||||
|
#
|
||||||
|
apiVersion = "3.0"
|
||||||
|
|
||||||
|
# Service Fabric Polling Interval (in seconds)
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
# Default: 10
|
||||||
|
#
|
||||||
|
refreshSeconds = 10
|
||||||
|
|
||||||
|
# Enable TLS connection.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# [serviceFabric.tls]
|
||||||
|
# ca = "/etc/ssl/ca.crt"
|
||||||
|
# cert = "/etc/ssl/servicefabric.crt"
|
||||||
|
# key = "/etc/ssl/servicefabric.key"
|
||||||
|
# insecureskipverify = true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Labels
|
||||||
|
|
||||||
|
The provider uses labels to configure how services are exposed through Træfik.
|
||||||
|
These can be set using Extensions and the Property Manager API
|
||||||
|
|
||||||
|
#### Extensions
|
||||||
|
|
||||||
|
Set labels with extensions through the services `ServiceManifest.xml` file.
|
||||||
|
Here is an example of an extension setting Træfik labels:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<StatelessServiceType ServiceTypeName="WebServiceType">
|
||||||
|
<Extensions>
|
||||||
|
<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.frontend.passHostHeader">true</Label>
|
||||||
|
</Labels>
|
||||||
|
</Extension>
|
||||||
|
</Extensions>
|
||||||
|
</StatelessServiceType>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 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.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl -X PUT \
|
||||||
|
'http://localhost:19080/Names/GettingStartedApplication2/WebService/$/GetProperty?api-version=6.0&IncludeValues=true' \
|
||||||
|
-d '{
|
||||||
|
"PropertyName": "traefik.frontend.rule.default",
|
||||||
|
"Value": {
|
||||||
|
"Kind": "String",
|
||||||
|
"Data": "PathPrefixStrip: /a/path/to/strip"
|
||||||
|
},
|
||||||
|
"CustomTypeId": "LabelType"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
This functionality will be released in a future version of the [sfctl](https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-application-lifecycle-sfctl) tool.
|
||||||
|
|
||||||
|
## Available Labels
|
||||||
|
|
||||||
|
Labels, set through extensions or the property manager, can be used on services to override default behaviour.
|
||||||
|
|
||||||
|
| 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 |
|
||||||
377
docs/configuration/backends/web.md
Normal file
377
docs/configuration/backends/web.md
Normal file
@@ -0,0 +1,377 @@
|
|||||||
|
# Web Backend
|
||||||
|
|
||||||
|
!!! 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.
|
||||||
|
|
||||||
|
Træfik can be configured:
|
||||||
|
|
||||||
|
- using a RESTful api.
|
||||||
|
- to use a monitoring system (like Prometheus, DataDog or StatD, ...).
|
||||||
|
- to expose a Web Dashboard.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Enable web backend.
|
||||||
|
[web]
|
||||||
|
|
||||||
|
# Web administration port.
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
# Default: ":8080"
|
||||||
|
#
|
||||||
|
address = ":8080"
|
||||||
|
|
||||||
|
# SSL certificate and key used.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# certFile = "traefik.crt"
|
||||||
|
# keyFile = "traefik.key"
|
||||||
|
|
||||||
|
# Set REST API to read-only mode.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
readOnly = true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Web UI
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
The `/ping` path of the api is excluded from authentication (since 1.4).
|
||||||
|
|
||||||
|
#### Basic Authentication
|
||||||
|
|
||||||
|
Passwords can be encoded in MD5, SHA1 and BCrypt: you can use `htpasswd` to generate those ones.
|
||||||
|
|
||||||
|
Users can be specified directly in the toml file, or indirectly by referencing an external file;
|
||||||
|
if both are provided, the two are merged, with external file contents having precedence.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[web]
|
||||||
|
# ...
|
||||||
|
|
||||||
|
# To enable basic auth on the webui with 2 user/pass: test:test and test2:test2
|
||||||
|
[web.auth.basic]
|
||||||
|
users = ["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"]
|
||||||
|
usersFile = "/path/to/.htpasswd"
|
||||||
|
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Digest Authentication
|
||||||
|
|
||||||
|
You can use `htdigest` to generate those ones.
|
||||||
|
|
||||||
|
Users can be specified directly in the toml file, or indirectly by referencing an external file;
|
||||||
|
if both are provided, the two are merged, with external file contents having precedence
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[web]
|
||||||
|
# ...
|
||||||
|
|
||||||
|
# To enable digest auth on the webui with 2 user/realm/pass: test:traefik:test and test2:traefik:test2
|
||||||
|
[web.auth.digest]
|
||||||
|
users = ["test:traefik:a2688e031edb4be6a3797f3882655c05 ", "test2:traefik:518845800f9e2bfb1f1f740ec24f074e"]
|
||||||
|
usersFile = "/path/to/.htdigest"
|
||||||
|
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Metrics
|
||||||
|
|
||||||
|
You can enable Traefik to export internal metrics to different monitoring systems.
|
||||||
|
|
||||||
|
### Prometheus
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[web]
|
||||||
|
# ...
|
||||||
|
|
||||||
|
# To enable Traefik to export internal metrics to Prometheus
|
||||||
|
[web.metrics.prometheus]
|
||||||
|
|
||||||
|
# Buckets for latency metrics
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: [0.1, 0.3, 1.2, 5]
|
||||||
|
buckets=[0.1,0.3,1.2,5.0]
|
||||||
|
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### DataDog
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[web]
|
||||||
|
# ...
|
||||||
|
|
||||||
|
# DataDog metrics exporter type
|
||||||
|
[web.metrics.datadog]
|
||||||
|
|
||||||
|
# DataDog's address.
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
# Default: "localhost:8125"
|
||||||
|
#
|
||||||
|
address = "localhost:8125"
|
||||||
|
|
||||||
|
# DataDog push interval
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "10s"
|
||||||
|
#
|
||||||
|
pushinterval = "10s"
|
||||||
|
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### StatsD
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[web]
|
||||||
|
# ...
|
||||||
|
|
||||||
|
# StatsD metrics exporter type
|
||||||
|
[web.metrics.statsd]
|
||||||
|
|
||||||
|
# StatD's address.
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
# Default: "localhost:8125"
|
||||||
|
#
|
||||||
|
address = "localhost:8125"
|
||||||
|
|
||||||
|
# StatD push interval
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "10s"
|
||||||
|
#
|
||||||
|
pushinterval = "10s"
|
||||||
|
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### InfluxDB
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[web]
|
||||||
|
# ...
|
||||||
|
|
||||||
|
# InfluxDB metrics exporter type
|
||||||
|
[web.metrics.influxdb]
|
||||||
|
|
||||||
|
# InfluxDB's address.
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
# Default: "localhost:8089"
|
||||||
|
#
|
||||||
|
address = "localhost:8089"
|
||||||
|
|
||||||
|
# InfluxDB push interval
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "10s"
|
||||||
|
#
|
||||||
|
pushinterval = "10s"
|
||||||
|
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Statistics
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[web]
|
||||||
|
# ...
|
||||||
|
|
||||||
|
# Enable more detailed statistics.
|
||||||
|
[web.statistics]
|
||||||
|
|
||||||
|
# Number of recent errors logged.
|
||||||
|
#
|
||||||
|
# Default: 10
|
||||||
|
#
|
||||||
|
recentErrors = 10
|
||||||
|
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
| Path | Method | Description |
|
||||||
|
|-----------------------------------------------------------------|:-------------:|----------------------------------------------------------------------------------------------------|
|
||||||
|
| `/` | `GET` | Provides a simple HTML frontend of Træfik |
|
||||||
|
| `/ping` | `GET`, `HEAD` | A simple endpoint to check for Træfik process liveness. Return a code `200` with the content: `OK` |
|
||||||
|
| `/health` | `GET` | json health metrics |
|
||||||
|
| `/api` | `GET` | Configuration for all providers |
|
||||||
|
| `/api/providers` | `GET` | Providers |
|
||||||
|
| `/api/providers/{provider}` | `GET`, `PUT` | Get or update provider |
|
||||||
|
| `/api/providers/{provider}/backends` | `GET` | List backends |
|
||||||
|
| `/api/providers/{provider}/backends/{backend}` | `GET` | Get backend |
|
||||||
|
| `/api/providers/{provider}/backends/{backend}/servers` | `GET` | List servers in backend |
|
||||||
|
| `/api/providers/{provider}/backends/{backend}/servers/{server}` | `GET` | Get a server in a backend |
|
||||||
|
| `/api/providers/{provider}/frontends` | `GET` | List frontends |
|
||||||
|
| `/api/providers/{provider}/frontends/{frontend}` | `GET` | Get a frontend |
|
||||||
|
| `/api/providers/{provider}/frontends/{frontend}/routes` | `GET` | List routes in a frontend |
|
||||||
|
| `/api/providers/{provider}/frontends/{frontend}/routes/{route}` | `GET` | Get a route in a frontend |
|
||||||
|
| `/metrics` | `GET` | Export internal metrics |
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
#### Ping
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl -sv "http://localhost:8080/ping"
|
||||||
|
```
|
||||||
|
```shell
|
||||||
|
* Trying ::1...
|
||||||
|
* Connected to localhost (::1) port 8080 (#0)
|
||||||
|
> GET /ping HTTP/1.1
|
||||||
|
> Host: localhost:8080
|
||||||
|
> User-Agent: curl/7.43.0
|
||||||
|
> Accept: */*
|
||||||
|
>
|
||||||
|
< HTTP/1.1 200 OK
|
||||||
|
< Date: Thu, 25 Aug 2016 01:35:36 GMT
|
||||||
|
< Content-Length: 2
|
||||||
|
< Content-Type: text/plain; charset=utf-8
|
||||||
|
<
|
||||||
|
* Connection #0 to host localhost left intact
|
||||||
|
OK
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Health
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl -s "http://localhost:8080/health" | jq .
|
||||||
|
```
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
// Træfik PID
|
||||||
|
"pid": 2458,
|
||||||
|
// Træfik server uptime (formated time)
|
||||||
|
"uptime": "39m6.885931127s",
|
||||||
|
// Træfik server uptime in seconds
|
||||||
|
"uptime_sec": 2346.885931127,
|
||||||
|
// current server date
|
||||||
|
"time": "2015-10-07 18:32:24.362238909 +0200 CEST",
|
||||||
|
// current server date in seconds
|
||||||
|
"unixtime": 1444235544,
|
||||||
|
// count HTTP response status code in realtime
|
||||||
|
"status_code_count": {
|
||||||
|
"502": 1
|
||||||
|
},
|
||||||
|
// count HTTP response status code since Træfik started
|
||||||
|
"total_status_code_count": {
|
||||||
|
"200": 7,
|
||||||
|
"404": 21,
|
||||||
|
"502": 13
|
||||||
|
},
|
||||||
|
// count HTTP response
|
||||||
|
"count": 1,
|
||||||
|
// count HTTP response
|
||||||
|
"total_count": 41,
|
||||||
|
// sum of all response time (formated time)
|
||||||
|
"total_response_time": "35.456865605s",
|
||||||
|
// sum of all response time in seconds
|
||||||
|
"total_response_time_sec": 35.456865605,
|
||||||
|
// average response time (formated time)
|
||||||
|
"average_response_time": "864.8016ms",
|
||||||
|
// average response time in seconds
|
||||||
|
"average_response_time_sec": 0.8648016000000001,
|
||||||
|
|
||||||
|
// request statistics [requires --web.statistics to be set]
|
||||||
|
// ten most recent requests with 4xx and 5xx status codes
|
||||||
|
"recent_errors": [
|
||||||
|
{
|
||||||
|
// status code
|
||||||
|
"status_code": 500,
|
||||||
|
// description of status code
|
||||||
|
"status": "Internal Server Error",
|
||||||
|
// request HTTP method
|
||||||
|
"method": "GET",
|
||||||
|
// request hostname
|
||||||
|
"host": "localhost",
|
||||||
|
// request path
|
||||||
|
"path": "/path",
|
||||||
|
// RFC 3339 formatted date/time
|
||||||
|
"time": "2016-10-21T16:59:15.418495872-07:00"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Provider configurations
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl -s "http://localhost:8080/api" | jq .
|
||||||
|
```
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"file": {
|
||||||
|
"frontends": {
|
||||||
|
"frontend2": {
|
||||||
|
"routes": {
|
||||||
|
"test_2": {
|
||||||
|
"rule": "Path:/test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"backend": "backend1"
|
||||||
|
},
|
||||||
|
"frontend1": {
|
||||||
|
"routes": {
|
||||||
|
"test_1": {
|
||||||
|
"rule": "Host:test.localhost"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"backend": "backend2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"backends": {
|
||||||
|
"backend2": {
|
||||||
|
"loadBalancer": {
|
||||||
|
"method": "drr"
|
||||||
|
},
|
||||||
|
"servers": {
|
||||||
|
"server2": {
|
||||||
|
"weight": 2,
|
||||||
|
"URL": "http://172.17.0.5:80"
|
||||||
|
},
|
||||||
|
"server1": {
|
||||||
|
"weight": 1,
|
||||||
|
"url": "http://172.17.0.4:80"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"backend1": {
|
||||||
|
"loadBalancer": {
|
||||||
|
"method": "wrr"
|
||||||
|
},
|
||||||
|
"circuitBreaker": {
|
||||||
|
"expression": "NetworkErrorRatio() > 0.5"
|
||||||
|
},
|
||||||
|
"servers": {
|
||||||
|
"server2": {
|
||||||
|
"weight": 1,
|
||||||
|
"url": "http://172.17.0.3:80"
|
||||||
|
},
|
||||||
|
"server1": {
|
||||||
|
"weight": 10,
|
||||||
|
"url": "http://172.17.0.2:80"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
61
docs/configuration/backends/zookeeper.md
Normal file
61
docs/configuration/backends/zookeeper.md
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# Zookeeper Backend
|
||||||
|
|
||||||
|
Træfik can be configured to use Zookeeper as a backend configuration.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
################################################################
|
||||||
|
# Zookeeper configuration backend
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# Enable Zookeeperconfiguration backend.
|
||||||
|
[zookeeper]
|
||||||
|
|
||||||
|
# Zookeeper server endpoint.
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
# Default: "127.0.0.1:2181"
|
||||||
|
#
|
||||||
|
endpoint = "127.0.0.1:2181"
|
||||||
|
|
||||||
|
# Enable watch Zookeeper changes.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: true
|
||||||
|
#
|
||||||
|
watch = true
|
||||||
|
|
||||||
|
# Prefix used for KV store.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "traefik"
|
||||||
|
#
|
||||||
|
prefix = "traefik"
|
||||||
|
|
||||||
|
# Override default configuration template.
|
||||||
|
# For advanced users :)
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# filename = "zookeeper.tmpl"
|
||||||
|
|
||||||
|
# Use Zookeeper user/pass authentication.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# username = foo
|
||||||
|
# password = bar
|
||||||
|
|
||||||
|
# Enable Zookeeper TLS connection.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
# [zookeeper.tls]
|
||||||
|
# ca = "/etc/ssl/ca.crt"
|
||||||
|
# cert = "/etc/ssl/zookeeper.crt"
|
||||||
|
# key = "/etc/ssl/zookeeper.key"
|
||||||
|
# insecureskipverify = true
|
||||||
|
```
|
||||||
|
|
||||||
|
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-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.
|
||||||
532
docs/configuration/commons.md
Normal file
532
docs/configuration/commons.md
Normal file
@@ -0,0 +1,532 @@
|
|||||||
|
# Global Configuration
|
||||||
|
|
||||||
|
## Main Section
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# DEPRECATED - for general usage instruction see [lifeCycle.graceTimeOut].
|
||||||
|
#
|
||||||
|
# If both the deprecated option and the new one are given, the deprecated one
|
||||||
|
# takes precedence.
|
||||||
|
# A value of zero is equivalent to omitting the parameter, causing
|
||||||
|
# [lifeCycle.graceTimeOut] to be effective. Pass zero to the new option in
|
||||||
|
# order to disable the grace period.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "0s"
|
||||||
|
#
|
||||||
|
# graceTimeOut = "10s"
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
# debug = true
|
||||||
|
|
||||||
|
# Periodically check if a new version has been released.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: true
|
||||||
|
#
|
||||||
|
# checkNewVersion = false
|
||||||
|
|
||||||
|
# Backends throttle duration.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "2s"
|
||||||
|
#
|
||||||
|
# ProvidersThrottleDuration = "2s"
|
||||||
|
|
||||||
|
# Controls the maximum idle (keep-alive) connections to keep per-host.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: 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.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
# InsecureSkipVerify = true
|
||||||
|
|
||||||
|
# Register Certificates in the RootCA.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: []
|
||||||
|
#
|
||||||
|
# RootCAs = [ "/mycert.cert" ]
|
||||||
|
|
||||||
|
# Entrypoints to be used by frontends that do not specify any entrypoint.
|
||||||
|
# Each frontend can specify its own entrypoints.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: ["http"]
|
||||||
|
#
|
||||||
|
# defaultEntryPoints = ["http", "https"]
|
||||||
|
```
|
||||||
|
|
||||||
|
- `graceTimeOut`: Duration to give active requests a chance to finish before Traefik stops.
|
||||||
|
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.
|
||||||
|
**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.
|
||||||
|
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.
|
||||||
|
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.
|
||||||
|
**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.
|
||||||
|
**Note** You can use file path or cert content directly
|
||||||
|
|
||||||
|
- `defaultEntryPoints`: Entrypoints to be used by frontends that do not specify any entrypoint.
|
||||||
|
Each frontend can specify its own entrypoints.
|
||||||
|
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Supported filters:
|
||||||
|
|
||||||
|
- `tag`
|
||||||
|
|
||||||
|
### Simple
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Simple matching constraint
|
||||||
|
constraints = ["tag==api"]
|
||||||
|
|
||||||
|
# Simple mismatching constraint
|
||||||
|
constraints = ["tag!=api"]
|
||||||
|
|
||||||
|
# Globbing
|
||||||
|
constraints = ["tag==us-*"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multiple
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Multiple constraints
|
||||||
|
# - "tag==" must match with at least one tag
|
||||||
|
# - "tag!=" must match with none of tags
|
||||||
|
constraints = ["tag!=us-*", "tag!=asia-*"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backend-specific
|
||||||
|
|
||||||
|
Supported backends:
|
||||||
|
|
||||||
|
- Docker
|
||||||
|
- Consul K/V
|
||||||
|
- BoltDB
|
||||||
|
- Zookeeper
|
||||||
|
- Etcd
|
||||||
|
- Consul Catalog
|
||||||
|
- Rancher
|
||||||
|
- Marathon
|
||||||
|
- Kubernetes (using a provider-specific mechanism based on label selectors)
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Backend-specific constraint
|
||||||
|
[consulCatalog]
|
||||||
|
# ...
|
||||||
|
constraints = ["tag==api"]
|
||||||
|
|
||||||
|
# Backend-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.
|
||||||
|
|
||||||
|
In the example below, if a 503 status is returned from the frontend "website", the custom error page at http://2.3.4.5/503.html is returned with the actual status code set in the HTTP header.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
The `503.html` page itself is not hosted on Traefik, but some other infrastructure.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[frontends]
|
||||||
|
[frontends.website]
|
||||||
|
backend = "website"
|
||||||
|
[frontends.website.errors]
|
||||||
|
[frontends.website.errors.network]
|
||||||
|
status = ["500-599"]
|
||||||
|
backend = "error"
|
||||||
|
query = "/{status}.html"
|
||||||
|
[frontends.website.routes.website]
|
||||||
|
rule = "Host: website.mydomain.com"
|
||||||
|
|
||||||
|
[backends]
|
||||||
|
[backends.website]
|
||||||
|
[backends.website.servers.website]
|
||||||
|
url = "https://1.2.3.4"
|
||||||
|
[backends.error]
|
||||||
|
[backends.error.servers.error]
|
||||||
|
url = "http://2.3.4.5"
|
||||||
|
```
|
||||||
|
|
||||||
|
In the above example, the error page rendered was based on the status code.
|
||||||
|
Instead, the query parameter can also be set to some generic error page like so: `query = "/500s.html"`
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
Rate limiting can be configured per frontend.
|
||||||
|
Multiple sets of rates can be added to each frontend, but the time periods must be unique.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[frontends]
|
||||||
|
[frontends.frontend1]
|
||||||
|
passHostHeader = true
|
||||||
|
entrypoints = ["http"]
|
||||||
|
backend = "backend1"
|
||||||
|
[frontends.frontend1.routes.test_1]
|
||||||
|
rule = "Path:/"
|
||||||
|
[frontends.frontend1.ratelimit]
|
||||||
|
extractorfunc = "client.ip"
|
||||||
|
[frontends.frontend1.ratelimit.rateset.rateset1]
|
||||||
|
period = "10s"
|
||||||
|
average = 100
|
||||||
|
burst = 200
|
||||||
|
[frontends.frontend1.ratelimit.rateset.rateset2]
|
||||||
|
period = "3s"
|
||||||
|
average = 5
|
||||||
|
burst = 10
|
||||||
|
```
|
||||||
|
|
||||||
|
In the above example, frontend1 is configured to limit requests by the client's ip address.
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
## Retry Configuration
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Enable retry sending request if network error
|
||||||
|
[retry]
|
||||||
|
|
||||||
|
# Number of attempts
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: (number servers in backend) -1
|
||||||
|
#
|
||||||
|
# attempts = 3
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Health Check Configuration
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Enable custom health check options.
|
||||||
|
[healthcheck]
|
||||||
|
|
||||||
|
# Set the default health check interval.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "30s"
|
||||||
|
#
|
||||||
|
# interval = "30s"
|
||||||
|
```
|
||||||
|
|
||||||
|
- `interval` set the default health check interval.
|
||||||
|
Will only be effective if health check paths are defined.
|
||||||
|
Given provider-specific support, the value may be overridden on a per-backend basis.
|
||||||
|
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.
|
||||||
|
|
||||||
|
## Life Cycle
|
||||||
|
|
||||||
|
Controls the behavior of Traefik during the shutdown phase.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[lifeCycle]
|
||||||
|
|
||||||
|
# Duration to keep accepting requests prior to initiating the graceful
|
||||||
|
# termination period (as defined by the `graceTimeOut` option). This
|
||||||
|
# option is meant to give downstream load-balancers sufficient time to
|
||||||
|
# take Traefik out of rotation.
|
||||||
|
# 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.
|
||||||
|
# The zero duration disables the request accepting grace period, i.e.,
|
||||||
|
# Traefik will immediately proceed to the grace period.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: 0
|
||||||
|
#
|
||||||
|
# requestAcceptGraceTimeout = "10s"
|
||||||
|
|
||||||
|
# Duration to give active requests a chance to finish before Traefik stops.
|
||||||
|
# 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.
|
||||||
|
# Note: in this time frame no new requests are accepted.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "10s"
|
||||||
|
#
|
||||||
|
# graceTimeOut = "10s"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Timeouts
|
||||||
|
|
||||||
|
### Responding Timeouts
|
||||||
|
|
||||||
|
`respondingTimeouts` are timeouts for incoming requests to the Traefik instance.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[respondingTimeouts]
|
||||||
|
|
||||||
|
# readTimeout is the maximum duration for reading the entire request, including the body.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "0s"
|
||||||
|
#
|
||||||
|
# readTimeout = "5s"
|
||||||
|
|
||||||
|
# writeTimeout is the maximum duration before timing out writes of the response.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "0s"
|
||||||
|
#
|
||||||
|
# writeTimeout = "5s"
|
||||||
|
|
||||||
|
# idleTimeout is the maximum duration an idle (keep-alive) connection will remain idle before closing itself.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "180s"
|
||||||
|
#
|
||||||
|
# idleTimeout = "360s"
|
||||||
|
```
|
||||||
|
|
||||||
|
- `readTimeout` is the maximum duration for reading the entire request, including the body.
|
||||||
|
If zero, no timeout exists.
|
||||||
|
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.
|
||||||
|
|
||||||
|
- `writeTimeout` is the maximum duration before timing out writes of the response.
|
||||||
|
It covers the time from the end of the request header read to the end of the response write.
|
||||||
|
If zero, no timeout exists.
|
||||||
|
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.
|
||||||
|
|
||||||
|
- `idleTimeout` is the maximum duration an idle (keep-alive) connection will remain idle before closing itself.
|
||||||
|
If zero, no timeout exists.
|
||||||
|
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.
|
||||||
|
|
||||||
|
### Forwarding Timeouts
|
||||||
|
|
||||||
|
`forwardingTimeouts` are timeouts for requests forwarded to the backend servers.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[forwardingTimeouts]
|
||||||
|
|
||||||
|
# dialTimeout is the amount of time to wait until a connection to a backend server can be established.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "30s"
|
||||||
|
#
|
||||||
|
# dialTimeout = "30s"
|
||||||
|
|
||||||
|
# responseHeaderTimeout is the amount of time to wait for a server's response headers after fully writing the request (including its body, if any).
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "0s"
|
||||||
|
#
|
||||||
|
# responseHeaderTimeout = "0s"
|
||||||
|
```
|
||||||
|
|
||||||
|
- `dialTimeout` is the amount of time to wait until a connection to a backend server can be established.
|
||||||
|
If zero, no timeout exists.
|
||||||
|
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.
|
||||||
|
|
||||||
|
- `responseHeaderTimeout` is 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.
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
### Idle Timeout (deprecated)
|
||||||
|
|
||||||
|
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.
|
||||||
|
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
|
||||||
|
#
|
||||||
|
# DEPRECATED - see [respondingTimeouts] section.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "180s"
|
||||||
|
#
|
||||||
|
IdleTimeout = "360s"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Override Default Configuration Template
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
For advanced users only.
|
||||||
|
|
||||||
|
Supported by all backends except: File backend, Web backend and DynamoDB backend.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[backend_name]
|
||||||
|
|
||||||
|
# Override default configuration template. For advanced users :)
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: ""
|
||||||
|
#
|
||||||
|
filename = "custom_config_template.tpml"
|
||||||
|
|
||||||
|
# Enable debug logging of generated configuration template.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
debugLogGeneratedTemplate = true
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[marathon]
|
||||||
|
filename = "my_custom_config_template.tpml"
|
||||||
|
```
|
||||||
|
|
||||||
|
The template files can be written using functions provided by:
|
||||||
|
|
||||||
|
- [go template](https://golang.org/pkg/text/template/)
|
||||||
|
- [sprig library](https://masterminds.github.io/sprig/)
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```tmpl
|
||||||
|
[backends]
|
||||||
|
[backends.backend1]
|
||||||
|
url = "http://firstserver"
|
||||||
|
[backends.backend2]
|
||||||
|
url = "http://secondserver"
|
||||||
|
|
||||||
|
{{$frontends := dict "frontend1" "backend1" "frontend2" "backend2"}}
|
||||||
|
[frontends]
|
||||||
|
{{range $frontend, $backend := $frontends}}
|
||||||
|
[frontends.{{$frontend}}]
|
||||||
|
backend = "{{$backend}}"
|
||||||
|
{{end}}
|
||||||
|
```
|
||||||
270
docs/configuration/entrypoints.md
Normal file
270
docs/configuration/entrypoints.md
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
# Entry Points Definition
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Entrypoints definition
|
||||||
|
#
|
||||||
|
# Default:
|
||||||
|
# [entryPoints]
|
||||||
|
# [entryPoints.http]
|
||||||
|
# address = ":80"
|
||||||
|
#
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.http]
|
||||||
|
address = ":80"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Redirect HTTP to HTTPS
|
||||||
|
|
||||||
|
To redirect an http entrypoint to an https entrypoint (with SNI support).
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.http]
|
||||||
|
address = ":80"
|
||||||
|
[entryPoints.http.redirect]
|
||||||
|
entryPoint = "https"
|
||||||
|
[entryPoints.https]
|
||||||
|
address = ":443"
|
||||||
|
[entryPoints.https.tls]
|
||||||
|
[[entryPoints.https.tls.certificates]]
|
||||||
|
certFile = "integration/fixtures/https/snitest.com.cert"
|
||||||
|
keyFile = "integration/fixtures/https/snitest.com.key"
|
||||||
|
[[entryPoints.https.tls.certificates]]
|
||||||
|
certFile = "integration/fixtures/https/snitest.org.cert"
|
||||||
|
keyFile = "integration/fixtures/https/snitest.org.key"
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
Please note that `regex` and `replacement` do not have to be set in the `redirect` structure if an entrypoint is defined for the redirection (they will not be used in this case).
|
||||||
|
|
||||||
|
## Rewriting URL
|
||||||
|
|
||||||
|
To redirect an entrypoint rewriting the URL.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.http]
|
||||||
|
address = ":80"
|
||||||
|
[entryPoints.http.redirect]
|
||||||
|
regex = "^http://localhost/(.*)"
|
||||||
|
replacement = "http://mydomain/$1"
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
Please note that `regex` and `replacement` do not have to be set in the `redirect` structure if an entrypoint is defined for the redirection (they will not be used in this case).
|
||||||
|
|
||||||
|
## TLS
|
||||||
|
|
||||||
|
Define an entrypoint with SNI support.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.https]
|
||||||
|
address = ":443"
|
||||||
|
[entryPoints.https.tls]
|
||||||
|
[[entryPoints.https.tls.certificates]]
|
||||||
|
certFile = "integration/fixtures/https/snitest.com.cert"
|
||||||
|
keyFile = "integration/fixtures/https/snitest.com.key"
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
If an empty TLS configuration is done, default self-signed certificates are generated.
|
||||||
|
|
||||||
|
## TLS Mutual Authentication
|
||||||
|
|
||||||
|
TLS Mutual Authentication can be `optional` or not.
|
||||||
|
If it's `optional`, Træfik will authorize connection with certificates not signed by a specified Certificate Authority (CA).
|
||||||
|
Otherwise, Træfik will only accept clients that present a certificate signed by a specified Certificate Authority (CA).
|
||||||
|
`ClientCAFiles` can be configured with multiple `CA:s` in the same file or use multiple files containing one or several `CA:s`.
|
||||||
|
The `CA:s` has to be in PEM format.
|
||||||
|
|
||||||
|
By default, `ClientCAFiles` is not optional, all clients will be required to present a valid cert.
|
||||||
|
The requirement will apply to all server certs in the entrypoint.
|
||||||
|
|
||||||
|
In the example below both `snitest.com` and `snitest.org` will require client certs
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.https]
|
||||||
|
address = ":443"
|
||||||
|
[entryPoints.https.tls]
|
||||||
|
[entryPoints.https.tls.ClientCA]
|
||||||
|
files = ["tests/clientca1.crt", "tests/clientca2.crt"]
|
||||||
|
optional = false
|
||||||
|
[[entryPoints.https.tls.certificates]]
|
||||||
|
certFile = "integration/fixtures/https/snitest.com.cert"
|
||||||
|
keyFile = "integration/fixtures/https/snitest.com.key"
|
||||||
|
[[entryPoints.https.tls.certificates]]
|
||||||
|
certFile = "integration/fixtures/https/snitest.org.cert"
|
||||||
|
keyFile = "integration/fixtures/https/snitest.org.key"
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
|
||||||
|
The deprecated argument `ClientCAFiles` allows adding Client CA files which are mandatory.
|
||||||
|
If this parameter exists, the new ones are not checked.
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
|
||||||
|
### Basic Authentication
|
||||||
|
|
||||||
|
Passwords can be encoded in MD5, SHA1 and BCrypt: you can use `htpasswd` to generate those ones.
|
||||||
|
|
||||||
|
Users can be specified directly in the toml file, or indirectly by referencing an external file;
|
||||||
|
if both are provided, the two are merged, with external file contents having precedence.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# To enable basic auth on an entrypoint with 2 user/pass: test:test and test2:test2
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.http]
|
||||||
|
address = ":80"
|
||||||
|
[entryPoints.http.auth.basic]
|
||||||
|
users = ["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"]
|
||||||
|
usersFile = "/path/to/.htpasswd"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Digest Authentication
|
||||||
|
|
||||||
|
You can use `htdigest` to generate those ones.
|
||||||
|
|
||||||
|
Users can be specified directly in the toml file, or indirectly by referencing an external file;
|
||||||
|
if both are provided, the two are merged, with external file contents having precedence
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# To enable digest auth on an entrypoint with 2 user/realm/pass: test:traefik:test and test2:traefik:test2
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.http]
|
||||||
|
address = ":80"
|
||||||
|
[entryPoints.http.auth.basic]
|
||||||
|
users = ["test:traefik:a2688e031edb4be6a3797f3882655c05 ", "test2:traefik:518845800f9e2bfb1f1f740ec24f074e"]
|
||||||
|
usersFile = "/path/to/.htdigest"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Forward Authentication
|
||||||
|
|
||||||
|
This configuration will first forward the request to `http://authserver.com/auth`.
|
||||||
|
|
||||||
|
If the response code is 2XX, access is granted and the original request is performed.
|
||||||
|
Otherwise, the response from the auth server is returned.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.http]
|
||||||
|
# ...
|
||||||
|
# To enable forward auth on an entrypoint
|
||||||
|
[entryPoints.http.auth.forward]
|
||||||
|
address = "https://authserver.com/auth"
|
||||||
|
|
||||||
|
# Trust existing X-Forwarded-* headers.
|
||||||
|
# Useful with another reverse proxy in front of Traefik.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
trustForwardHeader = true
|
||||||
|
|
||||||
|
# Enable forward auth TLS connection.
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
#
|
||||||
|
[entryPoints.http.auth.forward.tls]
|
||||||
|
cert = "authserver.crt"
|
||||||
|
key = "authserver.key"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Specify Minimum TLS Version
|
||||||
|
|
||||||
|
To specify an https entry point with a minimum TLS version, and specifying an array of cipher suites (from [crypto/tls](https://godoc.org/crypto/tls#pkg-constants)).
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.https]
|
||||||
|
address = ":443"
|
||||||
|
[entryPoints.https.tls]
|
||||||
|
minVersion = "VersionTLS12"
|
||||||
|
cipherSuites = ["TLS_RSA_WITH_AES_256_GCM_SHA384"]
|
||||||
|
[[entryPoints.https.tls.certificates]]
|
||||||
|
certFile = "integration/fixtures/https/snitest.com.cert"
|
||||||
|
keyFile = "integration/fixtures/https/snitest.com.key"
|
||||||
|
[[entryPoints.https.tls.certificates]]
|
||||||
|
certFile = "integration/fixtures/https/snitest.org.cert"
|
||||||
|
keyFile = "integration/fixtures/https/snitest.org.key"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Compression
|
||||||
|
|
||||||
|
To enable compression support using gzip format.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.http]
|
||||||
|
address = ":80"
|
||||||
|
compress = true
|
||||||
|
```
|
||||||
|
|
||||||
|
Responses are compressed when:
|
||||||
|
|
||||||
|
* The response body is larger than `512` bytes
|
||||||
|
* 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
|
||||||
|
|
||||||
|
To enable IP whitelisting at the entrypoint level.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.http]
|
||||||
|
address = ":80"
|
||||||
|
whiteListSourceRange = ["127.0.0.1/32", "192.168.1.7"]
|
||||||
|
```
|
||||||
|
|
||||||
|
## ProxyProtocol
|
||||||
|
|
||||||
|
To enable [ProxyProtocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) support.
|
||||||
|
Only IPs in `trustedIPs` will lead to remote client address replacement: you should declare your load-balancer IP or CIDR range here (in testing environment, you can trust everyone using `insecure = true`).
|
||||||
|
|
||||||
|
!!! danger
|
||||||
|
When queuing Træfik behind another load-balancer, be sure to carefully configure Proxy Protocol on both sides.
|
||||||
|
Otherwise, it could introduce a security risk in your system by forging requests.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.http]
|
||||||
|
address = ":80"
|
||||||
|
|
||||||
|
# Enable ProxyProtocol
|
||||||
|
[entryPoints.http.proxyProtocol]
|
||||||
|
# List of trusted IPs
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
# Default: []
|
||||||
|
#
|
||||||
|
trustedIPs = ["127.0.0.1/32", "192.168.1.7"]
|
||||||
|
|
||||||
|
# Insecure mode FOR TESTING ENVIRONNEMENT ONLY
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: false
|
||||||
|
#
|
||||||
|
# insecure = true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Forwarded Header
|
||||||
|
|
||||||
|
Only IPs in `trustedIPs` will be authorized to trust the client forwarded headers (`X-Forwarded-*`).
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.http]
|
||||||
|
address = ":80"
|
||||||
|
|
||||||
|
# Enable Forwarded Headers
|
||||||
|
[entryPoints.http.forwardedHeaders]
|
||||||
|
# List of trusted IPs
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
# Default: []
|
||||||
|
#
|
||||||
|
trustedIPs = ["127.0.0.1/32", "192.168.1.7"]
|
||||||
|
```
|
||||||
126
docs/configuration/metrics.md
Normal file
126
docs/configuration/metrics.md
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
# Metrics Definition
|
||||||
|
|
||||||
|
## Prometheus
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Metrics definition
|
||||||
|
[metrics]
|
||||||
|
#...
|
||||||
|
|
||||||
|
# To enable Traefik to export internal metrics to Prometheus
|
||||||
|
[metrics.prometheus]
|
||||||
|
|
||||||
|
# Name of the related entry point
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "traefik"
|
||||||
|
#
|
||||||
|
entryPoint = "traefik"
|
||||||
|
|
||||||
|
# Buckets for latency metrics
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: [0.1, 0.3, 1.2, 5]
|
||||||
|
#
|
||||||
|
buckets = [0.1,0.3,1.2,5.0]
|
||||||
|
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## DataDog
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Metrics definition
|
||||||
|
[metrics]
|
||||||
|
#...
|
||||||
|
|
||||||
|
# DataDog metrics exporter type
|
||||||
|
[metrics.datadog]
|
||||||
|
|
||||||
|
# DataDog's address.
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
# Default: "localhost:8125"
|
||||||
|
#
|
||||||
|
address = "localhost:8125"
|
||||||
|
|
||||||
|
# DataDog push interval
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "10s"
|
||||||
|
#
|
||||||
|
pushInterval = "10s"
|
||||||
|
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## StatsD
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Metrics definition
|
||||||
|
[metrics]
|
||||||
|
#...
|
||||||
|
|
||||||
|
# StatsD metrics exporter type
|
||||||
|
[metrics.statsd]
|
||||||
|
|
||||||
|
# StatD's address.
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
# Default: "localhost:8125"
|
||||||
|
#
|
||||||
|
address = "localhost:8125"
|
||||||
|
|
||||||
|
# StatD push interval
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "10s"
|
||||||
|
#
|
||||||
|
pushInterval = "10s"
|
||||||
|
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
### InfluxDB
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[metrics]
|
||||||
|
# ...
|
||||||
|
|
||||||
|
# InfluxDB metrics exporter type
|
||||||
|
[metrics.influxdb]
|
||||||
|
|
||||||
|
# InfluxDB's address.
|
||||||
|
#
|
||||||
|
# Required
|
||||||
|
# Default: "localhost:8089"
|
||||||
|
#
|
||||||
|
address = "localhost:8089"
|
||||||
|
|
||||||
|
# InfluxDB push interval
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "10s"
|
||||||
|
#
|
||||||
|
pushinterval = "10s"
|
||||||
|
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Statistics
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Metrics definition
|
||||||
|
[metrics]
|
||||||
|
# ...
|
||||||
|
|
||||||
|
# Enable more detailed statistics.
|
||||||
|
[metrics.statistics]
|
||||||
|
|
||||||
|
# Number of recent errors logged.
|
||||||
|
#
|
||||||
|
# Default: 10
|
||||||
|
#
|
||||||
|
recentErrors = 10
|
||||||
|
|
||||||
|
# ...
|
||||||
|
```
|
||||||
42
docs/configuration/ping.md
Normal file
42
docs/configuration/ping.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Ping Definition
|
||||||
|
|
||||||
|
```toml
|
||||||
|
# Ping definition
|
||||||
|
[ping]
|
||||||
|
# Name of the related entry point
|
||||||
|
#
|
||||||
|
# Optional
|
||||||
|
# Default: "traefik"
|
||||||
|
#
|
||||||
|
entryPoint = "traefik"
|
||||||
|
```
|
||||||
|
|
||||||
|
| Path | Method | Description |
|
||||||
|
|---------|---------------|----------------------------------------------------------------------------------------------------|
|
||||||
|
| `/ping` | `GET`, `HEAD` | A simple endpoint to check for Træfik process liveness. Return a code `200` with the content: `OK` |
|
||||||
|
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
Even if you have authentication configured on entry point, the `/ping` path of the api is excluded from authentication.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl -sv "http://localhost:8080/ping"
|
||||||
|
```
|
||||||
|
```shell
|
||||||
|
* Trying ::1...
|
||||||
|
* Connected to localhost (::1) port 8080 (#0)
|
||||||
|
> GET /ping HTTP/1.1
|
||||||
|
> Host: localhost:8080
|
||||||
|
> User-Agent: curl/7.43.0
|
||||||
|
> Accept: */*
|
||||||
|
>
|
||||||
|
< HTTP/1.1 200 OK
|
||||||
|
< Date: Thu, 25 Aug 2016 01:35:36 GMT
|
||||||
|
< Content-Length: 2
|
||||||
|
< Content-Type: text/plain; charset=utf-8
|
||||||
|
<
|
||||||
|
* Connection #0 to host localhost left intact
|
||||||
|
OK
|
||||||
|
```
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
a {
|
|
||||||
color: #37ABC8;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover, a:focus {
|
|
||||||
color: #25606F;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1, h2, h3, H4 {
|
|
||||||
color: #37ABC8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-default {
|
|
||||||
background-color: #37ABC8;
|
|
||||||
border-color: #25606F;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-default .navbar-nav>.active>a, .navbar-default .navbar-nav>.active>a:hover, .navbar-default .navbar-nav>.active>a:focus {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #25606F;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-default .navbar-nav>li>a:hover, .navbar-default .navbar-nav>li>a:focus {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #25606F;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-default .navbar-toggle {
|
|
||||||
border-color: #25606F;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-default .navbar-toggle:hover, .navbar-default .navbar-toggle:focus .navbar-toggle {
|
|
||||||
background-color: #25606F;
|
|
||||||
}
|
|
||||||
.navbar-default .navbar-collapse, .navbar-default .navbar-form {
|
|
||||||
border-color: #25606F;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote p {
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-default .navbar-nav>.open>a, .navbar-default .navbar-nav>.open>a:hover, .navbar-default .navbar-nav>.open>a:focus {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #25606F;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-menu>li>a:hover, .dropdown-menu>li>a:focus {
|
|
||||||
color: #fff;
|
|
||||||
text-decoration: none;
|
|
||||||
background-color: #25606F;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-menu>.active>a, .dropdown-menu>.active>a:hover, .dropdown-menu>.active>a:focus {
|
|
||||||
color: #fff;
|
|
||||||
text-decoration: none;
|
|
||||||
background-color: #25606F;
|
|
||||||
outline: 0;
|
|
||||||
}
|
|
||||||
4
docs/img/grpc.svg
Normal file
4
docs/img/grpc.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 186 KiB |
@@ -2,16 +2,16 @@
|
|||||||
<img src="img/traefik.logo.png" alt="Træfik" title="Træfik" />
|
<img src="img/traefik.logo.png" alt="Træfik" title="Træfik" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
[](https://travis-ci.org/containous/traefik)
|
[](https://semaphoreci.com/containous/traefik)
|
||||||
[](https://docs.traefik.io)
|
[](https://docs.traefik.io)
|
||||||
[](http://goreportcard.com/report/containous/traefik)
|
[](https://goreportcard.com/report/github.com/containous/traefik)
|
||||||
[](https://github.com/containous/traefik/blob/master/LICENSE.md)
|
[](https://github.com/containous/traefik/blob/master/LICENSE.md)
|
||||||
[](https://traefik.herokuapp.com)
|
[](https://traefik.herokuapp.com)
|
||||||
[](https://twitter.com/intent/follow?screen_name=traefikproxy)
|
[](https://twitter.com/intent/follow?screen_name=traefikproxy)
|
||||||
|
|
||||||
|
|
||||||
Træfik (pronounced like [traffic](https://speak-ipa.bearbin.net/speak.cgi?speak=%CB%88tr%C3%A6f%C9%AAk)) is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
|
Træfik (pronounced like [traffic](https://speak-ipa.bearbin.net/speak.cgi?speak=%CB%88tr%C3%A6f%C9%AAk)) is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
|
||||||
It supports several backends ([Docker](https://www.docker.com/), [Swarm](https://docs.docker.com/swarm), [Mesos/Marathon](https://mesosphere.github.io/marathon/), [Consul](https://www.consul.io/), [Etcd](https://coreos.com/etcd/), [Zookeeper](https://zookeeper.apache.org), [BoltDB](https://github.com/boltdb/bolt), [Amazon ECS](https://aws.amazon.com/ecs/), [Amazon DynamoDB](https://aws.amazon.com/dynamodb/), Rest API, file...) to manage its configuration automatically and dynamically.
|
It supports several backends ([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 a lot more) to manage its configuration automatically and dynamically.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ If you want your users to access some of your microservices from the Internet, y
|
|||||||
- path `domain.com/web` will point the microservice `web` in your private network
|
- path `domain.com/web` will point the microservice `web` in your private network
|
||||||
- domain `backoffice.domain.com` will point the microservices `backoffice` in your private network, load-balancing between your multiple instances
|
- domain `backoffice.domain.com` will point the microservices `backoffice` in your private network, load-balancing between your multiple instances
|
||||||
|
|
||||||
But a microservices architecture is dynamic... Services are added, removed, killed or upgraded often, eventually several times a day.
|
Microservices are often deployed in dynamic environments where services are added, removed, killed, upgraded or scaled many times a day.
|
||||||
|
|
||||||
Traditional reverse-proxies are not natively dynamic. You can't change their configuration and hot-reload easily.
|
Traditional reverse-proxies are not natively dynamic. You can't change their configuration and hot-reload easily.
|
||||||
|
|
||||||
@@ -35,20 +35,50 @@ Routes to your services will be created instantly.
|
|||||||
|
|
||||||
Run it and forget it!
|
Run it and forget it!
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- [It's fast](/benchmarks)
|
||||||
|
- No dependency hell, single binary made with go
|
||||||
|
- [Tiny](https://microbadger.com/images/traefik) [official](https://hub.docker.com/r/_/traefik/) docker image
|
||||||
|
- Rest API
|
||||||
|
- Hot-reloading of configuration. No need to restart the process
|
||||||
|
- Circuit breakers, retry
|
||||||
|
- Round Robin, rebalancer load-balancers
|
||||||
|
- Metrics (Rest, Prometheus, Datadog, Statsd, InfluxDB)
|
||||||
|
- Clean AngularJS Web UI
|
||||||
|
- Websocket, HTTP/2, GRPC ready
|
||||||
|
- Access Logs (JSON, CLF)
|
||||||
|
- [Let's Encrypt](https://letsencrypt.org) support (Automatic HTTPS with renewal)
|
||||||
|
- High Availability with cluster mode
|
||||||
|
|
||||||
|
|
||||||
|
## Supported backends
|
||||||
|
|
||||||
|
- [Docker](https://www.docker.com/) / [Swarm mode](https://docs.docker.com/engine/swarm/)
|
||||||
|
- [Kubernetes](https://kubernetes.io)
|
||||||
|
- [Mesos](https://github.com/apache/mesos) / [Marathon](https://mesosphere.github.io/marathon/)
|
||||||
|
- [Rancher](https://rancher.com) (API, Metadata)
|
||||||
|
- [Consul](https://www.consul.io/) / [Etcd](https://coreos.com/etcd/) / [Zookeeper](https://zookeeper.apache.org) / [BoltDB](https://github.com/boltdb/bolt)
|
||||||
|
- [Eureka](https://github.com/Netflix/eureka)
|
||||||
|
- [Amazon ECS](https://aws.amazon.com/ecs)
|
||||||
|
- [Amazon DynamoDB](https://aws.amazon.com/dynamodb)
|
||||||
|
- File
|
||||||
|
- Rest API
|
||||||
|
|
||||||
|
|
||||||
## Quickstart
|
## Quickstart
|
||||||
|
|
||||||
You can have a quick look at Træfik in this [Katacoda tutorial](https://www.katacoda.com/courses/traefik/deploy-load-balancer) that shows how to load balance requests between multiple Docker containers.
|
You can have a quick look at Træfik in this [Katacoda tutorial](https://www.katacoda.com/courses/traefik/deploy-load-balancer) that shows how to load balance requests between multiple Docker containers.
|
||||||
|
|
||||||
Here is a talk given by [Ed Robinson](https://github.com/errm) at the [ContainerCamp UK](https://container.camp) conference.
|
Here is a talk given by [Emile Vauge](https://github.com/emilevauge) at [GopherCon 2017](https://gophercon.com).
|
||||||
|
You will learn Træfik basics in less than 10 minutes.
|
||||||
|
|
||||||
|
[](https://www.youtube.com/watch?v=RgudiksfL-k)
|
||||||
|
|
||||||
|
Here is a talk given by [Ed Robinson](https://github.com/errm) at [ContainerCamp UK](https://container.camp) conference.
|
||||||
You will learn fundamental Træfik features and see some demos with Kubernetes.
|
You will learn fundamental Træfik features and see some demos with Kubernetes.
|
||||||
|
|
||||||
[](https://www.youtube.com/watch?v=aFtpIShV60I)
|
[](https://www.youtube.com/watch?v=aFtpIShV60I)
|
||||||
|
|
||||||
Here is a talk (in French) given by [Emile Vauge](https://github.com/emilevauge) at the [Devoxx France 2016](http://www.devoxx.fr) conference.
|
|
||||||
You will learn fundamental Træfik features and see some demos with Docker, Mesos/Marathon and Let's Encrypt.
|
|
||||||
|
|
||||||
[](http://www.youtube.com/watch?v=QvAz9mVx5TI)
|
|
||||||
|
|
||||||
## Get it
|
## Get it
|
||||||
|
|
||||||
@@ -95,9 +125,11 @@ networks:
|
|||||||
|
|
||||||
Start it from within the `traefik` folder:
|
Start it from within the `traefik` folder:
|
||||||
|
|
||||||
docker-compose up -d
|
```shell
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
In a browser you may open `http://localhost:8080` to access Træfik's dashboard and observe the following magic.
|
In a browser, you may open [http://localhost:8080](http://localhost:8080) to access Træfik's dashboard and observe the following magic.
|
||||||
|
|
||||||
Now, create a folder named `test` and create a `docker-compose.yml` in it with this content:
|
Now, create a folder named `test` and create a `docker-compose.yml` in it with this content:
|
||||||
|
|
||||||
@@ -129,7 +161,10 @@ docker-compose scale whoami=2
|
|||||||
Finally, test load-balancing between the two services `test_whoami_1` and `test_whoami_2`:
|
Finally, test load-balancing between the two services `test_whoami_1` and `test_whoami_2`:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ curl -H Host:whoami.docker.localhost http://127.0.0.1
|
curl -H Host:whoami.docker.localhost http://127.0.0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml
|
||||||
Hostname: ef194d07634a
|
Hostname: ef194d07634a
|
||||||
IP: 127.0.0.1
|
IP: 127.0.0.1
|
||||||
IP: ::1
|
IP: ::1
|
||||||
@@ -144,8 +179,13 @@ X-Forwarded-For: 172.17.0.1
|
|||||||
X-Forwarded-Host: 172.17.0.4:80
|
X-Forwarded-Host: 172.17.0.4:80
|
||||||
X-Forwarded-Proto: http
|
X-Forwarded-Proto: http
|
||||||
X-Forwarded-Server: dbb60406010d
|
X-Forwarded-Server: dbb60406010d
|
||||||
|
```
|
||||||
|
|
||||||
$ curl -H Host:whoami.docker.localhost http://127.0.0.1
|
```shell
|
||||||
|
curl -H Host:whoami.docker.localhost http://127.0.0.1
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml
|
||||||
Hostname: 6c3c5df0c79a
|
Hostname: 6c3c5df0c79a
|
||||||
IP: 127.0.0.1
|
IP: 127.0.0.1
|
||||||
IP: ::1
|
IP: ::1
|
||||||
|
|||||||
4
docs/theme/js/extra.js
vendored
Normal file
4
docs/theme/js/extra.js
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
/* Highlight */
|
||||||
|
(function(hljs) {
|
||||||
|
hljs.initHighlightingOnLoad();
|
||||||
|
})(hljs);
|
||||||
24
docs/theme/js/hljs/LICENSE
vendored
Normal file
24
docs/theme/js/hljs/LICENSE
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
Copyright (c) 2006, Ivan Sagalaev
|
||||||
|
All rights reserved.
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of highlight.js nor the names of its contributors
|
||||||
|
may be used to endorse or promote products derived from this software
|
||||||
|
without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
|
||||||
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
2
docs/theme/js/hljs/highlight.pack.js
vendored
Normal file
2
docs/theme/js/hljs/highlight.pack.js
vendored
Normal file
File diff suppressed because one or more lines are too long
104
docs/theme/partials/footer.html
vendored
Normal file
104
docs/theme/partials/footer.html
vendored
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
<!--
|
||||||
|
Copyright (c) 2016-2017 Martin Donath <martin.donath@squidfunk.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to
|
||||||
|
deal in the Software without restriction, including without limitation the
|
||||||
|
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||||
|
sell copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
|
IN THE SOFTWARE.
|
||||||
|
-->
|
||||||
|
|
||||||
|
{% import "partials/language.html" as lang %}
|
||||||
|
|
||||||
|
<!-- Application footer -->
|
||||||
|
<footer class="md-footer">
|
||||||
|
|
||||||
|
<!-- Link to previous and/or next page -->
|
||||||
|
{% if page.previous_page or page.next_page %}
|
||||||
|
<!--<div class="md-footer-nav">-->
|
||||||
|
<!--<nav class="md-footer-nav__inner md-grid">-->
|
||||||
|
<!-- -->
|
||||||
|
<!-- Link to previous page -->
|
||||||
|
<!--{% if page.previous_page %}-->
|
||||||
|
<!--<a href="{{ page.previous_page.url }}"-->
|
||||||
|
<!--title="{{ page.previous_page.title }}"-->
|
||||||
|
<!--class="md-flex md-footer-nav__link md-footer-nav__link--prev"-->
|
||||||
|
<!--rel="prev">-->
|
||||||
|
<!--<div class="md-flex__cell md-flex__cell--shrink">-->
|
||||||
|
<!--<i class="md-icon md-icon--arrow-back-->
|
||||||
|
<!--md-footer-nav__button"></i>-->
|
||||||
|
<!--</div>-->
|
||||||
|
<!--<div class="md-flex__cell md-flex__cell--stretch-->
|
||||||
|
<!--md-footer-nav__title">-->
|
||||||
|
<!--<span class="md-flex__ellipsis">-->
|
||||||
|
<!--<span class="md-footer-nav__direction">-->
|
||||||
|
<!--{{ lang.t("footer.previous") }} -->
|
||||||
|
<!--</span>-->
|
||||||
|
<!--{{ page.previous_page.title }}-->
|
||||||
|
<!--</span>-->
|
||||||
|
<!--</div>-->
|
||||||
|
<!--</a>-->
|
||||||
|
<!--{% endif %}-->
|
||||||
|
<!-- -->
|
||||||
|
<!-- Link to next page -->
|
||||||
|
<!--{% if page.next_page %}-->
|
||||||
|
<!--<a href="{{ page.next_page.url }}" title="{{ page.next_page.title }}"-->
|
||||||
|
<!--class="md-flex md-footer-nav__link md-footer-nav__link--next"-->
|
||||||
|
<!--rel="next">-->
|
||||||
|
<!--<div class="md-flex__cell md-flex__cell--stretch-->
|
||||||
|
<!--md-footer-nav__title">-->
|
||||||
|
<!--<span class="md-flex__ellipsis">-->
|
||||||
|
<!--<span class="md-footer-nav__direction">-->
|
||||||
|
<!--{{ lang.t("footer.next") }}-->
|
||||||
|
<!--</span>-->
|
||||||
|
<!--{{ page.next_page.title }}-->
|
||||||
|
<!--</span>-->
|
||||||
|
<!--</div>-->
|
||||||
|
<!--<div class="md-flex__cell md-flex__cell--shrink">-->
|
||||||
|
<!--<i class="md-icon md-icon--arrow-forward-->
|
||||||
|
<!--md-footer-nav__button"></i>-->
|
||||||
|
<!--</div>-->
|
||||||
|
<!--</a>-->
|
||||||
|
<!--{% endif %}-->
|
||||||
|
<!--</nav>-->
|
||||||
|
<!--</div>-->
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Further information -->
|
||||||
|
<div class="md-footer-meta md-typeset">
|
||||||
|
<div class="md-footer-meta__inner md-grid">
|
||||||
|
|
||||||
|
<!-- Copyright and theme information -->
|
||||||
|
<div class="md-footer-copyright">
|
||||||
|
{% if config.copyright %}
|
||||||
|
<div class="md-footer-copyright__highlight">
|
||||||
|
{{ config.copyright }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
powered by
|
||||||
|
<a href="http://www.mkdocs.org" title="MkDocs">MkDocs</a>
|
||||||
|
and
|
||||||
|
<a href="http://squidfunk.github.io/mkdocs-material/"
|
||||||
|
title="Material for MkDocs">
|
||||||
|
Material for MkDocs</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Social links -->
|
||||||
|
{% block social %}
|
||||||
|
{% include "partials/social.html" %}
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
96
docs/theme/styles/atom-one-light.css
vendored
Normal file
96
docs/theme/styles/atom-one-light.css
vendored
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Atom One Light by Daniel Gamage
|
||||||
|
Original One Light Syntax theme from https://github.com/atom/one-light-syntax
|
||||||
|
|
||||||
|
base: #fafafa
|
||||||
|
mono-1: #383a42
|
||||||
|
mono-2: #686b77
|
||||||
|
mono-3: #a0a1a7
|
||||||
|
hue-1: #0184bb
|
||||||
|
hue-2: #4078f2
|
||||||
|
hue-3: #a626a4
|
||||||
|
hue-4: #50a14f
|
||||||
|
hue-5: #e45649
|
||||||
|
hue-5-2: #c91243
|
||||||
|
hue-6: #986801
|
||||||
|
hue-6-2: #c18401
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
.hljs {
|
||||||
|
display: block;
|
||||||
|
overflow-x: auto;
|
||||||
|
padding: 0.5em;
|
||||||
|
color: #383a42;
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-comment,
|
||||||
|
.hljs-quote {
|
||||||
|
color: #a0a1a7;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-doctag,
|
||||||
|
.hljs-keyword,
|
||||||
|
.hljs-formula {
|
||||||
|
color: #a626a4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-section,
|
||||||
|
.hljs-name,
|
||||||
|
.hljs-selector-tag,
|
||||||
|
.hljs-deletion,
|
||||||
|
.hljs-subst {
|
||||||
|
color: #e45649;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-literal {
|
||||||
|
color: #0184bb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-string,
|
||||||
|
.hljs-regexp,
|
||||||
|
.hljs-addition,
|
||||||
|
.hljs-attribute,
|
||||||
|
.hljs-meta-string {
|
||||||
|
color: #50a14f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-built_in,
|
||||||
|
.hljs-class .hljs-title {
|
||||||
|
color: #c18401;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-attr,
|
||||||
|
.hljs-variable,
|
||||||
|
.hljs-template-variable,
|
||||||
|
.hljs-type,
|
||||||
|
.hljs-selector-class,
|
||||||
|
.hljs-selector-attr,
|
||||||
|
.hljs-selector-pseudo,
|
||||||
|
.hljs-number {
|
||||||
|
color: #986801;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-symbol,
|
||||||
|
.hljs-bullet,
|
||||||
|
.hljs-link,
|
||||||
|
.hljs-meta,
|
||||||
|
.hljs-selector-id,
|
||||||
|
.hljs-title {
|
||||||
|
color: #4078f2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-emphasis {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hljs-link {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
20
docs/theme/styles/extra.css
vendored
Normal file
20
docs/theme/styles/extra.css
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
.md-logo img {
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fix for Chrome */
|
||||||
|
.md-typeset__table td code {
|
||||||
|
word-break: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.md-typeset__table tr :nth-child(1) {
|
||||||
|
word-wrap: break-word;
|
||||||
|
max-width: 30em;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
text-align: justify;
|
||||||
|
}
|
||||||
1744
docs/toml.md
1744
docs/toml.md
File diff suppressed because it is too large
Load Diff
@@ -1,20 +1,25 @@
|
|||||||
# Clustering / High Availability (beta)
|
# Clustering / High Availability (beta)
|
||||||
|
|
||||||
This guide explains how tu use Træfik in high availability mode.
|
This guide explains how to use Træfik in high availability mode.
|
||||||
|
|
||||||
In order to deploy and configure multiple Træfik instances, without copying the same configuration file on each instance, we will use a distributed Key-Value store.
|
In order to deploy and configure multiple Træfik instances, without copying the same configuration file on each instance, we will use a distributed Key-Value store.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
You will need a working KV store cluster.
|
You will need a working KV store cluster.
|
||||||
|
_(Currently, we recommend [Consul](https://consul.io) .)_
|
||||||
|
|
||||||
## File configuration to KV store migration
|
## File configuration to KV store migration
|
||||||
|
|
||||||
We created a special Træfik command to help configuring your Key Value store from a Træfik TOML configuration file.
|
We created a special Træfik command to help configuring your Key Value store from a Træfik TOML configuration file.
|
||||||
|
|
||||||
Please refer to [this section](/user-guide/kv-config/#store-configuration-in-key-value-store) to get more details.
|
Please refer to [this section](/user-guide/kv-config/#store-configuration-in-key-value-store) to get more details.
|
||||||
|
|
||||||
## Deploy a Træfik cluster
|
## Deploy a Træfik cluster
|
||||||
|
|
||||||
Once your Træfik configuration is uploaded on your KV store, you can start each Træfik instance.
|
Once your Træfik configuration is uploaded on your KV store, you can start each Træfik instance.
|
||||||
A Træfik cluster is based on a master/slave model.
|
|
||||||
When starting, Træfik will elect a master. If this instance fails, another master will be automatically elected.
|
A Træfik cluster is based on a manager/worker model.
|
||||||
|
|
||||||
|
When starting, Træfik will elect a manager.
|
||||||
|
If this instance fails, another manager will be automatically elected.
|
||||||
|
|||||||
262
docs/user-guide/docker-and-lets-encrypt.md
Normal file
262
docs/user-guide/docker-and-lets-encrypt.md
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
# Docker & Traefik
|
||||||
|
|
||||||
|
In this use case, we want to use Træfik as a _layer-7_ load balancer with SSL termination for a set of micro-services used to run a web application.
|
||||||
|
|
||||||
|
We also want to automatically _discover any services_ on the Docker host and let Træfik reconfigure itself automatically when containers get created (or shut down) so HTTP traffic can be routed accordingly.
|
||||||
|
|
||||||
|
In addition, we want to use Let's Encrypt to automatically generate and renew SSL certificates per hostname.
|
||||||
|
|
||||||
|
## Setting Up
|
||||||
|
|
||||||
|
In order for this to work, you'll need a server with a public IP address, with Docker installed on it.
|
||||||
|
|
||||||
|
In this example, we're using the fictitious domain _my-awesome-app.org_.
|
||||||
|
|
||||||
|
In real-life, you'll want to use your own domain and have the DNS configured accordingly so the hostname records you'll want to use point to the aforementioned public IP address.
|
||||||
|
|
||||||
|
## Networking
|
||||||
|
|
||||||
|
Docker containers can only communicate with each other over TCP when they share at least one network.
|
||||||
|
This makes sense from a topological point of view in the context of networking, since Docker under the hood creates IPTable rules so containers can't reach other containers _unless you'd want to_.
|
||||||
|
|
||||||
|
In this example, we're going to use a single network called `web` where all containers that are handling HTTP traffic (including Træfik) will reside in.
|
||||||
|
|
||||||
|
On the Docker host, run the following command:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
docker network create web
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, let's create a directory on the server where we will configure the rest of Træfik:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
mkdir -p /opt/traefik
|
||||||
|
```
|
||||||
|
|
||||||
|
Within this directory, we're going to create 3 empty files:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
touch /opt/traefik/docker-compose.yml
|
||||||
|
touch /opt/traefik/acme.json && chmod 600 /opt/traefik/acme.json
|
||||||
|
touch /opt/traefik/traefik.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
The `docker-compose.yml` file will provide us with a simple, consistent and more importantly, a deterministic way to create Træfik.
|
||||||
|
|
||||||
|
The contents of the file is as follows:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '2'
|
||||||
|
|
||||||
|
services:
|
||||||
|
traefik:
|
||||||
|
image: traefik:1.3.5
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- 80:80
|
||||||
|
- 443:443
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- /opt/traefik/traefik.toml:/traefik.toml
|
||||||
|
- /opt/traefik/acme.json:/acme.json
|
||||||
|
container_name: traefik
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
|
|
||||||
|
As you can see, we're mounting the `traefik.toml` file as well as the (empty) `acme.json` file in the container.
|
||||||
|
Also, we're mounting the `/var/run/docker.sock` Docker socket in the container as well, so Træfik can listen to Docker events and reconfigure it's own internal configuration when containers are created (or shut down).
|
||||||
|
Also, we're making sure the container is automatically restarted by the Docker engine in case of problems (or: if the server is rebooted).
|
||||||
|
We're publishing the default HTTP ports `80` and `443` on the host, and making sure the container is placed within the `web` network we've created earlier on.
|
||||||
|
Finally, we're giving this container a static name called `traefik`.
|
||||||
|
|
||||||
|
Let's take a look at a simple `traefik.toml` configuration as well before we'll create the Træfik container:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
debug = false
|
||||||
|
|
||||||
|
logLevel = "ERROR"
|
||||||
|
defaultEntryPoints = ["https","http"]
|
||||||
|
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.http]
|
||||||
|
address = ":80"
|
||||||
|
[entryPoints.http.redirect]
|
||||||
|
entryPoint = "https"
|
||||||
|
[entryPoints.https]
|
||||||
|
address = ":443"
|
||||||
|
[entryPoints.https.tls]
|
||||||
|
|
||||||
|
[retry]
|
||||||
|
|
||||||
|
[docker]
|
||||||
|
endpoint = "unix:///var/run/docker.sock"
|
||||||
|
domain = "my-awesome-app.org"
|
||||||
|
watch = true
|
||||||
|
exposedbydefault = false
|
||||||
|
|
||||||
|
[acme]
|
||||||
|
email = "your-email-here@my-awesome-app.org"
|
||||||
|
storage = "acme.json"
|
||||||
|
entryPoint = "https"
|
||||||
|
OnHostRule = true
|
||||||
|
```
|
||||||
|
|
||||||
|
This is the minimum configuration required to do the following:
|
||||||
|
|
||||||
|
- Log `ERROR`-level messages (or more severe) to the console, but silence `DEBUG`-level messagse
|
||||||
|
- 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 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.
|
||||||
|
|
||||||
|
Alright, let's boot the container. From the `/opt/traefik` directory, run `docker-compose up -d` which will create and start the Træfik container.
|
||||||
|
|
||||||
|
## Exposing Web Services to the Outside World
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
The `docker-compose.yml` of our project looks like this:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: "2.1"
|
||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: my-docker-registry.com/my-awesome-app/app:latest
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: always
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
- default
|
||||||
|
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.admin.frontend.rule=Host:admin-app.my-awesome-app.org"
|
||||||
|
- "traefik.admin.protocol=https"
|
||||||
|
- "traefik.admin.port=9443"
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: my-docker-registry.com/back-end/5.7
|
||||||
|
restart: always
|
||||||
|
|
||||||
|
redis:
|
||||||
|
image: my-docker-registry.com/back-end/redis:4-alpine
|
||||||
|
restart: always
|
||||||
|
|
||||||
|
events:
|
||||||
|
image: my-docker-registry.com/my-awesome-app/events:latest
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
redis:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: always
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
- default
|
||||||
|
expose:
|
||||||
|
- "3000"
|
||||||
|
labels:
|
||||||
|
- "traefik.backend=my-awesome-app-events"
|
||||||
|
- "traefik.docker.network=web"
|
||||||
|
- "traefik.frontend.rule=Host:events.my-awesome-app.org"
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.port=3000"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
|
|
||||||
|
Here, we can see a set of services with two applications that we're actually exposing to the outside world.
|
||||||
|
Notice how there isn't a single container that has any published ports to the host -- everything is routed through Docker networks.
|
||||||
|
Also, only the containers that we want traffic to get routed to are attached to the `web` network we created at the start of this document.
|
||||||
|
|
||||||
|
Since the `traefik` container we've created and started earlier is also attached to this network, HTTP requests can now get routed to these containers.
|
||||||
|
|
||||||
|
### Labels
|
||||||
|
|
||||||
|
As mentioned earlier, we don't want containers exposed automatically by Træfik.
|
||||||
|
|
||||||
|
The reason behind this is simple: we want to have control over this process ourselves.
|
||||||
|
Thanks to Docker labels, we can tell Træfik how to create it's internal routing configuration.
|
||||||
|
|
||||||
|
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.admin.frontend.rule=Host:admin-app.my-awesome-app.org"
|
||||||
|
- "traefik.admin.protocol=https"
|
||||||
|
- "traefik.admin.port=9443"
|
||||||
|
```
|
||||||
|
|
||||||
|
We use both `container labels` and `service labels`.
|
||||||
|
|
||||||
|
#### Container 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.
|
||||||
|
With the `traefik.enable` label, we tell Træfik to include this container in it's 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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
### Service labels
|
||||||
|
|
||||||
|
`Service labels` allow managing many routes for the same container.
|
||||||
|
|
||||||
|
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`.
|
||||||
|
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.
|
||||||
|
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.
|
||||||
|
|
||||||
|
#### Gotchas and tips
|
||||||
|
|
||||||
|
- 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.
|
||||||
|
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
|
||||||
|
- Træfik supports websockets out of the box. In the example above, the `events`-service could be a NodeJS-based application which allows clients to connect using websocket protocol.
|
||||||
|
Thanks to the fact that HTTPS in our example is enforced, these websockets are automatically secure as well (WSS)
|
||||||
|
|
||||||
|
### Final thoughts
|
||||||
|
|
||||||
|
Using Træfik as a Layer-7 load balancer in combination with both Docker and Let's Encrypt provides you with an extremely flexible, powerful and self-configuring solution for your projects.
|
||||||
|
|
||||||
|
With Let's Encrypt, your endpoints are automatically secured with production-ready SSL certificates that are renewed automatically as well.
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
|
|
||||||
You will find here some configuration examples of Træfik.
|
You will find here some configuration examples of Træfik.
|
||||||
@@ -23,11 +22,11 @@ defaultEntryPoints = ["http", "https"]
|
|||||||
address = ":443"
|
address = ":443"
|
||||||
[entryPoints.https.tls]
|
[entryPoints.https.tls]
|
||||||
[[entryPoints.https.tls.certificates]]
|
[[entryPoints.https.tls.certificates]]
|
||||||
CertFile = "integration/fixtures/https/snitest.com.cert"
|
certFile = "integration/fixtures/https/snitest.com.cert"
|
||||||
KeyFile = "integration/fixtures/https/snitest.com.key"
|
keyFile = "integration/fixtures/https/snitest.com.key"
|
||||||
[[entryPoints.https.tls.certificates]]
|
[[entryPoints.https.tls.certificates]]
|
||||||
CertFile = "integration/fixtures/https/snitest.org.cert"
|
certFile = "integration/fixtures/https/snitest.org.cert"
|
||||||
KeyFile = "integration/fixtures/https/snitest.org.key"
|
keyFile = "integration/fixtures/https/snitest.org.key"
|
||||||
```
|
```
|
||||||
Note that we can either give path to certificate file or directly the file content itself ([like in this TOML example](/user-guide/kv-config/#upload-the-configuration-in-the-key-value-store)).
|
Note that we can either give path to certificate file or directly the file content itself ([like in this TOML example](/user-guide/kv-config/#upload-the-configuration-in-the-key-value-store)).
|
||||||
|
|
||||||
@@ -44,25 +43,26 @@ defaultEntryPoints = ["http", "https"]
|
|||||||
address = ":443"
|
address = ":443"
|
||||||
[entryPoints.https.tls]
|
[entryPoints.https.tls]
|
||||||
[[entryPoints.https.tls.certificates]]
|
[[entryPoints.https.tls.certificates]]
|
||||||
certFile = "tests/traefik.crt"
|
certFile = "examples/traefik.crt"
|
||||||
keyFile = "tests/traefik.key"
|
keyFile = "examples/traefik.key"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
Please note that `regex` and `replacement` do not have to be set in the `redirect` structure if an entrypoint is defined for the redirection (they will not be used in this case)
|
||||||
|
|
||||||
## Let's Encrypt support
|
## Let's Encrypt support
|
||||||
|
|
||||||
|
### Basic example
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[entryPoints]
|
[entryPoints]
|
||||||
[entryPoints.https]
|
[entryPoints.https]
|
||||||
address = ":443"
|
address = ":443"
|
||||||
[entryPoints.https.tls]
|
[entryPoints.https.tls]
|
||||||
# certs used as default certs
|
|
||||||
[[entryPoints.https.tls.certificates]]
|
|
||||||
certFile = "tests/traefik.crt"
|
|
||||||
keyFile = "tests/traefik.key"
|
|
||||||
[acme]
|
[acme]
|
||||||
email = "test@traefik.io"
|
email = "test@traefik.io"
|
||||||
storageFile = "acme.json"
|
storage = "acme.json"
|
||||||
onDemand = true
|
|
||||||
caServer = "http://172.18.0.1:4000/directory"
|
caServer = "http://172.18.0.1:4000/directory"
|
||||||
entryPoint = "https"
|
entryPoint = "https"
|
||||||
|
|
||||||
@@ -78,6 +78,168 @@ entryPoint = "https"
|
|||||||
main = "local4.com"
|
main = "local4.com"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
This configuration allows generating Let's Encrypt certificates for the four domains `local[1-4].com` with described SANs.
|
||||||
|
|
||||||
|
Traefik generates these certificates when it starts and it needs to be restart if new domains are added.
|
||||||
|
|
||||||
|
### OnHostRule option
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.https]
|
||||||
|
address = ":443"
|
||||||
|
[entryPoints.https.tls]
|
||||||
|
|
||||||
|
[acme]
|
||||||
|
email = "test@traefik.io"
|
||||||
|
storage = "acme.json"
|
||||||
|
onHostRule = true
|
||||||
|
caServer = "http://172.18.0.1:4000/directory"
|
||||||
|
entryPoint = "https"
|
||||||
|
|
||||||
|
[[acme.domains]]
|
||||||
|
main = "local1.com"
|
||||||
|
sans = ["test1.local1.com", "test2.local1.com"]
|
||||||
|
[[acme.domains]]
|
||||||
|
main = "local2.com"
|
||||||
|
sans = ["test1.local2.com", "test2x.local2.com"]
|
||||||
|
[[acme.domains]]
|
||||||
|
main = "local3.com"
|
||||||
|
[[acme.domains]]
|
||||||
|
main = "local4.com"
|
||||||
|
```
|
||||||
|
|
||||||
|
This configuration allows generating Let's Encrypt certificates for the four domains `local[1-4].com`.
|
||||||
|
|
||||||
|
Traefik generates these certificates when it starts.
|
||||||
|
|
||||||
|
If a backend is added with a `onHost` rule, Traefik will automatically generate the Let's Encrypt certificate for the new domain.
|
||||||
|
|
||||||
|
### OnDemand option
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.https]
|
||||||
|
address = ":443"
|
||||||
|
[entryPoints.https.tls]
|
||||||
|
|
||||||
|
[acme]
|
||||||
|
email = "test@traefik.io"
|
||||||
|
storage = "acme.json"
|
||||||
|
onDemand = true
|
||||||
|
caServer = "http://172.18.0.1:4000/directory"
|
||||||
|
entryPoint = "https"
|
||||||
|
```
|
||||||
|
|
||||||
|
This configuration allows generating a Let's Encrypt certificate during the first HTTPS request on a new domain.
|
||||||
|
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
This option simplifies the configuration but :
|
||||||
|
|
||||||
|
* TLS handshakes will be slow when requesting a hostname certificate for the first time, this can leads to DDoS attacks.
|
||||||
|
* Let's Encrypt have rate limiting: https://letsencrypt.org/docs/rate-limits
|
||||||
|
|
||||||
|
That's why, it's better to use the `onHostRule` option if possible.
|
||||||
|
|
||||||
|
### DNS challenge
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.https]
|
||||||
|
address = ":443"
|
||||||
|
[entryPoints.https.tls]
|
||||||
|
|
||||||
|
[acme]
|
||||||
|
email = "test@traefik.io"
|
||||||
|
storage = "acme.json"
|
||||||
|
dnsProvider = "digitalocean" # DNS Provider name (cloudflare, OVH, gandi...)
|
||||||
|
delayDontCheckDNS = 0
|
||||||
|
caServer = "http://172.18.0.1:4000/directory"
|
||||||
|
entryPoint = "https"
|
||||||
|
|
||||||
|
[[acme.domains]]
|
||||||
|
main = "local1.com"
|
||||||
|
sans = ["test1.local1.com", "test2.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.
|
||||||
|
This variables have to be set on the machine/container which host Traefik.
|
||||||
|
|
||||||
|
These variables are described [in this section](/configuration/acme/#dnsprovider).
|
||||||
|
|
||||||
|
### OnHostRule option and provided certificates
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.https]
|
||||||
|
address = ":443"
|
||||||
|
[entryPoints.https.tls]
|
||||||
|
[[entryPoints.https.tls.certificates]]
|
||||||
|
certFile = "examples/traefik.crt"
|
||||||
|
keyFile = "examples/traefik.key"
|
||||||
|
|
||||||
|
[acme]
|
||||||
|
email = "test@traefik.io"
|
||||||
|
storage = "acme.json"
|
||||||
|
onHostRule = true
|
||||||
|
caServer = "http://172.18.0.1:4000/directory"
|
||||||
|
entryPoint = "https"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Traefik will only try to generate a Let's encrypt certificate if the domain cannot be checked by the provided certificates.
|
||||||
|
|
||||||
|
### Cluster mode
|
||||||
|
|
||||||
|
#### Prerequisites
|
||||||
|
|
||||||
|
Before you use Let's Encrypt in a Traefik cluster, take a look to [the key-value store explanations](/user-guide/kv-config) and more precisely at [this section](/user-guide/kv-config/#store-configuration-in-key-value-store), which will describe how to migrate from a acme local storage *(acme.json file)* to a key-value store configuration.
|
||||||
|
|
||||||
|
#### Configuration
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.https]
|
||||||
|
address = ":443"
|
||||||
|
[entryPoints.https.tls]
|
||||||
|
|
||||||
|
[acme]
|
||||||
|
email = "test@traefik.io"
|
||||||
|
storage = "traefik/acme/account"
|
||||||
|
caServer = "http://172.18.0.1:4000/directory"
|
||||||
|
entryPoint = "https"
|
||||||
|
|
||||||
|
[[acme.domains]]
|
||||||
|
main = "local1.com"
|
||||||
|
sans = ["test1.local1.com", "test2.local1.com"]
|
||||||
|
[[acme.domains]]
|
||||||
|
main = "local2.com"
|
||||||
|
sans = ["test1.local2.com", "test2x.local2.com"]
|
||||||
|
[[acme.domains]]
|
||||||
|
main = "local3.com"
|
||||||
|
[[acme.domains]]
|
||||||
|
main = "local4.com"
|
||||||
|
|
||||||
|
[consul]
|
||||||
|
endpoint = "127.0.0.1:8500"
|
||||||
|
watch = true
|
||||||
|
prefix = "traefik"
|
||||||
|
```
|
||||||
|
|
||||||
|
This configuration allows to use the key `traefik/acme/account` to get/set Let's Encrypt certificates content.
|
||||||
|
The `consul` provider contains the configuration.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
It's possible to use others key-value store providers as described [here](/user-guide/kv-config/#key-value-store-configuration).
|
||||||
|
|
||||||
## Override entrypoints in frontends
|
## Override entrypoints in frontends
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
@@ -89,6 +251,7 @@ entryPoint = "https"
|
|||||||
[frontends.frontend2]
|
[frontends.frontend2]
|
||||||
backend = "backend1"
|
backend = "backend1"
|
||||||
passHostHeader = true
|
passHostHeader = true
|
||||||
|
passTLSCert = true
|
||||||
entrypoints = ["https"] # overrides defaultEntryPoints
|
entrypoints = ["https"] # overrides defaultEntryPoints
|
||||||
[frontends.frontend2.routes.test_1]
|
[frontends.frontend2.routes.test_1]
|
||||||
rule = "Host:{subdomain:[a-z]+}.localhost"
|
rule = "Host:{subdomain:[a-z]+}.localhost"
|
||||||
@@ -119,7 +282,7 @@ defaultEntryPoints = ["http"]
|
|||||||
## Pass Authenticated user to application via headers
|
## Pass Authenticated user to application via headers
|
||||||
|
|
||||||
Providing an authentication method as described above, it is possible to pass the user to the application
|
Providing an authentication method as described above, it is possible to pass the user to the application
|
||||||
via a configurable header value
|
via a configurable header value.
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
defaultEntryPoints = ["http"]
|
defaultEntryPoints = ["http"]
|
||||||
@@ -135,6 +298,91 @@ defaultEntryPoints = ["http"]
|
|||||||
## 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
|
```toml
|
||||||
IdleTimeout = "360s"
|
providersThrottleDuration = "5s"
|
||||||
ProvidersThrottleDuration = "5s"
|
|
||||||
|
[respondingTimeouts]
|
||||||
|
idleTimeout = "360s"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Securing Ping Health Check
|
||||||
|
|
||||||
|
The `/ping` health-check URL is enabled together with the web admin panel, enabled with the command-line `--web` or config file option `[web]`.
|
||||||
|
Thus, if you have a regular path for `/foo` and an entrypoint on `:80`, you would access them as follows:
|
||||||
|
|
||||||
|
* Regular path: `http://hostname:80/foo`
|
||||||
|
* Admin panel: `http://hostname:8080/`
|
||||||
|
* Ping URL: `http://hostname:8080/ping`
|
||||||
|
|
||||||
|
However, for security reasons, you may want to be able to expose the `/ping` health-check URL to outside health-checkers, e.g. an Internet service or cloud load-balancer, _without_ exposing your admin panel's port.
|
||||||
|
In many environments, the security staff may not _allow_ you to expose it.
|
||||||
|
|
||||||
|
You have two options:
|
||||||
|
|
||||||
|
* Enable `/ping` on a regular entrypoint
|
||||||
|
* Enable `/ping` on a dedicated port
|
||||||
|
|
||||||
|
### Enable ping health check on a regular entrypoint
|
||||||
|
|
||||||
|
To proxy `/ping` from a regular entrypoint to the admin one without exposing the panel, do the following:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[backends]
|
||||||
|
[backends.traefik]
|
||||||
|
[backends.traefik.servers.server1]
|
||||||
|
url = "http://localhost:8080"
|
||||||
|
weight = 10
|
||||||
|
|
||||||
|
[frontends]
|
||||||
|
[frontends.traefikadmin]
|
||||||
|
backend = "traefik"
|
||||||
|
[frontends.traefikadmin.routes.ping]
|
||||||
|
rule = "Path:/ping"
|
||||||
|
```
|
||||||
|
|
||||||
|
The above creates a new backend called `traefik`, listening on `http://localhost:8080`, i.e. the local admin port.
|
||||||
|
We only expose the admin panel via the `frontend` named `traefikadmin`, and only expose the `/ping` Path.
|
||||||
|
Be careful with the `traefikadmin` frontend. If you do _not_ specify a `Path:` rule, you would expose the entire dashboard.
|
||||||
|
|
||||||
|
### Enable ping health check on dedicated port
|
||||||
|
|
||||||
|
If you do not want to or cannot expose the health-check on a regular entrypoint - e.g. your security rules do not allow it, or you have a conflicting path - then you can enable health-check on its own entrypoint.
|
||||||
|
Use the following config:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
defaultEntryPoints = ["http"]
|
||||||
|
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.http]
|
||||||
|
address = ":80"
|
||||||
|
[entryPoints.ping]
|
||||||
|
address = ":8082"
|
||||||
|
|
||||||
|
[backends]
|
||||||
|
[backends.traefik]
|
||||||
|
[backends.traefik.servers.server1]
|
||||||
|
url = "http://localhost:8080"
|
||||||
|
weight = 10
|
||||||
|
|
||||||
|
[frontends]
|
||||||
|
[frontends.traefikadmin]
|
||||||
|
backend = "traefik"
|
||||||
|
entrypoints = ["ping"]
|
||||||
|
[frontends.traefikadmin.routes.ping]
|
||||||
|
rule = "Path:/ping"
|
||||||
|
```
|
||||||
|
|
||||||
|
The above is similar to the previous example, but instead of enabling `/ping` on the _default_ entrypoint, we enable it on a _dedicated_ entrypoint.
|
||||||
|
|
||||||
|
In the above example, you would access a regular path, admin panel and health-check as follows:
|
||||||
|
|
||||||
|
* Regular path: `http://hostname:80/foo`
|
||||||
|
* Admin panel: `http://hostname:8080/`
|
||||||
|
* Ping URL: `http://hostname:8082/ping`
|
||||||
|
|
||||||
|
Note the dedicated port `:8082` for `/ping`.
|
||||||
|
|
||||||
|
In the above example, it is _very_ important to create a named dedicated entrypoint, and do **not** include it in `defaultEntryPoints`.
|
||||||
|
Otherwise, you are likely to expose _all_ services via that entrypoint.
|
||||||
|
|
||||||
|
In the above example, we have two entrypoints, `http` and `ping`, but we only included `http` in `defaultEntryPoints`, while explicitly tying `frontend.traefikadmin` to the `ping` entrypoint.
|
||||||
|
This ensures that all the "normal" frontends will be exposed via entrypoint `http` and _not_ via entrypoint `ping`.
|
||||||
|
|||||||
151
docs/user-guide/grpc.md
Normal file
151
docs/user-guide/grpc.md
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
# gRPC example
|
||||||
|
|
||||||
|
This section explains how to use Traefik as reverse proxy for gRPC application with self-signed certificates.
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
As gRPC needs HTTP2, we need HTTPS certificates on both gRPC Server and Træfik.
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="/img/grpc.svg" alt="gRPC architecture" title="gRPC architecture" />
|
||||||
|
</p>
|
||||||
|
|
||||||
|
## gRPC Server certificate
|
||||||
|
|
||||||
|
In order to secure the gRPC server, we generate a self-signed certificate for backend url:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./backend.key -out ./backend.cert
|
||||||
|
```
|
||||||
|
|
||||||
|
That will prompt for information, the important answer is:
|
||||||
|
|
||||||
|
```
|
||||||
|
Common Name (e.g. server FQDN or YOUR name) []: backend.local
|
||||||
|
```
|
||||||
|
|
||||||
|
## gRPC Client certificate
|
||||||
|
|
||||||
|
Generate your self-signed certificate for frontend url:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./frontend.key -out ./frontend.cert
|
||||||
|
```
|
||||||
|
|
||||||
|
with
|
||||||
|
|
||||||
|
```
|
||||||
|
Common Name (e.g. server FQDN or YOUR name) []: frontend.local
|
||||||
|
```
|
||||||
|
|
||||||
|
## Træfik configuration
|
||||||
|
|
||||||
|
At last, we configure our Træfik instance to use both self-signed certificates.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
defaultEntryPoints = ["https"]
|
||||||
|
|
||||||
|
# For secure connection on backend.local
|
||||||
|
RootCAs = [ "./backend.cert" ]
|
||||||
|
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.https]
|
||||||
|
address = ":4443"
|
||||||
|
[entryPoints.https.tls]
|
||||||
|
# For secure connection on frontend.local
|
||||||
|
[[entryPoints.https.tls.certificates]]
|
||||||
|
certFile = "./frontend.cert"
|
||||||
|
keyFile = "./frontend.key"
|
||||||
|
|
||||||
|
|
||||||
|
[web]
|
||||||
|
address = ":8080"
|
||||||
|
|
||||||
|
[file]
|
||||||
|
|
||||||
|
[backends]
|
||||||
|
[backends.backend1]
|
||||||
|
[backends.backend1.servers.server1]
|
||||||
|
# Access on backend with HTTPS
|
||||||
|
url = "https://backend.local:8080"
|
||||||
|
|
||||||
|
|
||||||
|
[frontends]
|
||||||
|
[frontends.frontend1]
|
||||||
|
backend = "backend1"
|
||||||
|
[frontends.frontend1.routes.test_1]
|
||||||
|
rule = "Host:frontend.local"
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! 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.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
We don't need specific configuration to use gRPC in Træfik, we just need to be careful that all the exchanges (between client and Træfik, and between Træfik and backend) are HTTPS communications because gRPC uses HTTP2.
|
||||||
|
|
||||||
|
## A gRPC example in go
|
||||||
|
|
||||||
|
We will use the gRPC greeter example in [grpc-go](https://github.com/grpc/grpc-go/tree/master/examples/helloworld)
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
In order to use this gRPC example, we need to modify it to use HTTPS
|
||||||
|
|
||||||
|
So we modify the "gRPC server example" to use our own self-signed certificate:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Read cert and key file
|
||||||
|
BackendCert, _ := ioutil.ReadFile("./backend.cert")
|
||||||
|
BackendKey, _ := ioutil.ReadFile("./backend.key")
|
||||||
|
|
||||||
|
// Generate Certificate struct
|
||||||
|
cert, err := tls.X509KeyPair(BackendCert, BackendKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to parse certificate: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create credentials
|
||||||
|
creds := credentials.NewServerTLSFromCert(&cert)
|
||||||
|
|
||||||
|
// Use Credentials in gRPC server options
|
||||||
|
serverOption := grpc.Creds(creds)
|
||||||
|
var s *grpc.Server = grpc.NewServer(serverOption)
|
||||||
|
defer s.Stop()
|
||||||
|
|
||||||
|
pb.RegisterGreeterServer(s, &server{})
|
||||||
|
err := s.Serve(lis)
|
||||||
|
|
||||||
|
// ...
|
||||||
|
```
|
||||||
|
|
||||||
|
Next we will modify gRPC Client to use our Træfik self-signed certificate:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Read cert file
|
||||||
|
FrontendCert, _ := ioutil.ReadFile("./frontend.cert")
|
||||||
|
|
||||||
|
// Create CertPool
|
||||||
|
roots := x509.NewCertPool()
|
||||||
|
roots.AppendCertsFromPEM(FrontendCert)
|
||||||
|
|
||||||
|
// Create credentials
|
||||||
|
credsClient := credentials.NewClientTLSFromCert(roots, "")
|
||||||
|
|
||||||
|
// Dial with specific Transport (with credentials)
|
||||||
|
conn, err := grpc.Dial("frontend.local:4443", grpc.WithTransportCredentials(credsClient))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("did not connect: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer conn.Close()
|
||||||
|
client := pb.NewGreeterClient(conn)
|
||||||
|
|
||||||
|
name := "World"
|
||||||
|
r, err := client.SayHello(context.Background(), &pb.HelloRequest{Name: name})
|
||||||
|
|
||||||
|
// ...
|
||||||
|
```
|
||||||
|
|
||||||
@@ -1,26 +1,30 @@
|
|||||||
# Kubernetes Ingress Controller
|
# Kubernetes Ingress Controller
|
||||||
|
|
||||||
This guide explains how to use Træfik as an Ingress controller in a Kubernetes cluster.
|
This guide explains how to use Træfik as an Ingress controller in a Kubernetes cluster.
|
||||||
If you are not familiar with Ingresses in Kubernetes you might want to read the [Kubernetes user guide](http://kubernetes.io/docs/user-guide/ingress/)
|
|
||||||
|
If you are not familiar with Ingresses in Kubernetes you might want to read the [Kubernetes user guide](https://kubernetes.io/docs/concepts/services-networking/ingress/)
|
||||||
|
|
||||||
The config files used in this guide can be found in the [examples directory](https://github.com/containous/traefik/tree/master/examples/k8s)
|
The config files used in this guide can be found in the [examples directory](https://github.com/containous/traefik/tree/master/examples/k8s)
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
1. A working Kubernetes cluster. If you want to follow along with this guide, you should setup [minikube](http://kubernetes.io/docs/getting-started-guides/minikube/)
|
1. A working Kubernetes cluster. If you want to follow along with this guide, you should setup [minikube](https://kubernetes.io/docs/getting-started-guides/minikube/)
|
||||||
on your machine, as it is the quickest way to get a local Kubernetes cluster setup for experimentation and development.
|
on your machine, as it is the quickest way to get a local Kubernetes cluster setup for experimentation and development.
|
||||||
|
|
||||||
2. The `kubectl` binary should be [installed on your workstation](http://kubernetes.io/docs/getting-started-guides/minikube/#download-kubectl).
|
2. The `kubectl` binary should be [installed on your workstation](https://kubernetes.io/docs/getting-started-guides/minikube/#download-kubectl).
|
||||||
|
|
||||||
### Role Based Access Control configuration (Kubernetes 1.6+ only)
|
### Role Based Access Control configuration (Kubernetes 1.6+ only)
|
||||||
|
|
||||||
Kubernetes introduces [Role Based Access Control (RBAC)](https://kubernetes.io/docs/admin/authorization/rbac/) in 1.6+ to allow fine-grained control
|
Kubernetes introduces [Role Based Access Control (RBAC)](https://kubernetes.io/docs/admin/authorization/rbac/) in 1.6+ to allow fine-grained control of Kubernetes resources and API.
|
||||||
of Kubernetes resources and api.
|
|
||||||
|
|
||||||
If your cluster is configured with RBAC, you may need to authorize Traefik to use
|
If your cluster is configured with RBAC, you will need to authorize Træfik to use the Kubernetes API. There are two ways to set up the proper permission: Via namespace-specific RoleBindings or a single, global ClusterRoleBinding.
|
||||||
kubernetes API using ClusterRole and ClusterRoleBinding resources:
|
|
||||||
|
|
||||||
_Note: your cluster may have suitable ClusterRoles already setup, but the following should work everywhere_
|
RoleBindings per namespace enable to restrict granted permissions to the very namespaces only that Træfik is watching over, thereby following the least-privileges principle. This is the preferred approach if Træfik is not supposed to watch all namespaces, and the set of namespaces does not change dynamically. Otherwise, a single ClusterRoleBinding must be employed.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
RoleBindings per namespace are available in Træfik 1.5 and later. Please use ClusterRoleBindings for older versions.
|
||||||
|
|
||||||
|
For the sake of simplicity, this guide will use a ClusterRoleBinding:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
---
|
---
|
||||||
@@ -32,9 +36,9 @@ rules:
|
|||||||
- apiGroups:
|
- apiGroups:
|
||||||
- ""
|
- ""
|
||||||
resources:
|
resources:
|
||||||
- pods
|
|
||||||
- services
|
- services
|
||||||
- endpoints
|
- endpoints
|
||||||
|
- secrets
|
||||||
verbs:
|
verbs:
|
||||||
- get
|
- get
|
||||||
- list
|
- list
|
||||||
@@ -68,11 +72,18 @@ subjects:
|
|||||||
kubectl apply -f https://raw.githubusercontent.com/containous/traefik/master/examples/k8s/traefik-rbac.yaml
|
kubectl apply -f https://raw.githubusercontent.com/containous/traefik/master/examples/k8s/traefik-rbac.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
## Deploy Træfik using a Deployment object
|
For namespaced restrictions, one RoleBinding is required per watched namespace along with a corresponding configuration of Træfik's `kubernetes.namespaces` parameter.
|
||||||
|
|
||||||
We are going to deploy Træfik with a
|
## Deploy Træfik using a Deployment or DaemonSet
|
||||||
[Deployment](http://kubernetes.io/docs/user-guide/deployments/), as this will
|
|
||||||
allow you to easily roll out config changes or update the image.
|
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 Deployment objects looks like this:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
---
|
---
|
||||||
@@ -105,72 +116,168 @@ spec:
|
|||||||
containers:
|
containers:
|
||||||
- image: traefik
|
- image: traefik
|
||||||
name: traefik-ingress-lb
|
name: traefik-ingress-lb
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
cpu: 200m
|
|
||||||
memory: 30Mi
|
|
||||||
requests:
|
|
||||||
cpu: 100m
|
|
||||||
memory: 20Mi
|
|
||||||
ports:
|
|
||||||
- containerPort: 80
|
|
||||||
hostPort: 80
|
|
||||||
- containerPort: 8080
|
|
||||||
args:
|
args:
|
||||||
- --web
|
- --web
|
||||||
- --kubernetes
|
- --kubernetes
|
||||||
|
---
|
||||||
|
kind: Service
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: traefik-ingress-service
|
||||||
|
namespace: kube-system
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
k8s-app: traefik-ingress-lb
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 80
|
||||||
|
name: web
|
||||||
|
- protocol: TCP
|
||||||
|
port: 8080
|
||||||
|
name: admin
|
||||||
|
type: NodePort
|
||||||
```
|
```
|
||||||
[examples/k8s/traefik.yaml](https://github.com/containous/traefik/tree/master/examples/k8s/traefik.yaml)
|
[examples/k8s/traefik-deployment.yaml](https://github.com/containous/traefik/tree/master/examples/k8s/traefik-deployment.yaml)
|
||||||
|
|
||||||
> notice that we binding port 80 on the Træfik container to port 80 on the host.
|
!!! note
|
||||||
> With a multi node cluster we might expose Træfik with a NodePort or LoadBalancer service
|
The Service will expose two NodePorts which allow access to the ingress and the web interface.
|
||||||
> and run more than 1 replica of Træfik for high availability.
|
|
||||||
|
|
||||||
To deploy Træfik to your cluster start by submitting the deployment to the cluster with `kubectl`:
|
The DaemonSet objects looks not much different:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: traefik-ingress-controller
|
||||||
|
namespace: kube-system
|
||||||
|
---
|
||||||
|
kind: DaemonSet
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
metadata:
|
||||||
|
name: traefik-ingress-controller
|
||||||
|
namespace: kube-system
|
||||||
|
labels:
|
||||||
|
k8s-app: traefik-ingress-lb
|
||||||
|
spec:
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
k8s-app: traefik-ingress-lb
|
||||||
|
name: traefik-ingress-lb
|
||||||
|
spec:
|
||||||
|
serviceAccountName: traefik-ingress-controller
|
||||||
|
terminationGracePeriodSeconds: 60
|
||||||
|
hostNetwork: true
|
||||||
|
containers:
|
||||||
|
- image: traefik
|
||||||
|
name: traefik-ingress-lb
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
containerPort: 80
|
||||||
|
hostPort: 80
|
||||||
|
- name: admin
|
||||||
|
containerPort: 8080
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
args:
|
||||||
|
- -d
|
||||||
|
- --web
|
||||||
|
- --kubernetes
|
||||||
|
---
|
||||||
|
kind: Service
|
||||||
|
apiVersion: v1
|
||||||
|
metadata:
|
||||||
|
name: traefik-ingress-service
|
||||||
|
namespace: kube-system
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
k8s-app: traefik-ingress-lb
|
||||||
|
ports:
|
||||||
|
- protocol: TCP
|
||||||
|
port: 80
|
||||||
|
name: web
|
||||||
|
- 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)
|
||||||
|
|
||||||
|
To deploy Træfik to your cluster start by submitting one of the YAML files to the cluster with `kubectl`:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
kubectl apply -f https://raw.githubusercontent.com/containous/traefik/master/examples/k8s/traefik.yaml
|
kubectl apply -f https://raw.githubusercontent.com/containous/traefik/master/examples/k8s/traefik-deployment.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
### Check the deployment
|
```shell
|
||||||
|
kubectl apply -f https://raw.githubusercontent.com/containous/traefik/master/examples/k8s/traefik-ds.yaml
|
||||||
|
```
|
||||||
|
|
||||||
Now lets check if our deployment was successful.
|
There are some significant differences between using Deployments and DaemonSets:
|
||||||
|
|
||||||
|
- The Deployment has easier up and down scaling possibilities.
|
||||||
|
It can implement full pod lifecycle and supports rolling updates from Kubernetes 1.2.
|
||||||
|
At least one Pod is needed to run the Deployment.
|
||||||
|
- The DaemonSet automatically scales to all nodes that meets a specific selector and guarantees to fill nodes one at a time.
|
||||||
|
Rolling updates are fully supported from Kubernetes 1.7 for DaemonSets as well.
|
||||||
|
|
||||||
|
### Check the Pods
|
||||||
|
|
||||||
|
Now lets check if our command was successful.
|
||||||
|
|
||||||
Start by listing the pods in the `kube-system` namespace:
|
Start by listing the pods in the `kube-system` namespace:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$kubectl --namespace=kube-system get pods
|
kubectl --namespace=kube-system get pods
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
NAME READY STATUS RESTARTS AGE
|
NAME READY STATUS RESTARTS AGE
|
||||||
kube-addon-manager-minikubevm 1/1 Running 0 4h
|
kube-addon-manager-minikubevm 1/1 Running 0 4h
|
||||||
kubernetes-dashboard-s8krj 1/1 Running 0 4h
|
kubernetes-dashboard-s8krj 1/1 Running 0 4h
|
||||||
traefik-ingress-controller-678226159-eqseo 1/1 Running 0 7m
|
traefik-ingress-controller-678226159-eqseo 1/1 Running 0 7m
|
||||||
```
|
```
|
||||||
|
|
||||||
You should see that after submitting the Deployment to Kubernetes it has launched
|
You should see that after submitting the Deployment or DaemonSet to Kubernetes it has launched a Pod, and it is now running.
|
||||||
a pod, and it is now running. _It might take a few moments for kubernetes to pull
|
_It might take a few moments for kubernetes to pull the Træfik image and start the container._
|
||||||
the Træfik image and start the container._
|
|
||||||
|
|
||||||
> You could also check the deployment with the Kubernetes dashboard, run
|
!!! note
|
||||||
> `minikube dashboard` to open it in your browser, then choose the `kube-system`
|
You could also check the deployment with the Kubernetes dashboard, run
|
||||||
> namespace from the menu at the top right of the screen.
|
`minikube dashboard` to open it in your browser, then choose the `kube-system`
|
||||||
|
namespace from the menu at the top right of the screen.
|
||||||
|
|
||||||
You should now be able to access Træfik on port 80 of your minikube instance.
|
You should now be able to access Træfik on port 80 of your Minikube instance when using the DaemonSet:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
curl $(minikube ip)
|
curl $(minikube ip)
|
||||||
|
```
|
||||||
|
```
|
||||||
404 page not found
|
404 page not found
|
||||||
```
|
```
|
||||||
|
|
||||||
> We expect to see a 404 response here as we haven't yet given Træfik any configuration.
|
If you decided to use the deployment, then you need to target the correct NodePort, which can be seen then you execute `kubectl get services --namespace=kube-system`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl $(minikube ip):<NODEPORT>
|
||||||
|
```
|
||||||
|
```
|
||||||
|
404 page not found
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
We expect to see a 404 response here as we haven't yet given Træfik any configuration.
|
||||||
|
|
||||||
## Deploy Træfik using Helm Chart
|
## Deploy Træfik using Helm Chart
|
||||||
|
|
||||||
Instead of installing Træfik via a Deployment object, you can also use the Træfik Helm chart.
|
Instead of installing Træfik via an own object, you can also use the Træfik Helm chart.
|
||||||
|
|
||||||
|
This allows more complex configuration via Kubernetes [ConfigMap](https://kubernetes.io/docs/tasks/configure-pod-container/configmap/) and enabled TLS certificates.
|
||||||
|
|
||||||
Install Træfik chart by:
|
Install Træfik chart by:
|
||||||
|
|
||||||
```sh
|
```shell
|
||||||
helm install stable/traefik
|
helm install stable/traefik
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -178,8 +285,7 @@ For more information, check out [the doc](https://github.com/kubernetes/charts/t
|
|||||||
|
|
||||||
## Submitting An Ingress to the cluster.
|
## Submitting An Ingress to the cluster.
|
||||||
|
|
||||||
Lets start by creating a Service and an Ingress that will expose the
|
Lets start by creating a Service and an Ingress that will expose the [Træfik Web UI](https://github.com/containous/traefik#web-ui).
|
||||||
[Træfik Web UI](https://github.com/containous/traefik#web-ui).
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
@@ -203,7 +309,7 @@ metadata:
|
|||||||
kubernetes.io/ingress.class: traefik
|
kubernetes.io/ingress.class: traefik
|
||||||
spec:
|
spec:
|
||||||
rules:
|
rules:
|
||||||
- host: traefik-ui.local
|
- host: traefik-ui.minikube
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
- backend:
|
- backend:
|
||||||
@@ -216,23 +322,86 @@ spec:
|
|||||||
kubectl apply -f https://raw.githubusercontent.com/containous/traefik/master/examples/k8s/ui.yaml
|
kubectl apply -f https://raw.githubusercontent.com/containous/traefik/master/examples/k8s/ui.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
Now lets setup an entry in our /etc/hosts file to route `traefik-ui.local`
|
Now lets setup an entry in our /etc/hosts file to route `traefik-ui.minikube` to our cluster.
|
||||||
to our cluster.
|
|
||||||
|
|
||||||
> In production you would want to set up real dns entries.
|
In production you would want to set up real dns entries.
|
||||||
|
You can get the ip address of your minikube instance by running `minikube ip`
|
||||||
> You can get the ip address of your minikube instance by running `minikube ip`
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
echo "$(minikube ip) traefik-ui.local" | sudo tee -a /etc/hosts
|
echo "$(minikube ip) traefik-ui.minikube" | sudo tee -a /etc/hosts
|
||||||
```
|
```
|
||||||
|
|
||||||
We should now be able to visit [traefik-ui.local](http://traefik-ui.local) in the browser and view the Træfik Web UI.
|
We should now be able to visit [traefik-ui.minikube](http://traefik-ui.minikube) in the browser and view the Træfik Web UI.
|
||||||
|
|
||||||
|
## Basic Authentication
|
||||||
|
|
||||||
|
It's possible to add additional authentication annotations in the Ingress rule.
|
||||||
|
The source of the authentication is a secret that contains usernames and passwords inside the key auth.
|
||||||
|
To read about basic auth limitations see the [Kubernetes Ingress](/configuration/backends/kubernetes) configuration page.
|
||||||
|
|
||||||
|
#### Creating the Secret
|
||||||
|
|
||||||
|
A. Use `htpasswd` to create a file containing the username and the base64-encoded password:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
htpasswd -c ./auth myusername
|
||||||
|
```
|
||||||
|
|
||||||
|
You will be prompted for a password which you will have to enter twice.
|
||||||
|
`htpasswd` will create a file with the following:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cat auth
|
||||||
|
```
|
||||||
|
```
|
||||||
|
myusername:$apr1$78Jyn/1K$ERHKVRPPlzAX8eBtLuvRZ0
|
||||||
|
```
|
||||||
|
|
||||||
|
B. Now use `kubectl` to create a secret in the monitoring namespace using the file created by `htpasswd`.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
kubectl create secret generic mysecret --from-file auth --namespace=monitoring
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
Secret must be in same namespace as the ingress rule.
|
||||||
|
|
||||||
|
C. Create the ingress using the following annotations to specify basic auth and that the username and password is stored in `mysecret`.
|
||||||
|
|
||||||
|
- `ingress.kubernetes.io/auth-type: "basic"`
|
||||||
|
- `ingress.kubernetes.io/auth-secret: "mysecret"`
|
||||||
|
|
||||||
|
Following is a full ingress example based on Prometheus:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: prometheus-dashboard
|
||||||
|
namespace: monitoring
|
||||||
|
annotations:
|
||||||
|
kubernetes.io/ingress.class: traefik
|
||||||
|
ingress.kubernetes.io/auth-type: "basic"
|
||||||
|
ingress.kubernetes.io/auth-secret: "mysecret"
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: dashboard.prometheus.example.com
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- backend:
|
||||||
|
serviceName: prometheus
|
||||||
|
servicePort: 9090
|
||||||
|
```
|
||||||
|
|
||||||
|
You can apply the example ingress as following:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
kubectl create -f prometheus-ingress.yaml -n monitoring
|
||||||
|
```
|
||||||
|
|
||||||
## Name based routing
|
## Name based routing
|
||||||
|
|
||||||
In this example we are going to setup websites for 3 of the United Kingdoms
|
In this example we are going to setup websites for 3 of the United Kingdoms best loved cheeses, Cheddar, Stilton and Wensleydale.
|
||||||
best loved cheeses, Cheddar, Stilton and Wensleydale.
|
|
||||||
|
|
||||||
First lets start by launching the 3 pods for the cheese websites.
|
First lets start by launching the 3 pods for the cheese websites.
|
||||||
|
|
||||||
@@ -261,13 +430,6 @@ spec:
|
|||||||
containers:
|
containers:
|
||||||
- name: cheese
|
- name: cheese
|
||||||
image: errm/cheese:stilton
|
image: errm/cheese:stilton
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 100m
|
|
||||||
memory: 50Mi
|
|
||||||
limits:
|
|
||||||
cpu: 100m
|
|
||||||
memory: 50Mi
|
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- containerPort: 80
|
||||||
---
|
---
|
||||||
@@ -294,13 +456,6 @@ spec:
|
|||||||
containers:
|
containers:
|
||||||
- name: cheese
|
- name: cheese
|
||||||
image: errm/cheese:cheddar
|
image: errm/cheese:cheddar
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 100m
|
|
||||||
memory: 50Mi
|
|
||||||
limits:
|
|
||||||
cpu: 100m
|
|
||||||
memory: 50Mi
|
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- containerPort: 80
|
||||||
---
|
---
|
||||||
@@ -327,13 +482,6 @@ spec:
|
|||||||
containers:
|
containers:
|
||||||
- name: cheese
|
- name: cheese
|
||||||
image: errm/cheese:wensleydale
|
image: errm/cheese:wensleydale
|
||||||
resources:
|
|
||||||
requests:
|
|
||||||
cpu: 100m
|
|
||||||
memory: 50Mi
|
|
||||||
limits:
|
|
||||||
cpu: 100m
|
|
||||||
memory: 50Mi
|
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- containerPort: 80
|
||||||
```
|
```
|
||||||
@@ -389,8 +537,8 @@ spec:
|
|||||||
task: wensleydale
|
task: wensleydale
|
||||||
```
|
```
|
||||||
|
|
||||||
> Notice that we also set a [circuit breaker expression](https://docs.traefik.io/basics/#backends) for one of the backends
|
!!! note
|
||||||
> by setting the `traefik.backend.circuitbreaker` annotation on the service.
|
We also set a [circuit breaker expression](/basics/#backends) for one of the backends by setting the `traefik.backend.circuitbreaker` annotation on the service.
|
||||||
|
|
||||||
|
|
||||||
[examples/k8s/cheese-services.yaml](https://github.com/containous/traefik/tree/master/examples/k8s/cheese-services.yaml)
|
[examples/k8s/cheese-services.yaml](https://github.com/containous/traefik/tree/master/examples/k8s/cheese-services.yaml)
|
||||||
@@ -410,21 +558,21 @@ metadata:
|
|||||||
kubernetes.io/ingress.class: traefik
|
kubernetes.io/ingress.class: traefik
|
||||||
spec:
|
spec:
|
||||||
rules:
|
rules:
|
||||||
- host: stilton.local
|
- host: stilton.minikube
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
- path: /
|
- path: /
|
||||||
backend:
|
backend:
|
||||||
serviceName: stilton
|
serviceName: stilton
|
||||||
servicePort: http
|
servicePort: http
|
||||||
- host: cheddar.local
|
- host: cheddar.minikube
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
- path: /
|
- path: /
|
||||||
backend:
|
backend:
|
||||||
serviceName: cheddar
|
serviceName: cheddar
|
||||||
servicePort: http
|
servicePort: http
|
||||||
- host: wensleydale.local
|
- host: wensleydale.minikube
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
- path: /
|
- path: /
|
||||||
@@ -434,36 +582,32 @@ spec:
|
|||||||
```
|
```
|
||||||
[examples/k8s/cheese-ingress.yaml](https://github.com/containous/traefik/tree/master/examples/k8s/cheese-ingress.yaml)
|
[examples/k8s/cheese-ingress.yaml](https://github.com/containous/traefik/tree/master/examples/k8s/cheese-ingress.yaml)
|
||||||
|
|
||||||
> Notice that we list each hostname, and add a backend service.
|
!!! note
|
||||||
|
we list each hostname, and add a backend service.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
kubectl apply -f https://raw.githubusercontent.com/containous/traefik/master/examples/k8s/cheese-ingress.yaml
|
kubectl apply -f https://raw.githubusercontent.com/containous/traefik/master/examples/k8s/cheese-ingress.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
Now visit the [Træfik dashboard](http://traefik-ui.local/) and you should
|
Now visit the [Træfik dashboard](http://traefik-ui.minikube/) and you should see a frontend for each host.
|
||||||
see a frontend for each host. Along with a backend listing for each service
|
Along with a backend listing for each service with a Server set up for each pod.
|
||||||
with a Server set up for each pod.
|
|
||||||
|
|
||||||
If you edit your `/etc/hosts` again you should be able to access the cheese
|
If you edit your `/etc/hosts` again you should be able to access the cheese websites in your browser.
|
||||||
websites in your browser.
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
echo "$(minikube ip) stilton.local cheddar.local wensleydale.local" | sudo tee -a /etc/hosts
|
echo "$(minikube ip) stilton.minikube cheddar.minikube wensleydale.minikube" | sudo tee -a /etc/hosts
|
||||||
```
|
```
|
||||||
|
|
||||||
* [Stilton](http://stilton.local/)
|
* [Stilton](http://stilton.minikube/)
|
||||||
* [Cheddar](http://cheddar.local/)
|
* [Cheddar](http://cheddar.minikube/)
|
||||||
* [Wensleydale](http://wensleydale.local/)
|
* [Wensleydale](http://wensleydale.minikube/)
|
||||||
|
|
||||||
## Path based routing
|
## Path based routing
|
||||||
|
|
||||||
Now lets suppose that our fictional client has decided that while they are
|
Now lets suppose that our fictional client has decided that while they are super happy about our cheesy web design, when they asked for 3 websites they had not really bargained on having to buy 3 domain names.
|
||||||
super happy about our cheesy web design, when they asked for 3 websites
|
|
||||||
they had not really bargained on having to buy 3 domain names.
|
|
||||||
|
|
||||||
No problem, we say, why don't we reconfigure the sites to host all 3 under one domain.
|
No problem, we say, why don't we reconfigure the sites to host all 3 under one domain.
|
||||||
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: extensions/v1beta1
|
apiVersion: extensions/v1beta1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
@@ -474,7 +618,7 @@ metadata:
|
|||||||
traefik.frontend.rule.type: PathPrefixStrip
|
traefik.frontend.rule.type: PathPrefixStrip
|
||||||
spec:
|
spec:
|
||||||
rules:
|
rules:
|
||||||
- host: cheeses.local
|
- host: cheeses.minikube
|
||||||
http:
|
http:
|
||||||
paths:
|
paths:
|
||||||
- path: /stilton
|
- path: /stilton
|
||||||
@@ -492,43 +636,90 @@ spec:
|
|||||||
```
|
```
|
||||||
[examples/k8s/cheeses-ingress.yaml](https://github.com/containous/traefik/tree/master/examples/k8s/cheeses-ingress.yaml)
|
[examples/k8s/cheeses-ingress.yaml](https://github.com/containous/traefik/tree/master/examples/k8s/cheeses-ingress.yaml)
|
||||||
|
|
||||||
> Notice that we are configuring Træfik to strip the prefix from the url path
|
!!! note
|
||||||
> with the `traefik.frontend.rule.type` annotation so that we can use
|
we are configuring Træfik to strip the prefix from the url path with the `traefik.frontend.rule.type` annotation so that we can use the containers from the previous example without modification.
|
||||||
> the containers from the previous example without modification.
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
kubectl apply -f https://raw.githubusercontent.com/containous/traefik/master/examples/k8s/cheeses-ingress.yaml
|
kubectl apply -f https://raw.githubusercontent.com/containous/traefik/master/examples/k8s/cheeses-ingress.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
echo "$(minikube ip) cheeses.local" | sudo tee -a /etc/hosts
|
echo "$(minikube ip) cheeses.minikube" | sudo tee -a /etc/hosts
|
||||||
```
|
```
|
||||||
|
|
||||||
You should now be able to visit the websites in your browser.
|
You should now be able to visit the websites in your browser.
|
||||||
|
|
||||||
* [cheeses.local/stilton](http://cheeses.local/stilton/)
|
* [cheeses.minikube/stilton](http://cheeses.minikube/stilton/)
|
||||||
* [cheeses.local/cheddar](http://cheeses.local/cheddar/)
|
* [cheeses.minikube/cheddar](http://cheeses.minikube/cheddar/)
|
||||||
* [cheeses.local/wensleydale](http://cheeses.local/wensleydale/)
|
* [cheeses.minikube/wensleydale](http://cheeses.minikube/wensleydale/)
|
||||||
|
|
||||||
|
## Specifying priority for routing
|
||||||
|
|
||||||
|
Sometimes you need to specify priority for ingress route, especially when handling wildcard routes.
|
||||||
|
This can be done by adding annotation `traefik.frontend.priority`, i.e.:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
apiVersion: extensions/v1beta1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: wildcard-cheeses
|
||||||
|
annotations:
|
||||||
|
traefik.frontend.priority: "1"
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: *.minikube
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
backend:
|
||||||
|
serviceName: stilton
|
||||||
|
servicePort: http
|
||||||
|
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: specific-cheeses
|
||||||
|
annotations:
|
||||||
|
traefik.frontend.priority: "2"
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: specific.minikube
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
backend:
|
||||||
|
serviceName: stilton
|
||||||
|
servicePort: http
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that priority values must be quoted to avoid them being interpreted as numbers (which are illegal for annotations).
|
||||||
|
|
||||||
|
## Forwarding to ExternalNames
|
||||||
|
|
||||||
|
When specifying an [ExternalName](https://kubernetes.io/docs/concepts/services-networking/service/#services-without-selectors),
|
||||||
|
Træfik will forward requests to the given host accordingly and use HTTPS when the Service port matches 443.
|
||||||
|
This still requires setting up a proper port mapping on the Service from the Ingress port to the (external) Service port.
|
||||||
|
|
||||||
## Disable passing the Host header
|
## Disable passing the Host header
|
||||||
|
|
||||||
By default Træfik will pass the incoming Host header on to the upstream resource.
|
By default Træfik will pass the incoming Host header on to the upstream resource.
|
||||||
|
|
||||||
There are times however where you may not want this to be the case.
|
There are times however where you may not want this to be the case.
|
||||||
For example if your service is of the ExternalName type.
|
For example if your service is of the ExternalName type.
|
||||||
|
|
||||||
### Disable entirely
|
### Disable entirely
|
||||||
|
|
||||||
Add the following to your toml config:
|
Add the following to your toml config:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
disablePassHostHeaders = true
|
disablePassHostHeaders = true
|
||||||
```
|
```
|
||||||
|
|
||||||
### Disable per ingress
|
### Disable per ingress
|
||||||
|
|
||||||
To disable passing the Host header per ingress resource set the `traefik.frontend.passHostHeader`
|
To disable passing the Host header per ingress resource set the `traefik.frontend.passHostHeader` annotation on your ingress to `false`.
|
||||||
annotation on your ingress to `false`.
|
|
||||||
|
|
||||||
Here is an example ingress definition:
|
Here is an example ingress definition:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: extensions/v1beta1
|
apiVersion: extensions/v1beta1
|
||||||
kind: Ingress
|
kind: Ingress
|
||||||
@@ -549,6 +740,7 @@ spec:
|
|||||||
```
|
```
|
||||||
|
|
||||||
And an example service definition:
|
And an example service definition:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Service
|
kind: Service
|
||||||
@@ -562,19 +754,40 @@ spec:
|
|||||||
externalName: static.otherdomain.com
|
externalName: static.otherdomain.com
|
||||||
```
|
```
|
||||||
|
|
||||||
If you were to visit example.com/static the request would then be passed onto
|
If you were to visit `example.com/static` the request would then be passed onto `static.otherdomain.com/static` and s`tatic.otherdomain.com` would receive the request with the Host header being `static.otherdomain.com`.
|
||||||
static.otherdomain.com/static and static.otherdomain.com would receive the
|
|
||||||
request with the Host header being static.otherdomain.com.
|
|
||||||
|
|
||||||
Note: The per ingress annotation overides whatever the global value is set to.
|
!!! note
|
||||||
So you could set `disablePassHostHeaders` to `true` in your toml file and then enable passing
|
The per ingress annotation overides whatever the global value is set to.
|
||||||
the host header per ingress if you wanted.
|
So you could set `disablePassHostHeaders` to `true` in your toml file and then enable passing
|
||||||
|
the host header per ingress if you wanted.
|
||||||
|
|
||||||
## Excluding an ingress from Træfik
|
## Partitioning the Ingress object space
|
||||||
|
|
||||||
You can control which ingress Træfik cares about by using the `kubernetes.io/ingress.class` annotation.
|
By default, Træfik processes every Ingress objects it observes. At times, however, it may be desirable to ignore certain objects. The following sub-sections describe common use cases and how they can be handled with Træfik.
|
||||||
By default if the annotation is not set at all Træfik will include the ingress.
|
|
||||||
If the annotation is set to anything other than traefik or a blank string Træfik will ignore it.
|
|
||||||
|
|
||||||
|
### Between Træfik and other Ingress controller implementations
|
||||||
|
|
||||||

|
Sometimes Træfik runs along other Ingress controller implementations. One such example is when both Træfik and a cloud provider Ingress controller are active.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
### Resource limitations
|
||||||
|
|
||||||
|
The examples shown deliberately do not specify any [resource limitations](https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/) as there is no one size fits all.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
When in doubt, you should measure your resource needs, and adjust requests and limits accordingly.
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
# Key-value store configuration
|
# Key-value store configuration
|
||||||
|
|
||||||
Both [static global configuration](/user-guide/kv-config/#static-configuration-in-key-value-store) and [dynamic](/user-guide/kv-config/#dynamic-configuration-in-key-value-store) configuration can be sorted in a Key-value store.
|
Both [static global configuration](/user-guide/kv-config/#static-configuration-in-key-value-store) and [dynamic](/user-guide/kv-config/#dynamic-configuration-in-key-value-store) configuration can be sorted in a Key-value store.
|
||||||
@@ -9,20 +8,23 @@ Træfik supports several Key-value stores:
|
|||||||
|
|
||||||
- [Consul](https://consul.io)
|
- [Consul](https://consul.io)
|
||||||
- [etcd](https://coreos.com/etcd/)
|
- [etcd](https://coreos.com/etcd/)
|
||||||
- [ZooKeeper](https://zookeeper.apache.org/)
|
- [ZooKeeper](https://zookeeper.apache.org/)
|
||||||
- [boltdb](https://github.com/boltdb/bolt)
|
- [boltdb](https://github.com/boltdb/bolt)
|
||||||
|
|
||||||
# Static configuration in Key-value store
|
## Static configuration in Key-value store
|
||||||
|
|
||||||
We will see the steps to set it up with an easy example.
|
We will see the steps to set it up with an easy example.
|
||||||
Note that we could do the same with any other Key-value Store.
|
|
||||||
|
|
||||||
## docker-compose file for Consul
|
!!! note
|
||||||
|
We could do the same with any other Key-value Store.
|
||||||
|
|
||||||
The Træfik global configuration will be getted from a [Consul](https://consul.io) store.
|
### docker-compose file for Consul
|
||||||
|
|
||||||
First we have to launch Consul in a container.
|
The Træfik global configuration will be retrieved from a [Consul](https://consul.io) store.
|
||||||
The [docker-compose file](https://docs.docker.com/compose/compose-file/) allows us to launch Consul and four instances of the trivial app [emilevauge/whoamI](https://github.com/emilevauge/whoamI) :
|
|
||||||
|
First we have to launch Consul in a container.
|
||||||
|
|
||||||
|
The [docker-compose file](https://docs.docker.com/compose/compose-file/) allows us to launch Consul and four instances of the trivial app [emilevauge/whoamI](https://github.com/emilevauge/whoamI) :
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
consul:
|
consul:
|
||||||
@@ -37,27 +39,27 @@ consul:
|
|||||||
- "8301"
|
- "8301"
|
||||||
- "8301/udp"
|
- "8301/udp"
|
||||||
- "8302"
|
- "8302"
|
||||||
- "8302/udp"
|
- "8302/udp"
|
||||||
|
|
||||||
whoami1:
|
whoami1:
|
||||||
image: emilevauge/whoami
|
image: emilevauge/whoami
|
||||||
|
|
||||||
whoami2:
|
whoami2:
|
||||||
image: emilevauge/whoami
|
image: emilevauge/whoami
|
||||||
|
|
||||||
whoami3:
|
whoami3:
|
||||||
image: emilevauge/whoami
|
image: emilevauge/whoami
|
||||||
|
|
||||||
whoami4:
|
whoami4:
|
||||||
image: emilevauge/whoami
|
image: emilevauge/whoami
|
||||||
```
|
```
|
||||||
|
|
||||||
## Upload the configuration in the Key-value store
|
### Upload the configuration in the Key-value store
|
||||||
|
|
||||||
We should now fill the store with the Træfik global configuration, as we do with a [TOML file configuration](/toml).
|
We should now fill the store with the Træfik global configuration, as we do with a [TOML file configuration](/toml).
|
||||||
To do that, we can send the Key-value pairs via [curl commands](https://www.consul.io/intro/getting-started/kv.html) or via the [Web UI](https://www.consul.io/intro/getting-started/ui.html).
|
To do that, we can send the Key-value pairs via [curl commands](https://www.consul.io/intro/getting-started/kv.html) or via the [Web UI](https://www.consul.io/intro/getting-started/ui.html).
|
||||||
|
|
||||||
Fortunately, Træfik allows automation of this process using the `storeconfig` subcommand.
|
Fortunately, Træfik allows automation of this process using the `storeconfig` subcommand.
|
||||||
Please refer to the [store Træfik configuration](/user-guide/kv-config/#store-configuration-in-key-value-store) section to get documentation on it.
|
Please refer to the [store Træfik configuration](/user-guide/kv-config/#store-configuration-in-key-value-store) section to get documentation on it.
|
||||||
|
|
||||||
Here is the toml configuration we would like to store in the Key-value Store :
|
Here is the toml configuration we would like to store in the Key-value Store :
|
||||||
@@ -74,27 +76,29 @@ defaultEntryPoints = ["http", "https"]
|
|||||||
address = ":443"
|
address = ":443"
|
||||||
[entryPoints.https.tls]
|
[entryPoints.https.tls]
|
||||||
[[entryPoints.https.tls.certificates]]
|
[[entryPoints.https.tls.certificates]]
|
||||||
CertFile = "integration/fixtures/https/snitest.com.cert"
|
certFile = "integration/fixtures/https/snitest.com.cert"
|
||||||
KeyFile = "integration/fixtures/https/snitest.com.key"
|
keyFile = "integration/fixtures/https/snitest.com.key"
|
||||||
[[entryPoints.https.tls.certificates]]
|
[[entryPoints.https.tls.certificates]]
|
||||||
CertFile = """-----BEGIN CERTIFICATE-----
|
certFile = """-----BEGIN CERTIFICATE-----
|
||||||
<cert file content>
|
<cert file content>
|
||||||
-----END CERTIFICATE-----"""
|
-----END CERTIFICATE-----"""
|
||||||
KeyFile = """-----BEGIN CERTIFICATE-----
|
keyFile = """-----BEGIN CERTIFICATE-----
|
||||||
<key file content>
|
<key file content>
|
||||||
-----END CERTIFICATE-----"""
|
-----END CERTIFICATE-----"""
|
||||||
|
[entryPoints.other-https]
|
||||||
|
address = ":4443"
|
||||||
|
[entryPoints.other-https.tls]
|
||||||
|
|
||||||
[consul]
|
[consul]
|
||||||
endpoint = "127.0.0.1:8500"
|
endpoint = "127.0.0.1:8500"
|
||||||
watch = true
|
watch = true
|
||||||
prefix = "traefik"
|
prefix = "traefik"
|
||||||
|
|
||||||
[web]
|
[web]
|
||||||
address = ":8081"
|
address = ":8081"
|
||||||
```
|
```
|
||||||
|
|
||||||
And there, the same global configuration in the Key-value Store (using `prefix = "traefik"`):
|
And there, the same global configuration in the Key-value Store (using `prefix = "traefik"`):
|
||||||
|
|
||||||
| Key | Value |
|
| Key | Value |
|
||||||
|-----------------------------------------------------------|---------------------------------------------------------------|
|
|-----------------------------------------------------------|---------------------------------------------------------------|
|
||||||
@@ -107,20 +111,23 @@ And there, the same global configuration in the Key-value Store (using `prefix =
|
|||||||
| `/traefik/entrypoints/https/tls/certificates/0/keyfile` | `integration/fixtures/https/snitest.com.key` |
|
| `/traefik/entrypoints/https/tls/certificates/0/keyfile` | `integration/fixtures/https/snitest.com.key` |
|
||||||
| `/traefik/entrypoints/https/tls/certificates/1/certfile` | `--BEGIN CERTIFICATE--<cert file content>--END CERTIFICATE--` |
|
| `/traefik/entrypoints/https/tls/certificates/1/certfile` | `--BEGIN CERTIFICATE--<cert file content>--END CERTIFICATE--` |
|
||||||
| `/traefik/entrypoints/https/tls/certificates/1/keyfile` | `--BEGIN CERTIFICATE--<key file content>--END CERTIFICATE--` |
|
| `/traefik/entrypoints/https/tls/certificates/1/keyfile` | `--BEGIN CERTIFICATE--<key file content>--END CERTIFICATE--` |
|
||||||
|
| `/traefik/entrypoints/other-https/address` | `:4443`
|
||||||
| `/traefik/consul/endpoint` | `127.0.0.1:8500` |
|
| `/traefik/consul/endpoint` | `127.0.0.1:8500` |
|
||||||
| `/traefik/consul/watch` | `true` |
|
| `/traefik/consul/watch` | `true` |
|
||||||
| `/traefik/consul/prefix` | `traefik` |
|
| `/traefik/consul/prefix` | `traefik` |
|
||||||
| `/traefik/web/address` | `:8081` |
|
| `/traefik/web/address` | `:8081` |
|
||||||
|
|
||||||
In case you are setting key values manually:
|
In case you are setting key values manually:
|
||||||
|
|
||||||
- Remember to specify the indexes (`0`,`1`, `2`, ... ) under prefixes `/traefik/defaultentrypoints/` and `/traefik/entrypoints/https/tls/certificates/` in order to match the global configuration structure.
|
- Remember to specify the indexes (`0`,`1`, `2`, ... ) under prefixes `/traefik/defaultentrypoints/` and `/traefik/entrypoints/https/tls/certificates/` in order to match the global configuration structure.
|
||||||
- Be careful to give the correct IP address and port on the key `/traefik/consul/endpoint`.
|
- Be careful to give the correct IP address and port on the key `/traefik/consul/endpoint`.
|
||||||
|
|
||||||
Note that we can either give path to certificate file or directly the file content itself.
|
Note that we can either give path to certificate file or directly the file content itself.
|
||||||
|
|
||||||
## Launch Træfik
|
### Launch Træfik
|
||||||
|
|
||||||
We will now launch Træfik in a container.
|
We will now launch Træfik in a container.
|
||||||
|
|
||||||
We use CLI flags to setup the connection between Træfik and Consul.
|
We use CLI flags to setup the connection between Træfik and Consul.
|
||||||
All the rest of the global configuration is stored in Consul.
|
All the rest of the global configuration is stored in Consul.
|
||||||
|
|
||||||
@@ -135,11 +142,57 @@ traefik:
|
|||||||
- "8080:8080"
|
- "8080:8080"
|
||||||
```
|
```
|
||||||
|
|
||||||
NB : Be careful to give the correct IP address and port in the flag `--consul.endpoint`.
|
!!! warning
|
||||||
|
Be careful to give the correct IP address and port in the flag `--consul.endpoint`.
|
||||||
|
|
||||||
## TLS support
|
### Consul ACL Token support
|
||||||
|
|
||||||
|
To specify a Consul ACL token for Traefik, we have to set a System Environment variable named `CONSUL_HTTP_TOKEN` prior to starting Traefik.
|
||||||
|
This variable must be initialized with the ACL token value.
|
||||||
|
|
||||||
|
If Traefik is launched into a Docker container, the variable `CONSUL_HTTP_TOKEN` can be initialized with the `-e` Docker option : `-e "CONSUL_HTTP_TOKEN=[consul-acl-token-value]"`
|
||||||
|
|
||||||
|
If a Consul ACL is used to restrict Træfik read/write access, one of the following configurations is needed.
|
||||||
|
|
||||||
|
- HCL format :
|
||||||
|
|
||||||
|
```
|
||||||
|
key "traefik" {
|
||||||
|
policy = "write"
|
||||||
|
},
|
||||||
|
|
||||||
|
session "" {
|
||||||
|
policy = "write"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- JSON format :
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"key": {
|
||||||
|
"traefik": {
|
||||||
|
"policy": "write"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"session": {
|
||||||
|
"": {
|
||||||
|
"policy": "write"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### TLS support
|
||||||
|
|
||||||
|
To connect to a Consul endpoint using SSL, simply specify `https://` in the `consul.endpoint` property
|
||||||
|
|
||||||
|
- `--consul.endpoint=https://[consul-host]:[consul-ssl-port]`
|
||||||
|
|
||||||
|
### TLS support with client certificates
|
||||||
|
|
||||||
|
So far, only [Consul](https://consul.io) and [etcd](https://coreos.com/etcd/) support TLS connections with client certificates.
|
||||||
|
|
||||||
So far, only [Consul](https://consul.io) and [etcd](https://coreos.com/etcd/) support TLS connections.
|
|
||||||
To set it up, we should enable [consul security](https://www.consul.io/docs/internals/security.html) (or [etcd security](https://coreos.com/etcd/docs/latest/security.html)).
|
To set it up, we should enable [consul security](https://www.consul.io/docs/internals/security.html) (or [etcd security](https://coreos.com/etcd/docs/latest/security.html)).
|
||||||
|
|
||||||
Then, we have to provide CA, Cert and Key to Træfik using `consul` flags :
|
Then, we have to provide CA, Cert and Key to Træfik using `consul` flags :
|
||||||
@@ -147,7 +200,7 @@ Then, we have to provide CA, Cert and Key to Træfik using `consul` flags :
|
|||||||
- `--consul.tls`
|
- `--consul.tls`
|
||||||
- `--consul.tls.ca=path/to/the/file`
|
- `--consul.tls.ca=path/to/the/file`
|
||||||
- `--consul.tls.cert=path/to/the/file`
|
- `--consul.tls.cert=path/to/the/file`
|
||||||
- `--consul.tls.key=path/to/the/file`
|
- `--consul.tls.key=path/to/the/file`
|
||||||
|
|
||||||
Or etcd flags :
|
Or etcd flags :
|
||||||
|
|
||||||
@@ -156,17 +209,21 @@ Or etcd flags :
|
|||||||
- `--etcd.tls.cert=path/to/the/file`
|
- `--etcd.tls.cert=path/to/the/file`
|
||||||
- `--etcd.tls.key=path/to/the/file`
|
- `--etcd.tls.key=path/to/the/file`
|
||||||
|
|
||||||
Note that we can either give directly directly the file content itself (instead of the path to certificate) in a TOML file configuration.
|
!! note
|
||||||
|
We can either give directly directly the file content itself (instead of the path to certificate) in a TOML file configuration.
|
||||||
|
|
||||||
Remember the command `traefik --help` to display the updated list of flags.
|
Remember the command `traefik --help` to display the updated list of flags.
|
||||||
|
|
||||||
# Dynamic configuration in Key-value store
|
## Dynamic configuration in Key-value store
|
||||||
Following our example, we will provide backends/frontends rules to Træfik.
|
|
||||||
|
|
||||||
Note that this section is independent of the way Træfik got its static configuration.
|
Following our example, we will provide backends/frontends rules and HTTPS certificates to Træfik.
|
||||||
It means that the static configuration can either come from the same Key-value store or from any other sources.
|
|
||||||
|
!!! note
|
||||||
|
This section is independent of the way Træfik got its static configuration.
|
||||||
|
It means that the static configuration can either come from the same Key-value store or from any other sources.
|
||||||
|
|
||||||
|
### Key-value storage structure
|
||||||
|
|
||||||
## Key-value storage structure
|
|
||||||
Here is the toml configuration we would like to store in the store :
|
Here is the toml configuration we would like to store in the store :
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
@@ -176,7 +233,7 @@ Here is the toml configuration we would like to store in the store :
|
|||||||
[backends]
|
[backends]
|
||||||
[backends.backend1]
|
[backends.backend1]
|
||||||
[backends.backend1.circuitbreaker]
|
[backends.backend1.circuitbreaker]
|
||||||
expression = "NetworkErrorRatio() > 0.5"
|
expression = "NetworkErrorRatio() > 0.5"
|
||||||
[backends.backend1.servers.server1]
|
[backends.backend1.servers.server1]
|
||||||
url = "http://172.17.0.2:80"
|
url = "http://172.17.0.2:80"
|
||||||
weight = 10
|
weight = 10
|
||||||
@@ -185,10 +242,10 @@ Here is the toml configuration we would like to store in the store :
|
|||||||
weight = 1
|
weight = 1
|
||||||
[backends.backend2]
|
[backends.backend2]
|
||||||
[backends.backend1.maxconn]
|
[backends.backend1.maxconn]
|
||||||
amount = 10
|
amount = 10
|
||||||
extractorfunc = "request.host"
|
extractorfunc = "request.host"
|
||||||
[backends.backend2.LoadBalancer]
|
[backends.backend2.LoadBalancer]
|
||||||
method = "drr"
|
method = "drr"
|
||||||
[backends.backend2.servers.server1]
|
[backends.backend2.servers.server1]
|
||||||
url = "http://172.17.0.4:80"
|
url = "http://172.17.0.4:80"
|
||||||
weight = 1
|
weight = 1
|
||||||
@@ -211,10 +268,25 @@ Here is the toml configuration we would like to store in the store :
|
|||||||
[frontends.frontend3]
|
[frontends.frontend3]
|
||||||
entrypoints = ["http", "https"] # overrides defaultEntryPoints
|
entrypoints = ["http", "https"] # overrides defaultEntryPoints
|
||||||
backend = "backend2"
|
backend = "backend2"
|
||||||
rule = "Path:/test"
|
rule = "Path:/test"
|
||||||
|
|
||||||
|
[[tlsConfiguration]]
|
||||||
|
entryPoints = ["https"]
|
||||||
|
[tlsConfiguration.certificate]
|
||||||
|
certFile = "path/to/your.cert"
|
||||||
|
keyFile = "path/to/your.key"
|
||||||
|
[[tlsConfiguration]]
|
||||||
|
entryPoints = ["https","other-https"]
|
||||||
|
[tlsConfiguration.certificate]
|
||||||
|
certFile = """-----BEGIN CERTIFICATE-----
|
||||||
|
<cert file content>
|
||||||
|
-----END CERTIFICATE-----"""
|
||||||
|
keyFile = """-----BEGIN CERTIFICATE-----
|
||||||
|
<key file content>
|
||||||
|
-----END CERTIFICATE-----"""
|
||||||
```
|
```
|
||||||
|
|
||||||
And there, the same dynamic configuration in a KV Store (using `prefix = "traefik"`):
|
And there, the same dynamic configuration in a KV Store (using `prefix = "traefik"`):
|
||||||
|
|
||||||
- backend 1
|
- backend 1
|
||||||
|
|
||||||
@@ -257,13 +329,36 @@ And there, the same dynamic configuration in a KV Store (using `prefix = "traefi
|
|||||||
| `/traefik/frontends/frontend2/entrypoints` | `http,https` |
|
| `/traefik/frontends/frontend2/entrypoints` | `http,https` |
|
||||||
| `/traefik/frontends/frontend2/routes/test_2/rule` | `PathPrefix:/test` |
|
| `/traefik/frontends/frontend2/routes/test_2/rule` | `PathPrefix:/test` |
|
||||||
|
|
||||||
## Atomic configuration changes
|
- certificate 1
|
||||||
|
|
||||||
Træfik can watch the backends/frontends configuration changes and generate its configuration automatically.
|
| Key | Value |
|
||||||
|
|----------------------------------------------------|--------------------|
|
||||||
|
| `/traefik/tlsconfiguration/1/entrypoints` | `https` |
|
||||||
|
| `/traefik/tlsconfiguration/1/certificate/certfile` | `path/to/your.cert`|
|
||||||
|
| `/traefik/tlsconfiguration/1/certificate/keyfile` | `path/to/your.key` |
|
||||||
|
|
||||||
Note that only backends/frontends rules are dynamic, the rest of the Træfik configuration stay static.
|
- certificate 2
|
||||||
|
|
||||||
The [Etcd](https://github.com/coreos/etcd/issues/860) and [Consul](https://github.com/hashicorp/consul/issues/886) backends do not support updating multiple keys atomically. As a result, it may be possible for Træfik to read an intermediate configuration state despite judicious use of the `--providersThrottleDuration` flag. To solve this problem, Træfik supports a special key called `/traefik/alias`. If set, Træfik use the value as an alternative key prefix.
|
| Key | Value |
|
||||||
|
|----------------------------------------------------|-----------------------|
|
||||||
|
| `/traefik/tlsconfiguration/2/entrypoints` | `https,other-https` |
|
||||||
|
| `/traefik/tlsconfiguration/2/certificate/certfile` | `<cert file content>` |
|
||||||
|
| `/traefik/tlsconfiguration/2/certificate/certfile` | `<key file content>` |
|
||||||
|
### Atomic configuration changes
|
||||||
|
|
||||||
|
Træfik can watch the backends/frontends configuration changes and generate its configuration automatically.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
Only backends/frontends rules are dynamic, the rest of the Træfik configuration stay static.
|
||||||
|
|
||||||
|
The [Etcd](https://github.com/coreos/etcd/issues/860) and [Consul](https://github.com/hashicorp/consul/issues/886) backends do not support updating multiple keys atomically.
|
||||||
|
As a result, it may be possible for Træfik to read an intermediate configuration state despite judicious use of the `--providersThrottleDuration` flag.
|
||||||
|
To solve this problem, Træfik supports a special key called `/traefik/alias`.
|
||||||
|
If set, Træfik use the value as an alternative key prefix.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
The field `useAPIV3` allows using Etcd V3 API which should support updating multiple keys atomically with Etcd.
|
||||||
|
Etcd API V2 is deprecated and, in the future, Træfik will support API V3 by default.
|
||||||
|
|
||||||
Given the key structure below, Træfik will use the `http://172.17.0.2:80` as its only backend (frontend keys have been omitted for brevity).
|
Given the key structure below, Træfik will use the `http://172.17.0.2:80` as its only backend (frontend keys have been omitted for brevity).
|
||||||
|
|
||||||
@@ -273,7 +368,9 @@ Given the key structure below, Træfik will use the `http://172.17.0.2:80` as it
|
|||||||
| `/traefik_configurations/1/backends/backend1/servers/server1/url` | `http://172.17.0.2:80` |
|
| `/traefik_configurations/1/backends/backend1/servers/server1/url` | `http://172.17.0.2:80` |
|
||||||
| `/traefik_configurations/1/backends/backend1/servers/server1/weight` | `10` |
|
| `/traefik_configurations/1/backends/backend1/servers/server1/weight` | `10` |
|
||||||
|
|
||||||
When an atomic configuration change is required, you may write a new configuration at an alternative prefix. Here, although the `/traefik_configurations/2/...` keys have been set, the old configuration is still active because the `/traefik/alias` key still points to `/traefik_configurations/1`:
|
When an atomic configuration change is required, you may write a new configuration at an alternative prefix.
|
||||||
|
|
||||||
|
Here, although the `/traefik_configurations/2/...` keys have been set, the old configuration is still active because the `/traefik/alias` key still points to `/traefik_configurations/1`:
|
||||||
|
|
||||||
| Key | Value |
|
| Key | Value |
|
||||||
|-------------------------------------------------------------------------|-----------------------------|
|
|-------------------------------------------------------------------------|-----------------------------|
|
||||||
@@ -285,7 +382,9 @@ When an atomic configuration change is required, you may write a new configurati
|
|||||||
| `/traefik_configurations/2/backends/backend1/servers/server2/url` | `http://172.17.0.3:80` |
|
| `/traefik_configurations/2/backends/backend1/servers/server2/url` | `http://172.17.0.3:80` |
|
||||||
| `/traefik_configurations/2/backends/backend1/servers/server2/weight` | `5` |
|
| `/traefik_configurations/2/backends/backend1/servers/server2/weight` | `5` |
|
||||||
|
|
||||||
Once the `/traefik/alias` key is updated, the new `/traefik_configurations/2` configuration becomes active atomically. Here, we have a 50% balance between the `http://172.17.0.3:80` and the `http://172.17.0.4:80` hosts while no traffic is sent to the `172.17.0.2:80` host:
|
Once the `/traefik/alias` key is updated, the new `/traefik_configurations/2` configuration becomes active atomically.
|
||||||
|
|
||||||
|
Here, we have a 50% balance between the `http://172.17.0.3:80` and the `http://172.17.0.4:80` hosts while no traffic is sent to the `172.17.0.2:80` host:
|
||||||
|
|
||||||
| Key | Value |
|
| Key | Value |
|
||||||
|-------------------------------------------------------------------------|-----------------------------|
|
|-------------------------------------------------------------------------|-----------------------------|
|
||||||
@@ -297,22 +396,28 @@ Once the `/traefik/alias` key is updated, the new `/traefik_configurations/2` co
|
|||||||
| `/traefik_configurations/2/backends/backend1/servers/server2/url` | `http://172.17.0.4:80` |
|
| `/traefik_configurations/2/backends/backend1/servers/server2/url` | `http://172.17.0.4:80` |
|
||||||
| `/traefik_configurations/2/backends/backend1/servers/server2/weight` | `5` |
|
| `/traefik_configurations/2/backends/backend1/servers/server2/weight` | `5` |
|
||||||
|
|
||||||
Note that Træfik *will not watch for key changes in the `/traefik_configurations` prefix*. It will only watch for changes in the `/traefik/alias`.
|
!!! note
|
||||||
Further, if the `/traefik/alias` key is set, all other configuration with `/traefik/backends` or `/traefik/frontends` prefix are ignored.
|
Træfik *will not watch for key changes in the `/traefik_configurations` prefix*. It will only watch for changes in the `/traefik/alias`.
|
||||||
|
Further, if the `/traefik/alias` key is set, all other configuration with `/traefik/backends` or `/traefik/frontends` prefix are ignored.
|
||||||
|
|
||||||
# Store configuration in Key-value store
|
## Store configuration in Key-value store
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
Don't forget to [setup the connection between Træfik and Key-value store](/user-guide/kv-config/#launch-trfk).
|
||||||
|
|
||||||
Don't forget to [setup the connection between Træfik and Key-value store](/user-guide/kv-config/#launch-trfk).
|
|
||||||
The static Træfik configuration in a key-value store can be automatically created and updated, using the [`storeconfig` subcommand](/basics/#commands).
|
The static Træfik configuration in a key-value store can be automatically created and updated, using the [`storeconfig` subcommand](/basics/#commands).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ traefik storeconfig [flags] ...
|
traefik storeconfig [flags] ...
|
||||||
```
|
```
|
||||||
This command is here only to automate the [process which upload the configuration into the Key-value store](/user-guide/kv-config/#upload-the-configuration-in-the-key-value-store).
|
This command is here only to automate the [process which upload the configuration into the Key-value store](/user-guide/kv-config/#upload-the-configuration-in-the-key-value-store).
|
||||||
Træfik will not start but the [static configuration](/basics/#static-trfk-configuration) will be uploaded into the Key-value store.
|
Træfik will not start but the [static configuration](/basics/#static-trfk-configuration) will be uploaded into the Key-value store.
|
||||||
|
|
||||||
If you configured ACME (Let's Encrypt), your registration account and your certificates will also be uploaded.
|
If you configured ACME (Let's Encrypt), your registration account and your certificates will also be uploaded.
|
||||||
|
|
||||||
To upload your ACME certificates to the KV store, get your traefik TOML file and add the new `storage` option in the `acme` section:
|
If you configured a file backend `[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:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[acme]
|
[acme]
|
||||||
@@ -326,4 +431,4 @@ Then remove the line `storageFile = "acme.json"` from your TOML config file.
|
|||||||
|
|
||||||
That's it!
|
That's it!
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
|
|
||||||
# Marathon
|
# Marathon
|
||||||
|
|
||||||
This guide explains how to integrate Marathon and operate the cluster in a reliable way from Traefik's standpoint.
|
This guide explains how to integrate Marathon and operate the cluster in a reliable way from Traefik's standpoint.
|
||||||
|
|
||||||
# Host detection
|
## Host detection
|
||||||
|
|
||||||
Marathon offers multiple ways to run (Docker-containerized) applications, the most popular ones being
|
Marathon offers multiple ways to run (Docker-containerized) applications, the most popular ones being
|
||||||
|
|
||||||
@@ -13,20 +12,25 @@ Marathon offers multiple ways to run (Docker-containerized) applications, the mo
|
|||||||
|
|
||||||
Traefik tries to detect the configured mode and route traffic to the right IP addresses. It is possible to force using task hosts with the `forceTaskHostname` option.
|
Traefik tries to detect the configured mode and route traffic to the right IP addresses. It is possible to force using task hosts with the `forceTaskHostname` option.
|
||||||
|
|
||||||
Given the complexity of the subject, it is possible that the heuristic fails. Apart from filing an issue and waiting for the feature request / bug report to get addressed, one workaround for such situations is to customize the Marathon template file to the individual needs. (Note that this does _not_ require rebuilding Traefik but only to point the `filename` configuration parameter to a customized version of the `marathon.tmpl` file on Traefik startup.)
|
Given the complexity of the subject, it is possible that the heuristic fails.
|
||||||
|
Apart from filing an issue and waiting for the feature request / bug report to get addressed, one workaround for such situations is to customize the Marathon template file to the individual needs.
|
||||||
|
|
||||||
# Port detection
|
!!! note
|
||||||
|
This does _not_ require rebuilding Traefik but only to point the `filename` configuration parameter to a customized version of the `marathon.tmpl` file on Traefik startup.
|
||||||
|
|
||||||
Traefik also attempts to determine the right port (which is a [non-trivial matter in Marathon](https://mesosphere.github.io/marathon/docs/ports.html)). Following is the order by which Traefik tries to identify the port (the first one that yields a positive result will be used):
|
## Port detection
|
||||||
|
|
||||||
|
Traefik also attempts to determine the right port (which is a [non-trivial matter in Marathon](https://mesosphere.github.io/marathon/docs/ports.html)).
|
||||||
|
Following is the order by which Traefik tries to identify the port (the first one that yields a positive result will be used):
|
||||||
|
|
||||||
1. A arbitrary port specified through the `traefik.port` label.
|
1. A arbitrary port specified through the `traefik.port` label.
|
||||||
1. The task port (possibly indexed through the `traefik.portIndex` label, otherwise the first one).
|
1. The task port (possibly indexed through the `traefik.portIndex` label, otherwise the first one).
|
||||||
1. The port from the application's `portDefinitions` field (possibly indexed through the `traefik.portIndex` label, otherwise the first one).
|
1. The port from the application's `portDefinitions` field (possibly indexed through the `traefik.portIndex` label, otherwise the first one).
|
||||||
1. The port from the application's `ipAddressPerTask` field (possibly indexed through the `traefik.portIndex` label, otherwise the first one).
|
1. The port from the application's `ipAddressPerTask` field (possibly indexed through the `traefik.portIndex` label, otherwise the first one).
|
||||||
|
|
||||||
# Achieving high availability
|
## Achieving high availability
|
||||||
|
|
||||||
## Scenarios
|
### Scenarios
|
||||||
|
|
||||||
There are three scenarios where the availability of a Marathon application could be impaired along with the risk of losing or failing requests:
|
There are three scenarios where the availability of a Marathon application could be impaired along with the risk of losing or failing requests:
|
||||||
|
|
||||||
@@ -34,21 +38,30 @@ There are three scenarios where the availability of a Marathon application could
|
|||||||
- During the shutdown phase when Traefik still routes requests to the backend while the backend is already terminating.
|
- During the shutdown phase when Traefik still routes requests to the backend while the backend is already terminating.
|
||||||
- During a failure of the application when Traefik has not yet identified the backend as being erroneous.
|
- During a failure of the application when Traefik has not yet identified the backend as being erroneous.
|
||||||
|
|
||||||
The first two scenarios are common with every rolling upgrade of an application (i.e., a new version release or configuration update).
|
The first two scenarios are common with every rolling upgrade of an application (i.e. a new version release or configuration update).
|
||||||
|
|
||||||
The following sub-sections describe how to resolve or mitigate each scenario.
|
The following sub-sections describe how to resolve or mitigate each scenario.
|
||||||
|
|
||||||
### Startup
|
#### Startup
|
||||||
|
|
||||||
In general, it is possible to define [readiness checks](https://mesosphere.github.io/marathon/docs/readiness-checks.html) (available since Marathon version 1.1) per application and have Marathon take these into account during the startup phase. The idea is that each application provides an HTTP endpoint that Marathon queries periodically during an ongoing deployment in order to mark the associated readiness check result as successful if and only if the endpoint returns a response within the configured HTTP code range. As long as the check keeps failing, Marathon will not proceed with the deployment (within the configured upgrade stategy bounds).
|
It is possible to define [readiness checks](https://mesosphere.github.io/marathon/docs/readiness-checks.html) (available since Marathon version 1.1) per application and have Marathon take these into account during the startup phase.
|
||||||
|
|
||||||
Unfortunately, Traefik does not respect the result of the readiness check yet. Support is expected to land in a not-too-distant future release of Traefik, however, as being tracked by [issue 1559](https://github.com/containous/traefik/issues/1559).
|
The idea is that each application provides an HTTP endpoint that Marathon queries periodically during an ongoing deployment in order to mark the associated readiness check result as successful if and only if the endpoint returns a response within the configured HTTP code range.
|
||||||
|
As long as the check keeps failing, Marathon will not proceed with the deployment (within the configured upgrade strategy bounds).
|
||||||
|
|
||||||
A current mitigation strategy is to enable [retries](http://docs.traefik.io/toml/#retry-configuration) and make sure that a sufficient number of healthy application tasks exist so that one retry will likely hit one of those. Apart from its probabilistic nature, the workaround comes at the price of increased latency.
|
Beginning with version 1.4, Traefik respects readiness check results if the Traefik option is set and checks are configured on the applications accordingly.
|
||||||
|
|
||||||
### Shutdown
|
!!! note
|
||||||
|
Due to the way readiness check results are currently exposed by the Marathon API, ready tasks may be taken into rotation with a small delay.
|
||||||
|
It is on the order of one readiness check timeout interval (as configured on the application specifiation) and guarantees that non-ready tasks do not receive traffic prematurely.
|
||||||
|
|
||||||
It is possible to install a [termination handler](https://mesosphere.github.io/marathon/docs/health-checks.html) (available since Marathon version 1.3) with each application whose responsibility it is to delay the shutdown process long enough until the backend has been taken out of load-balancing rotation with reasonable confidence (i.e., Traefik has received an update from the Marathon event bus, recomputes the available Marathon backends, and applies the new configuration). Specifically, each termination handler should install a signal handler listening for a SIGTERM signal and implement the following steps on signal reception:
|
If readiness checks are not possible, a current mitigation strategy is to enable [retries](/configuration/commons#retry-configuration) and make sure that a sufficient number of healthy application tasks exist so that one retry will likely hit one of those.
|
||||||
|
Apart from its probabilistic nature, the workaround comes at the price of increased latency.
|
||||||
|
|
||||||
|
#### Shutdown
|
||||||
|
|
||||||
|
It is possible to install a [termination handler](https://mesosphere.github.io/marathon/docs/health-checks.html) (available since Marathon version 1.3) with each application whose responsibility it is to delay the shutdown process long enough until the backend has been taken out of load-balancing rotation with reasonable confidence (i.e., Traefik has received an update from the Marathon event bus, recomputes the available Marathon backends, and applies the new configuration).
|
||||||
|
Specifically, each termination handler should install a signal handler listening for a SIGTERM signal and implement the following steps on signal reception:
|
||||||
|
|
||||||
1. Disable Keep-Alive HTTP connections.
|
1. Disable Keep-Alive HTTP connections.
|
||||||
1. Keep accepting HTTP requests for a certain period of time.
|
1. Keep accepting HTTP requests for a certain period of time.
|
||||||
@@ -58,39 +71,57 @@ It is possible to install a [termination handler](https://mesosphere.github.io/m
|
|||||||
|
|
||||||
Traefik already ignores Marathon tasks whose state does not match `TASK_RUNNING`; since terminating tasks transition into the `TASK_KILLING` and eventually `TASK_KILLED` state, there is nothing further that needs to be done on Traefik's end.
|
Traefik already ignores Marathon tasks whose state does not match `TASK_RUNNING`; since terminating tasks transition into the `TASK_KILLING` and eventually `TASK_KILLED` state, there is nothing further that needs to be done on Traefik's end.
|
||||||
|
|
||||||
How long HTTP requests should continue to be accepted in step 2 depends on how long Traefik needs to receive and process the Marathon configuration update. Under regular operational conditions, it should be on the order of seconds, with 10 seconds possibly being a good default value.
|
How long HTTP requests should continue to be accepted in step 2 depends on how long Traefik needs to receive and process the Marathon configuration update.
|
||||||
|
Under regular operational conditions, it should be on the order of seconds, with 10 seconds possibly being a good default value.
|
||||||
|
|
||||||
Again, configuring Traefik to do retries (as discussed in the previous section) can serve as a decent workaround strategy. Paired with termination handlers, they would cover for those cases where either the termination sequence or Traefik cannot complete their part of the orchestration process in time.
|
Again, configuring Traefik to do retries (as discussed in the previous section) can serve as a decent workaround strategy.
|
||||||
|
Paired with termination handlers, they would cover for those cases where either the termination sequence or Traefik cannot complete their part of the orchestration process in time.
|
||||||
|
|
||||||
### Failure
|
#### Failure
|
||||||
|
|
||||||
A failing application always happens unexpectedly, and hence, it is very difficult or even impossible to rule out the adversal effects categorically. Failure reasons vary broadly and could stretch from unacceptable slowness, a task crash, or a network split.
|
A failing application always happens unexpectedly, and hence, it is very difficult or even impossible to rule out the adversal effects categorically.
|
||||||
|
|
||||||
|
Failure reasons vary broadly and could stretch from unacceptable slowness, a task crash, or a network split.
|
||||||
|
|
||||||
There are two mitigaton efforts:
|
There are two mitigaton efforts:
|
||||||
|
|
||||||
1. Configure [Marathon health checks](https://mesosphere.github.io/marathon/docs/health-checks.html) on each application.
|
1. Configure [Marathon health checks](https://mesosphere.github.io/marathon/docs/health-checks.html) on each application.
|
||||||
1. Configure Traefik health checks (possibly via the `traefik.backend.healthcheck.*` labels) and make sure they probe with proper frequency.
|
1. Configure Traefik health checks (possibly via the `traefik.backend.healthcheck.*` labels) and make sure they probe with proper frequency.
|
||||||
|
|
||||||
The Marathon health check makes sure that applications once deemed dysfunctional are being rescheduled to different slaves. However, they might take a while to get triggered and the follow-up processes to complete. For that reason, the Treafik health check provides an additional check that responds more rapidly and does not require a configuration reload to happen. Additionally, it protects from cases that the Marathon health check may not be able to cover, such as a network split.
|
The Marathon health check makes sure that applications once deemed dysfunctional are being rescheduled to different slaves.
|
||||||
|
However, they might take a while to get triggered and the follow-up processes to complete.
|
||||||
|
|
||||||
## (Non-)Alternatives
|
For that reason, the Treafik health check provides an additional check that responds more rapidly and does not require a configuration reload to happen.
|
||||||
|
Additionally, it protects from cases that the Marathon health check may not be able to cover, such as a network split.
|
||||||
|
|
||||||
There are a few alternatives of varying quality that are frequently asked for. The remaining section is going to explore them along with a benefit/cost trade-off.
|
### (Non-)Alternatives
|
||||||
|
|
||||||
### Reusing Marathon health checks
|
There are a few alternatives of varying quality that are frequently asked for.
|
||||||
|
|
||||||
|
The remaining section is going to explore them along with a benefit/cost trade-off.
|
||||||
|
|
||||||
|
#### Reusing Marathon health checks
|
||||||
|
|
||||||
It may seem obvious to reuse the Marathon health checks as a signal to Traefik whether an application should be taken into load-balancing rotation or not.
|
It may seem obvious to reuse the Marathon health checks as a signal to Traefik whether an application should be taken into load-balancing rotation or not.
|
||||||
|
|
||||||
Apart from the increased latency a failing health check may have, a major problem with this is is that Marathon does not persist the health check results. Consequently, if a master re-election occurs in the Marathon clusters, all health check results will revert to the _unknown_ state, effectively causing all applications inside the cluster to become unavailable and leading to a complete cluster failure. Re-elections do not only happen during regular maintenance work (often requiring rolling upgrades of the Marathon nodes) but also when the Marathon leader fails spontaneously). As such, there is no way to handle this situation deterministically.
|
Apart from the increased latency a failing health check may have, a major problem with this is is that Marathon does not persist the health check results.
|
||||||
|
Consequently, if a master re-election occurs in the Marathon clusters, all health check results will revert to the _unknown_ state, effectively causing all applications inside the cluster to become unavailable and leading to a complete cluster failure.
|
||||||
|
Re-elections do not only happen during regular maintenance work (often requiring rolling upgrades of the Marathon nodes) but also when the Marathon leader fails spontaneously.
|
||||||
|
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.
|
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 but moved away from it as [users reported the dramatic consequences](https://github.com/containous/traefik/issues/653).
|
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
|
#### Draining
|
||||||
|
|
||||||
Another common approach is to let a proxy drain backends that are supposed to shut down. That is, once a backend is supposed to shut down, Traefik would stop forwarding requests.
|
Another common approach is to let a proxy drain backends that are supposed to shut down.
|
||||||
|
That is, once a backend is supposed to shut down, Traefik would stop forwarding requests.
|
||||||
|
|
||||||
On the plus side, this would not require any modifications to the application in question. However, implementing this fully within Traefik seems like a non-trivial undertaking. Additionally, the approach is less flexible compared to a custom termination handler since only the latter allows for the implementation of custom termination sequences that go beyond simple request draining (e.g., persisting a snapshot state to disk prior to terminating).
|
On the plus side, this would not require any modifications to the application in question.
|
||||||
|
However, implementing this fully within Traefik seems like a non-trivial undertaking.
|
||||||
|
|
||||||
|
Additionally, the approach is less flexible compared to a custom termination handler since only the latter allows for the implementation of custom termination sequences that go beyond simple request draining (e.g., persisting a snapshot state to disk prior to terminating).
|
||||||
|
|
||||||
The feature is currently not implemented; a request for draining in general is at [issue 41](https://github.com/containous/traefik/issues/41).
|
The feature is currently not implemented; a request for draining in general is at [issue 41](https://github.com/containous/traefik/issues/41).
|
||||||
|
|||||||
@@ -1,26 +1,25 @@
|
|||||||
# Docker Swarm (mode) cluster
|
# Docker Swarm (mode) cluster
|
||||||
|
|
||||||
This section explains how to create a multi-host docker cluster with
|
This section explains how to create a multi-host docker cluster with swarm mode using [docker-machine](https://docs.docker.com/machine) and how to deploy Træfik on it.
|
||||||
swarm mode using [docker-machine](https://docs.docker.com/machine) and
|
|
||||||
how to deploy Træfik on it.
|
|
||||||
|
|
||||||
The cluster consists of:
|
The cluster consists of:
|
||||||
|
|
||||||
- 3 servers
|
- 3 servers
|
||||||
- 1 manager
|
- 1 manager
|
||||||
- 2 workers
|
- 2 workers
|
||||||
- 1 [overlay](https://docs.docker.com/engine/userguide/networking/dockernetworks/#an-overlay-network) network
|
- 1 [overlay](https://docs.docker.com/engine/userguide/networking/dockernetworks/#an-overlay-network) network (multi-host networking)
|
||||||
(multi-host networking)
|
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
1. You will need to install [docker-machine](https://docs.docker.com/machine/)
|
1. You will need to install [docker-machine](https://docs.docker.com/machine/)
|
||||||
2. You will need the latest [VirtualBox](https://www.virtualbox.org/wiki/Downloads)
|
2. You will need the latest [VirtualBox](https://www.virtualbox.org/wiki/Downloads)
|
||||||
|
|
||||||
|
|
||||||
## Cluster provisioning
|
## Cluster provisioning
|
||||||
|
|
||||||
First, let's create all the required nodes. It's a shorter version of
|
First, let's create all the required nodes.
|
||||||
the [swarm tutorial](https://docs.docker.com/engine/swarm/swarm-tutorial/).
|
It's a shorter version of the [swarm tutorial](https://docs.docker.com/engine/swarm/swarm-tutorial/).
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
docker-machine create -d virtualbox manager
|
docker-machine create -d virtualbox manager
|
||||||
@@ -28,11 +27,11 @@ docker-machine create -d virtualbox worker1
|
|||||||
docker-machine create -d virtualbox worker2
|
docker-machine create -d virtualbox worker2
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, let's setup the cluster, in order :
|
Then, let's setup the cluster, in order:
|
||||||
|
|
||||||
1. initialize the cluster
|
1. initialize the cluster
|
||||||
2. get the token for other host to join
|
1. get the token for other host to join
|
||||||
3. on both workers, join the cluster with the token
|
1. on both workers, join the cluster with the token
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
docker-machine ssh manager "docker swarm init \
|
docker-machine ssh manager "docker swarm init \
|
||||||
@@ -59,10 +58,12 @@ Let's validate the cluster is up and running.
|
|||||||
|
|
||||||
```shell
|
```shell
|
||||||
docker-machine ssh manager docker node ls
|
docker-machine ssh manager docker node ls
|
||||||
|
```
|
||||||
|
```
|
||||||
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
|
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
|
||||||
2a770ov9vixeadep674265u1n worker1 Ready Active
|
013v16l1sbuwjqcn7ucbu4jwt worker1 Ready Active
|
||||||
dbi3or4q8ii8elbws70g4hkdh * manager Ready Active Leader
|
8buzkquycd17jqjber0mo2gn8 worker2 Ready Active
|
||||||
esbhhy6vnqv90xomjaomdgy46 worker2 Ready Active
|
fnpj8ozfc85zvahx2r540xfcf * manager Ready Active Leader
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally, let's create a network for Træfik to use.
|
Finally, let's create a network for Træfik to use.
|
||||||
@@ -71,13 +72,11 @@ Finally, let's create a network for Træfik to use.
|
|||||||
docker-machine ssh manager "docker network create --driver=overlay traefik-net"
|
docker-machine ssh manager "docker network create --driver=overlay traefik-net"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Deploy Træfik
|
## Deploy Træfik
|
||||||
|
|
||||||
Let's deploy Træfik as a docker service in our cluster. The only
|
Let's deploy Træfik as a docker service in our cluster.
|
||||||
requirement for Træfik to work with swarm mode is that it needs to run
|
The only requirement for Træfik to work with swarm mode is that it needs to run on a manager node - we are going to use a [constraint](https://docs.docker.com/engine/reference/commandline/service_create/#/specify-service-constraints-constraint) for that.
|
||||||
on a manager node — we are going to use a
|
|
||||||
[constraint](https://docs.docker.com/engine/reference/commandline/service_create/#/specify-service-constraints-constraint) for
|
|
||||||
that.
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
docker-machine ssh manager "docker service create \
|
docker-machine ssh manager "docker service create \
|
||||||
@@ -96,24 +95,20 @@ docker-machine ssh manager "docker service create \
|
|||||||
|
|
||||||
Let's explain this command:
|
Let's explain this command:
|
||||||
|
|
||||||
- `--publish 80:80 --publish 8080:8080`: we publish port `80` and
|
| Option | Description |
|
||||||
`8080` on the cluster.
|
|-----------------------------------------------------------------------------|------------------------------------------------------------------------------------------------|
|
||||||
- `--constraint=node.role==manager`: we ask docker to schedule Træfik
|
| `--publish 80:80 --publish 8080:8080` | we publish port `80` and `8080` on the cluster. |
|
||||||
on a manager node.
|
| `--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`:
|
| `--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. |
|
||||||
we bind mount the docker socket where Træfik is scheduled to be able
|
| `--network traefik-net` | we attach the Træfik service (and thus the underlying container) to the `traefik-net` network. |
|
||||||
to speak to the daemon.
|
| `--docker` | enable docker backend, and `--docker.swarmmode` to enable the swarm mode on Træfik. |
|
||||||
- `--network traefik-net`: we attach the Træfik service (and thus
|
| `--web` | activate the webUI on port 8080 |
|
||||||
the underlying container) to the `traefik-net` network.
|
|
||||||
- `--docker`: enable docker backend, and `--docker.swarmmode` to
|
|
||||||
enable the swarm mode on Træfik.
|
|
||||||
- `--web`: activate the webUI on port 8080
|
|
||||||
|
|
||||||
## Deploy your apps
|
## Deploy your apps
|
||||||
|
|
||||||
We can now deploy our app on the cluster,
|
We can now deploy our app on the cluster, here [whoami](https://github.com/emilevauge/whoami), a simple web server in Go.
|
||||||
here [whoami](https://github.com/emilevauge/whoami), a simple web
|
We start 2 services, on the `traefik-net` network.
|
||||||
server in Go. We start 2 services, on the `traefik-net` network.
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
docker-machine ssh manager "docker service create \
|
docker-machine ssh manager "docker service create \
|
||||||
@@ -130,176 +125,209 @@ docker-machine ssh manager "docker service create \
|
|||||||
emilevauge/whoami"
|
emilevauge/whoami"
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that we set whoami1 to use sticky sessions (`--label traefik.backend.loadbalancer.sticky=true`). We'll demonstrate that later.
|
!!! note
|
||||||
If using `docker stack deploy`, there is [a specific way that the labels must be defined in the docker-compose file](https://github.com/containous/traefik/issues/994#issuecomment-269095109).
|
We set `whoami1` to use sticky sessions (`--label traefik.backend.loadbalancer.stickiness=true`).
|
||||||
|
We'll demonstrate that later.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
If using `docker stack deploy`, there is [a specific way that the labels must be defined in the docker-compose file](https://github.com/containous/traefik/issues/994#issuecomment-269095109).
|
||||||
|
|
||||||
Check that everything is scheduled and started:
|
Check that everything is scheduled and started:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
docker-machine ssh manager "docker service ls"
|
docker-machine ssh manager "docker service ls"
|
||||||
ID NAME REPLICAS IMAGE COMMAND
|
|
||||||
ab046gpaqtln whoami0 1/1 emilevauge/whoami
|
|
||||||
cgfg5ifzrpgm whoami1 1/1 emilevauge/whoami
|
|
||||||
dtpl249tfghc traefik 1/1 traefik --docker --docker.swarmmode --docker.domain=traefik --docker.watch --web
|
|
||||||
```
|
```
|
||||||
|
```
|
||||||
|
ID NAME MODE REPLICAS IMAGE PORTS
|
||||||
|
moq3dq4xqv6t traefik replicated 1/1 traefik:latest *:80->80/tcp,*:8080->8080/tcp
|
||||||
|
ysil6oto1wim whoami0 replicated 1/1 emilevauge/whoami:latest
|
||||||
|
z9re2mnl34k4 whoami1 replicated 1/1 emilevauge/whoami:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Access to your apps through Træfik
|
## Access to your apps through Træfik
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
curl -H Host:whoami0.traefik http://$(docker-machine ip manager)
|
curl -H Host:whoami0.traefik http://$(docker-machine ip manager)
|
||||||
Hostname: 8147a7746e7a
|
```
|
||||||
|
```yaml
|
||||||
|
Hostname: 5b0b3d148359
|
||||||
IP: 127.0.0.1
|
IP: 127.0.0.1
|
||||||
IP: ::1
|
IP: 10.0.0.8
|
||||||
IP: 10.0.9.3
|
IP: 10.0.0.4
|
||||||
IP: fe80::42:aff:fe00:903
|
IP: 172.18.0.5
|
||||||
IP: 172.18.0.3
|
|
||||||
IP: fe80::42:acff:fe12:3
|
|
||||||
GET / HTTP/1.1
|
GET / HTTP/1.1
|
||||||
Host: 10.0.9.3:80
|
Host: whoami0.traefik
|
||||||
User-Agent: curl/7.35.0
|
User-Agent: curl/7.55.1
|
||||||
Accept: */*
|
Accept: */*
|
||||||
Accept-Encoding: gzip
|
Accept-Encoding: gzip
|
||||||
X-Forwarded-For: 192.168.99.1
|
X-Forwarded-For: 10.255.0.2
|
||||||
X-Forwarded-Host: 10.0.9.3:80
|
X-Forwarded-Host: whoami0.traefik
|
||||||
X-Forwarded-Proto: http
|
X-Forwarded-Proto: http
|
||||||
X-Forwarded-Server: 8fbc39271b4c
|
X-Forwarded-Server: 77fc29c69fe4
|
||||||
|
```
|
||||||
|
```shell
|
||||||
curl -H Host:whoami1.traefik http://$(docker-machine ip manager)
|
curl -H Host:whoami1.traefik http://$(docker-machine ip manager)
|
||||||
Hostname: ba2c21488299
|
```
|
||||||
|
```yaml
|
||||||
|
Hostname: 3633163970f6
|
||||||
IP: 127.0.0.1
|
IP: 127.0.0.1
|
||||||
IP: ::1
|
IP: 10.0.0.14
|
||||||
IP: 10.0.9.4
|
IP: 10.0.0.6
|
||||||
IP: fe80::42:aff:fe00:904
|
IP: 172.18.0.5
|
||||||
IP: 172.18.0.2
|
|
||||||
IP: fe80::42:acff:fe12:2
|
|
||||||
GET / HTTP/1.1
|
GET / HTTP/1.1
|
||||||
Host: 10.0.9.4:80
|
Host: whoami1.traefik
|
||||||
User-Agent: curl/7.35.0
|
User-Agent: curl/7.55.1
|
||||||
Accept: */*
|
Accept: */*
|
||||||
Accept-Encoding: gzip
|
Accept-Encoding: gzip
|
||||||
X-Forwarded-For: 192.168.99.1
|
X-Forwarded-For: 10.255.0.2
|
||||||
X-Forwarded-Host: 10.0.9.4:80
|
X-Forwarded-Host: whoami1.traefik
|
||||||
X-Forwarded-Proto: http
|
X-Forwarded-Proto: http
|
||||||
X-Forwarded-Server: 8fbc39271b4c
|
X-Forwarded-Server: 77fc29c69fe4
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that as Træfik is published, you can access it from any machine
|
!!! note
|
||||||
and not only the manager.
|
As Træfik is published, you can access it from any machine and not only the manager.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
curl -H Host:whoami0.traefik http://$(docker-machine ip worker1)
|
curl -H Host:whoami0.traefik http://$(docker-machine ip worker1)
|
||||||
Hostname: 8147a7746e7a
|
```
|
||||||
|
```yaml
|
||||||
|
Hostname: 5b0b3d148359
|
||||||
IP: 127.0.0.1
|
IP: 127.0.0.1
|
||||||
IP: ::1
|
IP: 10.0.0.8
|
||||||
IP: 10.0.9.3
|
IP: 10.0.0.4
|
||||||
IP: fe80::42:aff:fe00:903
|
IP: 172.18.0.5
|
||||||
IP: 172.18.0.3
|
|
||||||
IP: fe80::42:acff:fe12:3
|
|
||||||
GET / HTTP/1.1
|
GET / HTTP/1.1
|
||||||
Host: 10.0.9.3:80
|
Host: whoami0.traefik
|
||||||
User-Agent: curl/7.35.0
|
User-Agent: curl/7.55.1
|
||||||
Accept: */*
|
Accept: */*
|
||||||
Accept-Encoding: gzip
|
Accept-Encoding: gzip
|
||||||
X-Forwarded-For: 192.168.99.1
|
X-Forwarded-For: 10.255.0.3
|
||||||
X-Forwarded-Host: 10.0.9.3:80
|
X-Forwarded-Host: whoami0.traefik
|
||||||
X-Forwarded-Proto: http
|
X-Forwarded-Proto: http
|
||||||
X-Forwarded-Server: 8fbc39271b4c
|
X-Forwarded-Server: 77fc29c69fe4
|
||||||
|
```
|
||||||
|
```shell
|
||||||
curl -H Host:whoami1.traefik http://$(docker-machine ip worker2)
|
curl -H Host:whoami1.traefik http://$(docker-machine ip worker2)
|
||||||
Hostname: ba2c21488299
|
```
|
||||||
|
```yaml
|
||||||
|
Hostname: 3633163970f6
|
||||||
IP: 127.0.0.1
|
IP: 127.0.0.1
|
||||||
IP: ::1
|
IP: 10.0.0.14
|
||||||
IP: 10.0.9.4
|
IP: 10.0.0.6
|
||||||
IP: fe80::42:aff:fe00:904
|
IP: 172.18.0.5
|
||||||
IP: 172.18.0.2
|
|
||||||
IP: fe80::42:acff:fe12:2
|
|
||||||
GET / HTTP/1.1
|
GET / HTTP/1.1
|
||||||
Host: 10.0.9.4:80
|
Host: whoami1.traefik
|
||||||
User-Agent: curl/7.35.0
|
User-Agent: curl/7.55.1
|
||||||
Accept: */*
|
Accept: */*
|
||||||
Accept-Encoding: gzip
|
Accept-Encoding: gzip
|
||||||
X-Forwarded-For: 192.168.99.1
|
X-Forwarded-For: 10.255.0.4
|
||||||
X-Forwarded-Host: 10.0.9.4:80
|
X-Forwarded-Host: whoami1.traefik
|
||||||
X-Forwarded-Proto: http
|
X-Forwarded-Proto: http
|
||||||
X-Forwarded-Server: 8fbc39271b4c
|
X-Forwarded-Server: 77fc29c69fe4
|
||||||
```
|
```
|
||||||
|
|
||||||
## Scale both services
|
## Scale both services
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
docker-machine ssh manager "docker service scale whoami0=5"
|
docker-machine ssh manager "docker service scale whoami0=5"
|
||||||
|
|
||||||
docker-machine ssh manager "docker service scale whoami1=5"
|
docker-machine ssh manager "docker service scale whoami1=5"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Check that we now have 5 replicas of each `whoami` service:
|
Check that we now have 5 replicas of each `whoami` service:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
docker-machine ssh manager "docker service ls"
|
docker-machine ssh manager "docker service ls"
|
||||||
ID NAME REPLICAS IMAGE COMMAND
|
|
||||||
ab046gpaqtln whoami0 5/5 emilevauge/whoami
|
|
||||||
cgfg5ifzrpgm whoami1 5/5 emilevauge/whoami
|
|
||||||
dtpl249tfghc traefik 1/1 traefik --docker --docker.swarmmode --docker.domain=traefik --docker.watch --web
|
|
||||||
```
|
```
|
||||||
## Access to your whoami0 through Træfik multiple times.
|
```
|
||||||
|
ID NAME MODE REPLICAS IMAGE PORTS
|
||||||
|
moq3dq4xqv6t traefik replicated 1/1 traefik:latest *:80->80/tcp,*:8080->8080/tcp
|
||||||
|
ysil6oto1wim whoami0 replicated 5/5 emilevauge/whoami:latest
|
||||||
|
z9re2mnl34k4 whoami1 replicated 5/5 emilevauge/whoami:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
## Access to your `whoami0` through Træfik multiple times.
|
||||||
|
|
||||||
|
Repeat the following command multiple times and note that the Hostname changes each time as Traefik load balances each request against the 5 tasks:
|
||||||
|
|
||||||
Repeat the following command multiple times and note that the Hostname changes each time as Traefik load balances each request against the 5 tasks.
|
|
||||||
```shell
|
```shell
|
||||||
curl -H Host:whoami0.traefik http://$(docker-machine ip manager)
|
curl -H Host:whoami0.traefik http://$(docker-machine ip manager)
|
||||||
Hostname: 8147a7746e7a
|
```
|
||||||
|
```yaml
|
||||||
|
Hostname: f3138d15b567
|
||||||
IP: 127.0.0.1
|
IP: 127.0.0.1
|
||||||
IP: ::1
|
IP: 10.0.0.5
|
||||||
IP: 10.0.9.3
|
IP: 10.0.0.4
|
||||||
IP: fe80::42:aff:fe00:903
|
|
||||||
IP: 172.18.0.3
|
IP: 172.18.0.3
|
||||||
IP: fe80::42:acff:fe12:3
|
|
||||||
GET / HTTP/1.1
|
GET / HTTP/1.1
|
||||||
Host: 10.0.9.3:80
|
Host: whoami0.traefik
|
||||||
User-Agent: curl/7.35.0
|
User-Agent: curl/7.55.1
|
||||||
Accept: */*
|
Accept: */*
|
||||||
Accept-Encoding: gzip
|
Accept-Encoding: gzip
|
||||||
X-Forwarded-For: 192.168.99.1
|
X-Forwarded-For: 10.255.0.2
|
||||||
X-Forwarded-Host: 10.0.9.3:80
|
X-Forwarded-Host: whoami0.traefik
|
||||||
X-Forwarded-Proto: http
|
X-Forwarded-Proto: http
|
||||||
X-Forwarded-Server: 8fbc39271b4c
|
X-Forwarded-Server: 77fc29c69fe4
|
||||||
```
|
```
|
||||||
|
|
||||||
Do the same against whoami1.
|
Do the same against `whoami1`:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
curl -H Host:whoami1.traefik http://$(docker-machine ip manager)
|
curl -c cookies.txt -H Host:whoami1.traefik http://$(docker-machine ip manager)
|
||||||
Hostname: ba2c21488299
|
```
|
||||||
|
```yaml
|
||||||
|
Hostname: 348e2f7bf432
|
||||||
IP: 127.0.0.1
|
IP: 127.0.0.1
|
||||||
IP: ::1
|
IP: 10.0.0.15
|
||||||
IP: 10.0.9.4
|
IP: 10.0.0.6
|
||||||
IP: fe80::42:aff:fe00:904
|
IP: 172.18.0.6
|
||||||
IP: 172.18.0.2
|
|
||||||
IP: fe80::42:acff:fe12:2
|
|
||||||
GET / HTTP/1.1
|
GET / HTTP/1.1
|
||||||
Host: 10.0.9.4:80
|
Host: whoami1.traefik
|
||||||
User-Agent: curl/7.35.0
|
User-Agent: curl/7.55.1
|
||||||
Accept: */*
|
Accept: */*
|
||||||
Accept-Encoding: gzip
|
Accept-Encoding: gzip
|
||||||
X-Forwarded-For: 192.168.99.1
|
X-Forwarded-For: 10.255.0.2
|
||||||
X-Forwarded-Host: 10.0.9.4:80
|
X-Forwarded-Host: whoami1.traefik
|
||||||
X-Forwarded-Proto: http
|
X-Forwarded-Proto: http
|
||||||
X-Forwarded-Server: 8fbc39271b4c
|
X-Forwarded-Server: 77fc29c69fe4
|
||||||
```
|
|
||||||
Wait, I thought we added the sticky flag to whoami1? Traefik relies on a cookie to maintain stickyness so you'll need to test this with a browser.
|
|
||||||
|
|
||||||
First you need to add whoami1.traefik to your hosts file:
|
|
||||||
```ssh
|
|
||||||
if [ -n "$(grep whoami1.traefik /etc/hosts)" ];
|
|
||||||
then
|
|
||||||
echo "whoami1.traefik already exists (make sure the ip is current)";
|
|
||||||
else
|
|
||||||
sudo -- sh -c -e "echo '$(docker-machine ip manager)\twhoami1.traefik'
|
|
||||||
>> /etc/hosts";
|
|
||||||
fi
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Now open your browser and go to http://whoami1.traefik/
|
Because the sticky sessions require cookies to work, we used the `-c cookies.txt` option to store the cookie into a file.
|
||||||
|
The cookie contains the IP of the container to which the session sticks:
|
||||||
|
|
||||||
You will now see that stickyness is maintained.
|
```shell
|
||||||
|
cat ./cookies.txt
|
||||||
|
```
|
||||||
|
```
|
||||||
|
# Netscape HTTP Cookie File
|
||||||
|
# https://curl.haxx.se/docs/http-cookies.html
|
||||||
|
# This file was generated by libcurl! Edit at your own risk.
|
||||||
|
|
||||||

|
whoami1.traefik FALSE / FALSE 0 _TRAEFIK_BACKEND http://10.0.0.15:80
|
||||||
|
```
|
||||||
|
|
||||||
|
If you load the cookies file (`-b cookies.txt`) for the next request, you will see that stickiness is maintained:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl -b cookies.txt -H Host:whoami1.traefik http://$(docker-machine ip manager)
|
||||||
|
```
|
||||||
|
```yaml
|
||||||
|
Hostname: 348e2f7bf432
|
||||||
|
IP: 127.0.0.1
|
||||||
|
IP: 10.0.0.15
|
||||||
|
IP: 10.0.0.6
|
||||||
|
IP: 172.18.0.6
|
||||||
|
GET / HTTP/1.1
|
||||||
|
Host: whoami1.traefik
|
||||||
|
User-Agent: curl/7.55.1
|
||||||
|
Accept: */*
|
||||||
|
Accept-Encoding: gzip
|
||||||
|
Cookie: _TRAEFIK_BACKEND=http://10.0.0.15:80
|
||||||
|
X-Forwarded-For: 10.255.0.2
|
||||||
|
X-Forwarded-Host: whoami1.traefik
|
||||||
|
X-Forwarded-Proto: http
|
||||||
|
X-Forwarded-Server: 77fc29c69fe4
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Swarm cluster
|
# Swarm cluster
|
||||||
|
|
||||||
This section explains how to create a multi-host [swarm](https://docs.docker.com/swarm) cluster using [docker-machine](https://docs.docker.com/machine/) and how to deploy Træfik on it.
|
This section explains how to create a multi-host [swarm](https://docs.docker.com/swarm) cluster using [docker-machine](https://docs.docker.com/machine/) and how to deploy Træfik on it.
|
||||||
|
|
||||||
The cluster consists of:
|
The cluster consists of:
|
||||||
|
|
||||||
- 2 servers
|
- 2 servers
|
||||||
@@ -97,14 +98,17 @@ docker $(docker-machine config mhs-demo0) run \
|
|||||||
|
|
||||||
Let's explain this command:
|
Let's explain this command:
|
||||||
|
|
||||||
- `-p 80:80 -p 8080:8080`: we bind ports 80 and 8080
|
| Option | Description |
|
||||||
- `--net=my-net`: run the container on the network my-net
|
|-------------------------------------------|---------------------------------------------------------------|
|
||||||
- `-v /var/lib/boot2docker/:/ssl`: mount the ssl keys generated by docker-machine
|
| `-p 80:80 -p 8080:8080` | we bind ports 80 and 8080 |
|
||||||
- `-c /dev/null`: empty config file
|
| `--net=my-net` | run the container on the network my-net |
|
||||||
- `--docker`: enable docker backend
|
| `-v /var/lib/boot2docker/:/ssl` | mount the ssl keys generated by docker-machine |
|
||||||
- `--docker.endpoint=tcp://172.18.0.1:3376`: connect to the swarm master using the docker_gwbridge network
|
| `-c /dev/null` | empty config file |
|
||||||
- `--docker.tls`: enable TLS using the docker-machine keys
|
| `--docker` | enable docker backend |
|
||||||
- `--web`: activate the webUI on port 8080
|
| `--docker.endpoint=tcp://172.18.0.1:3376` | connect to the swarm master using the docker_gwbridge network |
|
||||||
|
| `--docker.tls` | enable TLS using the docker-machine keys |
|
||||||
|
| `--web` | activate the webUI on port 8080 |
|
||||||
|
|
||||||
|
|
||||||
## Deploy your apps
|
## Deploy your apps
|
||||||
|
|
||||||
@@ -120,6 +124,8 @@ Check that everything is started:
|
|||||||
|
|
||||||
```shell
|
```shell
|
||||||
docker ps
|
docker ps
|
||||||
|
```
|
||||||
|
```
|
||||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||||
ba2c21488299 emilevauge/whoami "/whoamI" 8 seconds ago Up 9 seconds 80/tcp mhs-demo1/whoami1
|
ba2c21488299 emilevauge/whoami "/whoamI" 8 seconds ago Up 9 seconds 80/tcp mhs-demo1/whoami1
|
||||||
8147a7746e7a emilevauge/whoami "/whoamI" 19 seconds ago Up 20 seconds 80/tcp mhs-demo0/whoami0
|
8147a7746e7a emilevauge/whoami "/whoamI" 19 seconds ago Up 20 seconds 80/tcp mhs-demo0/whoami0
|
||||||
@@ -130,6 +136,8 @@ ba2c21488299 emilevauge/whoami "/whoamI" 8 seconds ago
|
|||||||
|
|
||||||
```shell
|
```shell
|
||||||
curl -H Host:whoami0.traefik http://$(docker-machine ip mhs-demo0)
|
curl -H Host:whoami0.traefik http://$(docker-machine ip mhs-demo0)
|
||||||
|
```
|
||||||
|
```yaml
|
||||||
Hostname: 8147a7746e7a
|
Hostname: 8147a7746e7a
|
||||||
IP: 127.0.0.1
|
IP: 127.0.0.1
|
||||||
IP: ::1
|
IP: ::1
|
||||||
@@ -146,8 +154,12 @@ X-Forwarded-For: 192.168.99.1
|
|||||||
X-Forwarded-Host: 10.0.9.3:80
|
X-Forwarded-Host: 10.0.9.3:80
|
||||||
X-Forwarded-Proto: http
|
X-Forwarded-Proto: http
|
||||||
X-Forwarded-Server: 8fbc39271b4c
|
X-Forwarded-Server: 8fbc39271b4c
|
||||||
|
```
|
||||||
|
|
||||||
|
```shell
|
||||||
curl -H Host:whoami1.traefik http://$(docker-machine ip mhs-demo0)
|
curl -H Host:whoami1.traefik http://$(docker-machine ip mhs-demo0)
|
||||||
|
```
|
||||||
|
```yaml
|
||||||
Hostname: ba2c21488299
|
Hostname: ba2c21488299
|
||||||
IP: 127.0.0.1
|
IP: 127.0.0.1
|
||||||
IP: ::1
|
IP: ::1
|
||||||
@@ -166,4 +178,4 @@ X-Forwarded-Proto: http
|
|||||||
X-Forwarded-Server: 8fbc39271b4c
|
X-Forwarded-Server: 8fbc39271b4c
|
||||||
```
|
```
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
@@ -19,19 +19,19 @@ address = ":7888"
|
|||||||
################################################################
|
################################################################
|
||||||
# rules
|
# rules
|
||||||
################################################################
|
################################################################
|
||||||
[backends]
|
[backends]
|
||||||
[backends.backend]
|
[backends.backend]
|
||||||
[backends.backend.LoadBalancer]
|
[backends.backend.LoadBalancer]
|
||||||
method = "drr"
|
method = "drr"
|
||||||
[backends.backend.servers.server1]
|
[backends.backend.servers.server1]
|
||||||
url = "http://127.0.0.1:8081"
|
url = "http://127.0.0.1:8081"
|
||||||
[backends.backend.servers.server2]
|
[backends.backend.servers.server2]
|
||||||
url = "http://127.0.0.1:8082"
|
url = "http://127.0.0.1:8082"
|
||||||
[backends.backend.servers.server3]
|
[backends.backend.servers.server3]
|
||||||
url = "http://127.0.0.1:8083"
|
url = "http://127.0.0.1:8083"
|
||||||
[frontends]
|
[frontends]
|
||||||
[frontends.frontend]
|
[frontends.frontend]
|
||||||
backend = "backend"
|
backend = "backend"
|
||||||
passHostHeader = true
|
passHostHeader = true
|
||||||
[frontends.frontend.routes.test]
|
[frontends.frontend.routes.test]
|
||||||
rule = "Path: /test"
|
rule = "Path: /test"
|
||||||
|
|||||||
@@ -19,24 +19,24 @@ address = ":7888"
|
|||||||
################################################################
|
################################################################
|
||||||
# rules
|
# rules
|
||||||
################################################################
|
################################################################
|
||||||
[backends]
|
[backends]
|
||||||
[backends.backend1]
|
[backends.backend1]
|
||||||
[backends.backend1.servers.server1]
|
[backends.backend1.servers.server1]
|
||||||
url = "http://127.0.0.1:8081"
|
url = "http://127.0.0.1:8081"
|
||||||
[backends.backend2]
|
[backends.backend2]
|
||||||
[backends.backend2.LoadBalancer]
|
[backends.backend2.LoadBalancer]
|
||||||
method = "drr"
|
method = "drr"
|
||||||
[backends.backend2.servers.server1]
|
[backends.backend2.servers.server1]
|
||||||
url = "http://127.0.0.1:8082"
|
url = "http://127.0.0.1:8082"
|
||||||
[backends.backend2.servers.server2]
|
[backends.backend2.servers.server2]
|
||||||
url = "http://127.0.0.1:8083"
|
url = "http://127.0.0.1:8083"
|
||||||
[frontends]
|
[frontends]
|
||||||
[frontends.frontend1]
|
[frontends.frontend1]
|
||||||
backend = "backend1"
|
backend = "backend1"
|
||||||
[frontends.frontend1.routes.test_1]
|
[frontends.frontend1.routes.test_1]
|
||||||
rule = "Path: /test1"
|
rule = "Path: /test1"
|
||||||
[frontends.frontend2]
|
[frontends.frontend2]
|
||||||
backend = "backend2"
|
backend = "backend2"
|
||||||
passHostHeader = true
|
passHostHeader = true
|
||||||
[frontends.frontend2.routes.test_2]
|
[frontends.frontend2.routes.test_2]
|
||||||
rule = "Path: /test2"
|
rule = "Path: /test2"
|
||||||
30
examples/acme/Docker_Acme.md
Normal file
30
examples/acme/Docker_Acme.md
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# ACME Testing environment
|
||||||
|
|
||||||
|
## Objectives
|
||||||
|
|
||||||
|
In our integration ACME tests, we use a simulated Let's Encrypt container based stack named boulder.
|
||||||
|
|
||||||
|
The goal of this directory is to provide to developers a Traefik-boulder full stack environment.
|
||||||
|
This environment may be used in order to quickly test developments on ACME certificates management.
|
||||||
|
|
||||||
|
The provided Boulder stack is based on the environment used during integration tests.
|
||||||
|
|
||||||
|
## Directory content
|
||||||
|
|
||||||
|
* **compose-acme.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.
|
||||||
|
|
||||||
|
## Shell script
|
||||||
|
|
||||||
|
### Description
|
||||||
|
|
||||||
|
To work fine, boulder needs a domain name, with a related IP and storage file. The shell script allows to check the environment before launching the Docker environment with the rights parameters and to managing this environment.
|
||||||
|
|
||||||
|
### Use
|
||||||
|
|
||||||
|
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.
|
||||||
|
* **--stop** : Stop and delete the current Docker environment.
|
||||||
|
* **--restart--** : Concatenate **--stop** and **--start** actions.
|
||||||
33
examples/acme/acme.toml
Normal file
33
examples/acme/acme.toml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
logLevel = "DEBUG"
|
||||||
|
|
||||||
|
defaultEntryPoints = ["http", "https"]
|
||||||
|
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.http]
|
||||||
|
address = ":80"
|
||||||
|
[entryPoints.http.redirect]
|
||||||
|
entryPoint = "https"
|
||||||
|
[entryPoints.https]
|
||||||
|
address = ":443"
|
||||||
|
[entryPoints.https.tls]
|
||||||
|
|
||||||
|
|
||||||
|
[acme]
|
||||||
|
email = "test@traefik.io"
|
||||||
|
storage = "/etc/traefik/conf/acme.json"
|
||||||
|
entryPoint = "https"
|
||||||
|
onDemand = false
|
||||||
|
OnHostRule = true
|
||||||
|
caServer = "http://traefik.localhost.com:4000/directory"
|
||||||
|
|
||||||
|
|
||||||
|
[web]
|
||||||
|
address = ":8080"
|
||||||
|
|
||||||
|
[docker]
|
||||||
|
endpoint = "unix:///var/run/docker.sock"
|
||||||
|
domain = "traefik.localhost.com"
|
||||||
|
watch = true
|
||||||
|
exposedbydefault = false
|
||||||
|
|
||||||
|
|
||||||
89
examples/acme/compose-acme.yml
Normal file
89
examples/acme/compose-acme.yml
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
expose:
|
||||||
|
- "8080"
|
||||||
|
labels:
|
||||||
|
- "traefik.port=8080"
|
||||||
|
- "traefik.backend=traefikception"
|
||||||
|
- "traefik.frontend.rule=Host:traefik.localhost.com"
|
||||||
|
- "traefik.enable=true"
|
||||||
|
depends_on:
|
||||||
|
- boulder
|
||||||
101
examples/acme/manage_acme_docker_environment.sh
Executable file
101
examples/acme/manage_acme_docker_environment.sh
Executable file
@@ -0,0 +1,101 @@
|
|||||||
|
#! /usr/bin/env bash
|
||||||
|
|
||||||
|
# Initialize variables
|
||||||
|
readonly traefik_url="traefik.localhost.com"
|
||||||
|
readonly basedir=$(dirname $0)
|
||||||
|
readonly doc_file=$basedir"/compose-acme.yml"
|
||||||
|
|
||||||
|
# Stop and remove Docker environment
|
||||||
|
down_environment() {
|
||||||
|
echo "STOP Docker environment"
|
||||||
|
! docker-compose -f $doc_file down -v &>/dev/null && \
|
||||||
|
echo "[ERROR] Impossible to stop the Docker environment" && exit 11
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and start Docker-compose environment or subpart of its services (if services are listed)
|
||||||
|
# $@ : List of services to start (optional)
|
||||||
|
up_environment() {
|
||||||
|
echo "START Docker environment"
|
||||||
|
! docker-compose -f $doc_file up -d $@ &>/dev/null && \
|
||||||
|
echo "[ERROR] Impossible to start Docker environment" && exit 21
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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 && \
|
||||||
|
chmod 600 $basedir/acme.json # Needed for ACME
|
||||||
|
}
|
||||||
|
|
||||||
|
# Start all the environement
|
||||||
|
start() {
|
||||||
|
init_environment
|
||||||
|
echo "Start boulder environment"
|
||||||
|
up_environment bmysql brabbitmq 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
|
||||||
|
sleep 5
|
||||||
|
let waiting_counter-=1
|
||||||
|
if [[ $waiting_counter -eq 0 ]]; then
|
||||||
|
echo "[ERROR] Impossible to start boulder container in the allowed time, the Docker environment will be stopped"
|
||||||
|
down_environment
|
||||||
|
exit 41
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "START Traefik container"
|
||||||
|
up_environment traefik
|
||||||
|
}
|
||||||
|
|
||||||
|
# Script usage
|
||||||
|
show_usage() {
|
||||||
|
echo
|
||||||
|
echo "USAGE : manage_acme_docker_environment.sh [--start|--stop|--restart]"
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main method
|
||||||
|
# $@ All parameters given
|
||||||
|
main() {
|
||||||
|
|
||||||
|
[[ $# -ne 1 ]] && show_usage && exit 1
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
"--start")
|
||||||
|
# Start boulder environment
|
||||||
|
start
|
||||||
|
echo "ENVIRONMENT SUCCESSFULLY STARTED"
|
||||||
|
;;
|
||||||
|
"--stop")
|
||||||
|
! down_environment
|
||||||
|
echo "ENVIRONMENT SUCCESSFULLY STOPPED"
|
||||||
|
;;
|
||||||
|
"--restart")
|
||||||
|
down_environment
|
||||||
|
start
|
||||||
|
echo "ENVIRONMENT SUCCESSFULLY STARTED"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
show_usage && exit 2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
main $@
|
||||||
200
examples/cluster/docker-compose.yml
Normal file
200
examples/cluster/docker-compose.yml
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
version: '2'
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
## KV part ##
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
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:
|
||||||
|
- 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
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
## TRAEFIK part ##
|
||||||
|
|
||||||
|
traefik-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"
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
- "8080:8080"
|
||||||
|
- "443:443"
|
||||||
|
- "5001:443" # Needed for SNI 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"
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
networks:
|
||||||
|
net:
|
||||||
|
driver: bridge
|
||||||
|
ipam:
|
||||||
|
config:
|
||||||
|
- subnet: 10.0.1.0/26
|
||||||
216
examples/cluster/manage_cluster_docker_environment.sh
Executable file
216
examples/cluster/manage_cluster_docker_environment.sh
Executable file
@@ -0,0 +1,216 @@
|
|||||||
|
#! /usr/bin/env bash
|
||||||
|
|
||||||
|
# Initialize variables
|
||||||
|
readonly basedir=$(dirname $0)
|
||||||
|
readonly doc_file=$basedir"/docker-compose.yml"
|
||||||
|
export COMPOSE_PROJECT_NAME="cluster"
|
||||||
|
|
||||||
|
# Stop and remove Docker environment
|
||||||
|
down_environment() {
|
||||||
|
echo "DOWN Docker environment"
|
||||||
|
! docker-compose -f $doc_file down -v &>/dev/null && \
|
||||||
|
echo "[ERROR] Unable to stop the Docker environment" && exit 11
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and start Docker-compose environment or subpart of its services (if services are listed)
|
||||||
|
# $@ : List of services to start (optional)
|
||||||
|
up_environment() {
|
||||||
|
echo "START Docker environment "$@
|
||||||
|
! docker-compose -f $doc_file up -d $@ &>/dev/null && \
|
||||||
|
echo "[ERROR] Unable to start Docker environment ${@}" && exit 21
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Stop and remove Docker environment
|
||||||
|
delete_services() {
|
||||||
|
echo "DELETE services "$@
|
||||||
|
! docker-compose -f $doc_file stop $@ &>/dev/null && \
|
||||||
|
echo "[ERROR] Unable to stop services "$@ && exit 31
|
||||||
|
! docker-compose -f $doc_file rm -vf $@ &>/dev/null && \
|
||||||
|
echo "[ERROR] Unable to delete services "$@ && exit 31
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
start_consul() {
|
||||||
|
up_environment consul
|
||||||
|
waiting_counter=12
|
||||||
|
# Not start Traefik store config if consul is not started
|
||||||
|
echo "WAIT for consul..."
|
||||||
|
sleep 5
|
||||||
|
while [[ -z $(curl -s http://10.0.1.2:8500/v1/status/leader) ]]; do
|
||||||
|
sleep 5
|
||||||
|
let waiting_counter-=1
|
||||||
|
if [[ $waiting_counter -eq 0 ]]; then
|
||||||
|
echo "[ERROR] Unable to start consul container in the allowed time, the Docker environment will be stopped"
|
||||||
|
down_environment
|
||||||
|
exit 41
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
start_etcd3() {
|
||||||
|
up_environment etcd3
|
||||||
|
waiting_counter=12
|
||||||
|
# Not start Traefik store config if consul is not started
|
||||||
|
echo "WAIT for ETCD3..."
|
||||||
|
while [[ -z $(curl -s --connect-timeout 2 http://10.0.1.12:2379/version) ]]; do
|
||||||
|
sleep 5
|
||||||
|
let waiting_counter-=1
|
||||||
|
if [[ $waiting_counter -eq 0 ]]; then
|
||||||
|
echo "[ERROR] Unable to start etcd3 container in the allowed time, the Docker environment will be stopped"
|
||||||
|
down_environment
|
||||||
|
exit 51
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
start_storeconfig_consul() {
|
||||||
|
# Create traefik.toml with consul provider
|
||||||
|
cp $basedir/traefik.toml.tmpl $basedir/traefik.toml
|
||||||
|
echo '
|
||||||
|
[consul]
|
||||||
|
endpoint = "10.0.1.2:8500"
|
||||||
|
watch = true
|
||||||
|
prefix = "traefik"' >> $basedir/traefik.toml
|
||||||
|
up_environment traefik-storeconfig
|
||||||
|
rm -f $basedir/traefik.toml
|
||||||
|
waiting_counter=5
|
||||||
|
delete_services traefik-storeconfig
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
start_storeconfig_etcd3() {
|
||||||
|
# Create traefik.toml with consul provider
|
||||||
|
cp $basedir/traefik.toml.tmpl $basedir/traefik.toml
|
||||||
|
echo '
|
||||||
|
[etcd]
|
||||||
|
endpoint = "10.0.1.12:2379"
|
||||||
|
watch = true
|
||||||
|
prefix = "/traefik"
|
||||||
|
useAPIV3 = true' >> $basedir/traefik.toml
|
||||||
|
up_environment traefik-storeconfig
|
||||||
|
rm -f $basedir/traefik.toml
|
||||||
|
waiting_counter=5
|
||||||
|
# Don't start Traefik store config if ETCD3 is not started
|
||||||
|
echo "Delete storage file key..."
|
||||||
|
while [[ $(docker-compose -f $doc_file up --exit-code-from etcdctl-ping etcdctl-ping &>/dev/null) -ne 0 && $waiting_counter -gt 0 ]]; do
|
||||||
|
sleep 5
|
||||||
|
let waiting_counter-=1
|
||||||
|
done
|
||||||
|
delete_services traefik-storeconfig etcdctl-ping
|
||||||
|
}
|
||||||
|
|
||||||
|
start_traefik() {
|
||||||
|
up_environment traefik01
|
||||||
|
# Waiting for the first instance which is mapped to the host as leader before to start the second one
|
||||||
|
waiting_counter=5
|
||||||
|
echo "WAIT for traefik leader..."
|
||||||
|
sleep 10
|
||||||
|
while [[ -z $(curl -s --connect-timeout 3 http://10.0.1.8:8080/ping) ]]; do
|
||||||
|
sleep 2
|
||||||
|
let waiting_counter-=1
|
||||||
|
if [[ $waiting_counter -eq 0 ]]; then
|
||||||
|
echo "[ERROR] Unable to start Traefik leader container in the allowed time, the Docker environment will be stopped"
|
||||||
|
down_environment
|
||||||
|
exit 51
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
up_environment whoami01
|
||||||
|
waiting_counter=5
|
||||||
|
echo "WAIT for whoami..."
|
||||||
|
sleep 10
|
||||||
|
while [[ -z $(curl -s --connect-timeout 3 http://10.0.1.10) ]]; do
|
||||||
|
sleep 2
|
||||||
|
let waiting_counter-=1
|
||||||
|
if [[ $waiting_counter -eq 0 ]]; then
|
||||||
|
echo "[ERROR] Unable to start whoami container in the allowed time, the Docker environment will be stopped"
|
||||||
|
down_environment
|
||||||
|
exit 52
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
up_environment traefik02 whoami02
|
||||||
|
}
|
||||||
|
|
||||||
|
# Start boulder services
|
||||||
|
start_boulder() {
|
||||||
|
echo "Start boulder environment"
|
||||||
|
up_environment bmysql brabbitmq 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
|
||||||
|
sleep 5
|
||||||
|
let waiting_counter-=1
|
||||||
|
if [[ $waiting_counter -eq 0 ]]; then
|
||||||
|
echo "[ERROR] Unable to start boulder container in the allowed time, the Docker environment will be stopped"
|
||||||
|
down_environment
|
||||||
|
exit 61
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "Boulder started."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Script usage
|
||||||
|
show_usage() {
|
||||||
|
echo
|
||||||
|
echo "USAGE : manage_cluster_docker_environment.sh [--start [--consul|--etcd3]|--stop|--restart [--consul|--etcd3]]"
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
# Main method
|
||||||
|
# $@ All parameters given
|
||||||
|
main() {
|
||||||
|
|
||||||
|
[[ $# -lt 1 && $# -gt 2 ]] && show_usage && exit 1
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
"--start")
|
||||||
|
[[ $# -ne 2 ]] && show_usage && exit 2
|
||||||
|
# The domains who01.localhost.com and who02.localhost.com have to refer 127.0.0.1
|
||||||
|
# I, the /etc/hosts file
|
||||||
|
for whoami_idx in "01" "02"; do
|
||||||
|
[[ -z $(cat /etc/hosts | grep "127.0.0.1" | grep -vE "^#" | grep "who${whoami_idx}.localhost.com") ]] && \
|
||||||
|
echo "[ERROR] Domain who${whoami_idx}.localhost.com has to refer to 127.0.0.1 into /etc/hosts file." && \
|
||||||
|
exit 3
|
||||||
|
done
|
||||||
|
case $2 in
|
||||||
|
"--etcd3")
|
||||||
|
echo "USE ETCD V3 AS KV STORE"
|
||||||
|
export TRAEFIK_CMD="--etcd --etcd.endpoint=10.0.1.12:2379 --etcd.useAPIV3=true"
|
||||||
|
start_boulder && \
|
||||||
|
start_etcd3 && \
|
||||||
|
start_storeconfig_etcd3 && \
|
||||||
|
start_traefik
|
||||||
|
;;
|
||||||
|
"--consul")
|
||||||
|
echo "USE CONSUL AS KV STORE"
|
||||||
|
export TRAEFIK_CMD="--consul --consul.endpoint=10.0.1.2:8500"
|
||||||
|
start_boulder && \
|
||||||
|
start_consul && \
|
||||||
|
start_storeconfig_consul && \
|
||||||
|
start_traefik
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
show_usage && exit 4
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
echo "ENVIRONMENT SUCCESSFULLY STARTED"
|
||||||
|
;;
|
||||||
|
"--stop")
|
||||||
|
! down_environment
|
||||||
|
echo "ENVIRONMENT SUCCESSFULLY STOPPED"
|
||||||
|
;;
|
||||||
|
"--restart")
|
||||||
|
[[ $# -ne 2 ]] && show_usage && exit 5
|
||||||
|
down_environment
|
||||||
|
main --start $2
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
show_usage && exit 6
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
main $@
|
||||||
27
examples/cluster/traefik.toml.tmpl
Normal file
27
examples/cluster/traefik.toml.tmpl
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
logLevel = "DEBUG"
|
||||||
|
|
||||||
|
defaultEntryPoints = ["http", "https"]
|
||||||
|
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.http]
|
||||||
|
address = ":80"
|
||||||
|
[entryPoints.https]
|
||||||
|
address = ":443"
|
||||||
|
[entryPoints.https.tls]
|
||||||
|
|
||||||
|
[acme]
|
||||||
|
email = "test@traefik.io"
|
||||||
|
storage = "traefik/acme/account"
|
||||||
|
entryPoint = "https"
|
||||||
|
OnHostRule = true
|
||||||
|
caServer = "http://traefik.boulder.com:4000/directory"
|
||||||
|
|
||||||
|
|
||||||
|
[web]
|
||||||
|
address = ":8080"
|
||||||
|
|
||||||
|
[docker]
|
||||||
|
endpoint = "unix:///var/run/docker.sock"
|
||||||
|
domain = "localhost.com"
|
||||||
|
watch = true
|
||||||
|
exposedbydefault = false
|
||||||
@@ -56,4 +56,4 @@ services:
|
|||||||
- "mesos-slave:172.17.0.1"
|
- "mesos-slave:172.17.0.1"
|
||||||
environment:
|
environment:
|
||||||
- MARATHON_ZK=zk://zookeeper:2181/marathon
|
- MARATHON_ZK=zk://zookeeper:2181/marathon
|
||||||
- MARATHON_MASTER=zk://zookeeper:2181/mesos
|
- MARATHON_MASTER=zk://zookeeper:2181/mesos
|
||||||
|
|||||||
@@ -23,3 +23,14 @@ curl -i -H "Accept: application/json" -X PUT -d "Host:test.localhost" ht
|
|||||||
curl -i -H "Accept: application/json" -X PUT -d "backend1" http://localhost:8500/v1/kv/traefik/frontends/frontend2/backend
|
curl -i -H "Accept: application/json" -X PUT -d "backend1" http://localhost:8500/v1/kv/traefik/frontends/frontend2/backend
|
||||||
curl -i -H "Accept: application/json" -X PUT -d "http" http://localhost:8500/v1/kv/traefik/frontends/frontend2/entrypoints
|
curl -i -H "Accept: application/json" -X PUT -d "http" http://localhost:8500/v1/kv/traefik/frontends/frontend2/entrypoints
|
||||||
curl -i -H "Accept: application/json" -X PUT -d "Path:/test" http://localhost:8500/v1/kv/traefik/frontends/frontend2/routes/test_2/rule
|
curl -i -H "Accept: application/json" -X PUT -d "Path:/test" http://localhost:8500/v1/kv/traefik/frontends/frontend2/routes/test_2/rule
|
||||||
|
|
||||||
|
|
||||||
|
# certificate 1
|
||||||
|
curl -i -H "Accept: application/json" -X PUT -d "https" http://localhost:8500/v1/kv/traefik/tlsconfiguration/pair1/entrypoints
|
||||||
|
curl -i -H "Accept: application/json" -X PUT -d "/tmp/test1.crt" http://localhost:8500/v1/kv/traefik/tlsconfiguration/pair1/certificate/certfile
|
||||||
|
curl -i -H "Accept: application/json" -X PUT -d "/tmp/test1.key" http://localhost:8500/v1/kv/traefik/tlsconfiguration/pair1/certificate/keyfile
|
||||||
|
|
||||||
|
# certificate 2
|
||||||
|
curl -i -H "Accept: application/json" -X PUT -d "http,https" http://localhost:8500/v1/kv/traefik/tlsconfiguration/pair2/entrypoints
|
||||||
|
curl -i -H "Accept: application/json" -X PUT -d "/tmp/test2.crt" http://localhost:8500/v1/kv/traefik/tlsconfiguration/pair2/certificate/certfile
|
||||||
|
curl -i -H "Accept: application/json" -X PUT -d "/tmp/test2.key" http://localhost:8500/v1/kv/traefik/tlsconfiguration/pair2/certificate/keyfile
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user