Compare commits
551 Commits
v1.6.3
...
v2.0.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6069df6cbd | ||
|
|
5e7b6e4860 | ||
|
|
ea6fa6e889 | ||
|
|
3e914256ce | ||
|
|
85ce16b34f | ||
|
|
d306c8fd50 | ||
|
|
8d7eccad5d | ||
|
|
d18edd6f77 | ||
|
|
cad3704efd | ||
|
|
9a4b455c3f | ||
|
|
01c8798e4e | ||
|
|
61744fba11 | ||
|
|
0034bef6b9 | ||
|
|
63c3ed3931 | ||
|
|
8a5db8a3ee | ||
|
|
adc2b62c22 | ||
|
|
1f2fe08c33 | ||
|
|
77b1933833 | ||
|
|
c4df78b4b9 | ||
|
|
c1dc783512 | ||
|
|
518a37e776 | ||
|
|
b143101f82 | ||
|
|
2be6f4d153 | ||
|
|
ac612734c8 | ||
|
|
ffe69c67fc | ||
|
|
b3057a0ec3 | ||
|
|
563f059e73 | ||
|
|
6bbe7262ef | ||
|
|
55a1a81010 | ||
|
|
97ec764db7 | ||
|
|
f6df556eb0 | ||
|
|
5cd9396dae | ||
|
|
886a6bdbe0 | ||
|
|
ab60e702d2 | ||
|
|
17141b3589 | ||
|
|
8f23243cb8 | ||
|
|
c2345c6e9a | ||
|
|
2617de2cdd | ||
|
|
9cf6827ccc | ||
|
|
681892148e | ||
|
|
558452a143 | ||
|
|
5a173fa968 | ||
|
|
72397ef90c | ||
|
|
79ad4b4544 | ||
|
|
49f3713c4f | ||
|
|
4b5c3ccf58 | ||
|
|
21dec70971 | ||
|
|
0f2b774ea1 | ||
|
|
e929caf15a | ||
|
|
8d848c3d60 | ||
|
|
b8b0c8f3e5 | ||
|
|
15e78da7eb | ||
|
|
d80700810f | ||
|
|
c1de6abf23 | ||
|
|
01b916eaa0 | ||
|
|
62c03b3318 | ||
|
|
65679af61d | ||
|
|
821ad31cf6 | ||
|
|
ea750ad813 | ||
|
|
3d7633f4a6 | ||
|
|
d356ef1c5b | ||
|
|
fce762febf | ||
|
|
535280c162 | ||
|
|
bb8a193244 | ||
|
|
e6bdfa1d29 | ||
|
|
d1d2611665 | ||
|
|
8389b46b5c | ||
|
|
b9f826554c | ||
|
|
0750235712 | ||
|
|
ee0e014617 | ||
|
|
2e20394af4 | ||
|
|
6ab991ebf4 | ||
|
|
ef8894ef26 | ||
|
|
8b4efa1760 | ||
|
|
b0b8b75258 | ||
|
|
2e19e45aa4 | ||
|
|
e1d097ea20 | ||
|
|
ed12366d52 | ||
|
|
4919b638f9 | ||
|
|
49563e638b | ||
|
|
07d0eb9ae6 | ||
|
|
336135c392 | ||
|
|
d2b38e6ac4 | ||
|
|
883f90dded | ||
|
|
58e82743f8 | ||
|
|
51a0994d2d | ||
|
|
da20db862d | ||
|
|
d6c9f51082 | ||
|
|
08d7bb0d08 | ||
|
|
1bcb3d8cc2 | ||
|
|
c17de070fb | ||
|
|
b893374dc1 | ||
|
|
fe532ed4f2 | ||
|
|
6377a19b12 | ||
|
|
ca7ea68a6a | ||
|
|
a45f285a5c | ||
|
|
fa2c57f7cb | ||
|
|
0779c6a139 | ||
|
|
2916f540c1 | ||
|
|
7932e317c8 | ||
|
|
fd26cf265d | ||
|
|
3e76c25887 | ||
|
|
a0e2f47679 | ||
|
|
d70add10ab | ||
|
|
119d0134e0 | ||
|
|
2e085fa253 | ||
|
|
f8f7edd124 | ||
|
|
79ecff7b42 | ||
|
|
0f2c4fb5f4 | ||
|
|
ec1952157b | ||
|
|
cd38359458 | ||
|
|
8a86777db8 | ||
|
|
e7033071b9 | ||
|
|
f99a473436 | ||
|
|
c4b7e8f288 | ||
|
|
f346251719 | ||
|
|
4c3cf87f62 | ||
|
|
cb417b8077 | ||
|
|
076d6abfe4 | ||
|
|
82308c9a53 | ||
|
|
5d35079809 | ||
|
|
50e24f461c | ||
|
|
37886892c8 | ||
|
|
72ffa91fe0 | ||
|
|
9908137638 | ||
|
|
f3ecc040c8 | ||
|
|
e271378a97 | ||
|
|
5d050ae3ac | ||
|
|
615ceab597 | ||
|
|
f1b085fa36 | ||
|
|
bd4c822670 | ||
|
|
03d5a95bde | ||
|
|
e2ec64947a | ||
|
|
dabd9e2208 | ||
|
|
4c060a78cc | ||
|
|
cfaf47c8a2 | ||
|
|
87da7520de | ||
|
|
4a68d29ce2 | ||
|
|
0ca2149408 | ||
|
|
0cfaab02c0 | ||
|
|
2d54065082 | ||
|
|
3cfbe7cf6d | ||
|
|
e2d8a95c91 | ||
|
|
3419f9aeb9 | ||
|
|
ebded2cbc0 | ||
|
|
fb617044e0 | ||
|
|
5a0b5470e7 | ||
|
|
6b4144ad10 | ||
|
|
8f16ff9c49 | ||
|
|
ac6b11037d | ||
|
|
848e45c22c | ||
|
|
2c0bf335ba | ||
|
|
aef24dd74b | ||
|
|
c2c6aee18a | ||
|
|
6451b47621 | ||
|
|
2b2cfdfb32 | ||
|
|
5f4d440493 | ||
|
|
5f0451affe | ||
|
|
156f6b8d3c | ||
|
|
f0ee2890b2 | ||
|
|
16c283c91a | ||
|
|
db13dbdf46 | ||
|
|
06905cb14a | ||
|
|
6ea9c4dd3f | ||
|
|
c5c8382742 | ||
|
|
115ddc6a4a | ||
|
|
54ca0ce34f | ||
|
|
f19c497621 | ||
|
|
0561a20c06 | ||
|
|
162490dadf | ||
|
|
30087794ba | ||
|
|
9ebe3c38b2 | ||
|
|
7155f0d50d | ||
|
|
75e05ca142 | ||
|
|
5d4423910d | ||
|
|
0de1ff8634 | ||
|
|
e5fb1ffeb7 | ||
|
|
8c53318dac | ||
|
|
0d6f259adc | ||
|
|
85ab0e6e70 | ||
|
|
a18294d417 | ||
|
|
fecd0ca391 | ||
|
|
97bd92c76f | ||
|
|
49b89c30d8 | ||
|
|
8228a8e3f7 | ||
|
|
78be3df99a | ||
|
|
2f0db9a974 | ||
|
|
227fab3867 | ||
|
|
9537449b07 | ||
|
|
246b245959 | ||
|
|
a433e469cc | ||
|
|
04958c6951 | ||
|
|
b54c956c5e | ||
|
|
8735263930 | ||
|
|
a79d6aa669 | ||
|
|
7efafa5a2c | ||
|
|
0b436563bd | ||
|
|
5d379dc3e3 | ||
|
|
8c60774c6a | ||
|
|
9b2423aaba | ||
|
|
fc8c24e987 | ||
|
|
d7bd69714d | ||
|
|
099bbb8be7 | ||
|
|
c29a69a60d | ||
|
|
69e4f35d9a | ||
|
|
ff40467207 | ||
|
|
190c6c661f | ||
|
|
e633799c14 | ||
|
|
f7c6c562a5 | ||
|
|
bc6e9d5042 | ||
|
|
a0b1d54012 | ||
|
|
60b5286f8c | ||
|
|
aa3ea17a8f | ||
|
|
698621f127 | ||
|
|
906f4fe8f7 | ||
|
|
ddf199566c | ||
|
|
a47d770e71 | ||
|
|
057498ed01 | ||
|
|
fa562dc916 | ||
|
|
0be895febb | ||
|
|
11a0078966 | ||
|
|
92f8e5cd3f | ||
|
|
5b3762be08 | ||
|
|
3b01488c8d | ||
|
|
2f65572247 | ||
|
|
e42ddfc3d6 | ||
|
|
d63636243c | ||
|
|
a0b9c0d007 | ||
|
|
1f7a4174ba | ||
|
|
761c58e040 | ||
|
|
01c3d3905c | ||
|
|
c815a732ef | ||
|
|
5d91c7e15c | ||
|
|
c39d21c178 | ||
|
|
b6498cdcbc | ||
|
|
a09dfa3ce1 | ||
|
|
d3ae88f108 | ||
|
|
1fad7e5a1c | ||
|
|
19546ab518 | ||
|
|
e6e9a86919 | ||
|
|
c6dd1dccc3 | ||
|
|
993caf5058 | ||
|
|
450471d30a | ||
|
|
7eeecd23ac | ||
|
|
21c94141ba | ||
|
|
bc2cba5aa4 | ||
|
|
5e49354bf2 | ||
|
|
55334b2062 | ||
|
|
74dc5b1c58 | ||
|
|
ac11323fdd | ||
|
|
8c2e99432d | ||
|
|
aa26927d61 | ||
|
|
22ee8700ca | ||
|
|
df55c24cb5 | ||
|
|
99ddd7f9cb | ||
|
|
82b2a102ed | ||
|
|
c7df82e695 | ||
|
|
638960284e | ||
|
|
8e9b8a0953 | ||
|
|
3f044c48fa | ||
|
|
37d8e32e0b | ||
|
|
46ce807624 | ||
|
|
e6a88f3531 | ||
|
|
95d86d84b4 | ||
|
|
70fa42aee0 | ||
|
|
ba99fbe390 | ||
|
|
6a55772cda | ||
|
|
6dcb51a4bd | ||
|
|
c875819a2e | ||
|
|
6d4cf0d892 | ||
|
|
78a9d20691 | ||
|
|
7c2409b5a7 | ||
|
|
0335f6fba9 | ||
|
|
2c7b7cd6ca | ||
|
|
5632952665 | ||
|
|
7eeac63139 | ||
|
|
1b54f4d32a | ||
|
|
e8e9dd9400 | ||
|
|
b722748ec3 | ||
|
|
609b2630d7 | ||
|
|
5bdf8a5ea3 | ||
|
|
7a2592b2fa | ||
|
|
546bebc860 | ||
|
|
ad51f4f2a5 | ||
|
|
94a6f8426b | ||
|
|
32f7fb8bff | ||
|
|
a777c3553c | ||
|
|
51650c1412 | ||
|
|
157580c232 | ||
|
|
05f052b092 | ||
|
|
1431ac5751 | ||
|
|
a9deeb321b | ||
|
|
ec86149b1e | ||
|
|
31f92001e2 | ||
|
|
d69977c229 | ||
|
|
44e06a1a1e | ||
|
|
f9689d1562 | ||
|
|
4cb1ae4626 | ||
|
|
f04813fa02 | ||
|
|
742029d8a4 | ||
|
|
f74526a36e | ||
|
|
61e1836472 | ||
|
|
8d8e509fe6 | ||
|
|
147e79ea07 | ||
|
|
5eae95ee46 | ||
|
|
9e26f0b058 | ||
|
|
8cc3c4a6b7 | ||
|
|
1d8bdd4384 | ||
|
|
5acd43efaf | ||
|
|
7033b996c6 | ||
|
|
0c76a8ac89 | ||
|
|
f10516deb7 | ||
|
|
d4311f9cf5 | ||
|
|
6a50a6fd5a | ||
|
|
29473ef356 | ||
|
|
1f1ecb15f6 | ||
|
|
38d655636d | ||
|
|
9ab5cbf235 | ||
|
|
fdf14cd101 | ||
|
|
f63873cc73 | ||
|
|
c2938ff138 | ||
|
|
ab2c98d931 | ||
|
|
0ae8cd9a9d | ||
|
|
f3aefe282c | ||
|
|
a80cca95a2 | ||
|
|
c52f4b043d | ||
|
|
253060b4f3 | ||
|
|
36966da701 | ||
|
|
bb7c4aaf7e | ||
|
|
bd4846aa9c | ||
|
|
c68ebaa2ca | ||
|
|
538424b01c | ||
|
|
48e7a87741 | ||
|
|
74ace58ae1 | ||
|
|
913d8737cc | ||
|
|
b98f5ed8b1 | ||
|
|
e4bb506ace | ||
|
|
0f0ba099c9 | ||
|
|
f400292be7 | ||
|
|
efc6560d83 | ||
|
|
4055654e9b | ||
|
|
56488d435f | ||
|
|
f586950528 | ||
|
|
a302731cd1 | ||
|
|
00728e711c | ||
|
|
ef753838e7 | ||
|
|
acb79d6f73 | ||
|
|
157c796294 | ||
|
|
0861c59bec | ||
|
|
e4a7375d34 | ||
|
|
6bbac65f7e | ||
|
|
845f1a7377 | ||
|
|
9c8e518423 | ||
|
|
bd3b787fd5 | ||
|
|
27e4a8a227 | ||
|
|
cf2d7497e4 | ||
|
|
df41cd925e | ||
|
|
e46de74328 | ||
|
|
feeb7f81a6 | ||
|
|
2beb5236d0 | ||
|
|
f062ee80c8 | ||
|
|
a7bb768e98 | ||
|
|
07be89d6e9 | ||
|
|
d81c4e6d1a | ||
|
|
870755e90d | ||
|
|
bd3c8c3cde | ||
|
|
278b3180c3 | ||
|
|
bb2686a08f | ||
|
|
202783ca7d | ||
|
|
308904110a | ||
|
|
60b4095c75 | ||
|
|
d04b4fa2cc | ||
|
|
2d449f63e0 | ||
|
|
1ec4e03738 | ||
|
|
9cd47dd2aa | ||
|
|
015cd7a3d0 | ||
|
|
7ff6e6b66f | ||
|
|
e92b01c528 | ||
|
|
bb33128552 | ||
|
|
86add29838 | ||
|
|
70712a0f62 | ||
|
|
4db937b571 | ||
|
|
ad6f41c77a | ||
|
|
e6040e55f5 | ||
|
|
dad0e75121 | ||
|
|
c159e316be | ||
|
|
b4ac3d4470 | ||
|
|
43d22d7a2f | ||
|
|
d62f7e2082 | ||
|
|
cfe2f1a1e6 | ||
|
|
6f6ebb8025 | ||
|
|
7732e2307e | ||
|
|
8c733abef3 | ||
|
|
4809476c19 | ||
|
|
d727761e5d | ||
|
|
4d79c2a6d2 | ||
|
|
8627256e74 | ||
|
|
ed0c7d9c49 | ||
|
|
fb4717d5f3 | ||
|
|
09b489a614 | ||
|
|
402f7011d4 | ||
|
|
838dd8c19f | ||
|
|
91cafd1752 | ||
|
|
eea60b6baa | ||
|
|
baf8d63cb4 | ||
|
|
967e4208da | ||
|
|
ba3a579d07 | ||
|
|
1d53077fc7 | ||
|
|
4b480ece13 | ||
|
|
7d2b7cd7f1 | ||
|
|
73b4df4e18 | ||
|
|
a23a9228da | ||
|
|
37aa902cef | ||
|
|
bafb583666 | ||
|
|
aabebb2185 | ||
|
|
f611ef0edd | ||
|
|
d8f69700e6 | ||
|
|
c8ae97fd38 | ||
|
|
d50b6a34bc | ||
|
|
853be929bc | ||
|
|
3bb04142f3 | ||
|
|
d53fbb9d7f | ||
|
|
a1911a9608 | ||
|
|
ff2e2d5026 | ||
|
|
a953d3ad89 | ||
|
|
9ce444b91a | ||
|
|
ae8be89767 | ||
|
|
5774d100c1 | ||
|
|
dbe720f0f1 | ||
|
|
5afc8f2b12 | ||
|
|
c7e008f57a | ||
|
|
14b7152bf0 | ||
|
|
3ef6bf2118 | ||
|
|
f0ab2721a5 | ||
|
|
2721c2017c | ||
|
|
a7c158f0e1 | ||
|
|
7ff9193cf5 | ||
|
|
5ce4a2d05c | ||
|
|
031451abab | ||
|
|
8d75aba7eb | ||
|
|
027093a5a5 | ||
|
|
bdc0e3bfcf | ||
|
|
b2a57ca1f3 | ||
|
|
6ef0e6791b | ||
|
|
9374d6b3b9 | ||
|
|
f173ff02e3 | ||
|
|
ba2046491a | ||
|
|
083b471bcf | ||
|
|
bf73127e0b | ||
|
|
333b785061 | ||
|
|
79bf19c897 | ||
|
|
0c0ecc1cdc | ||
|
|
bacd58ed7b | ||
|
|
689f120410 | ||
|
|
2303301d38 | ||
|
|
f323df466d | ||
|
|
b1f1a5b757 | ||
|
|
0d262561d1 | ||
|
|
12c713b187 | ||
|
|
b1836587f2 | ||
|
|
04d8b5d483 | ||
|
|
461ebf6d88 | ||
|
|
41eb4f1c70 | ||
|
|
31a8e3e39a | ||
|
|
139f280f35 | ||
|
|
17ad5153b8 | ||
|
|
bb14ec70bd | ||
|
|
e8e36bd9d5 | ||
|
|
f9b1106df2 | ||
|
|
df600d6f3c | ||
|
|
157e76e829 | ||
|
|
dbc3b85cd0 | ||
|
|
11691019a0 | ||
|
|
3192307d59 | ||
|
|
ba8c9295ac | ||
|
|
d5436fb28b | ||
|
|
886cc83ad9 | ||
|
|
9e012a6b54 | ||
|
|
5eda08e9b8 | ||
|
|
ec6e46e2cb | ||
|
|
56fe023a12 | ||
|
|
aa705dd691 | ||
|
|
aa6fea7f21 | ||
|
|
e31c85aace | ||
|
|
1c3e4124f8 | ||
|
|
586ba31120 | ||
|
|
c1757372d3 | ||
|
|
7451449dd6 | ||
|
|
5b2b29043c | ||
|
|
2758664226 | ||
|
|
bb3f28ffa7 | ||
|
|
6ceb2af4a7 | ||
|
|
d5b649bf1c | ||
|
|
81f23cc732 | ||
|
|
b59276ff1c | ||
|
|
2e95832812 | ||
|
|
01f2b3cd20 | ||
|
|
2240bf9430 | ||
|
|
db036edccd | ||
|
|
1fbf5b84a2 | ||
|
|
08e1f626c1 | ||
|
|
c0d08f5e3e | ||
|
|
eac20d61df | ||
|
|
dec3f0798a | ||
|
|
bddb4cc33c | ||
|
|
62ded580ce | ||
|
|
51227241b7 | ||
|
|
9cf4e730e7 | ||
|
|
e9c63f3988 | ||
|
|
2c47691cf1 | ||
|
|
599b699ac9 | ||
|
|
a5beeb4f04 | ||
|
|
67a0b4b4b1 | ||
|
|
a7200a292b | ||
|
|
e76836b948 | ||
|
|
bfdd1997f6 | ||
|
|
9420308667 | ||
|
|
83e09acc9f | ||
|
|
d6d795e286 | ||
|
|
c09febfffc | ||
|
|
5b3bba8f6e | ||
|
|
085593b9e5 | ||
|
|
e2a5d4f83e | ||
|
|
3c8c5ebb96 | ||
|
|
e2bac47a0a | ||
|
|
5c4692a0df | ||
|
|
9227d32d57 | ||
|
|
c37b040217 | ||
|
|
5a1d2aa4b6 | ||
|
|
4a3b1f3847 | ||
|
|
68cc826519 | ||
|
|
e691168cdc | ||
|
|
4eda1e1bd4 | ||
|
|
df11e67bb4 | ||
|
|
b7d20496f3 | ||
|
|
dce65ab9c2 | ||
|
|
97295f270b | ||
|
|
8e64bc8785 | ||
|
|
a1bbaec71f | ||
|
|
3b3ca89483 | ||
|
|
b4e3bca6fa | ||
|
|
9daae9c705 | ||
|
|
05968eb232 | ||
|
|
36dcfbfe2d | ||
|
|
e6ce61fdf0 | ||
|
|
21b8b2deb5 | ||
|
|
6b82a77e36 | ||
|
|
1954a49f37 | ||
|
|
de3aeb9732 | ||
|
|
03ce6a1cc4 | ||
|
|
762ef12eb6 |
2
.gitattributes
vendored
@@ -1 +1 @@
|
||||
# vendor/github.com/xenolf/lego/providers/dns/cloudxns/cloudxns.go eol=crlf
|
||||
# vendor/github.com/go-acme/lego/providers/dns/cloudxns/cloudxns.go eol=crlf
|
||||
|
||||
22
.github/ISSUE_TEMPLATE.md
vendored
@@ -2,10 +2,10 @@
|
||||
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
|
||||
|
||||
The issue tracker is for reporting bugs and feature requests only.
|
||||
For end-user related support questions, refer to one of the following:
|
||||
For end-user related support questions, please refer to one of the following:
|
||||
|
||||
- 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://slack.traefik.io
|
||||
|
||||
-->
|
||||
|
||||
@@ -16,6 +16,17 @@ For end-user related support questions, refer to one of the following:
|
||||
If you intend to ask a support question: DO NOT FILE AN ISSUE.
|
||||
-->
|
||||
|
||||
### Did you try using a 1.7.x configuration for the version 2.0?
|
||||
|
||||
- [ ] Yes
|
||||
- [ ] No
|
||||
|
||||
<!--
|
||||
|
||||
If you just checked the "Yes" box, be aware that this is probably not a bug. The configurations between 1.X and 2.X are NOT compatible. Please have a look here https://docs.traefik.io/v2.0/getting-started/configuration-overview/.
|
||||
|
||||
-->
|
||||
|
||||
### What did you do?
|
||||
|
||||
<!--
|
||||
@@ -23,9 +34,8 @@ If you intend to ask a support question: DO NOT FILE AN ISSUE.
|
||||
HOW TO WRITE A GOOD ISSUE?
|
||||
|
||||
- Respect the issue template as much 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.
|
||||
- Explain the conditions which led you to write this issue: the context.
|
||||
- The title should be short and descriptive.
|
||||
- Explain the conditions which led you to report 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
|
||||
@@ -66,7 +76,7 @@ Add more configuration information here.
|
||||
-->
|
||||
|
||||
|
||||
### If applicable, please paste the log output at DEBUG level (`--logLevel=DEBUG` switch)
|
||||
### If applicable, please paste the log output at DEBUG level (`--log.level=DEBUG` switch)
|
||||
|
||||
```
|
||||
(paste your output here)
|
||||
|
||||
88
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
|
||||
|
||||
The issue tracker is for reporting bugs and feature requests only.
|
||||
For end-user related support questions, please refer to one of the following:
|
||||
|
||||
- Stack Overflow (using the "traefik" tag): https://stackoverflow.com/questions/tagged/traefik
|
||||
- the Traefik community Slack channel: https://slack.traefik.io
|
||||
|
||||
-->
|
||||
|
||||
|
||||
### Do you want to request a *feature* or report a *bug*?
|
||||
|
||||
Bug
|
||||
|
||||
### Did you try using a 1.7.x configuration for the version 2.0?
|
||||
|
||||
- [ ] Yes
|
||||
- [ ] No
|
||||
|
||||
<!--
|
||||
|
||||
If you just checked the "Yes" box, be aware that this is probably not a bug. The configurations between 1.X and 2.X are NOT compatible. Please have a look here https://docs.traefik.io/v2.0/getting-started/configuration-overview/.
|
||||
|
||||
-->
|
||||
|
||||
### What did you do?
|
||||
|
||||
<!--
|
||||
|
||||
HOW TO WRITE A GOOD BUG REPORT?
|
||||
|
||||
- Respect the issue template as much as possible.
|
||||
- The title should be short and descriptive.
|
||||
- Explain the conditions which led you to report 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
|
||||
|
||||
-->
|
||||
|
||||
### What did you expect to see?
|
||||
|
||||
|
||||
|
||||
### What did you see instead?
|
||||
|
||||
|
||||
|
||||
### 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
|
||||
|
||||
For the alpine Traefik Docker image:
|
||||
docker run [IMAGE] traefik version
|
||||
ex: docker run traefik traefik version
|
||||
-->
|
||||
|
||||
```
|
||||
(paste your output here)
|
||||
```
|
||||
|
||||
### What is your environment & configuration (arguments, toml, provider, platform, ...)?
|
||||
|
||||
```toml
|
||||
# (paste your configuration here)
|
||||
```
|
||||
|
||||
<!--
|
||||
Add more configuration information here.
|
||||
-->
|
||||
|
||||
|
||||
### If applicable, please paste the log output in DEBUG level (`--log.level=DEBUG` switch)
|
||||
|
||||
```
|
||||
(paste your output here)
|
||||
```
|
||||
36
.github/ISSUE_TEMPLATE/Feature_request.md
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
|
||||
|
||||
The issue tracker is for reporting bugs and feature requests only.
|
||||
For end-user related support questions, please refer to one of the following:
|
||||
|
||||
- Stack Overflow (using the "traefik" tag): https://stackoverflow.com/questions/tagged/traefik
|
||||
- the Traefik community Slack channel: https://slack.traefik.io
|
||||
|
||||
-->
|
||||
|
||||
|
||||
### Do you want to request a *feature* or report a *bug*?
|
||||
|
||||
Feature
|
||||
|
||||
### What did you expect to see?
|
||||
|
||||
<!--
|
||||
|
||||
HOW TO WRITE A GOOD ISSUE?
|
||||
|
||||
- Respect the issue template as much as possible.
|
||||
- The title should be short and descriptive.
|
||||
- Explain the conditions which led you to report 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
|
||||
|
||||
-->
|
||||
72
.github/ISSUE_TEMPLATE/bugs.md
vendored
@@ -1,72 +0,0 @@
|
||||
<!--
|
||||
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
|
||||
|
||||
The issue tracker is for reporting bugs and feature requests only.
|
||||
For end-user related support questions, refer to one of the following:
|
||||
|
||||
- Stack Overflow (using the "traefik" tag): https://stackoverflow.com/questions/tagged/traefik
|
||||
- the Traefik community Slack channel: https://traefik.herokuapp.com
|
||||
|
||||
-->
|
||||
|
||||
|
||||
### Do you want to request a *feature* or report a *bug*?
|
||||
|
||||
Bug
|
||||
|
||||
### What did you do?
|
||||
|
||||
<!--
|
||||
|
||||
HOW TO WRITE A GOOD ISSUE?
|
||||
|
||||
- Respect the issue template as much 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.
|
||||
- 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
|
||||
|
||||
-->
|
||||
|
||||
### What did you expect to see?
|
||||
|
||||
|
||||
|
||||
### What did you see instead?
|
||||
|
||||
|
||||
|
||||
### 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
|
||||
|
||||
For the alpine Traefik Docker image:
|
||||
docker run [IMAGE] traefik version
|
||||
ex: docker run traefik traefik version
|
||||
-->
|
||||
|
||||
```
|
||||
(paste your output here)
|
||||
```
|
||||
|
||||
### What is your environment & configuration (arguments, toml, provider, platform, ...)?
|
||||
|
||||
```toml
|
||||
# (paste your configuration here)
|
||||
```
|
||||
|
||||
<!--
|
||||
Add more configuration information here.
|
||||
-->
|
||||
|
||||
|
||||
### If applicable, please paste the log output in DEBUG level (`--logLevel=DEBUG` switch)
|
||||
|
||||
```
|
||||
(paste your output here)
|
||||
```
|
||||
32
.github/ISSUE_TEMPLATE/features.md
vendored
@@ -1,32 +0,0 @@
|
||||
<!--
|
||||
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
|
||||
|
||||
The issue tracker is for reporting bugs and feature requests only.
|
||||
For end-user related support questions, refer to one of the following:
|
||||
|
||||
- Stack Overflow (using the "traefik" tag): https://stackoverflow.com/questions/tagged/traefik
|
||||
- the Traefik community Slack channel: https://traefik.herokuapp.com
|
||||
|
||||
-->
|
||||
|
||||
|
||||
### Do you want to request a *feature* or report a *bug*?
|
||||
|
||||
Feature
|
||||
|
||||
### What did you expect to see?
|
||||
|
||||
<!--
|
||||
|
||||
HOW TO WRITE A GOOD ISSUE?
|
||||
|
||||
- Respect the issue template as much 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.
|
||||
- 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
|
||||
|
||||
-->
|
||||
|
||||
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -12,7 +12,7 @@ HOW TO WRITE A GOOD PULL REQUEST?
|
||||
- Write useful descriptions and titles.
|
||||
- Address review comments in terms of additional commits.
|
||||
- Do not amend/squash existing ones unless the PR is trivial.
|
||||
- Read the contributing guide: https://github.com/containous/traefik/blob/master/.github/CONTRIBUTING.md.
|
||||
- Read the contributing guide: https://github.com/containous/traefik/blob/master/CONTRIBUTING.md.
|
||||
|
||||
-->
|
||||
|
||||
|
||||
18
.gitignore
vendored
@@ -1,15 +1,17 @@
|
||||
/dist
|
||||
/autogen/genstatic/gen.go
|
||||
.idea/
|
||||
.intellij/
|
||||
*.iml
|
||||
.vscode/
|
||||
.DS_Store
|
||||
/dist
|
||||
/webui/.tmp/
|
||||
/site/
|
||||
/docs/site/
|
||||
/static/
|
||||
/autogen/
|
||||
/traefik
|
||||
/traefik.toml
|
||||
/static/
|
||||
/webui/.tmp/
|
||||
.vscode/
|
||||
/site/
|
||||
/traefik.yml
|
||||
*.log
|
||||
*.exe
|
||||
.DS_Store
|
||||
/examples/acme/acme.json
|
||||
cover.out
|
||||
|
||||
87
.golangci.toml
Normal file
@@ -0,0 +1,87 @@
|
||||
[run]
|
||||
deadline = "10m"
|
||||
skip-files = []
|
||||
|
||||
[linters-settings]
|
||||
|
||||
[linters-settings.govet]
|
||||
check-shadowing = false
|
||||
|
||||
[linters-settings.golint]
|
||||
min-confidence = 0.0
|
||||
|
||||
[linters-settings.gocyclo]
|
||||
min-complexity = 14.0
|
||||
|
||||
[linters-settings.maligned]
|
||||
suggest-new = true
|
||||
|
||||
[linters-settings.goconst]
|
||||
min-len = 3.0
|
||||
min-occurrences = 4.0
|
||||
|
||||
[linters-settings.misspell]
|
||||
locale = "US"
|
||||
|
||||
[linters]
|
||||
enable-all = true
|
||||
disable = [
|
||||
"gocyclo", # FIXME must be fixed
|
||||
"gosec",
|
||||
"dupl",
|
||||
"maligned",
|
||||
"lll",
|
||||
"unparam",
|
||||
"prealloc",
|
||||
"scopelint",
|
||||
"gochecknoinits",
|
||||
"gochecknoglobals",
|
||||
# uncomment when the CI will be updated
|
||||
# "bodyclose", # Too many false-positive and panics.
|
||||
]
|
||||
|
||||
[issues]
|
||||
exclude-use-default = false
|
||||
max-per-linter = 0
|
||||
max-same-issues = 0
|
||||
exclude = [
|
||||
"SA1019: http.CloseNotifier is deprecated: the CloseNotifier interface predates Go's context package. New code should use Request.Context instead.", # FIXME must be fixed
|
||||
"Error return value of .((os\\.)?std(out|err)\\..*|.*Close|.*Flush|os\\.Remove(All)?|.*printf?|os\\.(Un)?Setenv). is not checked",
|
||||
"should have a package comment, unless it's in another file for this package",
|
||||
]
|
||||
[[issues.exclude-rules]]
|
||||
path = ".+_test.go"
|
||||
linters = ["goconst"]
|
||||
[[issues.exclude-rules]]
|
||||
path = "integration/.+_test.go"
|
||||
text = "Error return value of `cmd\\.Process\\.Kill` is not checked"
|
||||
[[issues.exclude-rules]]
|
||||
path = "integration/(consul_catalog_test|constraint_test).go"
|
||||
text = "Error return value of `(s.deregisterService|s.deregisterAgentService)` is not checked"
|
||||
[[issues.exclude-rules]]
|
||||
path = "integration/grpc_test.go"
|
||||
text = "Error return value of `closer` is not checked"
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/h2c/h2c.go"
|
||||
text = "Error return value of `rw.Write` is not checked"
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/middlewares/recovery/recovery.go"
|
||||
text = "`logger` can be `github.com/containous/traefik/vendor/github.com/stretchr/testify/assert.TestingT`"
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/provider/docker/builder_test.go"
|
||||
text = "(U1000: func )?`(.+)` is unused"
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/provider/kubernetes/builder_(endpoint|service)_test.go"
|
||||
text = "(U1000: func )?`(.+)` is unused"
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/config/parser/.+_test.go"
|
||||
text = "U1000: field `(foo|fuu)` is unused"
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/server/service/bufferpool.go"
|
||||
text = "SA6002: argument should be pointer-like to avoid allocations"
|
||||
[[issues.exclude-rules]]
|
||||
path = "cmd/configuration.go"
|
||||
text = "string `traefik` has (\\d) occurrences, make it a constant"
|
||||
[[issues.exclude-rules]] # FIXME must be fixed
|
||||
path = "cmd/context.go"
|
||||
text = "S1000: should use a simple channel send/receive instead of `select` with a single case"
|
||||
@@ -1,42 +0,0 @@
|
||||
{
|
||||
"Vendor": true,
|
||||
"Sort": [
|
||||
"path",
|
||||
"line",
|
||||
"column",
|
||||
"severity",
|
||||
"linter"
|
||||
],
|
||||
"Test": true,
|
||||
"Cyclo": 15,
|
||||
"Enable": [
|
||||
"gotypex",
|
||||
"nakedret",
|
||||
"vet",
|
||||
"goimports",
|
||||
"golint",
|
||||
"ineffassign",
|
||||
"gotype",
|
||||
"misspell",
|
||||
"structcheck",
|
||||
"gosimple",
|
||||
"unconvert",
|
||||
"varcheck",
|
||||
"errcheck",
|
||||
"unused",
|
||||
"deadcode",
|
||||
"staticcheck"
|
||||
],
|
||||
"Disable": [
|
||||
"gas",
|
||||
"maligned",
|
||||
"interfacer",
|
||||
"goconst",
|
||||
"gocyclo",
|
||||
"vetshadow"
|
||||
],
|
||||
"Exclude": [
|
||||
"autogen/.*"
|
||||
],
|
||||
"Deadline": "5m"
|
||||
}
|
||||
58
.goreleaser.yml
Normal file
@@ -0,0 +1,58 @@
|
||||
project_name: traefik
|
||||
|
||||
before:
|
||||
hooks:
|
||||
- go generate
|
||||
|
||||
builds:
|
||||
- binary: traefik
|
||||
|
||||
main: ./cmd/traefik/traefik.go
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
ldflags:
|
||||
- -s -w -X github.com/containous/traefik/pkg/version.Version={{.Version}} -X github.com/containous/traefik/pkg/version.Codename={{.Env.CODENAME}} -X github.com/containous/traefik/pkg/version.BuildDate={{.Date}}
|
||||
|
||||
goos:
|
||||
- linux
|
||||
- darwin
|
||||
- windows
|
||||
- freebsd
|
||||
- openbsd
|
||||
goarch:
|
||||
- amd64
|
||||
- 386
|
||||
- arm
|
||||
- arm64
|
||||
- ppc64le
|
||||
goarm:
|
||||
- 7
|
||||
- 6
|
||||
- 5
|
||||
ignore:
|
||||
- goos: darwin
|
||||
goarch: 386
|
||||
- goos: openbsd
|
||||
goarch: arm
|
||||
- goos: freebsd
|
||||
goarch: arm
|
||||
|
||||
changelog:
|
||||
skip: true
|
||||
|
||||
archive:
|
||||
name_template: '{{ .ProjectName }}_v{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm
|
||||
}}v{{ .Arm }}{{ end }}'
|
||||
format: tar.gz
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
files:
|
||||
- LICENSE.md
|
||||
- CHANGELOG.md
|
||||
|
||||
checksum:
|
||||
name_template: "{{ .ProjectName }}_v{{ .Version }}_checksums.txt"
|
||||
|
||||
release:
|
||||
disable: true
|
||||
@@ -1,10 +0,0 @@
|
||||
- repo: git://github.com/pre-commit/pre-commit-hooks
|
||||
sha: 44e1753f98b0da305332abe26856c3e621c5c439
|
||||
hooks:
|
||||
- id: detect-private-key
|
||||
- repo: git://github.com/containous/pre-commit-hooks
|
||||
sha: 35e641b5107671e94102b0ce909648559e568d61
|
||||
hooks:
|
||||
- id: goFmt
|
||||
- id: goLint
|
||||
- id: goErrcheck
|
||||
4
.semaphoreci/cleanup.sh
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
sudo rm -rf static
|
||||
20
.semaphoreci/golang.sh
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
curl -O https://dl.google.com/go/go1.12.linux-amd64.tar.gz
|
||||
|
||||
tar -xvf go1.12.linux-amd64.tar.gz
|
||||
rm -rf go1.12.linux-amd64.tar.gz
|
||||
|
||||
sudo mkdir -p /usr/local/golang/1.12/go
|
||||
sudo mv go /usr/local/golang/1.12/
|
||||
|
||||
sudo rm /usr/local/bin/go
|
||||
sudo chmod +x /usr/local/golang/1.12/go/bin/go
|
||||
sudo ln -s /usr/local/golang/1.12/go/bin/go /usr/local/bin/go
|
||||
|
||||
export GOROOT="/usr/local/golang/1.12/go"
|
||||
export GOTOOLDIR="/usr/local/golang/1.12/go/pkg/tool/linux_amd64"
|
||||
|
||||
go version
|
||||
6
.semaphoreci/job1.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
if [ -n "$SHOULD_TEST" ]; then ci_retry make pull-images; fi
|
||||
|
||||
if [ -n "$SHOULD_TEST" ]; then ci_retry make test-integration; fi
|
||||
8
.semaphoreci/job2.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
ci_retry make validate
|
||||
|
||||
if [ -n "$SHOULD_TEST" ]; then ci_retry make test-unit; fi
|
||||
|
||||
if [ -n "$SHOULD_TEST" ]; then make -j"${N_MAKE_JOBS}" crossbinary-default-parallel; fi
|
||||
@@ -1,11 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
sudo -E apt-get -yq update
|
||||
sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install docker-ce=${DOCKER_VERSION}*
|
||||
docker version
|
||||
export DOCKER_VERSION=17.03.1
|
||||
|
||||
pip install --user -r requirements.txt
|
||||
# shellcheck source=/dev/null
|
||||
source .semaphoreci/vars
|
||||
|
||||
make pull-images
|
||||
ci_retry make validate
|
||||
if [ -z "${PULL_REQUEST_NUMBER}" ]; then SHOULD_TEST="-*-"; else TEMP_STORAGE=$(curl --silent https://patch-diff.githubusercontent.com/raw/containous/traefik/pull/"${PULL_REQUEST_NUMBER}".diff | patch --dry-run -p1 -R); fi
|
||||
|
||||
if [ -n "$TEMP_STORAGE" ]; then SHOULD_TEST=$(echo "$TEMP_STORAGE" | grep -Ev '(.md|.yaml|.yml)' || :); fi
|
||||
|
||||
if [ -n "$SHOULD_TEST" ]; then sudo -E apt-get -yq update; fi
|
||||
|
||||
if [ -n "$SHOULD_TEST" ]; then sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install docker-ce=${DOCKER_VERSION}*; fi
|
||||
|
||||
if [ -n "$SHOULD_TEST" ]; then docker version; fi
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
make test-unit
|
||||
ci_retry make test-integration
|
||||
make -j${N_MAKE_JOBS} crossbinary-default-parallel
|
||||
@@ -10,7 +10,7 @@ else
|
||||
export VERSION=''
|
||||
fi
|
||||
|
||||
export CODENAME=tetedemoine
|
||||
export CODENAME=faisselle
|
||||
|
||||
export N_MAKE_JOBS=2
|
||||
|
||||
@@ -24,8 +24,8 @@ function ci_retry {
|
||||
until [ $n -ge $NRETRY ]
|
||||
do
|
||||
"$@" && break
|
||||
n=$[$n+1]
|
||||
echo "$@ failed, attempt ${n}/${NRETRY}"
|
||||
n=$((n+1))
|
||||
echo "${*} failed, attempt ${n}/${NRETRY}"
|
||||
sleep $NSLEEP
|
||||
done
|
||||
|
||||
@@ -34,4 +34,3 @@ function ci_retry {
|
||||
}
|
||||
|
||||
export -f ci_retry
|
||||
|
||||
|
||||
20
.travis.yml
@@ -11,11 +11,11 @@ env:
|
||||
global:
|
||||
- REPO: $TRAVIS_REPO_SLUG
|
||||
- VERSION: $TRAVIS_TAG
|
||||
- CODENAME: tetedemoine
|
||||
- N_MAKE_JOBS: 2
|
||||
- CODENAME: faisselle
|
||||
|
||||
script:
|
||||
- echo "Skipping tests... (Tests are executed on SemaphoreCI)"
|
||||
- if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then make docs; fi
|
||||
|
||||
before_deploy:
|
||||
- >
|
||||
@@ -24,15 +24,14 @@ before_deploy:
|
||||
sudo -E apt-get -yq update;
|
||||
sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install docker-ce=${DOCKER_VERSION}*;
|
||||
docker version;
|
||||
make image;
|
||||
make build-image;
|
||||
if [ "$TRAVIS_TAG" ]; then
|
||||
make -j${N_MAKE_JOBS} crossbinary-parallel;
|
||||
tar cfz dist/traefik-${VERSION}.src.tar.gz --exclude-vcs --exclude dist .;
|
||||
make release-packages;
|
||||
fi;
|
||||
curl -sI https://github.com/containous/structor/releases/latest | grep -Fi Location | tr -d '\r' | sed "s/tag/download/g" | awk -F " " '{ print $2 "/structor_linux-amd64"}' | wget --output-document=$GOPATH/bin/structor -i -;
|
||||
chmod +x $GOPATH/bin/structor;
|
||||
structor -o containous -r traefik --dockerfile-url="https://raw.githubusercontent.com/containous/traefik/master/docs.Dockerfile" --menu.js-url="https://raw.githubusercontent.com/containous/structor/master/traefik-menu.js.gotmpl" --rqts-url="https://raw.githubusercontent.com/containous/structor/master/requirements-override.txt" --exp-branch=master --debug;
|
||||
curl -sfL https://raw.githubusercontent.com/containous/structor/master/godownloader.sh | bash -s -- -b "${GOPATH}/bin" ${STRUCTOR_VERSION}
|
||||
structor -o containous -r traefik --dockerfile-url="https://raw.githubusercontent.com/containous/traefik/v1.7/docs.Dockerfile" --menu.js-url="https://raw.githubusercontent.com/containous/structor/master/traefik-menu.js.gotmpl" --rqts-url="https://raw.githubusercontent.com/containous/structor/master/requirements-override.txt" --force-edit-url --exp-branch=master --debug;
|
||||
fi
|
||||
|
||||
deploy:
|
||||
- provider: releases
|
||||
api_key: ${GITHUB_TOKEN}
|
||||
@@ -48,11 +47,6 @@ deploy:
|
||||
on:
|
||||
repo: containous/traefik
|
||||
tags: true
|
||||
- provider: script
|
||||
script: sh script/deploy-docker.sh
|
||||
skip_cleanup: true
|
||||
on:
|
||||
repo: containous/traefik
|
||||
- provider: pages
|
||||
edge: false
|
||||
github_token: ${GITHUB_TOKEN}
|
||||
|
||||
865
CHANGELOG.md
@@ -1,5 +1,870 @@
|
||||
# Change Log
|
||||
|
||||
## [v2.0.0-alpha5](https://github.com/containous/traefik/tree/v2.0.0-alpha5) (2019-06-17)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v2.0.0-alpha4...v2.0.0-alpha5)
|
||||
|
||||
**Enhancements:**
|
||||
- **[acme]** Remove timeout/interval from the ACME Provider ([#4842](https://github.com/containous/traefik/pull/4842) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[api]** API: expose runtime representation ([#4841](https://github.com/containous/traefik/pull/4841) by [mpl](https://github.com/mpl))
|
||||
- **[cli]** New static configuration loading system. ([#4935](https://github.com/containous/traefik/pull/4935) by [ldez](https://github.com/ldez))
|
||||
- **[k8s,k8s/crd,tcp]** Add support for TCP (in kubernetes CRD) ([#4885](https://github.com/containous/traefik/pull/4885) by [mpl](https://github.com/mpl))
|
||||
- **[server]** Rework loadbalancer support ([#4933](https://github.com/containous/traefik/pull/4933) by [juliens](https://github.com/juliens))
|
||||
- **[sticky-session]** HttpOnly and Secure flags on the affinity cookie ([#4947](https://github.com/containous/traefik/pull/4947) by [gheibia](https://github.com/gheibia))
|
||||
- **[tls]** Define TLS options on the Router configuration ([#4931](https://github.com/containous/traefik/pull/4931) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[tracing]** Added support for Haystack tracing ([#4555](https://github.com/containous/traefik/pull/4555) by [aantono](https://github.com/aantono))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[logs]** Fix typos in data collection message ([#4891](https://github.com/containous/traefik/pull/4891) by [mpl](https://github.com/mpl))
|
||||
- **[middleware]** change doc references to scheme[Rr]edirect -> redirect[Ss]cheme ([#4959](https://github.com/containous/traefik/pull/4959) by [topiaruss](https://github.com/topiaruss))
|
||||
- **[rules]** Allow matching with FQDN hosts with trailing periods ([#4763](https://github.com/containous/traefik/pull/4763) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[tcp]** Remove first byte wait when tcp catches all ([#4938](https://github.com/containous/traefik/pull/4938) by [juliens](https://github.com/juliens))
|
||||
- **[tcp]** Don't add TCP proxy when error occurs during creation. ([#4858](https://github.com/containous/traefik/pull/4858) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** Add note about ACME renewal ([#4860](https://github.com/containous/traefik/pull/4860) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[docker]** Remove traefik.port from documentation ([#4886](https://github.com/containous/traefik/pull/4886) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Clarify docs with labels in Swarm Mode ([#4847](https://github.com/containous/traefik/pull/4847) by [mikesir87](https://github.com/mikesir87))
|
||||
- **[k8s]** Fix typo in the CRD documentation ([#4902](https://github.com/containous/traefik/pull/4902) by [llussy](https://github.com/llussy))
|
||||
- **[middleware,provider]** fix the documentation about middleware labels. ([#4888](https://github.com/containous/traefik/pull/4888) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Review documentation ([#4798](https://github.com/containous/traefik/pull/4798) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** compress link fixed ([#4817](https://github.com/containous/traefik/pull/4817) by [gato](https://github.com/gato))
|
||||
- **[middleware]** Fix strip prefix documentation ([#4829](https://github.com/containous/traefik/pull/4829) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware]** Fix Kubernetes Docs for Middlewares ([#4943](https://github.com/containous/traefik/pull/4943) by [HurricanKai](https://github.com/HurricanKai))
|
||||
- **[middleware]** Correct typo in documentation on rate limiting ([#4939](https://github.com/containous/traefik/pull/4939) by [ableuler](https://github.com/ableuler))
|
||||
- **[middleware]** docker-compose basic auth needs double dollar signs ([#4831](https://github.com/containous/traefik/pull/4831) by [muhlemmer](https://github.com/muhlemmer))
|
||||
- **[middleware]** Adds a reference to the middleware overview. ([#4824](https://github.com/containous/traefik/pull/4824) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Update headers middleware docs for kubernetes crd ([#4955](https://github.com/containous/traefik/pull/4955) by [orhanhenrik](https://github.com/orhanhenrik))
|
||||
- **[rancher]** fix: Rancher documentation. ([#4818](https://github.com/containous/traefik/pull/4818) by [ldez](https://github.com/ldez))
|
||||
- **[rancher]** Specify that Rancher provider is for 1.x only ([#4923](https://github.com/containous/traefik/pull/4923) by [bradjones1](https://github.com/bradjones1))
|
||||
- **[tls]** fix: typo in routing example. ([#4849](https://github.com/containous/traefik/pull/4849) by [ldez](https://github.com/ldez))
|
||||
- Clarification of the correct pronunciation of the word "Traefik" ([#4834](https://github.com/containous/traefik/pull/4834) by [ylamlum-g4m](https://github.com/ylamlum-g4m))
|
||||
- Fix typos in documentation ([#4884](https://github.com/containous/traefik/pull/4884) by [michael-k](https://github.com/michael-k))
|
||||
- Entry points CLI description. ([#4896](https://github.com/containous/traefik/pull/4896) by [ldez](https://github.com/ldez))
|
||||
- Improve the "reading path" for new contributors ([#4908](https://github.com/containous/traefik/pull/4908) by [dduportal](https://github.com/dduportal))
|
||||
- Fixed spelling typo ([#4848](https://github.com/containous/traefik/pull/4848) by [mikesir87](https://github.com/mikesir87))
|
||||
- Fixed readme misspelling ([#4882](https://github.com/containous/traefik/pull/4882) by [antondalgren](https://github.com/antondalgren))
|
||||
- Minor fix in documentation ([#4811](https://github.com/containous/traefik/pull/4811) by [mmatur](https://github.com/mmatur))
|
||||
- Add Mathieu Lonjaret to maintainers ([#4950](https://github.com/containous/traefik/pull/4950) by [emilevauge](https://github.com/emilevauge))
|
||||
- Fix a typo in documentation ([#4794](https://github.com/containous/traefik/pull/4794) by [groovytron](https://github.com/groovytron))
|
||||
|
||||
**Misc:**
|
||||
- Cherry pick v1.7 into v2.0 ([#4948](https://github.com/containous/traefik/pull/4948) by [ldez](https://github.com/ldez))
|
||||
- Cherry pick v1.7 into v2.0 ([#4823](https://github.com/containous/traefik/pull/4823) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v1.7.12](https://github.com/containous/traefik/tree/v1.7.12) (2019-05-29)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.11...v1.7.12)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Allow SANs for wildcards domain. ([#4821](https://github.com/containous/traefik/pull/4821) by [vizv](https://github.com/vizv))
|
||||
- **[acme]** fix: update lego. ([#4910](https://github.com/containous/traefik/pull/4910) by [ldez](https://github.com/ldez))
|
||||
- **[api,authentication]** Remove authentication hashes from API ([#4918](https://github.com/containous/traefik/pull/4918) by [ldez](https://github.com/ldez))
|
||||
- **[consul]** Enhance KV logs. ([#4877](https://github.com/containous/traefik/pull/4877) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Fix kubernetes template for backend responseforwarding flushinterval setting ([#4901](https://github.com/containous/traefik/pull/4901) by [ravilr](https://github.com/ravilr))
|
||||
- **[metrics]** Upgraded DataDog tracing library to 1.13.0 ([#4878](https://github.com/containous/traefik/pull/4878) by [aantono](https://github.com/aantono))
|
||||
- **[server]** Add missing callback on close of hijacked connections ([#4900](https://github.com/containous/traefik/pull/4900) by [ravilr](https://github.com/ravilr))
|
||||
|
||||
**Documentation:**
|
||||
- **[docker]** Docs: Troubleshooting help for Docker Swarm labels ([#4751](https://github.com/containous/traefik/pull/4751) by [gregberns](https://github.com/gregberns))
|
||||
- **[logs]** Adds a log fields documentation. ([#4890](https://github.com/containous/traefik/pull/4890) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v1.7.11](https://github.com/containous/traefik/tree/v1.7.11) (2019-04-26)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.10...v1.7.11)
|
||||
|
||||
**Enhancements:**
|
||||
- **[k8s,k8s/ingress]** Enhance k8s tests maintainability ([#4696](https://github.com/containous/traefik/pull/4696) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** fix: update lego. ([#4800](https://github.com/containous/traefik/pull/4800) by [ldez](https://github.com/ldez))
|
||||
- **[authentication,middleware]** Forward all header values from forward auth response ([#4515](https://github.com/containous/traefik/pull/4515) by [ctas582](https://github.com/ctas582))
|
||||
- **[cluster]** Remove usage of github.com/satori/go.uuid ([#4722](https://github.com/containous/traefik/pull/4722) by [aaslamin](https://github.com/aaslamin))
|
||||
- **[kv]** Enhance KV client error management ([#4819](https://github.com/containous/traefik/pull/4819) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[tls]** Improve log message about redundant TLS certificate ([#4765](https://github.com/containous/traefik/pull/4765) by [mpl](https://github.com/mpl))
|
||||
- **[tracing]** Update zipkin-go-opentracing. ([#4720](https://github.com/containous/traefik/pull/4720) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** Documentation Update: Hosting.de wildcard support tested ([#4747](https://github.com/containous/traefik/pull/4747) by [martinhoefling](https://github.com/martinhoefling))
|
||||
- **[acme]** Update Wildcard Domain documentation ([#4682](https://github.com/containous/traefik/pull/4682) by [DWSR](https://github.com/DWSR))
|
||||
- **[middleware]** Keep consistent order ([#4690](https://github.com/containous/traefik/pull/4690) by [maxifom](https://github.com/maxifom))
|
||||
|
||||
## [v2.0.0-alpha4](https://github.com/containous/traefik/tree/v2.0.0-alpha4) (2019-04-17)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v2.0.0-alpha3...v2.0.0-alpha4)
|
||||
|
||||
**Enhancements:**
|
||||
- **[logs]** log.loglevel becomes log.level in configuration ([#4775](https://github.com/containous/traefik/pull/4775) by [juliens](https://github.com/juliens))
|
||||
- **[marathon,tcp]** Handle TCP in the marathon provider ([#4728](https://github.com/containous/traefik/pull/4728) by [juliens](https://github.com/juliens))
|
||||
- **[middleware]** Enable CORS configuration ([#3809](https://github.com/containous/traefik/pull/3809) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[rancher]** Add Rancher provider ([#4647](https://github.com/containous/traefik/pull/4647) by [SantoDE](https://github.com/SantoDE))
|
||||
- **[tracing]** Update tracing dependencies ([#4721](https://github.com/containous/traefik/pull/4721) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[docker]** Insensitive case for allow-empty value. ([#4745](https://github.com/containous/traefik/pull/4745) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Fix response modifier initial building ([#4719](https://github.com/containous/traefik/pull/4719) by [mpl](https://github.com/mpl))
|
||||
- **[middleware]** Set X-Forwarded-* headers ([#4707](https://github.com/containous/traefik/pull/4707) by [mpl](https://github.com/mpl))
|
||||
- **[tcp]** Fix EOF error ([#4733](https://github.com/containous/traefik/pull/4733) by [juliens](https://github.com/juliens))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** Use the same case every where for entryPoints. ([#4764](https://github.com/containous/traefik/pull/4764) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Fix two minor nits in Traefik 2.0 docs ([#4692](https://github.com/containous/traefik/pull/4692) by [cfra](https://github.com/cfra))
|
||||
- **[k8s,k8s/crd]** k8s static configuration explanation ([#4767](https://github.com/containous/traefik/pull/4767) by [ldez](https://github.com/ldez))
|
||||
- **[marathon]** Enhance Marathon documentation ([#4776](https://github.com/containous/traefik/pull/4776) by [ldez](https://github.com/ldez))
|
||||
- **[middleware,k8s,k8s/crd]** Fix typo: middleware -> middlewares. ([#4781](https://github.com/containous/traefik/pull/4781) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Adds middlewares examples for k8s. ([#4713](https://github.com/containous/traefik/pull/4713) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Remove invalid commas. ([#4706](https://github.com/containous/traefik/pull/4706) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Fix doc about removing headers ([#4708](https://github.com/containous/traefik/pull/4708) by [mpl](https://github.com/mpl))
|
||||
- **[middleware]** Update the middleware documentation ([#4729](https://github.com/containous/traefik/pull/4729) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[tracing]** Fix typo in tracing docs ([#4737](https://github.com/containous/traefik/pull/4737) by [timoschwarzer](https://github.com/timoschwarzer))
|
||||
- Improve the Documentation with a Reference Section ([#4714](https://github.com/containous/traefik/pull/4714) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- Adds a note in traefik.sample.toml ([#4757](https://github.com/containous/traefik/pull/4757) by [ldez](https://github.com/ldez))
|
||||
- Update to v2.0 readme links ([#4700](https://github.com/containous/traefik/pull/4700) by [karnthis](https://github.com/karnthis))
|
||||
- Remove dumpcerts.sh ([#4783](https://github.com/containous/traefik/pull/4783) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Misc:**
|
||||
- Cherry pick v1.7 into v2.0 ([#4787](https://github.com/containous/traefik/pull/4787) by [ldez](https://github.com/ldez))
|
||||
- Cherry pick v1.7 into v2.0 ([#4695](https://github.com/containous/traefik/pull/4695) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
|
||||
## [v2.0.0-alpha3](https://github.com/containous/traefik/tree/v2.0.0-alpha3) (2019-03-29)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v2.0.0-alpha2...v2.0.0-alpha3)
|
||||
|
||||
**Enhancements:**
|
||||
- **[acme,k8s,k8s/crd]** Document the TLS with ACME case ([#4654](https://github.com/containous/traefik/pull/4654) by [mpl](https://github.com/mpl))
|
||||
- **[docker,tcp]** Add support for TCP labels in Docker provider ([#4621](https://github.com/containous/traefik/pull/4621) by [juliens](https://github.com/juliens))
|
||||
- **[provider]** Remove BaseProvider ([#4661](https://github.com/containous/traefik/pull/4661) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[server]** Fix panic while server shutdown ([#4644](https://github.com/containous/traefik/pull/4644) by [juliens](https://github.com/juliens))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme,k8s,k8s/crd]** Full ACME+CRD example ([#4652](https://github.com/containous/traefik/pull/4652) by [mpl](https://github.com/mpl))
|
||||
- **[acme]** Enhance manual dnsChallenge documentation ([#4636](https://github.com/containous/traefik/pull/4636) by [ntaranov](https://github.com/ntaranov))
|
||||
- **[docker]** Fix Getting started ([#4646](https://github.com/containous/traefik/pull/4646) by [mmatur](https://github.com/mmatur))
|
||||
- **[docker]** docker-compose examples ([#4642](https://github.com/containous/traefik/pull/4642) by [karnthis](https://github.com/karnthis))
|
||||
- **[middleware]** Fix typo in forwardAuth middleware documentation ([#4638](https://github.com/containous/traefik/pull/4638) by [AkeemMcLennon](https://github.com/AkeemMcLennon))
|
||||
- **[middleware]** Enhance middleware examples. ([#4680](https://github.com/containous/traefik/pull/4680) by [ldez](https://github.com/ldez))
|
||||
- Fix typos in docs ([#4662](https://github.com/containous/traefik/pull/4662) by [SeMeKh](https://github.com/SeMeKh))
|
||||
- Remove old links in readme ([#4651](https://github.com/containous/traefik/pull/4651) by [ldez](https://github.com/ldez))
|
||||
- Fix some minors errors on the documentation ([#4664](https://github.com/containous/traefik/pull/4664) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- Fix dead maintainers link on the README.md ([#4639](https://github.com/containous/traefik/pull/4639) by [benjaminch](https://github.com/benjaminch))
|
||||
- Update traefik.sample.toml ([#4657](https://github.com/containous/traefik/pull/4657) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v2.0.0-alpha2](https://github.com/containous/traefik/tree/v2.0.0-alpha2) (2019-03-19)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v2.0.0-alpha1...v2.0.0-alpha2)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[k8s,k8s/crd]** Fix log messages about label selector ([#4629](https://github.com/containous/traefik/pull/4629) by [mpl](https://github.com/mpl))
|
||||
- **[server]** Fix problem in aggregator provider ([#4625](https://github.com/containous/traefik/pull/4625) by [juliens](https://github.com/juliens))
|
||||
|
||||
**Documentation:**
|
||||
- **[k8s,k8s/crd]** doc: kubernetes CRD provider ([#4620](https://github.com/containous/traefik/pull/4620) by [mpl](https://github.com/mpl))
|
||||
- **[webui]** change docs and adjust dashboard for v2 alpha ([#4632](https://github.com/containous/traefik/pull/4632) by [SantoDE](https://github.com/SantoDE))
|
||||
|
||||
## [v2.0.0-alpha1](https://github.com/containous/traefik/tree/v2.0.0-alpha1) (2019-03-18)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.0-rc1...v2.0.0-alpha1)
|
||||
|
||||
**Enhancements:**
|
||||
- **[acme,kv]** Remove Deprecated StorageFile ([#4252](https://github.com/containous/traefik/pull/4252) by [juliens](https://github.com/juliens))
|
||||
- **[acme]** Migrate to go-acme/lego. ([#4589](https://github.com/containous/traefik/pull/4589) by [ldez](https://github.com/ldez))
|
||||
- **[authentication,logs,etcd]** Remove deprecated elements ([#3715](https://github.com/containous/traefik/pull/3715) by [geraldcroes](https://github.com/geraldcroes))
|
||||
- **[authentication,middleware]** Basic Auth custom realm ([#3917](https://github.com/containous/traefik/pull/3917) by [tcoupin](https://github.com/tcoupin))
|
||||
- **[docker]** Adds default rule system on Docker provider. ([#4413](https://github.com/containous/traefik/pull/4413) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Adds Docker provider support ([#4399](https://github.com/containous/traefik/pull/4399) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Update to Go1.12. Support of TLS1.3 ([#4540](https://github.com/containous/traefik/pull/4540) by [ldez](https://github.com/ldez))
|
||||
- **[etcd]** Remove etcd v2 ([#3739](https://github.com/containous/traefik/pull/3739) by [geraldcroes](https://github.com/geraldcroes))
|
||||
- **[k8s/ingress]** Adds Kubernetes provider support ([#4476](https://github.com/containous/traefik/pull/4476) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[k8s/ingress]** Adds update ingress status ([#4603](https://github.com/containous/traefik/pull/4603) by [juliens](https://github.com/juliens))
|
||||
- **[k8s/ingress]** k8s integration tests ([#4569](https://github.com/containous/traefik/pull/4569) by [juliens](https://github.com/juliens))
|
||||
- **[k8s/ingress]** Custom resource definition ([#4591](https://github.com/containous/traefik/pull/4591) by [ldez](https://github.com/ldez))
|
||||
- **[marathon]** Adds Marathon support. ([#4415](https://github.com/containous/traefik/pull/4415) by [ldez](https://github.com/ldez))
|
||||
- **[metrics]** Add HTTP authentication to influxdb metric backend ([#3600](https://github.com/containous/traefik/pull/3600) by [halfa](https://github.com/halfa))
|
||||
- **[middleware,provider]** IPStrategy for selecting IP in whitelist ([#3778](https://github.com/containous/traefik/pull/3778) by [juliens](https://github.com/juliens))
|
||||
- **[middleware,provider]** Enables the use of elements declared in other providers ([#4372](https://github.com/containous/traefik/pull/4372) by [geraldcroes](https://github.com/geraldcroes))
|
||||
- **[middleware]** Migrates the pass client tls cert middleware ([#4373](https://github.com/containous/traefik/pull/4373) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[middleware]** Migrates Compress from bool to struct ([#3714](https://github.com/containous/traefik/pull/3714) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[middleware]** Updates for jaeger tracing client. ([#3688](https://github.com/containous/traefik/pull/3688) by [tcolgate](https://github.com/tcolgate))
|
||||
- **[middleware]** Add forwarded headers on entry point configuration ([#4364](https://github.com/containous/traefik/pull/4364) by [juliens](https://github.com/juliens))
|
||||
- **[middleware]** SchemeRedirect Middleware ([#4400](https://github.com/containous/traefik/pull/4400) by [geraldcroes](https://github.com/geraldcroes))
|
||||
- **[provider]** Add health check timeout parameter ([#3813](https://github.com/containous/traefik/pull/3813) by [jbiel](https://github.com/jbiel))
|
||||
- **[provider]** Removes deprecated templates ([#3649](https://github.com/containous/traefik/pull/3649) by [geraldcroes](https://github.com/geraldcroes))
|
||||
- **[provider]** Remove everything templates related ([#4595](https://github.com/containous/traefik/pull/4595) by [mpl](https://github.com/mpl))
|
||||
- **[provider]** Small code enhancements on providers ([#3707](https://github.com/containous/traefik/pull/3707) by [vdemeester](https://github.com/vdemeester))
|
||||
- **[provider]** Migrate rest provider ([#4253](https://github.com/containous/traefik/pull/4253) by [juliens](https://github.com/juliens))
|
||||
- **[provider]** Labels parser. ([#4236](https://github.com/containous/traefik/pull/4236) by [ldez](https://github.com/ldez))
|
||||
- **[rules]** New rule syntax ([#4437](https://github.com/containous/traefik/pull/4437) by [juliens](https://github.com/juliens))
|
||||
- **[server]** Dynamic Configuration Refactoring ([#4168](https://github.com/containous/traefik/pull/4168) by [ldez](https://github.com/ldez))
|
||||
- **[server]** Remove old global config and use new static config ([#4222](https://github.com/containous/traefik/pull/4222) by [juliens](https://github.com/juliens))
|
||||
- **[tcp]** Adds TCP support ([#4587](https://github.com/containous/traefik/pull/4587) by [juliens](https://github.com/juliens))
|
||||
- **[tracing]** Instana tracer implementation ([#4453](https://github.com/containous/traefik/pull/4453) by [notsureifkevin](https://github.com/notsureifkevin))
|
||||
- **[tracing]** Make Zipkin trace rate configurable ([#3968](https://github.com/containous/traefik/pull/3968) by [negz](https://github.com/negz))
|
||||
- **[webui]** Upgrade angular cli version ([#4450](https://github.com/containous/traefik/pull/4450) by [Slashgear](https://github.com/Slashgear))
|
||||
- **[webui]** Update docker node version ([#4448](https://github.com/containous/traefik/pull/4448) by [Slashgear](https://github.com/Slashgear))
|
||||
- **[webui]** Ignore target/dependencies in docker copy ([#4449](https://github.com/containous/traefik/pull/4449) by [Slashgear](https://github.com/Slashgear))
|
||||
- **[webui]** Format code with prettier ([#4463](https://github.com/containous/traefik/pull/4463) by [Slashgear](https://github.com/Slashgear))
|
||||
- **[webui]** No need for npm progress=false ([#3702](https://github.com/containous/traefik/pull/3702) by [vdemeester](https://github.com/vdemeester))
|
||||
- **[webui]** Migrate to a work in progress webui ([#4568](https://github.com/containous/traefik/pull/4568) by [Slashgear](https://github.com/Slashgear))
|
||||
- **[webui]** Include lint in build process ([#4462](https://github.com/containous/traefik/pull/4462) by [Slashgear](https://github.com/Slashgear))
|
||||
- **[webui]** Dropping rxjs-compat in favor of pipe ([#4520](https://github.com/containous/traefik/pull/4520) by [imcotton](https://github.com/imcotton))
|
||||
- New packaging system. ([#4593](https://github.com/containous/traefik/pull/4593) by [ldez](https://github.com/ldez))
|
||||
- Updates Backoff ([#4457](https://github.com/containous/traefik/pull/4457) by [ldez](https://github.com/ldez))
|
||||
- Remove the bug command ([#4556](https://github.com/containous/traefik/pull/4556) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- Small code enhancements ([#3712](https://github.com/containous/traefik/pull/3712) by [mmatur](https://github.com/mmatur))
|
||||
- Remove deprecated elements ([#3666](https://github.com/containous/traefik/pull/3666) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- Clean old ([#4612](https://github.com/containous/traefik/pull/4612) by [ldez](https://github.com/ldez))
|
||||
- Update anonymize/collect ([#4590](https://github.com/containous/traefik/pull/4590) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[k8s,k8s/crd]** Remove IngressEndpoint in CRD provider ([#4616](https://github.com/containous/traefik/pull/4616) by [juliens](https://github.com/juliens))
|
||||
- **[logs]** Allow user to configure traefik log ([#4604](https://github.com/containous/traefik/pull/4604) by [mmatur](https://github.com/mmatur))
|
||||
- **[server]** Fix lock problem in server ([#4600](https://github.com/containous/traefik/pull/4600) by [juliens](https://github.com/juliens))
|
||||
- Clean files during tests. ([#4607](https://github.com/containous/traefik/pull/4607) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme,docker]** Synchronize documentation ([#4571](https://github.com/containous/traefik/pull/4571) by [juliens](https://github.com/juliens))
|
||||
- **[acme]** Enhance acme page. ([#4611](https://github.com/containous/traefik/pull/4611) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Rename Docker_Acme.md to Readme.md ([#4025](https://github.com/containous/traefik/pull/4025) by [vineetvermait](https://github.com/vineetvermait))
|
||||
- **[acme]** fix: some DNS provider link. ([#3637](https://github.com/containous/traefik/pull/3637) by [ldez](https://github.com/ldez))
|
||||
- **[file]** Update the file provider documentation ([#4588](https://github.com/containous/traefik/pull/4588) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[tcp]** Use rule HostSNI in documentation ([#4592](https://github.com/containous/traefik/pull/4592) by [bbinet](https://github.com/bbinet))
|
||||
- Documentation Revamp ([#4475](https://github.com/containous/traefik/pull/4475) by [geraldcroes](https://github.com/geraldcroes))
|
||||
- Add Gerald, Jean-Baptiste and Damien to maintainers ([#3982](https://github.com/containous/traefik/pull/3982) by [emilevauge](https://github.com/emilevauge))
|
||||
- fix broken links in readme.md ([#3967](https://github.com/containous/traefik/pull/3967) by [AndrewSav](https://github.com/AndrewSav))
|
||||
- Add master overhaul notice ([#3961](https://github.com/containous/traefik/pull/3961) by [emilevauge](https://github.com/emilevauge))
|
||||
- Complete maintainers processes ([#3696](https://github.com/containous/traefik/pull/3696) by [mmatur](https://github.com/mmatur))
|
||||
- Complete maintainers processes ([#3681](https://github.com/containous/traefik/pull/3681) by [emilevauge](https://github.com/emilevauge))
|
||||
- Adds a maintainer's page into the documentation. ([#4614](https://github.com/containous/traefik/pull/4614) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Misc:**
|
||||
- Cherry pick v1.7 into master ([#4565](https://github.com/containous/traefik/pull/4565) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- Cherry pick v1.7 into master ([#4511](https://github.com/containous/traefik/pull/4511) by [ldez](https://github.com/ldez))
|
||||
- Cherry pick v1.7 into master ([#4492](https://github.com/containous/traefik/pull/4492) by [ldez](https://github.com/ldez))
|
||||
- Cherry pick v1.7 into master ([#4440](https://github.com/containous/traefik/pull/4440) by [ldez](https://github.com/ldez))
|
||||
- Cherry pick v1.7 into master ([#4365](https://github.com/containous/traefik/pull/4365) by [ldez](https://github.com/ldez))
|
||||
- Cherry pick v1.7 into master ([#4303](https://github.com/containous/traefik/pull/4303) by [ldez](https://github.com/ldez))
|
||||
- Cherry pick v1.7 into master ([#4271](https://github.com/containous/traefik/pull/4271) by [ldez](https://github.com/ldez))
|
||||
- Cherry pick v1.7 into master ([#4268](https://github.com/containous/traefik/pull/4268) by [ldez](https://github.com/ldez))
|
||||
- Cherry pick v1.7 into master ([#4229](https://github.com/containous/traefik/pull/4229) by [juliens](https://github.com/juliens))
|
||||
- Cherry pick v1.7 into master ([#4206](https://github.com/containous/traefik/pull/4206) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.7.4 into master ([#4137](https://github.com/containous/traefik/pull/4137) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.7.3 into master ([#4046](https://github.com/containous/traefik/pull/4046) by [ldez](https://github.com/ldez))
|
||||
- Merge current v1.7 into master ([#3992](https://github.com/containous/traefik/pull/3992) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.7.2 into master ([#3983](https://github.com/containous/traefik/pull/3983) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.7.0 into master ([#3925](https://github.com/containous/traefik/pull/3925) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.7.0-rc5 into master ([#3903](https://github.com/containous/traefik/pull/3903) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.7.0-rc4 into master ([#3867](https://github.com/containous/traefik/pull/3867) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.7.0-rc2 into master ([#3634](https://github.com/containous/traefik/pull/3634) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v1.7.10](https://github.com/containous/traefik/tree/v1.7.10) (2019-03-28)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.9...v1.7.10)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** fix: update lego. ([#4670](https://github.com/containous/traefik/pull/4670) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Migrate to go-acme/lego. ([#4577](https://github.com/containous/traefik/pull/4577) by [ldez](https://github.com/ldez))
|
||||
- **[authentication,middleware]** Reorder Auth and TLSClientHeaders middleware ([#4557](https://github.com/containous/traefik/pull/4557) by [tomberek](https://github.com/tomberek))
|
||||
- **[k8s/ingress]** Support external name service on global default backend ([#4564](https://github.com/containous/traefik/pull/4564) by [kippandrew](https://github.com/kippandrew))
|
||||
- **[k8s/ingress]** Loop through service ports for global backend ([#4486](https://github.com/containous/traefik/pull/4486) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Add entrypoints prefix in kubernetes frontend/backend id ([#4679](https://github.com/containous/traefik/pull/4679) by [juliens](https://github.com/juliens))
|
||||
- **[websocket]** Exclude websocket connections from Average Response Time ([#4313](https://github.com/containous/traefik/pull/4313) by [siyu6974](https://github.com/siyu6974))
|
||||
- **[middleware]** Added support for configuring trace headers for DataDog tracing ([#4516](https://github.com/containous/traefik/pull/4516) by [aantono](https://github.com/aantono))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** Add _FILE Environment Variable Documentation ([#4643](https://github.com/containous/traefik/pull/4643) by [dargmuesli](https://github.com/dargmuesli))
|
||||
- **[docker]** Add TraefikEE as security workaround ([#4606](https://github.com/containous/traefik/pull/4606) by [emilevauge](https://github.com/emilevauge))
|
||||
|
||||
## [v1.7.9](https://github.com/containous/traefik/tree/v1.7.9) (2019-02-11)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.8...v1.7.9)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Updates of Lego. ([#4480](https://github.com/containous/traefik/pull/4480) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** app-root on non-explicit path include "/" in the redirect ([#4458](https://github.com/containous/traefik/pull/4458) by [doctori](https://github.com/doctori))
|
||||
- **[middleware]** Missing trailers with retry ([#4442](https://github.com/containous/traefik/pull/4442) by [juliens](https://github.com/juliens))
|
||||
- **[rancher]** Handle errors when working with rancher ([#4378](https://github.com/containous/traefik/pull/4378) by [apsifly](https://github.com/apsifly))
|
||||
- **[servicefabric]** Add support for specifying the name of the endpoint. ([#4479](https://github.com/containous/traefik/pull/4479) by [ldez](https://github.com/ldez))
|
||||
- **[tls]** insecureSkipVerify for the passTLSCert transport ([#4438](https://github.com/containous/traefik/pull/4438) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[tracing]** Add Tracing Header Context Name option for Jaeger ([#4459](https://github.com/containous/traefik/pull/4459) by [gadoor](https://github.com/gadoor))
|
||||
|
||||
**Documentation:**
|
||||
- **[metrics]** Update default value of buckets for Prometheus ([#4468](https://github.com/containous/traefik/pull/4468) by [adam-golab](https://github.com/adam-golab))
|
||||
- **[rules]** Fixes the display of the associativity rules. ([#4478](https://github.com/containous/traefik/pull/4478) by [ldez](https://github.com/ldez))
|
||||
- Fixed curl example ([#4471](https://github.com/containous/traefik/pull/4471) by [rgarrigue](https://github.com/rgarrigue))
|
||||
|
||||
## [v1.7.8](https://github.com/containous/traefik/tree/v1.7.8) (2019-01-29)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.7...v1.7.8)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Updates lego. ([#4428](https://github.com/containous/traefik/pull/4428) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Updates lego. ([#4376](https://github.com/containous/traefik/pull/4376) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Fixes docker swarm mode refresh second for KV. ([#4420](https://github.com/containous/traefik/pull/4420) by [ldez](https://github.com/ldez))
|
||||
- **[ecs]** Generic awsvpc support, not just Fargate ([#4360](https://github.com/containous/traefik/pull/4360) by [maartenvanderhoef](https://github.com/maartenvanderhoef))
|
||||
- **[ecs]** Cache existing task definitions to avoid rate limiting ([#4177](https://github.com/containous/traefik/pull/4177) by [hwhelan-CB](https://github.com/hwhelan-CB))
|
||||
- **[tls]** Check for dynamic tls updates on configuration preload ([#4022](https://github.com/containous/traefik/pull/4022) by [ffilippopoulos](https://github.com/ffilippopoulos))
|
||||
- **[tracing]** Support Datadog tracer priority sampling ([#4359](https://github.com/containous/traefik/pull/4359) by [jcassee](https://github.com/jcassee))
|
||||
- Update to Go 1.11.5 [CVE-2019-6486](https://nvd.nist.gov/vuln/detail/CVE-2019-6486)
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** More detailed info about Google Cloud DNS. ([#4395](https://github.com/containous/traefik/pull/4395) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Tested wildcard ACME challenge with DNSimple ([#4384](https://github.com/containous/traefik/pull/4384) by [tstackhouse](https://github.com/tstackhouse))
|
||||
- **[docker]** Note about quotes for entrypoint definition with docker-compose ([#4390](https://github.com/containous/traefik/pull/4390) by [Dragnucs](https://github.com/Dragnucs))
|
||||
- **[k8s]** Allow Træfik to update Ingress status ([#4397](https://github.com/containous/traefik/pull/4397) by [rbq](https://github.com/rbq))
|
||||
- **[k8s]** Minor formatting fixes ([#4394](https://github.com/containous/traefik/pull/4394) by [dbirks](https://github.com/dbirks))
|
||||
- **[metrics]** Missing information about statistics parameter ([#4393](https://github.com/containous/traefik/pull/4393) by [decima](https://github.com/decima))
|
||||
- **[rules]** Route priorities: document minimum priority value ([#4374](https://github.com/containous/traefik/pull/4374) by [tw-360vier](https://github.com/tw-360vier))
|
||||
- Removed repeated entryPoints.http from grpc.md ([#4370](https://github.com/containous/traefik/pull/4370) by [ishaanbahal](https://github.com/ishaanbahal))
|
||||
- Happy 2019 ([#4367](https://github.com/containous/traefik/pull/4367) by [emilevauge](https://github.com/emilevauge))
|
||||
|
||||
**Misc:**
|
||||
- Assert that test timeout service is ready. ([#4398](https://github.com/containous/traefik/pull/4398) by [timoreimann](https://github.com/timoreimann))
|
||||
|
||||
## [v1.7.7](https://github.com/containous/traefik/tree/v1.7.7) (2019-01-08)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.6...v1.7.7)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Update Lego ([#4277](https://github.com/containous/traefik/pull/4277) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Check for watched namespace before getting kubernetes objects ([#4327](https://github.com/containous/traefik/pull/4327) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Allow empty path with App-root annotation ([#4326](https://github.com/containous/traefik/pull/4326) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** kubernetes: sort and uniq TLS secrets ([#4307](https://github.com/containous/traefik/pull/4307) by [zarqman](https://github.com/zarqman))
|
||||
- **[k8s]** Skip TLS section with no secret in Kubernetes ingress ([#4340](https://github.com/containous/traefik/pull/4340) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[middleware,consul,consulcatalog,docker,ecs,k8s,marathon,mesos,rancher]** Add Pass TLS Cert Issuer and Domain Component ([#4298](https://github.com/containous/traefik/pull/4298) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[middleware]** Retry middleware : store headers per attempts and propagate them when responding. ([#4299](https://github.com/containous/traefik/pull/4299) by [jlevesy](https://github.com/jlevesy))
|
||||
- **[middleware]** Redirection status codes for methods different than GET ([#4116](https://github.com/containous/traefik/pull/4116) by [r--w](https://github.com/r--w))
|
||||
- Test and exit for jq error before domain loop ([#4347](https://github.com/containous/traefik/pull/4347) by [muhlemmer](https://github.com/muhlemmer))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** Letsencrypt - Add info on httpreq format ([#4355](https://github.com/containous/traefik/pull/4355) by [goetas](https://github.com/goetas))
|
||||
- **[docker]** Update broken link for Docker service constraints ([#4289](https://github.com/containous/traefik/pull/4289) by [clrech](https://github.com/clrech))
|
||||
- **[middleware]** Add extractorfunc values ([#4351](https://github.com/containous/traefik/pull/4351) by [hsmade](https://github.com/hsmade))
|
||||
- **[provider]** Rephrase the `traefik.backend` definition in documentation ([#4317](https://github.com/containous/traefik/pull/4317) by [dduportal](https://github.com/dduportal))
|
||||
- Harden Traefik systemd service ([#4302](https://github.com/containous/traefik/pull/4302) by [jacksgt](https://github.com/jacksgt))
|
||||
|
||||
## [v1.7.6](https://github.com/containous/traefik/tree/v1.7.6) (2018-12-07)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.5...v1.7.6)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[consulcatalog]** Fix label segmentation when using custom prefix ([#4272](https://github.com/containous/traefik/pull/4272) by [hsmade](https://github.com/hsmade))
|
||||
- Update to Go 1.11.3 [CVE-2018-16875](https://nvd.nist.gov/vuln/detail/CVE-2018-16875)
|
||||
|
||||
## [v1.7.5](https://github.com/containous/traefik/tree/v1.7.5) (2018-12-03)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.4...v1.7.5)
|
||||
|
||||
**Enhancements:**
|
||||
- **[docker]** [docker backend] - Add config flag to set refreshSeconds for swarmmode ticker ([#4105](https://github.com/containous/traefik/pull/4105) by [WTFKr0](https://github.com/WTFKr0))
|
||||
- **[k8s]** Support canary weight for external name service ([#4135](https://github.com/containous/traefik/pull/4135) by [yue9944882](https://github.com/yue9944882))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Fix ACME spec and Cloudflare. ([#4201](https://github.com/containous/traefik/pull/4201) by [ldez](https://github.com/ldez))
|
||||
- **[authentication,middleware]** Remove X-Forwarded-Uri and X-Forwarded-Method from untrusted IP ([#4036](https://github.com/containous/traefik/pull/4036) by [stffabi](https://github.com/stffabi))
|
||||
- **[authentication,middleware]** Allow usersFile comments ([#4159](https://github.com/containous/traefik/pull/4159) by [thde](https://github.com/thde))
|
||||
- **[authentication]** Fix partial declaration of authentication. ([#4212](https://github.com/containous/traefik/pull/4212) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Verify ctx when we send configuration message in docker provider ([#4185](https://github.com/containous/traefik/pull/4185) by [juliens](https://github.com/juliens))
|
||||
- **[ecs]** Filter ECS tasks by LastStatus before adding to list of service tasks ([#4255](https://github.com/containous/traefik/pull/4255) by [hwhelan-CB](https://github.com/hwhelan-CB))
|
||||
- **[healthcheck]** Query params in health check ([#4188](https://github.com/containous/traefik/pull/4188) by [mmatur](https://github.com/mmatur))
|
||||
- **[metrics]** Upgraded DD APM library ([#4189](https://github.com/containous/traefik/pull/4189) by [aantono](https://github.com/aantono))
|
||||
- **[middleware]** Fix ssl force host secure middleware ([#4138](https://github.com/containous/traefik/pull/4138) by [mmatur](https://github.com/mmatur))
|
||||
- **[oxy]** Fix unannonced trailers problem when body is empty ([#4258](https://github.com/containous/traefik/pull/4258) by [juliens](https://github.com/juliens))
|
||||
- **[provider,server]** Log configuration errors from providers and keeps listening ([#4230](https://github.com/containous/traefik/pull/4230) by [geraldcroes](https://github.com/geraldcroes))
|
||||
- **[tls]** Implement Case-insensitive SNI matching ([#4132](https://github.com/containous/traefik/pull/4132) by [dtomcej](https://github.com/dtomcej))
|
||||
- Use ParseInt instead of Atoi for parsing durations ([#4263](https://github.com/containous/traefik/pull/4263) by [mmatur](https://github.com/mmatur))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** ACME DNS provider is called `acme-dns` ([#4166](https://github.com/containous/traefik/pull/4166) by [robsdedude](https://github.com/robsdedude))
|
||||
- **[docker]** Add a "Security Consideration" section in the Docker's backend section of the documentation ([#4225](https://github.com/containous/traefik/pull/4225) by [dduportal](https://github.com/dduportal))
|
||||
- **[docker]** Clarify swarm loadbalancer documentation ([#4194](https://github.com/containous/traefik/pull/4194) by [jlevesy](https://github.com/jlevesy))
|
||||
- **[docker]** Fix spelling in comment ([#4169](https://github.com/containous/traefik/pull/4169) by [giocomai](https://github.com/giocomai))
|
||||
- **[docker]** Update swarm mode endpoint ([#4208](https://github.com/containous/traefik/pull/4208) by [siyu6974](https://github.com/siyu6974))
|
||||
- **[k8s]** Include an explicit list of kubernetes protocol annotations in docs. ([#4170](https://github.com/containous/traefik/pull/4170) by [shanna](https://github.com/shanna))
|
||||
- **[k8s]** Improve kubernetes TLS user guide ([#4175](https://github.com/containous/traefik/pull/4175) by [mterring](https://github.com/mterring))
|
||||
- **[k8s]** frame-deny should be set to true to enable the header ([#4171](https://github.com/containous/traefik/pull/4171) by [swestcott](https://github.com/swestcott))
|
||||
- **[rules]** Matcher associativity rule. ([#4244](https://github.com/containous/traefik/pull/4244) by [ldez](https://github.com/ldez))
|
||||
- Documentation: Rename "admin panel" to "dashboard ([#4156](https://github.com/containous/traefik/pull/4156) by [thernstig](https://github.com/thernstig))
|
||||
|
||||
## [v1.7.4](https://github.com/containous/traefik/tree/v1.7.4) (2018-10-30)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.3...v1.7.4)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Support custom DNS resolvers for Let's Encrypt. ([#4101](https://github.com/containous/traefik/pull/4101) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** fix: netcup and DuckDNS. ([#4094](https://github.com/containous/traefik/pull/4094) by [ldez](https://github.com/ldez))
|
||||
- **[authentication,logs,middleware]** Fix display of client username field ([#4093](https://github.com/containous/traefik/pull/4093) by [Ullaakut](https://github.com/Ullaakut))
|
||||
- **[authentication,middleware]** Nil request body with retry ([#4075](https://github.com/containous/traefik/pull/4075) by [ldez](https://github.com/ldez))
|
||||
- **[consul,consulcatalog,docker,ecs,k8s,marathon,mesos,rancher]** Add flush interval option on backend ([#4112](https://github.com/containous/traefik/pull/4112) by [juliens](https://github.com/juliens))
|
||||
- **[consulcatalog,docker,ecs,marathon,mesos,rancher]** Remove the trailing dot if the domain is not defined. ([#4095](https://github.com/containous/traefik/pull/4095) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Provider docker shutdown problem ([#4122](https://github.com/containous/traefik/pull/4122) by [juliens](https://github.com/juliens))
|
||||
- **[k8s]** Add default path if nothing present ([#4097](https://github.com/containous/traefik/pull/4097) by [SantoDE](https://github.com/SantoDE))
|
||||
- **[k8s]** Add the missing pass-client-tls annotation to the kubernetes provider ([#4118](https://github.com/containous/traefik/pull/4118) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[logs]** Fix access log field parsing ([#4113](https://github.com/containous/traefik/pull/4113) by [Ullaakut](https://github.com/Ullaakut))
|
||||
- **[middleware]** Add static redirect ([#4090](https://github.com/containous/traefik/pull/4090) by [SantoDE](https://github.com/SantoDE))
|
||||
- **[rules]** Add keepTrailingSlash option ([#4062](https://github.com/containous/traefik/pull/4062) by [juliens](https://github.com/juliens))
|
||||
- **[rules]** Case insensitive host rule ([#3931](https://github.com/containous/traefik/pull/3931) by [bgandon](https://github.com/bgandon))
|
||||
- **[tls]** Fix certificate insertion loop to keep valid certificate and ignore the bad one ([#4050](https://github.com/containous/traefik/pull/4050) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[webui]** Typo in the UI. ([#4096](https://github.com/containous/traefik/pull/4096) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** Adds the note: acme.domains is a startup configuration ([#4065](https://github.com/containous/traefik/pull/4065) by [geraldcroes](https://github.com/geraldcroes))
|
||||
- **[acme]** exoscale move from .ch to .com ([#4130](https://github.com/containous/traefik/pull/4130) by [greut](https://github.com/greut))
|
||||
- **[acme]** Fixing a typo. ([#4124](https://github.com/containous/traefik/pull/4124) by [konovalov-nk](https://github.com/konovalov-nk))
|
||||
- **[acme]** Add a note about TLS-ALPN challenge. ([#4106](https://github.com/containous/traefik/pull/4106) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Clarify DuckDNS does not support multiple TXT records ([#4061](https://github.com/containous/traefik/pull/4061) by [KnicKnic](https://github.com/KnicKnic))
|
||||
- **[docker]** Domain is also optional for "normal" mode ([#4086](https://github.com/containous/traefik/pull/4086) by [herver](https://github.com/herver))
|
||||
- **[provider]** Fix mistake in the documentation of several backends ([#4133](https://github.com/containous/traefik/pull/4133) by [whalehub](https://github.com/whalehub))
|
||||
- Replaces emilevauge/whoami by containous/whoami in the documentation ([#4111](https://github.com/containous/traefik/pull/4111) by [geraldcroes](https://github.com/geraldcroes))
|
||||
- Uses ASCII characters to spell Traefik ([#4063](https://github.com/containous/traefik/pull/4063) by [geraldcroes](https://github.com/geraldcroes))
|
||||
|
||||
**Misc:**
|
||||
- **[tls]** Add double wildcard test ([#4091](https://github.com/containous/traefik/pull/4091) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[webui]** Removed unused imports ([#4123](https://github.com/containous/traefik/pull/4123) by [mwvdev](https://github.com/mwvdev))
|
||||
|
||||
## [v1.7.3](https://github.com/containous/traefik/tree/v1.7.3) (2018-10-15)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.2...v1.7.3)
|
||||
|
||||
**Enhancements:**
|
||||
- Improve the CLI help ([#3996](https://github.com/containous/traefik/pull/3996) by [dduportal](https://github.com/dduportal))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** DNS challenge Cloudflare auth zone ([#4042](https://github.com/containous/traefik/pull/4042) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** ACME DNS challenges ([#3998](https://github.com/containous/traefik/pull/3998) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Don't initialize ACME provider if storage is empty ([#3988](https://github.com/containous/traefik/pull/3988) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Fix: acme DNS providers ([#4021](https://github.com/containous/traefik/pull/4021) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Prevent some malformed errors in LE. ([#4015](https://github.com/containous/traefik/pull/4015) by [ldez](https://github.com/ldez))
|
||||
- **[authentication,consulcatalog,docker,ecs,etcd,kv,marathon,mesos,rancher]** Add the AuthResponseHeaders to the labels ([#3973](https://github.com/containous/traefik/pull/3973) by [Crypto89](https://github.com/Crypto89))
|
||||
- **[docker]** usebindportip can fall back on the container ip / port ([#4018](https://github.com/containous/traefik/pull/4018) by [geraldcroes](https://github.com/geraldcroes))
|
||||
- **[k8s]** Avoid flapping of multiple Ingress definitions ([#3862](https://github.com/containous/traefik/pull/3862) by [rtreffer](https://github.com/rtreffer))
|
||||
- **[middleware,server]** Log stack on panic ([#4033](https://github.com/containous/traefik/pull/4033) by [ldez](https://github.com/ldez))
|
||||
- **[middleware,server]** Fix recover from panic handler ([#4031](https://github.com/containous/traefik/pull/4031) by [mmatur](https://github.com/mmatur))
|
||||
- **[server,websocket]** Fix update oxy ([#4009](https://github.com/containous/traefik/pull/4009) by [mmatur](https://github.com/mmatur))
|
||||
|
||||
**Documentation:**
|
||||
- **[docker]** Add tags label to Docker provider documentation ([#3896](https://github.com/containous/traefik/pull/3896) by [artheus](https://github.com/artheus))
|
||||
- **[docker]** Added two examples with labels in docker-compose.yml ([#3891](https://github.com/containous/traefik/pull/3891) by [pascalandy](https://github.com/pascalandy))
|
||||
- **[k8s]** Move buffering annotation documentation to service ([#3991](https://github.com/containous/traefik/pull/3991) by [ldez](https://github.com/ldez))
|
||||
- Fix a typo ([#3995](https://github.com/containous/traefik/pull/3995) by [arnydo](https://github.com/arnydo))
|
||||
|
||||
## [v1.7.2](https://github.com/containous/traefik/tree/v1.7.2) (2018-10-04)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.1...v1.7.2)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme,cluster,kv]** TLS, ACME, cluster and several entrypoints. ([#3962](https://github.com/containous/traefik/pull/3962) by [ldez](https://github.com/ldez))
|
||||
- **[cluster,kv]** Correctly initialize kv store if storage key missing ([#3958](https://github.com/containous/traefik/pull/3958) by [jfrabaute](https://github.com/jfrabaute))
|
||||
- **[cluster,kv]** Return an error if kv store CA cert is invalid ([#3956](https://github.com/containous/traefik/pull/3956) by [jfrabaute](https://github.com/jfrabaute))
|
||||
- **[file]** Do not Errorf during file watcher verification test loop. ([#3938](https://github.com/containous/traefik/pull/3938) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[k8s]** Add Template-ability check to Kubernetes API Fields ([#3964](https://github.com/containous/traefik/pull/3964) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[logs]** Colored logs on windows. ([#3966](https://github.com/containous/traefik/pull/3966) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Whitelist log for deprecated configuration. ([#3963](https://github.com/containous/traefik/pull/3963) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Trimming whitespace in XFF for IP whitelisting ([#3971](https://github.com/containous/traefik/pull/3971) by [olmoser](https://github.com/olmoser))
|
||||
- **[rules]** Rule parsing error. ([#3976](https://github.com/containous/traefik/pull/3976) by [ldez](https://github.com/ldez))
|
||||
- Global configuration log at start ([#3954](https://github.com/containous/traefik/pull/3954) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- **[logs]** Document the default accessLog format ([#3942](https://github.com/containous/traefik/pull/3942) by [dfredell](https://github.com/dfredell))
|
||||
|
||||
## [v1.7.1](https://github.com/containous/traefik/tree/v1.7.1) (2018-09-28)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.0...v1.7.1)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme,cluster]** Don't remove static certs from config when cluster mode ([#3946](https://github.com/containous/traefik/pull/3946) by [Juliens](https://github.com/Juliens))
|
||||
- **[acme]** Fix TLS ALPN cluster mode. ([#3934](https://github.com/containous/traefik/pull/3934) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Don't challenge ACME when host rule on another entry point ([#3923](https://github.com/containous/traefik/pull/3923) by [Juliens](https://github.com/Juliens))
|
||||
- **[tls]** Use the first static certificate as a fallback when no default is given ([#3948](https://github.com/containous/traefik/pull/3948) by [Juliens](https://github.com/Juliens))
|
||||
|
||||
## [v1.7.0](https://github.com/containous/traefik/tree/v1.7.0) (2018-09-24)
|
||||
[Commits](https://github.com/containous/traefik/compare/v1.7.0-rc1...v1.7.0)
|
||||
[Commits pre RC](https://github.com/containous/traefik/compare/v1.6.0-rc1...v1.7.0-rc1)
|
||||
|
||||
**Enhancements:**
|
||||
- **[acme]** Simplify get acme client ([#3499](https://github.com/containous/traefik/pull/3499) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Simplify acme e2e tests. ([#3534](https://github.com/containous/traefik/pull/3534) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Add option to select algorithm to generate ACME certificates ([#3319](https://github.com/containous/traefik/pull/3319) by [mmatur](https://github.com/mmatur))
|
||||
- **[acme]** Enable to override certificates in key-value store when using storeconfig ([#3202](https://github.com/containous/traefik/pull/3202) by [thomasjpfan](https://github.com/thomasjpfan))
|
||||
- **[acme]** ACME TLS ALPN ([#3553](https://github.com/containous/traefik/pull/3553) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Remove acme provider dependency in server ([#3225](https://github.com/containous/traefik/pull/3225) by [Juliens](https://github.com/Juliens))
|
||||
- **[acme]** Use official Pebble Image. ([#3708](https://github.com/containous/traefik/pull/3708) by [ldez](https://github.com/ldez))
|
||||
- **[api,cluster]** Improved cluster api to include the current leader node ([#3100](https://github.com/containous/traefik/pull/3100) by [aantono](https://github.com/aantono))
|
||||
- **[authentication,consul,consulcatalog,docker,ecs,kv,marathon,mesos,rancher]** Auth support in frontends ([#3559](https://github.com/containous/traefik/pull/3559) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[authentication,k8s]** Auth support in frontends for k8s and file ([#3460](https://github.com/containous/traefik/pull/3460) by [Zatte](https://github.com/Zatte))
|
||||
- **[authentication,middleware]** Add xforwarded method ([#3424](https://github.com/containous/traefik/pull/3424) by [erik-sjoestedt](https://github.com/erik-sjoestedt))
|
||||
- **[authentication,middleware]** Forward auth headers ([#3521](https://github.com/containous/traefik/pull/3521) by [hwhelan-CB](https://github.com/hwhelan-CB))
|
||||
- **[consul,etcd,tls]** Improve TLS integration tests ([#3679](https://github.com/containous/traefik/pull/3679) by [mmatur](https://github.com/mmatur))
|
||||
- **[consulcatalog,docker,ecs,file,k8s,kv,marathon,mesos,rancher]** Add SSLForceHost support. ([#3246](https://github.com/containous/traefik/pull/3246) by [ldez](https://github.com/ldez))
|
||||
- **[consulcatalog]** Multiple frontends for consulcatalog ([#3796](https://github.com/containous/traefik/pull/3796) by [hsmade](https://github.com/hsmade))
|
||||
- **[consulcatalog]** Add support for stale reads from Consul catalog ([#3523](https://github.com/containous/traefik/pull/3523) by [marenzo](https://github.com/marenzo))
|
||||
- **[docker]** Add a default value for the docker.network configuration ([#3471](https://github.com/containous/traefik/pull/3471) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[ecs]** Support for AWS ECS Fargate ([#3379](https://github.com/containous/traefik/pull/3379) by [mmatur](https://github.com/mmatur))
|
||||
- **[ecs]** Add support for ECS constraints ([#3537](https://github.com/containous/traefik/pull/3537) by [andrewstucki](https://github.com/andrewstucki))
|
||||
- **[ecs]** Add segment support for ECS ([#3817](https://github.com/containous/traefik/pull/3817) by [mmatur](https://github.com/mmatur))
|
||||
- **[ecs]** Support `traefik.backend` for ECS ([#3510](https://github.com/containous/traefik/pull/3510) by [hwhelan-CB](https://github.com/hwhelan-CB))
|
||||
- **[ecs]** Allow binding ECS container port ([#3533](https://github.com/containous/traefik/pull/3533) by [andrewstucki](https://github.com/andrewstucki))
|
||||
- **[healthcheck,consul,consulcatalog,docker,ecs,kv,marathon,mesos,rancher]** Override health check scheme ([#3315](https://github.com/containous/traefik/pull/3315) by [ldez](https://github.com/ldez))
|
||||
- **[healthcheck]** Support 3xx HTTP status codes for health check ([#3364](https://github.com/containous/traefik/pull/3364) by [SniperCZE](https://github.com/SniperCZE))
|
||||
- **[healthcheck]** Support all 2xx HTTP status code for health check. ([#3362](https://github.com/containous/traefik/pull/3362) by [ldez](https://github.com/ldez))
|
||||
- **[healthcheck]** Add HTTP headers to healthcheck. ([#3047](https://github.com/containous/traefik/pull/3047) by [zetaab](https://github.com/zetaab))
|
||||
- **[k8s]** Add more k8s tests ([#3491](https://github.com/containous/traefik/pull/3491) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Substitute hardcoded "<namespace>/<name>" with k8s ListerGetter ([#3470](https://github.com/containous/traefik/pull/3470) by [yue9944882](https://github.com/yue9944882))
|
||||
- **[k8s]** Custom frontend name for test helper ([#3444](https://github.com/containous/traefik/pull/3444) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Add annotation to allow modifiers to be used properly in kubernetes ([#3481](https://github.com/containous/traefik/pull/3481) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Create Global Backend Ingress ([#3404](https://github.com/containous/traefik/pull/3404) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Specify backend servers' weight via annotation for kubernetes ([#3112](https://github.com/containous/traefik/pull/3112) by [yue9944882](https://github.com/yue9944882))
|
||||
- **[k8s]** Support multi-port services. ([#3121](https://github.com/containous/traefik/pull/3121) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[k8s]** Mapping ExternalNames to custom ports ([#3231](https://github.com/containous/traefik/pull/3231) by [gildas](https://github.com/gildas))
|
||||
- **[k8s]** Allow any kubernetes ingressClass value ([#3516](https://github.com/containous/traefik/pull/3516) by [rtreffer](https://github.com/rtreffer))
|
||||
- **[k8s]** Enable Ingress Status updates ([#3324](https://github.com/containous/traefik/pull/3324) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Add possibility to set a protocol ([#3648](https://github.com/containous/traefik/pull/3648) by [SantoDE](https://github.com/SantoDE))
|
||||
- **[k8s]** Remove unnecessary loop ([#3799](https://github.com/containous/traefik/pull/3799) by [ZloyDyadka](https://github.com/ZloyDyadka))
|
||||
- **[kv]** Use index-based syntax in KV tests. ([#3352](https://github.com/containous/traefik/pull/3352) by [ldez](https://github.com/ldez))
|
||||
- **[logs,middleware]** Make accesslogs.logTheRoundTrip async to get lost performance ([#3152](https://github.com/containous/traefik/pull/3152) by [ryarnyah](https://github.com/ryarnyah))
|
||||
- **[logs,middleware]** Added duration filter for logs ([#3463](https://github.com/containous/traefik/pull/3463) by [rodrigodiez](https://github.com/rodrigodiez))
|
||||
- **[marathon]** Sane default and configurable Marathon request timeouts ([#3286](https://github.com/containous/traefik/pull/3286) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[marathon]** Adding compatibility for marathon 1.5 ([#3505](https://github.com/containous/traefik/pull/3505) by [TrevinTeacutter](https://github.com/TrevinTeacutter))
|
||||
- **[mesos]** Segments Labels: Mesos ([#3383](https://github.com/containous/traefik/pull/3383) by [drewkerrigan](https://github.com/drewkerrigan))
|
||||
- **[metrics]** Metrics: Add support for InfluxDB Database / RetentionPolicy and HTTP client ([#3391](https://github.com/containous/traefik/pull/3391) by [drewkerrigan](https://github.com/drewkerrigan))
|
||||
- **[middleware,consulcatalog,docker,ecs,kv,marathon,mesos,rancher]** Pass the TLS Cert infos in headers ([#3826](https://github.com/containous/traefik/pull/3826) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[middleware,server]** Extreme Makeover: server refactoring ([#3461](https://github.com/containous/traefik/pull/3461) by [ldez](https://github.com/ldez))
|
||||
- **[middleware,tracing]** Added integration support for DataDog APM Tracing ([#3517](https://github.com/containous/traefik/pull/3517) by [aantono](https://github.com/aantono))
|
||||
- **[middleware,tracing]** Create a custom logger for jaeger ([#3541](https://github.com/containous/traefik/pull/3541) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware]** Performance enhancements for the rules matchers. ([#3563](https://github.com/containous/traefik/pull/3563) by [ShaneSaww](https://github.com/ShaneSaww))
|
||||
- **[middleware]** Extract internal router creation from server ([#3204](https://github.com/containous/traefik/pull/3204) by [Juliens](https://github.com/Juliens))
|
||||
- **[rules]** CNAME flattening ([#3403](https://github.com/containous/traefik/pull/3403) by [gamalan](https://github.com/gamalan))
|
||||
- **[servicefabric]** Add HTTP headers to healthcheck. ([#3205](https://github.com/containous/traefik/pull/3205) by [ldez](https://github.com/ldez))
|
||||
- **[tls]** Support TLS MinVersion and CipherSuite as CLI option. ([#3107](https://github.com/containous/traefik/pull/3107) by [ldez](https://github.com/ldez))
|
||||
- **[tls]** Improve TLS Handshake ([#3512](https://github.com/containous/traefik/pull/3512) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[webui]** Add some missing elements in the WebUI ([#3327](https://github.com/containous/traefik/pull/3327) by [ldez](https://github.com/ldez))
|
||||
- Call functions to enable block/mutex pprof profiles. ([#3564](https://github.com/containous/traefik/pull/3564) by [timoreimann](https://github.com/timoreimann))
|
||||
- Minor changes ([#3554](https://github.com/containous/traefik/pull/3554) by [ldez](https://github.com/ldez))
|
||||
- Generated assets file are only mandatory in main ([#3386](https://github.com/containous/traefik/pull/3386) by [Juliens](https://github.com/Juliens))
|
||||
- h2c server ([#3387](https://github.com/containous/traefik/pull/3387) by [Juliens](https://github.com/Juliens))
|
||||
- Fix backend reuse ([#3312](https://github.com/containous/traefik/pull/3312) by [arnested](https://github.com/arnested))
|
||||
- Upgrade GRPC dependencies ([#3342](https://github.com/containous/traefik/pull/3342) by [gottwald](https://github.com/gottwald))
|
||||
- Implement h2c with backend ([#3371](https://github.com/containous/traefik/pull/3371) by [Juliens](https://github.com/Juliens))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme,cluster]** StoreConfig always initializes the account if it is missing ([#3844](https://github.com/containous/traefik/pull/3844) by [geraldcroes](https://github.com/geraldcroes))
|
||||
- **[acme,provider]** Create init method on provider interface ([#3580](https://github.com/containous/traefik/pull/3580) by [Juliens](https://github.com/Juliens))
|
||||
- **[acme]** Does not generate ACME certificate if domain is checked by dynamic certificate ([#3238](https://github.com/containous/traefik/pull/3238) by [Juliens](https://github.com/Juliens))
|
||||
- **[acme]** Ensure only certificates from ACME enabled entrypoint are used ([#3880](https://github.com/containous/traefik/pull/3880) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[acme]** Fix acme account deletion without provider change ([#3664](https://github.com/containous/traefik/pull/3664) by [zyclonite](https://github.com/zyclonite))
|
||||
- **[acme]** Fix some DNS providers issues ([#3915](https://github.com/containous/traefik/pull/3915) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Fix LEGO update ([#3895](https://github.com/containous/traefik/pull/3895) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Set a keyType to ACME if the account is stored with no KeyType ([#3733](https://github.com/containous/traefik/pull/3733) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Fix ACME certificate for wildcard and root domains ([#3675](https://github.com/containous/traefik/pull/3675) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Update lego ([#3659](https://github.com/containous/traefik/pull/3659) by [mmatur](https://github.com/mmatur))
|
||||
- **[acme]** Bump LEGO version ([#3888](https://github.com/containous/traefik/pull/3888) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Serve TLS-Challenge certificate in first ([#3605](https://github.com/containous/traefik/pull/3605) by [nmengin](https://github.com/nmengin))
|
||||
- **[api,authentication,webui]** Auth section in web UI. ([#3628](https://github.com/containous/traefik/pull/3628) by [ldez](https://github.com/ldez))
|
||||
- **[api]** Remove TLS in API ([#3665](https://github.com/containous/traefik/pull/3665) by [mmatur](https://github.com/mmatur))
|
||||
- **[authentication,consulcatalog,docker,ecs,k8s,kv,marathon,mesos,rancher]** Auth Forward with certificates in templates. ([#3804](https://github.com/containous/traefik/pull/3804) by [ldez](https://github.com/ldez))
|
||||
- **[authentication,middleware,provider]** Don't pass the Authorization header to the backends ([#3606](https://github.com/containous/traefik/pull/3606) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[authentication,middleware]** Do not copy hop-by-hop headers to forward auth request ([#3907](https://github.com/containous/traefik/pull/3907) by [stffabi](https://github.com/stffabi))
|
||||
- **[authentication,middleware]** Remove hop-by-hop headers from forward auth response ([#3900](https://github.com/containous/traefik/pull/3900) by [stffabi](https://github.com/stffabi))
|
||||
- **[docker]** Uses both binded HostIP and HostPort when useBindPortIP=true ([#3638](https://github.com/containous/traefik/pull/3638) by [geraldcroes](https://github.com/geraldcroes))
|
||||
- **[ecs]** Fix 400 bad request on AWS ECS API ([#3629](https://github.com/containous/traefik/pull/3629) by [mmatur](https://github.com/mmatur))
|
||||
- **[k8s]** Fix Rewrite-target regex ([#3699](https://github.com/containous/traefik/pull/3699) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Don't merge kubernetes ingresses when priority is set ([#3743](https://github.com/containous/traefik/pull/3743) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Prevent unparsable strings from being rendered in the Kubernetes template ([#3753](https://github.com/containous/traefik/pull/3753) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Correct App-Root kubernetes behavior ([#3592](https://github.com/containous/traefik/pull/3592) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Add more K8s Unit Tests ([#3583](https://github.com/containous/traefik/pull/3583) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Fix rewrite-target Annotation behavior ([#3582](https://github.com/containous/traefik/pull/3582) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Fix panic setting ingress status ([#3492](https://github.com/containous/traefik/pull/3492) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[kv]** KV and authentication ([#3615](https://github.com/containous/traefik/pull/3615) by [ldez](https://github.com/ldez))
|
||||
- **[kv]** Add missing quotes around backendName in kv template ([#3885](https://github.com/containous/traefik/pull/3885) by [NatMarchand](https://github.com/NatMarchand))
|
||||
- **[kv]** Include missing key in error message for KV store ([#3779](https://github.com/containous/traefik/pull/3779) by [camelpunch](https://github.com/camelpunch))
|
||||
- **[logs]** Add logs when error is generated in error handler ([#3571](https://github.com/containous/traefik/pull/3571) by [Juliens](https://github.com/Juliens))
|
||||
- **[logs]** Add interface to Træfik logger ([#3889](https://github.com/containous/traefik/pull/3889) by [nmengin](https://github.com/nmengin))
|
||||
- **[metrics]** Avoid a panic during Prometheus registering ([#3717](https://github.com/containous/traefik/pull/3717) by [nmengin](https://github.com/nmengin))
|
||||
- **[middleware,tracing]** Fix tracing duplicated headers ([#3878](https://github.com/containous/traefik/pull/3878) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware,websocket]** Enable retry on websocket ([#3825](https://github.com/containous/traefik/pull/3825) by [Juliens](https://github.com/Juliens))
|
||||
- **[middleware]** Avoid retries when any data was written to the backend ([#3285](https://github.com/containous/traefik/pull/3285) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[middleware]** Extend https redirection tests, and fix incorrect behavior ([#3742](https://github.com/containous/traefik/pull/3742) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[middleware]** Send 'Retry-After' to comply with RFC6585. ([#3593](https://github.com/containous/traefik/pull/3593) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Correct Entrypoint Redirect with Stripped or Added Path ([#3631](https://github.com/containous/traefik/pull/3631) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[middleware]** Fix error pages ([#3894](https://github.com/containous/traefik/pull/3894) by [Juliens](https://github.com/Juliens))
|
||||
- **[oxy]** Handle Te header when http2 ([#3824](https://github.com/containous/traefik/pull/3824) by [Juliens](https://github.com/Juliens))
|
||||
- **[server]** Avoid goroutine leak in server ([#3851](https://github.com/containous/traefik/pull/3851) by [nmengin](https://github.com/nmengin))
|
||||
- **[server]** Avoid panic during stop ([#3898](https://github.com/containous/traefik/pull/3898) by [nmengin](https://github.com/nmengin))
|
||||
- **[tracing]** Added default configuration for DataDog APM Tracer ([#3655](https://github.com/containous/traefik/pull/3655) by [aantono](https://github.com/aantono))
|
||||
- **[tracing]** Added support for Trace name truncation for traces ([#3689](https://github.com/containous/traefik/pull/3689) by [aantono](https://github.com/aantono))
|
||||
- **[websocket]** Handle shutdown of Hijacked connections ([#3636](https://github.com/containous/traefik/pull/3636) by [Juliens](https://github.com/Juliens))
|
||||
- **[webui]** Added Dashboard table item for Rate Limits ([#3893](https://github.com/containous/traefik/pull/3893) by [codecyclist](https://github.com/codecyclist))
|
||||
- Fix logger in Oxy ([#3913](https://github.com/containous/traefik/pull/3913) by [ldez](https://github.com/ldez))
|
||||
- H2C: Remove buggy line in init to make verbose switch working ([#3701](https://github.com/containous/traefik/pull/3701) by [dduportal](https://github.com/dduportal))
|
||||
- Updating oxy dependency ([#3700](https://github.com/containous/traefik/pull/3700) by [crholm](https://github.com/crholm))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** Update ACME documentation about TLS-ALPN challenge ([#3756](https://github.com/containous/traefik/pull/3756) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Fix some DNS provider link ([#3639](https://github.com/containous/traefik/pull/3639) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Fix documentation for route53 acme provider ([#3811](https://github.com/containous/traefik/pull/3811) by [A-Shleifman](https://github.com/A-Shleifman))
|
||||
- **[acme]** Update Namecheap status ([#3604](https://github.com/containous/traefik/pull/3604) by [stoinov](https://github.com/stoinov))
|
||||
- **[docker]** Fix style in examples/quickstart ([#3705](https://github.com/containous/traefik/pull/3705) by [korigod](https://github.com/korigod))
|
||||
- **[docker]** Change syntax in quick start guide ([#3726](https://github.com/containous/traefik/pull/3726) by [trotro](https://github.com/trotro))
|
||||
- **[docker]** Typo in docker-and-lets-encrypt.md ([#3724](https://github.com/containous/traefik/pull/3724) by [A-Shleifman](https://github.com/A-Shleifman))
|
||||
- **[docker]** Improve the wording in the documentation for Docker and fix title for Docker User Guide ([#3797](https://github.com/containous/traefik/pull/3797) by [dduportal](https://github.com/dduportal))
|
||||
- **[k8s]** Add a k8s guide section on traffic splitting via service weights. ([#3556](https://github.com/containous/traefik/pull/3556) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[k8s]** Change code block of traefik-web-ui to match file ([#3542](https://github.com/containous/traefik/pull/3542) by [drewgwallace](https://github.com/drewgwallace))
|
||||
- **[k8s]** Fix typo which breaks k8s example manifest ([#3441](https://github.com/containous/traefik/pull/3441) by [GeertJohan](https://github.com/GeertJohan))
|
||||
- **[k8s]** Correct Modifier in Kubernetes Documentation ([#3610](https://github.com/containous/traefik/pull/3610) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Improve Connection Limit Kubernetes Documentation ([#3711](https://github.com/containous/traefik/pull/3711) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Add traefik prefix to k8s annotations ([#3682](https://github.com/containous/traefik/pull/3682) by [zifeo](https://github.com/zifeo))
|
||||
- **[k8s]** Update kubernetes docs to reflect https options ([#3807](https://github.com/containous/traefik/pull/3807) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Update kubernetes.md ([#3719](https://github.com/containous/traefik/pull/3719) by [kmaris](https://github.com/kmaris))
|
||||
- **[metrics]** Adding grafana dashboards based on prometheus metrics ([#3393](https://github.com/containous/traefik/pull/3393) by [deimosfr](https://github.com/deimosfr))
|
||||
- **[middleware,tracing]** Fix missing tracing backend in documentation ([#3706](https://github.com/containous/traefik/pull/3706) by [mmatur](https://github.com/mmatur))
|
||||
- **[provider]** Typo in auth labels. ([#3730](https://github.com/containous/traefik/pull/3730) by [ldez](https://github.com/ldez))
|
||||
- **[servicefabric]** Fix Service Fabric docs to use v1.6 labels ([#3209](https://github.com/containous/traefik/pull/3209) by [jjcollinge](https://github.com/jjcollinge))
|
||||
- **[tracing]** Simple documentation grammar update in tracing ([#3720](https://github.com/containous/traefik/pull/3720) by [loadstar81](https://github.com/loadstar81))
|
||||
- Replace unrendered emoji ([#3690](https://github.com/containous/traefik/pull/3690) by [korigod](https://github.com/korigod))
|
||||
- Make the "base domain" on all providers ([#3835](https://github.com/containous/traefik/pull/3835) by [dduportal](https://github.com/dduportal))
|
||||
- Prepare release v1.7.0-rc5 ([#3902](https://github.com/containous/traefik/pull/3902) by [dduportal](https://github.com/dduportal))
|
||||
- Prepare release v1.7.0-rc3 ([#3709](https://github.com/containous/traefik/pull/3709) by [mmatur](https://github.com/mmatur))
|
||||
- Prepare release v1.7.0-rc4 ([#3864](https://github.com/containous/traefik/pull/3864) by [Juliens](https://github.com/Juliens))
|
||||
- Prepare release v1.7.0-rc2 ([#3632](https://github.com/containous/traefik/pull/3632) by [nmengin](https://github.com/nmengin))
|
||||
- Prepare release v1.7.0-rc1 ([#3578](https://github.com/containous/traefik/pull/3578) by [mmatur](https://github.com/mmatur))
|
||||
|
||||
**Misc:**
|
||||
- **[webui]** Removed non-applicable default tests and fixed custom tests ([#3908](https://github.com/containous/traefik/pull/3908) by [codecyclist](https://github.com/codecyclist))
|
||||
- Merge v1.6.6 into v1.7 ([#3802](https://github.com/containous/traefik/pull/3802) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.5 into v1.7 ([#3595](https://github.com/containous/traefik/pull/3595) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.4 into master ([#3502](https://github.com/containous/traefik/pull/3502) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.3 into master ([#3439](https://github.com/containous/traefik/pull/3439) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.2 into master ([#3367](https://github.com/containous/traefik/pull/3367) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.1 into master ([#3326](https://github.com/containous/traefik/pull/3326) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.0 into master ([#3253](https://github.com/containous/traefik/pull/3253) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.0-rc6 into master ([#3203](https://github.com/containous/traefik/pull/3203) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.0-rc5 into master ([#3180](https://github.com/containous/traefik/pull/3180) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.0-rc4 into master ([#3129](https://github.com/containous/traefik/pull/3129) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v1.7.0-rc5](https://github.com/containous/traefik/tree/v1.7.0-rc5) (2018-09-18)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.0-rc4...v1.7.0-rc5)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Ensure only certificates from ACME enabled entrypoint are used ([#3880](https://github.com/containous/traefik/pull/3880) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[acme]** Fix LEGO update ([#3895](https://github.com/containous/traefik/pull/3895) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Bump LEGO version ([#3888](https://github.com/containous/traefik/pull/3888) by [ldez](https://github.com/ldez))
|
||||
- **[authentication,middleware]** Remove hop-by-hop headers from forward auth response ([#3900](https://github.com/containous/traefik/pull/3900) by [stffabi](https://github.com/stffabi))
|
||||
- **[kv]** Add missing quotes around backendName in kv template ([#3885](https://github.com/containous/traefik/pull/3885) by [NatMarchand](https://github.com/NatMarchand))
|
||||
- **[logs]** Add interface to Træfik logger ([#3889](https://github.com/containous/traefik/pull/3889) by [nmengin](https://github.com/nmengin))
|
||||
- **[middleware,tracing]** Fix tracing duplicated headers ([#3878](https://github.com/containous/traefik/pull/3878) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware]** Fix error pages ([#3894](https://github.com/containous/traefik/pull/3894) by [Juliens](https://github.com/Juliens))
|
||||
- **[server]** Avoid panic during stop ([#3898](https://github.com/containous/traefik/pull/3898) by [nmengin](https://github.com/nmengin))
|
||||
|
||||
## [v1.7.0-rc4](https://github.com/containous/traefik/tree/v1.7.0-rc4) (2018-09-07)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.0-rc3...v1.7.0-rc4)
|
||||
|
||||
**Enhancements:**
|
||||
- **[acme]** Use official Pebble Image. ([#3708](https://github.com/containous/traefik/pull/3708) by [ldez](https://github.com/ldez))
|
||||
- **[consulcatalog]** Multiple frontends for consulcatalog ([#3796](https://github.com/containous/traefik/pull/3796) by [hsmade](https://github.com/hsmade))
|
||||
- **[ecs]** Add segment support for ECS ([#3817](https://github.com/containous/traefik/pull/3817) by [mmatur](https://github.com/mmatur))
|
||||
- **[k8s]** Remove unnecessary loop ([#3799](https://github.com/containous/traefik/pull/3799) by [ZloyDyadka](https://github.com/ZloyDyadka))
|
||||
- **[middleware,consulcatalog,docker,ecs,kv,marathon,mesos,rancher]** Pass the TLS Cert infos in headers ([#3826](https://github.com/containous/traefik/pull/3826) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme,cluster]** StoreConfig always initializes the account if it is missing ([#3844](https://github.com/containous/traefik/pull/3844) by [geraldcroes](https://github.com/geraldcroes))
|
||||
- **[acme]** Set a keyType to ACME if the account is stored with no KeyType ([#3733](https://github.com/containous/traefik/pull/3733) by [nmengin](https://github.com/nmengin))
|
||||
- **[authentication,consulcatalog,docker,ecs,k8s,kv,marathon,mesos,rancher]** Auth Forward with certificates in templates. ([#3804](https://github.com/containous/traefik/pull/3804) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Prevent unparsable strings from being rendered in the Kubernetes template ([#3753](https://github.com/containous/traefik/pull/3753) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Don't merge kubernetes ingresses when priority is set ([#3743](https://github.com/containous/traefik/pull/3743) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[kv]** Include missing key in error message for KV store ([#3779](https://github.com/containous/traefik/pull/3779) by [camelpunch](https://github.com/camelpunch))
|
||||
- **[metrics]** Avoid a panic during Prometheus registering ([#3717](https://github.com/containous/traefik/pull/3717) by [nmengin](https://github.com/nmengin))
|
||||
- **[middleware,websocket]** Enable retry on websocket ([#3825](https://github.com/containous/traefik/pull/3825) by [Juliens](https://github.com/Juliens))
|
||||
- **[middleware]** Extend https redirection tests, and fix incorrect behavior ([#3742](https://github.com/containous/traefik/pull/3742) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[oxy]** Handle Te header when http2 ([#3824](https://github.com/containous/traefik/pull/3824) by [Juliens](https://github.com/Juliens))
|
||||
- **[server]** Avoid goroutine leak in server ([#3851](https://github.com/containous/traefik/pull/3851) by [nmengin](https://github.com/nmengin))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** Fix documentation for route53 acme provider ([#3811](https://github.com/containous/traefik/pull/3811) by [A-Shleifman](https://github.com/A-Shleifman))
|
||||
- **[acme]** Update ACME documentation about TLS-ALPN challenge ([#3756](https://github.com/containous/traefik/pull/3756) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Change syntax in quick start guide ([#3726](https://github.com/containous/traefik/pull/3726) by [trotro](https://github.com/trotro))
|
||||
- **[docker]** Improve the wording in the documentation for Docker and fix title for Docker User Guide ([#3797](https://github.com/containous/traefik/pull/3797) by [dduportal](https://github.com/dduportal))
|
||||
- **[docker]** Typo in docker-and-lets-encrypt.md ([#3724](https://github.com/containous/traefik/pull/3724) by [A-Shleifman](https://github.com/A-Shleifman))
|
||||
- **[k8s]** Update kubernetes docs to reflect https options ([#3807](https://github.com/containous/traefik/pull/3807) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Update kubernetes.md ([#3719](https://github.com/containous/traefik/pull/3719) by [kmaris](https://github.com/kmaris))
|
||||
- **[k8s]** Improve Connection Limit Kubernetes Documentation ([#3711](https://github.com/containous/traefik/pull/3711) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[provider]** Typo in auth labels. ([#3730](https://github.com/containous/traefik/pull/3730) by [ldez](https://github.com/ldez))
|
||||
- **[tracing]** Simple documentation grammar update in tracing ([#3720](https://github.com/containous/traefik/pull/3720) by [loadstar81](https://github.com/loadstar81))
|
||||
- Make the "base domain" on all providers ([#3835](https://github.com/containous/traefik/pull/3835) by [dduportal](https://github.com/dduportal))
|
||||
|
||||
**Misc:**
|
||||
- Merge v1.6.6 into v1.7 ([#3802](https://github.com/containous/traefik/pull/3802) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v1.6.6](https://github.com/containous/traefik/tree/v1.6.6) (2018-08-20)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.6.5...v1.6.6)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Avoid duplicated ACME resolution ([#3751](https://github.com/containous/traefik/pull/3751) by [nmengin](https://github.com/nmengin))
|
||||
- **[api]** Remove TLS in API ([#3788](https://github.com/containous/traefik/pull/3788) by [Juliens](https://github.com/Juliens))
|
||||
- **[cluster]** Remove unusable `--cluster` flag ([#3616](https://github.com/containous/traefik/pull/3616) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[ecs]** Fix bad condition in ECS provider ([#3609](https://github.com/containous/traefik/pull/3609) by [mmatur](https://github.com/mmatur))
|
||||
- Set keepalive on TCP socket so idleTimeout works ([#3740](https://github.com/containous/traefik/pull/3740) by [ajardan](https://github.com/ajardan))
|
||||
|
||||
**Documentation:**
|
||||
- A tiny rewording on the documentation API's page ([#3794](https://github.com/containous/traefik/pull/3794) by [dduportal](https://github.com/dduportal))
|
||||
- Adding warnings and solution about the configuration exposure ([#3790](https://github.com/containous/traefik/pull/3790) by [dduportal](https://github.com/dduportal))
|
||||
- Fix path to the debug pprof API ([#3608](https://github.com/containous/traefik/pull/3608) by [multani](https://github.com/multani))
|
||||
|
||||
**Misc:**
|
||||
- **[oxy,websocket]** Update oxy dependency ([#3777](https://github.com/containous/traefik/pull/3777) by [Juliens](https://github.com/Juliens))
|
||||
|
||||
## [v1.7.0-rc3](https://github.com/containous/traefik/tree/v1.7.0-rc3) (2018-08-01)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.0-rc2...v1.7.0-rc3)
|
||||
|
||||
**Enhancements:**
|
||||
- **[consul,etcd,tls]** Improve TLS integration tests ([#3679](https://github.com/containous/traefik/pull/3679) by [mmatur](https://github.com/mmatur))
|
||||
- **[k8s]** Add possibility to set a protocol ([#3648](https://github.com/containous/traefik/pull/3648) by [SantoDE](https://github.com/SantoDE))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Fix acme account deletion without provider change ([#3664](https://github.com/containous/traefik/pull/3664) by [zyclonite](https://github.com/zyclonite))
|
||||
- **[acme]** Update lego ([#3659](https://github.com/containous/traefik/pull/3659) by [mmatur](https://github.com/mmatur))
|
||||
- **[acme]** Fix ACME certificate for wildcard and root domains ([#3675](https://github.com/containous/traefik/pull/3675) by [nmengin](https://github.com/nmengin))
|
||||
- **[api]** Remove TLS in API ([#3665](https://github.com/containous/traefik/pull/3665) by [mmatur](https://github.com/mmatur))
|
||||
- **[docker]** Uses both binded HostIP and HostPort when useBindPortIP=true ([#3638](https://github.com/containous/traefik/pull/3638) by [geraldcroes](https://github.com/geraldcroes))
|
||||
- **[k8s]** Fix Rewrite-target regex ([#3699](https://github.com/containous/traefik/pull/3699) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[middleware]** Correct Entrypoint Redirect with Stripped or Added Path ([#3631](https://github.com/containous/traefik/pull/3631) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[tracing]** Added default configuration for DataDog APM Tracer ([#3655](https://github.com/containous/traefik/pull/3655) by [aantono](https://github.com/aantono))
|
||||
- **[tracing]** Added support for Trace name truncation for traces ([#3689](https://github.com/containous/traefik/pull/3689) by [aantono](https://github.com/aantono))
|
||||
- **[websocket]** Handle shutdown of Hijacked connections ([#3636](https://github.com/containous/traefik/pull/3636) by [Juliens](https://github.com/Juliens))
|
||||
- H2C: Remove buggy line in init to make verbose switch working ([#3701](https://github.com/containous/traefik/pull/3701) by [dduportal](https://github.com/dduportal))
|
||||
- Updating oxy dependency ([#3700](https://github.com/containous/traefik/pull/3700) by [crholm](https://github.com/crholm))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** Update Namecheap status ([#3604](https://github.com/containous/traefik/pull/3604) by [stoinov](https://github.com/stoinov))
|
||||
- **[acme]** Fix some DNS provider link ([#3639](https://github.com/containous/traefik/pull/3639) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Fix style in examples/quickstart ([#3705](https://github.com/containous/traefik/pull/3705) by [korigod](https://github.com/korigod))
|
||||
- **[k8s]** Add traefik prefix to k8s annotations ([#3682](https://github.com/containous/traefik/pull/3682) by [zifeo](https://github.com/zifeo))
|
||||
- **[middleware,tracing]** Fix missing tracing backend in documentation ([#3706](https://github.com/containous/traefik/pull/3706) by [mmatur](https://github.com/mmatur))
|
||||
- Replace unrendered emoji ([#3690](https://github.com/containous/traefik/pull/3690) by [korigod](https://github.com/korigod))
|
||||
|
||||
## [v1.7.0-rc2](https://github.com/containous/traefik/tree/v1.7.0-rc2) (2018-07-17)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.0-rc1...v1.7.0-rc2)
|
||||
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme,provider]** Create init method on provider interface ([#3580](https://github.com/containous/traefik/pull/3580) by [Juliens](https://github.com/Juliens))
|
||||
- **[acme]** Serve TLS-Challenge certificate in first ([#3605](https://github.com/containous/traefik/pull/3605) by [nmengin](https://github.com/nmengin))
|
||||
- **[api,authentication,webui]** Auth section in web UI. ([#3628](https://github.com/containous/traefik/pull/3628) by [ldez](https://github.com/ldez))
|
||||
- **[authentication,middleware,provider]** Don't pass the Authorization header to the backends ([#3606](https://github.com/containous/traefik/pull/3606) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[ecs]** Fix 400 bad request on AWS ECS API ([#3629](https://github.com/containous/traefik/pull/3629) by [mmatur](https://github.com/mmatur))
|
||||
- **[k8s]** Fix rewrite-target Annotation behavior ([#3582](https://github.com/containous/traefik/pull/3582) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Correct App-Root kubernetes behavior ([#3592](https://github.com/containous/traefik/pull/3592) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Add more K8s Unit Tests ([#3583](https://github.com/containous/traefik/pull/3583) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[kv]** KV and authentication ([#3615](https://github.com/containous/traefik/pull/3615) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Send 'Retry-After' to comply with RFC6585. ([#3593](https://github.com/containous/traefik/pull/3593) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- **[k8s]** Correct Modifier in Kubernetes Documentation ([#3610](https://github.com/containous/traefik/pull/3610) by [dtomcej](https://github.com/dtomcej))
|
||||
|
||||
**Misc:**
|
||||
- Merge v1.6.5 into v1.7 ([#3595](https://github.com/containous/traefik/pull/3595) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v1.6.5](https://github.com/containous/traefik/tree/v1.6.5) (2018-07-09)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.6.4...v1.6.5)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Add a mutex on local store for HTTPChallenges ([#3579](https://github.com/containous/traefik/pull/3579) by [Juliens](https://github.com/Juliens))
|
||||
- **[consulcatalog]** Split the error handling from Consul Catalog (deadlock) ([#3560](https://github.com/containous/traefik/pull/3560) by [ortz](https://github.com/ortz))
|
||||
- **[docker]** segment labels: multiple frontends for one backend. ([#3511](https://github.com/containous/traefik/pull/3511) by [ldez](https://github.com/ldez))
|
||||
- **[kv]** Better support on same prefix at the same level in the KV ([#3532](https://github.com/containous/traefik/pull/3532) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[logs]** Add logs when error is generated in error handler ([#3567](https://github.com/containous/traefik/pull/3567) by [Juliens](https://github.com/Juliens))
|
||||
- **[middleware]** Create middleware to be able to handle HTTP pipelining correctly ([#3513](https://github.com/containous/traefik/pull/3513) by [mmatur](https://github.com/mmatur))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** The gandiv5 provider works with wildcard ([#3506](https://github.com/containous/traefik/pull/3506) by [manu5801](https://github.com/manu5801))
|
||||
- **[kv]** Update keyFile first/last line comment in kv-config.md ([#3558](https://github.com/containous/traefik/pull/3558) by [madnight](https://github.com/madnight))
|
||||
- Minor formatting issue in user-guide ([#3546](https://github.com/containous/traefik/pull/3546) by [Vanuan](https://github.com/Vanuan))
|
||||
|
||||
## [v1.7.0-rc1](https://github.com/containous/traefik/tree/v1.7.0-rc1) (2018-07-09)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.6.0-rc1...v1.7.0-rc1)
|
||||
|
||||
**Enhancements:**
|
||||
- **[acme]** Simplify get acme client ([#3499](https://github.com/containous/traefik/pull/3499) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Simplify acme e2e tests. ([#3534](https://github.com/containous/traefik/pull/3534) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Add option to select algorithm to generate ACME certificates ([#3319](https://github.com/containous/traefik/pull/3319) by [mmatur](https://github.com/mmatur))
|
||||
- **[acme]** Enable to override certificates in key-value store when using storeconfig ([#3202](https://github.com/containous/traefik/pull/3202) by [thomasjpfan](https://github.com/thomasjpfan))
|
||||
- **[acme]** ACME TLS ALPN ([#3553](https://github.com/containous/traefik/pull/3553) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Remove acme provider dependency in server ([#3225](https://github.com/containous/traefik/pull/3225) by [Juliens](https://github.com/Juliens))
|
||||
- **[api,cluster]** Improved cluster api to include the current leader node ([#3100](https://github.com/containous/traefik/pull/3100) by [aantono](https://github.com/aantono))
|
||||
- **[authentication,k8s]** Auth support in frontends for k8s and file ([#3460](https://github.com/containous/traefik/pull/3460) by [Zatte](https://github.com/Zatte))
|
||||
- **[authentication,middleware]** Add xforwarded method ([#3424](https://github.com/containous/traefik/pull/3424) by [erik-sjoestedt](https://github.com/erik-sjoestedt))
|
||||
- **[authentication,middleware]** Forward auth headers ([#3521](https://github.com/containous/traefik/pull/3521) by [hwhelan-CB](https://github.com/hwhelan-CB))
|
||||
- **[consul,consulcatalog,docker,ecs,kv,marathon,mesos,rancher]** Auth support in frontends ([#3559](https://github.com/containous/traefik/pull/3559) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[consulcatalog,docker,ecs,file,k8s,kv,marathon,mesos,rancher]** Add SSLForceHost support. ([#3246](https://github.com/containous/traefik/pull/3246) by [ldez](https://github.com/ldez))
|
||||
- **[consulcatalog]** Add support for stale reads from Consul catalog ([#3523](https://github.com/containous/traefik/pull/3523) by [marenzo](https://github.com/marenzo))
|
||||
- **[docker]** Add a default value for the docker.network configuration ([#3471](https://github.com/containous/traefik/pull/3471) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[ecs]** Support for AWS ECS Fargate ([#3379](https://github.com/containous/traefik/pull/3379) by [mmatur](https://github.com/mmatur))
|
||||
- **[ecs]** Add support for ECS constraints ([#3537](https://github.com/containous/traefik/pull/3537) by [andrewstucki](https://github.com/andrewstucki))
|
||||
- **[ecs]** Support `traefik.backend` for ECS ([#3510](https://github.com/containous/traefik/pull/3510) by [hwhelan-CB](https://github.com/hwhelan-CB))
|
||||
- **[ecs]** Allow binding ECS container port ([#3533](https://github.com/containous/traefik/pull/3533) by [andrewstucki](https://github.com/andrewstucki))
|
||||
- **[healthcheck,consul,consulcatalog,docker,ecs,kv,marathon,mesos,rancher]** Override health check scheme ([#3315](https://github.com/containous/traefik/pull/3315) by [ldez](https://github.com/ldez))
|
||||
- **[healthcheck]** Support 3xx HTTP status codes for health check ([#3364](https://github.com/containous/traefik/pull/3364) by [SniperCZE](https://github.com/SniperCZE))
|
||||
- **[healthcheck]** Support all 2xx HTTP status code for health check. ([#3362](https://github.com/containous/traefik/pull/3362) by [ldez](https://github.com/ldez))
|
||||
- **[healthcheck]** Add HTTP headers to healthcheck. ([#3047](https://github.com/containous/traefik/pull/3047) by [zetaab](https://github.com/zetaab))
|
||||
- **[k8s]** Add more k8s tests ([#3491](https://github.com/containous/traefik/pull/3491) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Substitute hardcoded "<namespace>/<name>" with k8s ListerGetter ([#3470](https://github.com/containous/traefik/pull/3470) by [yue9944882](https://github.com/yue9944882))
|
||||
- **[k8s]** Custom frontend name for test helper ([#3444](https://github.com/containous/traefik/pull/3444) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Add annotation to allow modifiers to be used properly in kubernetes ([#3481](https://github.com/containous/traefik/pull/3481) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Create Global Backend Ingress ([#3404](https://github.com/containous/traefik/pull/3404) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Specify backend servers' weight via annotation for kubernetes ([#3112](https://github.com/containous/traefik/pull/3112) by [yue9944882](https://github.com/yue9944882))
|
||||
- **[k8s]** Support multi-port services. ([#3121](https://github.com/containous/traefik/pull/3121) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[k8s]** Mapping ExternalNames to custom ports ([#3231](https://github.com/containous/traefik/pull/3231) by [gildas](https://github.com/gildas))
|
||||
- **[k8s]** Allow any kubernetes ingressClass value ([#3516](https://github.com/containous/traefik/pull/3516) by [rtreffer](https://github.com/rtreffer))
|
||||
- **[k8s]** Enable Ingress Status updates ([#3324](https://github.com/containous/traefik/pull/3324) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[kv]** Use index-based syntax in KV tests. ([#3352](https://github.com/containous/traefik/pull/3352) by [ldez](https://github.com/ldez))
|
||||
- **[logs,middleware]** Make accesslogs.logTheRoundTrip async to get lost performance ([#3152](https://github.com/containous/traefik/pull/3152) by [ryarnyah](https://github.com/ryarnyah))
|
||||
- **[logs,middleware]** Added duration filter for logs ([#3463](https://github.com/containous/traefik/pull/3463) by [rodrigodiez](https://github.com/rodrigodiez))
|
||||
- **[marathon]** Adding compatibility for marathon 1.5 ([#3505](https://github.com/containous/traefik/pull/3505) by [TrevinTeacutter](https://github.com/TrevinTeacutter))
|
||||
- **[marathon]** Sane default and configurable Marathon request timeouts ([#3286](https://github.com/containous/traefik/pull/3286) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[mesos]** Segments Labels: Mesos ([#3383](https://github.com/containous/traefik/pull/3383) by [drewkerrigan](https://github.com/drewkerrigan))
|
||||
- **[metrics]** Metrics: Add support for InfluxDB Database / RetentionPolicy and HTTP client ([#3391](https://github.com/containous/traefik/pull/3391) by [drewkerrigan](https://github.com/drewkerrigan))
|
||||
- **[middleware,server]** Extreme Makeover: server refactoring ([#3461](https://github.com/containous/traefik/pull/3461) by [ldez](https://github.com/ldez))
|
||||
- **[middleware,tracing]** Added integration support for DataDog APM Tracing ([#3517](https://github.com/containous/traefik/pull/3517) by [aantono](https://github.com/aantono))
|
||||
- **[middleware,tracing]** Create a custom logger for jaeger ([#3541](https://github.com/containous/traefik/pull/3541) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware]** Performance enhancements for the rules matchers. ([#3563](https://github.com/containous/traefik/pull/3563) by [ShaneSaww](https://github.com/ShaneSaww))
|
||||
- **[middleware]** Extract internal router creation from server ([#3204](https://github.com/containous/traefik/pull/3204) by [Juliens](https://github.com/Juliens))
|
||||
- **[rules]** CNAME flattening ([#3403](https://github.com/containous/traefik/pull/3403) by [gamalan](https://github.com/gamalan))
|
||||
- **[servicefabric]** Add white list for Service Fabric ([#3079](https://github.com/containous/traefik/pull/3079) by [ldez](https://github.com/ldez))
|
||||
- **[servicefabric]** Add HTTP headers to healthcheck. ([#3205](https://github.com/containous/traefik/pull/3205) by [ldez](https://github.com/ldez))
|
||||
- **[tls]** Improve TLS Handshake ([#3512](https://github.com/containous/traefik/pull/3512) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[tls]** Support TLS MinVersion and CipherSuite as CLI option. ([#3107](https://github.com/containous/traefik/pull/3107) by [ldez](https://github.com/ldez))
|
||||
- **[webui]** Add some missing elements in the WebUI ([#3327](https://github.com/containous/traefik/pull/3327) by [ldez](https://github.com/ldez))
|
||||
- Minor changes ([#3554](https://github.com/containous/traefik/pull/3554) by [ldez](https://github.com/ldez))
|
||||
- h2c server ([#3387](https://github.com/containous/traefik/pull/3387) by [Juliens](https://github.com/Juliens))
|
||||
- Fix backend reuse ([#3312](https://github.com/containous/traefik/pull/3312) by [arnested](https://github.com/arnested))
|
||||
- Call functions to enable block/mutex pprof profiles. ([#3564](https://github.com/containous/traefik/pull/3564) by [timoreimann](https://github.com/timoreimann))
|
||||
- Implement h2c with backend ([#3371](https://github.com/containous/traefik/pull/3371) by [Juliens](https://github.com/Juliens))
|
||||
- Upgrade GRPC dependencies ([#3342](https://github.com/containous/traefik/pull/3342) by [gottwald](https://github.com/gottwald))
|
||||
- Generated assets file are only mandatory in main ([#3386](https://github.com/containous/traefik/pull/3386) by [Juliens](https://github.com/Juliens))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Does not generate ACME certificate if domain is checked by dynamic certificate ([#3238](https://github.com/containous/traefik/pull/3238) by [Juliens](https://github.com/Juliens))
|
||||
- **[k8s]** Fix panic setting ingress status ([#3492](https://github.com/containous/traefik/pull/3492) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[logs]** Add logs when error is generated in error handler ([#3571](https://github.com/containous/traefik/pull/3571) by [Juliens](https://github.com/Juliens))
|
||||
- **[middleware]** Avoid retries when any data was written to the backend ([#3285](https://github.com/containous/traefik/pull/3285) by [marco-jantke](https://github.com/marco-jantke))
|
||||
|
||||
**Documentation:**
|
||||
- **[k8s]** Add a k8s guide section on traffic splitting via service weights. ([#3556](https://github.com/containous/traefik/pull/3556) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[k8s]** Change code block of traefik-web-ui to match file ([#3542](https://github.com/containous/traefik/pull/3542) by [drewgwallace](https://github.com/drewgwallace))
|
||||
- **[k8s]** Fix typo which breaks k8s example manifest ([#3441](https://github.com/containous/traefik/pull/3441) by [GeertJohan](https://github.com/GeertJohan))
|
||||
- **[metrics]** Adding grafana dashboards based on prometheus metrics ([#3393](https://github.com/containous/traefik/pull/3393) by [deimosfr](https://github.com/deimosfr))
|
||||
- **[servicefabric]** Fix Service Fabric docs to use v1.6 labels ([#3209](https://github.com/containous/traefik/pull/3209) by [jjcollinge](https://github.com/jjcollinge))
|
||||
|
||||
**Misc:**
|
||||
- Merge v1.6.4 into master ([#3502](https://github.com/containous/traefik/pull/3502) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.3 into master ([#3439](https://github.com/containous/traefik/pull/3439) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.2 into master ([#3367](https://github.com/containous/traefik/pull/3367) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.1 into master ([#3326](https://github.com/containous/traefik/pull/3326) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.0 into master ([#3253](https://github.com/containous/traefik/pull/3253) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.0-rc6 into master ([#3203](https://github.com/containous/traefik/pull/3203) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.0-rc5 into master ([#3180](https://github.com/containous/traefik/pull/3180) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.0-rc4 into master ([#3129](https://github.com/containous/traefik/pull/3129) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v1.6.4](https://github.com/containous/traefik/tree/v1.6.4) (2018-06-15)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.6.3...v1.6.4)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Use logrus writer instead of os.Stderr ([#3498](https://github.com/containous/traefik/pull/3498) by [ldez](https://github.com/ldez))
|
||||
- **[consulcatalog]** Enclose IPv6 addresses in "[]" ([#3477](https://github.com/containous/traefik/pull/3477) by [herver](https://github.com/herver))
|
||||
- **[docker,ecs,marathon,mesos,rancher]** Use net.JoinHostPort for servers URL ([#3484](https://github.com/containous/traefik/pull/3484) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Backend name with docker-compose and segments. ([#3485](https://github.com/containous/traefik/pull/3485) by [ldez](https://github.com/ldez))
|
||||
- **[oxy]** Handle buffer pool for oxy ([#3450](https://github.com/containous/traefik/pull/3450) by [Juliens](https://github.com/Juliens))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** The exoscale provider works with wildcard ([#3479](https://github.com/containous/traefik/pull/3479) by [greut](https://github.com/greut))
|
||||
- **[consul,docker]** Edit wording ([#3438](https://github.com/containous/traefik/pull/3438) by [mayank23](https://github.com/mayank23))
|
||||
- **[k8s]** Add missing annotation documentation. ([#3454](https://github.com/containous/traefik/pull/3454) by [ldez](https://github.com/ldez))
|
||||
- **[kv]** Fix typo in kv user guide ([#3474](https://github.com/containous/traefik/pull/3474) by [shambarick](https://github.com/shambarick))
|
||||
- Clean metrics documentation. ([#3488](https://github.com/containous/traefik/pull/3488) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v1.6.3](https://github.com/containous/traefik/tree/v1.6.3) (2018-06-05)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.6.2...v1.6.3)
|
||||
|
||||
|
||||
@@ -2,17 +2,11 @@
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||
nationality, personal appearance, race, religion, or sexual identity and
|
||||
orientation.
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience,nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
@@ -22,53 +16,36 @@ include:
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
|
||||
Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
|
||||
Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at contact@containo.us
|
||||
All complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at contact@containo.us
|
||||
All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances.
|
||||
The project team is obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at [http://contributor-covenant.org/version/1/4][version]
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
259
CONTRIBUTING.md
@@ -1,260 +1,3 @@
|
||||
# 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 `dep` dependency management tool is 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.10-alpine
|
||||
---> 8c6473912976
|
||||
Step 1 : RUN go get github.com/golang/dep/cmd/dep
|
||||
[...]
|
||||
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/containous/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 dependency management
|
||||
|
||||
[dep](https://github.com/golang/dep) is not required for building; however, it is necessary to modify dependencies (i.e., add, update, or remove third-party packages)
|
||||
|
||||
You need to use [dep](https://github.com/golang/dep) >= O.4.1.
|
||||
|
||||
If you want to add a dependency, use `dep ensure -add` to have [dep](https://github.com/golang/dep) put it into the vendor folder and update the dep manifest/lock files (`Gopkg.toml` and `Gopkg.lock`, respectively).
|
||||
|
||||
A following `make dep-prune` run should be triggered to trim down the size of the vendor folder.
|
||||
The final result must be committed into VCS.
|
||||
|
||||
Here's a full example using dep to add a new dependency:
|
||||
|
||||
```bash
|
||||
# install the new main dependency github.com/foo/bar and minimize vendor size
|
||||
$ dep ensure -add 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/).
|
||||
See <https://docs.traefik.io/v2.0/contributing/thank-you/>.
|
||||
|
||||
@@ -2,4 +2,5 @@ FROM scratch
|
||||
COPY script/ca-certificates.crt /etc/ssl/certs/
|
||||
COPY dist/traefik /
|
||||
EXPOSE 80
|
||||
VOLUME ["/tmp"]
|
||||
ENTRYPOINT ["/traefik"]
|
||||
|
||||
1489
Gopkg.lock
generated
158
Gopkg.toml
@@ -19,17 +19,34 @@
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/ArthurHlt/go-eureka-client"
|
||||
required = [
|
||||
"k8s.io/code-generator/cmd/client-gen",
|
||||
"k8s.io/code-generator/cmd/deepcopy-gen",
|
||||
"k8s.io/code-generator/cmd/defaulter-gen",
|
||||
"k8s.io/code-generator/cmd/lister-gen",
|
||||
"k8s.io/code-generator/cmd/informer-gen",
|
||||
]
|
||||
|
||||
[prune]
|
||||
non-go = true
|
||||
go-tests = true
|
||||
unused-packages = true
|
||||
[[prune.project]]
|
||||
name = "k8s.io/code-generator"
|
||||
non-go = false
|
||||
unused-packages = false
|
||||
|
||||
#[[constraint]]
|
||||
# branch = "master"
|
||||
# name = "github.com/ArthurHlt/go-eureka-client"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/BurntSushi/toml"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/BurntSushi/ty"
|
||||
#[[constraint]]
|
||||
# branch = "master"
|
||||
# name = "github.com/BurntSushi/ty"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
@@ -44,38 +61,35 @@
|
||||
branch = "master"
|
||||
name = "github.com/armon/go-proxyproto"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/aws/aws-sdk-go"
|
||||
version = "1.13.1"
|
||||
#[[constraint]]
|
||||
# name = "github.com/aws/aws-sdk-go"
|
||||
# version = "1.13.11"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/cenk/backoff"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/containous/flaeg"
|
||||
version = "1.0.1"
|
||||
name = "github.com/cenkalti/backoff"
|
||||
version = "2.1.1"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/containous/mux"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/containous/staert"
|
||||
version = "3.1.0"
|
||||
branch = "containous-fork"
|
||||
name = "github.com/containous/alice"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/containous/traefik-extra-service-fabric"
|
||||
version = "1.1.5"
|
||||
#[[constraint]]
|
||||
# name = "github.com/thoas/stats"
|
||||
# # related to https://github.com/thoas/stats/pull/32
|
||||
# revision = "4975baf6a358ed3ddaa42133996e1959f96c9300"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/coreos/go-systemd"
|
||||
version = "14.0.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/docker/leadership"
|
||||
source = "github.com/containous/leadership"
|
||||
#[[constraint]]
|
||||
# branch = "master"
|
||||
# name = "github.com/docker/leadership"
|
||||
# source = "github.com/containous/leadership"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/eapache/channels"
|
||||
@@ -103,37 +117,25 @@
|
||||
branch = "master"
|
||||
name = "github.com/gorilla/websocket"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/hashicorp/consul"
|
||||
version = "1.0.6"
|
||||
#[[constraint]]
|
||||
# name = "github.com/hashicorp/consul"
|
||||
# version = "1.0.6"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/influxdata/influxdb"
|
||||
version = "1.3.7"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/jjcollinge/servicefabric"
|
||||
#[[constraint]]
|
||||
# branch = "master"
|
||||
# name = "github.com/jjcollinge/servicefabric"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/abronan/valkeyrie"
|
||||
#[[constraint]]
|
||||
# branch = "master"
|
||||
# name = "github.com/abronan/valkeyrie"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/mesosphere/mesos-dns"
|
||||
source = "https://github.com/containous/mesos-dns.git"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/mitchellh/copystructure"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/mitchellh/hashstructure"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/mitchellh/mapstructure"
|
||||
#[[constraint]]
|
||||
# name = "github.com/mesosphere/mesos-dns"
|
||||
# source = "https://github.com/containous/mesos-dns.git"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/opentracing/opentracing-go"
|
||||
@@ -149,8 +151,8 @@
|
||||
name = "github.com/ryanuber/go-glob"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/satori/go.uuid"
|
||||
version = "1.1.0"
|
||||
name = "github.com/Masterminds/sprig"
|
||||
version = "2.19.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
@@ -162,11 +164,13 @@
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/uber/jaeger-client-go"
|
||||
version = "2.9.0"
|
||||
source = "github.com/jaegertracing/jaeger-client-go"
|
||||
version = "2.16.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/uber/jaeger-lib"
|
||||
version = "1.1.0"
|
||||
source = "github.com/jaegertracing/jaeger-lib"
|
||||
version = "2.0.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "v1"
|
||||
@@ -181,13 +185,20 @@
|
||||
name = "github.com/vulcand/oxy"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/xenolf/lego"
|
||||
# version = "1.0.0"
|
||||
name = "github.com/go-acme/lego"
|
||||
version = "2.6.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "google.golang.org/grpc"
|
||||
version = "1.5.2"
|
||||
version = "1.13.0"
|
||||
|
||||
[[override]]
|
||||
name = "golang.org/x/sys"
|
||||
revision = "1c9583448a9c3aa0f9a6a5241bf73c0bd8aafded"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/golang/protobuf"
|
||||
version = "v1.3.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "gopkg.in/fsnotify.v1"
|
||||
@@ -196,15 +207,23 @@
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/client-go"
|
||||
version = "6.0.0"
|
||||
version = "8.0.0" # 8.0.0
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/code-generator"
|
||||
version = "kubernetes-1.11.7"
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/api"
|
||||
version = "kubernetes-1.9.0"
|
||||
version = "kubernetes-1.11.7" # "kubernetes-1.11.7"
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/apimachinery"
|
||||
version = "kubernetes-1.9.0"
|
||||
version = "kubernetes-1.11.7" # "kubernetes-1.11.7"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/json-iterator/go"
|
||||
version = "1.1.6"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
@@ -249,10 +268,21 @@
|
||||
revision = "7e6055773c5137efbeb3bd2410d705fe10ab6bfd"
|
||||
|
||||
[[override]]
|
||||
branch = "master"
|
||||
version = "v1.1.1"
|
||||
name = "github.com/miekg/dns"
|
||||
|
||||
[prune]
|
||||
non-go = true
|
||||
go-tests = true
|
||||
unused-packages = true
|
||||
[[constraint]]
|
||||
name = "github.com/patrickmn/go-cache"
|
||||
version = "2.1.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "gopkg.in/DataDog/dd-trace-go.v1"
|
||||
version = "1.13.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/instana/go-sensor"
|
||||
version = "1.4.12"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/ExpediaDotCom/haystack-client-go"
|
||||
version = "0.2.3"
|
||||
|
||||
154
MAINTAINER.md
@@ -1,154 +0,0 @@
|
||||
# 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]_
|
||||
236
Makefile
@@ -1,4 +1,22 @@
|
||||
.PHONY: all
|
||||
.PHONY: all docs docs-serve
|
||||
|
||||
SRCS = $(shell git ls-files '*.go' | grep -v '^vendor/')
|
||||
|
||||
TAG_NAME := $(shell git tag -l --contains HEAD)
|
||||
SHA := $(shell git rev-parse HEAD)
|
||||
VERSION_GIT := $(if $(TAG_NAME),$(TAG_NAME),$(SHA))
|
||||
VERSION := $(if $(VERSION),$(VERSION),$(VERSION_GIT))
|
||||
|
||||
BIND_DIR := "dist"
|
||||
|
||||
GIT_BRANCH := $(subst heads/,,$(shell git rev-parse --abbrev-ref HEAD 2>/dev/null))
|
||||
TRAEFIK_DEV_IMAGE := traefik-dev$(if $(GIT_BRANCH),:$(subst /,-,$(GIT_BRANCH)))
|
||||
|
||||
REPONAME := $(shell echo $(REPO) | tr '[:upper:]' '[:lower:]')
|
||||
TRAEFIK_IMAGE := $(if $(REPONAME),$(REPONAME),"containous/traefik")
|
||||
|
||||
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")
|
||||
DOCKER_BUILD_ARGS := $(if $(DOCKER_VERSION), "--build-arg=DOCKER_VERSION=$(DOCKER_VERSION)",)
|
||||
|
||||
TRAEFIK_ENVS := \
|
||||
-e OS_ARCH_ARG \
|
||||
@@ -11,128 +29,132 @@ TRAEFIK_ENVS := \
|
||||
-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/')
|
||||
|
||||
BIND_DIR := "dist"
|
||||
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))
|
||||
TRAEFIK_DEV_IMAGE := traefik-dev$(if $(GIT_BRANCH),:$(subst /,-,$(GIT_BRANCH)))
|
||||
REPONAME := $(shell echo $(REPO) | tr '[:upper:]' '[:lower:]')
|
||||
TRAEFIK_IMAGE := $(if $(REPONAME),$(REPONAME),"containous/traefik")
|
||||
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_RUN_OPTS := $(TRAEFIK_ENVS) $(TRAEFIK_MOUNT) "$(TRAEFIK_DEV_IMAGE)"
|
||||
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_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 $*=$($*)
|
||||
PRE_TARGET ?= build-dev-image
|
||||
|
||||
default: binary
|
||||
|
||||
all: generate-webui build ## validate all checks, build linux binary, run all tests\ncross non-linux binaries
|
||||
$(DOCKER_RUN_TRAEFIK) ./script/make.sh
|
||||
|
||||
binary: generate-webui build ## build the linux binary
|
||||
$(DOCKER_RUN_TRAEFIK) ./script/make.sh generate binary
|
||||
|
||||
crossbinary: generate-webui build ## cross build the non-linux binaries
|
||||
$(DOCKER_RUN_TRAEFIK) ./script/make.sh generate crossbinary
|
||||
|
||||
crossbinary-parallel:
|
||||
$(MAKE) generate-webui
|
||||
$(MAKE) build crossbinary-default crossbinary-others
|
||||
|
||||
crossbinary-default: generate-webui build
|
||||
$(DOCKER_RUN_TRAEFIK_NOTTY) ./script/make.sh generate crossbinary-default
|
||||
|
||||
crossbinary-default-parallel:
|
||||
$(MAKE) generate-webui
|
||||
$(MAKE) build crossbinary-default
|
||||
|
||||
crossbinary-others: generate-webui build
|
||||
$(DOCKER_RUN_TRAEFIK_NOTTY) ./script/make.sh generate crossbinary-others
|
||||
|
||||
crossbinary-others-parallel:
|
||||
$(MAKE) generate-webui
|
||||
$(MAKE) build crossbinary-others
|
||||
|
||||
test: build ## run the unit and integration tests
|
||||
$(DOCKER_RUN_TRAEFIK) ./script/make.sh generate test-unit binary test-integration
|
||||
|
||||
test-unit: build ## run the unit tests
|
||||
$(DOCKER_RUN_TRAEFIK) ./script/make.sh generate test-unit
|
||||
|
||||
test-integration: build ## run the integration tests
|
||||
$(DOCKER_RUN_TRAEFIK) ./script/make.sh generate binary test-integration
|
||||
TEST_HOST=1 ./script/make.sh test-integration
|
||||
|
||||
validate: build ## validate code, vendor and autogen
|
||||
$(DOCKER_RUN_TRAEFIK) ./script/make.sh validate-gofmt validate-govet validate-golint validate-misspell validate-vendor validate-autogen
|
||||
|
||||
build: dist
|
||||
## Build Dev Docker image
|
||||
build-dev-image: dist
|
||||
docker build $(DOCKER_BUILD_ARGS) -t "$(TRAEFIK_DEV_IMAGE)" -f build.Dockerfile .
|
||||
|
||||
build-webui:
|
||||
docker build -t traefik-webui -f webui/Dockerfile webui
|
||||
|
||||
build-no-cache: dist
|
||||
## Build Dev Docker image without cache
|
||||
build-dev-image-no-cache: dist
|
||||
docker build --no-cache -t "$(TRAEFIK_DEV_IMAGE)" -f build.Dockerfile .
|
||||
|
||||
shell: build ## start a shell inside the build env
|
||||
$(DOCKER_RUN_TRAEFIK) /bin/bash
|
||||
|
||||
image-dirty: binary ## build a docker 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
|
||||
|
||||
## Create the "dist" directory
|
||||
dist:
|
||||
mkdir dist
|
||||
|
||||
## Build WebUI Docker image
|
||||
build-webui-image:
|
||||
docker build -t traefik-webui -f webui/Dockerfile webui
|
||||
|
||||
## Generate WebUI
|
||||
generate-webui: build-webui-image
|
||||
if [ ! -d "static" ]; then \
|
||||
mkdir -p static; \
|
||||
docker run --rm -v "$$PWD/static":'/src/static' traefik-webui npm run build; \
|
||||
docker run --rm -v "$$PWD/static":'/src/static' traefik-webui chown -R $(shell id -u):$(shell id -g) ../static; \
|
||||
echo 'For more informations show `webui/readme.md`' > $$PWD/static/DONT-EDIT-FILES-IN-THIS-DIRECTORY.md; \
|
||||
fi
|
||||
|
||||
## Build the linux binary
|
||||
binary: generate-webui $(PRE_TARGET)
|
||||
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate binary
|
||||
|
||||
## Build the binary for the standard plaforms (linux, darwin, windows)
|
||||
crossbinary-default: generate-webui build-dev-image
|
||||
$(DOCKER_RUN_TRAEFIK_NOTTY) ./script/make.sh generate crossbinary-default
|
||||
|
||||
## Build the binary for the standard plaforms (linux, darwin, windows) in parallel
|
||||
crossbinary-default-parallel:
|
||||
$(MAKE) generate-webui
|
||||
$(MAKE) build-dev-image crossbinary-default
|
||||
|
||||
## Run the unit and integration tests
|
||||
test: build-dev-image
|
||||
$(DOCKER_RUN_TRAEFIK) ./script/make.sh generate test-unit binary test-integration
|
||||
|
||||
## Run the unit tests
|
||||
test-unit: $(PRE_TARGET)
|
||||
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate test-unit
|
||||
|
||||
## Pull all images for integration tests
|
||||
pull-images:
|
||||
grep --no-filename -E '^\s+image:' ./integration/resources/compose/*.yml | awk '{print $$2}' | sort | uniq | xargs -P 6 -n 1 docker pull
|
||||
|
||||
## Run the integration tests
|
||||
test-integration: $(PRE_TARGET)
|
||||
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK),TEST_CONTAINER=1) ./script/make.sh generate binary test-integration
|
||||
TEST_HOST=1 ./script/make.sh test-integration
|
||||
|
||||
## Validate code and docs
|
||||
validate-files: $(PRE_TARGET)
|
||||
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate validate-lint validate-misspell
|
||||
bash $(CURDIR)/script/validate-shell-script.sh
|
||||
|
||||
## Validate code, docs, and vendor
|
||||
validate: $(PRE_TARGET)
|
||||
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate validate-lint validate-misspell validate-vendor
|
||||
bash $(CURDIR)/script/validate-shell-script.sh
|
||||
|
||||
## Clean up static directory and build a Docker Traefik image
|
||||
build-image: binary
|
||||
rm -rf static
|
||||
docker build -t $(TRAEFIK_IMAGE) .
|
||||
|
||||
## Build a Docker Traefik image
|
||||
build-image-dirty: binary
|
||||
docker build -t $(TRAEFIK_IMAGE) .
|
||||
|
||||
## Start a shell inside the build env
|
||||
shell: build-dev-image
|
||||
$(DOCKER_RUN_TRAEFIK) /bin/bash
|
||||
|
||||
## Build documentation site
|
||||
docs:
|
||||
make -C ./docs docs
|
||||
|
||||
## Serve the documentation site localy
|
||||
docs-serve:
|
||||
make -C ./docs docs-serve
|
||||
|
||||
## Generate CRD clientset
|
||||
generate-crd:
|
||||
./script/update-generated-crd-code.sh
|
||||
|
||||
## Download dependencies
|
||||
dep-ensure:
|
||||
dep ensure -v
|
||||
./script/prune-dep.sh
|
||||
|
||||
## Clean vendor directory
|
||||
dep-prune:
|
||||
./script/prune-dep.sh
|
||||
|
||||
## Create packages for the release
|
||||
release-packages: generate-webui build-dev-image
|
||||
rm -rf dist
|
||||
$(DOCKER_RUN_TRAEFIK_NOTTY) goreleaser release --skip-publish
|
||||
$(DOCKER_RUN_TRAEFIK_NOTTY) tar cfz dist/traefik-${VERSION}.src.tar.gz \
|
||||
--exclude-vcs \
|
||||
--exclude .idea \
|
||||
--exclude .travis \
|
||||
--exclude .semaphoreci \
|
||||
--exclude .github \
|
||||
--exclude dist .
|
||||
$(DOCKER_RUN_TRAEFIK_NOTTY) chown -R $(shell id -u):$(shell id -g) dist/
|
||||
|
||||
## Format the Code
|
||||
fmt:
|
||||
gofmt -s -l -w $(SRCS)
|
||||
|
||||
run-dev:
|
||||
go generate
|
||||
go build ./cmd/traefik
|
||||
./traefik
|
||||
|
||||
generate-webui: build-webui
|
||||
if [ ! -d "static" ]; then \
|
||||
mkdir -p static; \
|
||||
docker run --rm -v "$$PWD/static":'/src/static' traefik-webui npm run build; \
|
||||
echo 'For more informations show `webui/readme.md`' > $$PWD/static/DONT-EDIT-FILES-IN-THIS-DIRECTORY.md; \
|
||||
fi
|
||||
|
||||
lint:
|
||||
script/validate-golint
|
||||
|
||||
fmt:
|
||||
gofmt -s -l -w $(SRCS)
|
||||
|
||||
pull-images:
|
||||
grep --no-filename -E '^\s+image:' ./integration/resources/compose/*.yml | awk '{print $$2}' | sort | uniq | xargs -P 6 -n 1 docker pull
|
||||
|
||||
dep-ensure:
|
||||
dep ensure -v
|
||||
./script/prune-dep.sh
|
||||
|
||||
dep-prune:
|
||||
./script/prune-dep.sh
|
||||
|
||||
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)
|
||||
|
||||
69
README.md
@@ -1,6 +1,6 @@
|
||||
|
||||
<p align="center">
|
||||
<img src="docs/img/traefik.logo.png" alt="Træfik" title="Træfik" />
|
||||
<img src="docs/content/assets/img/traefik.logo.png" alt="Traefik" title="Traefik" />
|
||||
</p>
|
||||
|
||||
[](https://semaphoreci.com/containous/traefik)
|
||||
@@ -8,13 +8,13 @@
|
||||
[](http://goreportcard.com/report/containous/traefik)
|
||||
[](https://microbadger.com/images/traefik)
|
||||
[](https://github.com/containous/traefik/blob/master/LICENSE.md)
|
||||
[](https://traefik.herokuapp.com)
|
||||
[](https://twitter.com/intent/follow?screen_name=traefikproxy)
|
||||
[](https://slack.traefik.io)
|
||||
[](https://twitter.com/intent/follow?screen_name=traefik)
|
||||
|
||||
|
||||
Træfik is a modern HTTP reverse proxy and load balancer that makes deploying microservices easy.
|
||||
Træfik integrates with your existing infrastructure components ([Docker](https://www.docker.com/), [Swarm mode](https://docs.docker.com/engine/swarm/), [Kubernetes](https://kubernetes.io), [Marathon](https://mesosphere.github.io/marathon/), [Consul](https://www.consul.io/), [Etcd](https://coreos.com/etcd/), [Rancher](https://rancher.com), [Amazon ECS](https://aws.amazon.com/ecs), ...) and configures itself automatically and dynamically.
|
||||
Pointing Træfik at your orchestrator should be the _only_ configuration step you need.
|
||||
Traefik (pronounced _traffic_) is a modern HTTP reverse proxy and load balancer that makes deploying microservices easy.
|
||||
Traefik integrates with your existing infrastructure components ([Docker](https://www.docker.com/), [Swarm mode](https://docs.docker.com/engine/swarm/), [Kubernetes](https://kubernetes.io), [Marathon](https://mesosphere.github.io/marathon/), [Consul](https://www.consul.io/), [Etcd](https://coreos.com/etcd/), [Rancher](https://rancher.com), [Amazon ECS](https://aws.amazon.com/ecs), ...) and configures itself automatically and dynamically.
|
||||
Pointing Traefik at your orchestrator should be the _only_ configuration step you need.
|
||||
|
||||
---
|
||||
|
||||
@@ -23,18 +23,18 @@ Pointing Træfik at your orchestrator should be the _only_ configuration step yo
|
||||
**[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)** .
|
||||
|
||||
---
|
||||
|
||||
:warning: Please be aware that the old configurations for Traefik v1.X are NOT compatible with the v2.X config as of now. If you're testing out v2, please ensure you are using a [v2 configuration](https://docs.traefik.io/v2.0/).
|
||||
|
||||
## Overview
|
||||
|
||||
Imagine that you have deployed a bunch of microservices with the help of an orchestrator (like Swarm or Kubernetes) or a service registry (like etcd or consul).
|
||||
@@ -43,14 +43,14 @@ Now you want users to access these microservices, and you need a reverse proxy.
|
||||
Traditional reverse-proxies require that you configure _each_ route that will connect paths and subdomains to _each_ microservice.
|
||||
In an environment where you add, remove, kill, upgrade, or scale your services _many_ times a day, the task of keeping the routes up to date becomes tedious.
|
||||
|
||||
**This is when Træfik can help you!**
|
||||
**This is when Traefik can help you!**
|
||||
|
||||
Træfik listens to your service registry/orchestrator API and instantly generates the routes so your microservices are connected to the outside world -- without further intervention from your part.
|
||||
Traefik listens to your service registry/orchestrator API and instantly generates the routes so your microservices are connected to the outside world -- without further intervention from your part.
|
||||
|
||||
**Run Træfik and let it do the work for you!**
|
||||
_(But if you'd rather configure some of your routes manually, Træfik supports that too!)_
|
||||
**Run Traefik and let it do the work for you!**
|
||||
_(But if you'd rather configure some of your routes manually, Traefik supports that too!)_
|
||||
|
||||

|
||||

|
||||
|
||||
## Features
|
||||
|
||||
@@ -58,12 +58,11 @@ _(But if you'd rather configure some of your routes manually, Træfik supports t
|
||||
- Supports multiple load balancing algorithms
|
||||
- Provides HTTPS to your microservices by leveraging [Let's Encrypt](https://letsencrypt.org) (wildcard certificates support)
|
||||
- Circuit breakers, retry
|
||||
- High Availability with cluster mode (beta)
|
||||
- See the magic through its clean web UI
|
||||
- Websocket, HTTP/2, GRPC ready
|
||||
- Provides metrics (Rest, Prometheus, Datadog, Statsd, InfluxDB)
|
||||
- Keeps access logs (JSON, CLF)
|
||||
- [Fast](https://docs.traefik.io/benchmarks) ... which is nice
|
||||
- Fast
|
||||
- Exposes a Rest API
|
||||
- Packaged as a single binary file (made with :heart: with go) and available as a [tiny](https://microbadger.com/images/traefik) [official](https://hub.docker.com/r/_/traefik/) docker image
|
||||
|
||||
@@ -85,28 +84,26 @@ _(But if you'd rather configure some of your routes manually, Træfik supports t
|
||||
|
||||
## Quickstart
|
||||
|
||||
To get your hands on Træfik, you can use the [5-Minute Quickstart](http://docs.traefik.io/#the-trfik-quickstart-using-docker) in our documentation (you will need Docker).
|
||||
|
||||
Alternatively, if you don't want to install anything on your computer, you can try Træfik online in this great [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.
|
||||
To get your hands on Traefik, you can use the [5-Minute Quickstart](http://docs.traefik.io/#the-traefik-quickstart-using-docker) in our documentation (you will need Docker).
|
||||
|
||||
## Web UI
|
||||
|
||||
You can access the simple HTML frontend of Træfik.
|
||||
You can access the simple HTML frontend of Traefik.
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
## Documentation
|
||||
|
||||
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).
|
||||
A collection of contributions around Traefik can be found at [https://awesome.traefik.io](https://awesome.traefik.io).
|
||||
|
||||
:warning: If you're testing out v2, please ensure you are using the [v2 documentation](https://docs.traefik.io/v2.0/).
|
||||
|
||||
## Support
|
||||
|
||||
To get community support, you can:
|
||||
- join the Træfik community Slack channel: [](https://traefik.herokuapp.com)
|
||||
- join the Traefik community Slack channel: [](https://slack.traefik.io)
|
||||
- use [Stack Overflow](https://stackoverflow.com/questions/tagged/traefik) (using the `traefik` tag)
|
||||
|
||||
If you need commercial support, please contact [Containo.us](https://containo.us) by mail: <mailto:support@containo.us>.
|
||||
@@ -133,19 +130,21 @@ git clone https://github.com/containous/traefik
|
||||
|
||||
## Introductory Videos
|
||||
|
||||
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.
|
||||
:warning: Please be aware that these videos are for v1.X. The old configurations for Traefik v1.X are NOT compatible with Traefik v2. If you're testing out v2, please ensure you are using a [v2 configuration](https://docs.traefik.io/v2.0/).
|
||||
|
||||
Here is a talk given by [Emile Vauge](https://github.com/emilevauge) at GopherCon 2017.
|
||||
You will learn Traefik 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 Traefik features and see some demos with Kubernetes.
|
||||
|
||||
[](https://www.youtube.com/watch?v=aFtpIShV60I)
|
||||
|
||||
## Maintainers
|
||||
|
||||
[Information about process and maintainers](MAINTAINER.md)
|
||||
[Information about process and maintainers](docs/content/contributing/maintainers.md)
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -164,18 +163,16 @@ Each version is supported until the next one is released (e.g. 1.1.x will be sup
|
||||
|
||||
We use [Semantic Versioning](http://semver.org/)
|
||||
|
||||
## Plumbing
|
||||
## Mailing lists
|
||||
|
||||
- [Oxy](https://github.com/vulcand/oxy): an awesome proxy library made by Mailgun folks
|
||||
- [Gorilla mux](https://github.com/gorilla/mux): famous request router
|
||||
- [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
|
||||
- General announcements, new releases: mail at news+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/news)
|
||||
- Security announcements: mail at security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
|
||||
|
||||
## Credits
|
||||
|
||||
Kudos to [Peka](http://peka.byethost11.com/photoblog/) for his awesome work on the logo .
|
||||
Kudos to [Peka](http://peka.byethost11.com/photoblog/) for his awesome work on the logo .
|
||||
|
||||
Traefik's logo is licensed under the Creative Commons 3.0 Attributions license.
|
||||
|
||||
Traefik's logo was inspired by the gopher stickers made by Takuya Ueda (https://twitter.com/tenntenn).
|
||||
The original Go gopher was designed by Renee French (http://reneefrench.blogspot.com/).
|
||||
The original Go gopher was designed by Renee French (http://reneefrench.blogspot.com/).
|
||||
|
||||
329
acme/account.go
@@ -1,329 +0,0 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
acmeprovider "github.com/containous/traefik/provider/acme"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/xenolf/lego/acme"
|
||||
)
|
||||
|
||||
// Account is used to store lets encrypt registration info
|
||||
type Account struct {
|
||||
Email string
|
||||
Registration *acme.RegistrationResource
|
||||
PrivateKey []byte
|
||||
DomainsCertificate DomainsCertificates
|
||||
ChallengeCerts map[string]*ChallengeCert
|
||||
HTTPChallenge map[string]map[string][]byte
|
||||
}
|
||||
|
||||
// ChallengeCert stores a challenge certificate
|
||||
type ChallengeCert struct {
|
||||
Certificate []byte
|
||||
PrivateKey []byte
|
||||
certificate *tls.Certificate
|
||||
}
|
||||
|
||||
// Init account struct
|
||||
func (a *Account) Init() error {
|
||||
err := a.DomainsCertificate.Init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = a.RemoveAccountV1Values()
|
||||
if err != nil {
|
||||
log.Errorf("Unable to remove ACME Account V1 values during account initialization: %v", err)
|
||||
}
|
||||
|
||||
for _, cert := range a.ChallengeCerts {
|
||||
if cert.certificate == nil {
|
||||
certificate, err := tls.X509KeyPair(cert.Certificate, cert.PrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cert.certificate = &certificate
|
||||
}
|
||||
|
||||
if cert.certificate.Leaf == nil {
|
||||
leaf, err := x509.ParseCertificate(cert.certificate.Certificate[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cert.certificate.Leaf = leaf
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewAccount creates an account
|
||||
func NewAccount(email string, certs []*DomainsCertificate) (*Account, error) {
|
||||
// Create a user. New accounts need an email and private key to start
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domainsCerts := DomainsCertificates{Certs: certs}
|
||||
err = domainsCerts.Init()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Account{
|
||||
Email: email,
|
||||
PrivateKey: x509.MarshalPKCS1PrivateKey(privateKey),
|
||||
DomainsCertificate: DomainsCertificates{Certs: domainsCerts.Certs},
|
||||
ChallengeCerts: map[string]*ChallengeCert{}}, nil
|
||||
}
|
||||
|
||||
// GetEmail returns email
|
||||
func (a *Account) GetEmail() string {
|
||||
return a.Email
|
||||
}
|
||||
|
||||
// GetRegistration returns lets encrypt registration resource
|
||||
func (a *Account) GetRegistration() *acme.RegistrationResource {
|
||||
return a.Registration
|
||||
}
|
||||
|
||||
// GetPrivateKey returns private key
|
||||
func (a *Account) GetPrivateKey() crypto.PrivateKey {
|
||||
if privateKey, err := x509.ParsePKCS1PrivateKey(a.PrivateKey); err == nil {
|
||||
return privateKey
|
||||
}
|
||||
|
||||
log.Errorf("Cannot unmarshall private key %+v", a.PrivateKey)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveAccountV1Values removes ACME account V1 values
|
||||
func (a *Account) RemoveAccountV1Values() error {
|
||||
// Check if ACME Account is in ACME V1 format
|
||||
if a.Registration != nil {
|
||||
isOldRegistration, err := regexp.MatchString(acmeprovider.RegistrationURLPathV1Regexp, a.Registration.URI)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isOldRegistration {
|
||||
a.reset()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Account) reset() {
|
||||
log.Debug("Reset ACME account object.")
|
||||
a.Email = ""
|
||||
a.Registration = nil
|
||||
a.PrivateKey = nil
|
||||
}
|
||||
|
||||
// Certificate is used to store certificate info
|
||||
type Certificate struct {
|
||||
Domain string
|
||||
CertURL string
|
||||
CertStableURL string
|
||||
PrivateKey []byte
|
||||
Certificate []byte
|
||||
}
|
||||
|
||||
// DomainsCertificates stores a certificate for multiple domains
|
||||
type DomainsCertificates struct {
|
||||
Certs []*DomainsCertificate
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) Len() int {
|
||||
return len(dc.Certs)
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) Swap(i, j int) {
|
||||
dc.Certs[i], dc.Certs[j] = dc.Certs[j], dc.Certs[i]
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) Less(i, j int) bool {
|
||||
if reflect.DeepEqual(dc.Certs[i].Domains, dc.Certs[j].Domains) {
|
||||
return dc.Certs[i].tlsCert.Leaf.NotAfter.After(dc.Certs[j].tlsCert.Leaf.NotAfter)
|
||||
}
|
||||
|
||||
if dc.Certs[i].Domains.Main == dc.Certs[j].Domains.Main {
|
||||
return strings.Join(dc.Certs[i].Domains.SANs, ",") < strings.Join(dc.Certs[j].Domains.SANs, ",")
|
||||
}
|
||||
|
||||
return dc.Certs[i].Domains.Main < dc.Certs[j].Domains.Main
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) removeDuplicates() {
|
||||
sort.Sort(dc)
|
||||
for i := 0; i < len(dc.Certs); i++ {
|
||||
for i2 := i + 1; i2 < len(dc.Certs); i2++ {
|
||||
if reflect.DeepEqual(dc.Certs[i].Domains, dc.Certs[i2].Domains) {
|
||||
// delete
|
||||
log.Warnf("Remove duplicate cert: %+v, expiration :%s", dc.Certs[i2].Domains, dc.Certs[i2].tlsCert.Leaf.NotAfter.String())
|
||||
dc.Certs = append(dc.Certs[:i2], dc.Certs[i2+1:]...)
|
||||
i2--
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) removeEmpty() {
|
||||
certs := []*DomainsCertificate{}
|
||||
for _, cert := range dc.Certs {
|
||||
if cert.Certificate != nil && len(cert.Certificate.Certificate) > 0 && len(cert.Certificate.PrivateKey) > 0 {
|
||||
certs = append(certs, cert)
|
||||
}
|
||||
}
|
||||
dc.Certs = certs
|
||||
}
|
||||
|
||||
// Init DomainsCertificates
|
||||
func (dc *DomainsCertificates) Init() error {
|
||||
dc.lock.Lock()
|
||||
defer dc.lock.Unlock()
|
||||
|
||||
dc.removeEmpty()
|
||||
|
||||
for _, domainsCertificate := range dc.Certs {
|
||||
tlsCert, err := tls.X509KeyPair(domainsCertificate.Certificate.Certificate, domainsCertificate.Certificate.PrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
domainsCertificate.tlsCert = &tlsCert
|
||||
|
||||
if domainsCertificate.tlsCert.Leaf == nil {
|
||||
leaf, err := x509.ParseCertificate(domainsCertificate.tlsCert.Certificate[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
domainsCertificate.tlsCert.Leaf = leaf
|
||||
}
|
||||
}
|
||||
|
||||
dc.removeDuplicates()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) renewCertificates(acmeCert *Certificate, domain types.Domain) error {
|
||||
dc.lock.Lock()
|
||||
defer dc.lock.Unlock()
|
||||
|
||||
for _, domainsCertificate := range dc.Certs {
|
||||
if reflect.DeepEqual(domain, domainsCertificate.Domains) {
|
||||
tlsCert, err := tls.X509KeyPair(acmeCert.Certificate, acmeCert.PrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
domainsCertificate.Certificate = acmeCert
|
||||
domainsCertificate.tlsCert = &tlsCert
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("certificate to renew not found for domain %s", domain.Main)
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) addCertificateForDomains(acmeCert *Certificate, domain types.Domain) (*DomainsCertificate, error) {
|
||||
dc.lock.Lock()
|
||||
defer dc.lock.Unlock()
|
||||
|
||||
tlsCert, err := tls.X509KeyPair(acmeCert.Certificate, acmeCert.PrivateKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cert := DomainsCertificate{Domains: domain, Certificate: acmeCert, tlsCert: &tlsCert}
|
||||
dc.Certs = append(dc.Certs, &cert)
|
||||
return &cert, nil
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) getCertificateForDomain(domainToFind string) (*DomainsCertificate, bool) {
|
||||
dc.lock.RLock()
|
||||
defer dc.lock.RUnlock()
|
||||
|
||||
for _, domainsCertificate := range dc.Certs {
|
||||
for _, domain := range domainsCertificate.Domains.ToStrArray() {
|
||||
if strings.HasPrefix(domain, "*.") && types.MatchDomain(domainToFind, domain) {
|
||||
return domainsCertificate, true
|
||||
}
|
||||
if domain == domainToFind {
|
||||
return domainsCertificate, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) exists(domainToFind types.Domain) (*DomainsCertificate, bool) {
|
||||
dc.lock.RLock()
|
||||
defer dc.lock.RUnlock()
|
||||
|
||||
for _, domainsCertificate := range dc.Certs {
|
||||
if reflect.DeepEqual(domainToFind, domainsCertificate.Domains) {
|
||||
return domainsCertificate, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificates) toDomainsMap() map[string]*tls.Certificate {
|
||||
domainsCertificatesMap := make(map[string]*tls.Certificate)
|
||||
|
||||
for _, domainCertificate := range dc.Certs {
|
||||
certKey := domainCertificate.Domains.Main
|
||||
|
||||
if domainCertificate.Domains.SANs != nil {
|
||||
sort.Strings(domainCertificate.Domains.SANs)
|
||||
|
||||
for _, dnsName := range domainCertificate.Domains.SANs {
|
||||
if dnsName != domainCertificate.Domains.Main {
|
||||
certKey += fmt.Sprintf(",%s", dnsName)
|
||||
}
|
||||
}
|
||||
}
|
||||
domainsCertificatesMap[certKey] = domainCertificate.tlsCert
|
||||
}
|
||||
return domainsCertificatesMap
|
||||
}
|
||||
|
||||
// DomainsCertificate contains a certificate for multiple domains
|
||||
type DomainsCertificate struct {
|
||||
Domains types.Domain
|
||||
Certificate *Certificate
|
||||
tlsCert *tls.Certificate
|
||||
}
|
||||
|
||||
func (dc *DomainsCertificate) needRenew() bool {
|
||||
for _, c := range dc.tlsCert.Certificate {
|
||||
crt, err := x509.ParseCertificate(c)
|
||||
if err != nil {
|
||||
// If there's an error, we assume the cert is broken, and needs update
|
||||
return true
|
||||
}
|
||||
|
||||
// <= 30 days left, renew certificate
|
||||
if crt.NotAfter.Before(time.Now().Add(24 * 30 * time.Hour)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
747
acme/acme.go
@@ -1,747 +0,0 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
fmtlog "log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/BurntSushi/ty/fun"
|
||||
"github.com/cenk/backoff"
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/mux"
|
||||
"github.com/containous/staert"
|
||||
"github.com/containous/traefik/cluster"
|
||||
"github.com/containous/traefik/log"
|
||||
acmeprovider "github.com/containous/traefik/provider/acme"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/containous/traefik/tls/generate"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/containous/traefik/version"
|
||||
"github.com/eapache/channels"
|
||||
"github.com/xenolf/lego/acme"
|
||||
legolog "github.com/xenolf/lego/log"
|
||||
"github.com/xenolf/lego/providers/dns"
|
||||
)
|
||||
|
||||
var (
|
||||
// OSCPMustStaple enables OSCP stapling as from https://github.com/xenolf/lego/issues/270
|
||||
OSCPMustStaple = false
|
||||
)
|
||||
|
||||
// ACME allows to connect to lets encrypt and retrieve certs
|
||||
// Deprecated Please use provider/acme/Provider
|
||||
type ACME struct {
|
||||
Email string `description:"Email address used for registration"`
|
||||
Domains []types.Domain `description:"SANs (alternative domains) to each main domain using format: --acme.domains='main.com,san1.com,san2.com' --acme.domains='main.net,san1.net,san2.net'"`
|
||||
Storage string `description:"File or key used for certificates storage."`
|
||||
StorageFile string // Deprecated
|
||||
OnDemand bool `description:"(Deprecated) Enable on demand certificate generation. This will request a certificate from Let's Encrypt during the first TLS handshake for a hostname that does not yet have a certificate."` // Deprecated
|
||||
OnHostRule bool `description:"Enable certificate generation on frontends Host rules."`
|
||||
CAServer string `description:"CA server to use."`
|
||||
EntryPoint string `description:"Entrypoint to proxy acme challenge to."`
|
||||
DNSChallenge *acmeprovider.DNSChallenge `description:"Activate DNS-01 Challenge"`
|
||||
HTTPChallenge *acmeprovider.HTTPChallenge `description:"Activate HTTP-01 Challenge"`
|
||||
DNSProvider string `description:"(Deprecated) Activate DNS-01 Challenge"` // Deprecated
|
||||
DelayDontCheckDNS flaeg.Duration `description:"(Deprecated) Assume DNS propagates after a delay in seconds rather than finding and querying nameservers."` // Deprecated
|
||||
ACMELogging bool `description:"Enable debug logging of ACME actions."`
|
||||
client *acme.Client
|
||||
defaultCertificate *tls.Certificate
|
||||
store cluster.Store
|
||||
challengeHTTPProvider *challengeHTTPProvider
|
||||
checkOnDemandDomain func(domain string) bool
|
||||
jobs *channels.InfiniteChannel
|
||||
TLSConfig *tls.Config `description:"TLS config in case wildcard certs are used"`
|
||||
dynamicCerts *safe.Safe
|
||||
}
|
||||
|
||||
func (a *ACME) init() error {
|
||||
acme.UserAgent = fmt.Sprintf("containous-traefik/%s", version.Version)
|
||||
if a.ACMELogging {
|
||||
legolog.Logger = fmtlog.New(os.Stderr, "legolog: ", fmtlog.LstdFlags)
|
||||
} else {
|
||||
legolog.Logger = fmtlog.New(ioutil.Discard, "", 0)
|
||||
}
|
||||
// no certificates in TLS config, so we add a default one
|
||||
cert, err := generate.DefaultCertificate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.defaultCertificate = cert
|
||||
|
||||
a.jobs = channels.NewInfiniteChannel()
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddRoutes add routes on internal router
|
||||
func (a *ACME) AddRoutes(router *mux.Router) {
|
||||
router.Methods(http.MethodGet).
|
||||
Path(acme.HTTP01ChallengePath("{token}")).
|
||||
Handler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
if a.challengeHTTPProvider == nil {
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
vars := mux.Vars(req)
|
||||
if token, ok := vars["token"]; ok {
|
||||
domain, _, err := net.SplitHostPort(req.Host)
|
||||
if err != nil {
|
||||
log.Debugf("Unable to split host and port: %v. Fallback to request host.", err)
|
||||
domain = req.Host
|
||||
}
|
||||
tokenValue := a.challengeHTTPProvider.getTokenValue(token, domain)
|
||||
if len(tokenValue) > 0 {
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
rw.Write(tokenValue)
|
||||
return
|
||||
}
|
||||
}
|
||||
rw.WriteHeader(http.StatusNotFound)
|
||||
}))
|
||||
}
|
||||
|
||||
// CreateClusterConfig creates a tls.config using ACME configuration in cluster mode
|
||||
func (a *ACME) CreateClusterConfig(leadership *cluster.Leadership, tlsConfig *tls.Config, certs *safe.Safe, checkOnDemandDomain func(domain string) bool) error {
|
||||
err := a.init()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(a.Storage) == 0 {
|
||||
return errors.New("Empty Store, please provide a key for certs storage")
|
||||
}
|
||||
a.checkOnDemandDomain = checkOnDemandDomain
|
||||
a.dynamicCerts = certs
|
||||
tlsConfig.Certificates = append(tlsConfig.Certificates, *a.defaultCertificate)
|
||||
tlsConfig.GetCertificate = a.getCertificate
|
||||
a.TLSConfig = tlsConfig
|
||||
listener := func(object cluster.Object) error {
|
||||
account := object.(*Account)
|
||||
account.Init()
|
||||
if !leadership.IsLeader() {
|
||||
a.client, err = a.buildACMEClient(account)
|
||||
if err != nil {
|
||||
log.Errorf("Error building ACME client %+v: %s", object, err.Error())
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
datastore, err := cluster.NewDataStore(
|
||||
leadership.Pool.Ctx(),
|
||||
staert.KvSource{
|
||||
Store: leadership.Store,
|
||||
Prefix: a.Storage,
|
||||
},
|
||||
&Account{},
|
||||
listener)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.store = datastore
|
||||
|
||||
ticker := time.NewTicker(24 * time.Hour)
|
||||
leadership.Pool.AddGoCtx(func(ctx context.Context) {
|
||||
log.Info("Starting ACME renew job...")
|
||||
defer log.Info("Stopped ACME renew job...")
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
a.renewCertificates()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
leadership.AddListener(a.leadershipListener)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *ACME) leadershipListener(elected bool) error {
|
||||
if elected {
|
||||
_, err := a.store.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
transaction, object, err := a.store.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
account := object.(*Account)
|
||||
account.Init()
|
||||
// Reset Account values if caServer changed, thus registration URI can be updated
|
||||
if account != nil && account.Registration != nil && !strings.HasPrefix(account.Registration.URI, a.CAServer) {
|
||||
account.reset()
|
||||
}
|
||||
|
||||
var needRegister bool
|
||||
if account == nil || len(account.Email) == 0 {
|
||||
domainsCerts := DomainsCertificates{Certs: []*DomainsCertificate{}}
|
||||
if account != nil {
|
||||
domainsCerts = account.DomainsCertificate
|
||||
}
|
||||
|
||||
account, err = NewAccount(a.Email, domainsCerts.Certs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
needRegister = true
|
||||
}
|
||||
|
||||
a.client, err = a.buildACMEClient(account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if needRegister {
|
||||
// New users will need to register; be sure to save it
|
||||
log.Debug("Register...")
|
||||
|
||||
reg, err := a.client.Register(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
account.Registration = reg
|
||||
}
|
||||
|
||||
err = transaction.Commit(account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.retrieveCertificates()
|
||||
a.renewCertificates()
|
||||
a.runJobs()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *ACME) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
domain := types.CanonicalDomain(clientHello.ServerName)
|
||||
account := a.store.Get().(*Account)
|
||||
|
||||
if providedCertificate := a.getProvidedCertificate(domain); providedCertificate != nil {
|
||||
return providedCertificate, nil
|
||||
}
|
||||
|
||||
if domainCert, ok := account.DomainsCertificate.getCertificateForDomain(domain); ok {
|
||||
log.Debugf("ACME got domain cert %s", domain)
|
||||
return domainCert.tlsCert, nil
|
||||
}
|
||||
if a.OnDemand {
|
||||
if a.checkOnDemandDomain != nil && !a.checkOnDemandDomain(domain) {
|
||||
return nil, nil
|
||||
}
|
||||
return a.loadCertificateOnDemand(clientHello)
|
||||
}
|
||||
log.Debugf("No certificate found or generated for %s", domain)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (a *ACME) retrieveCertificates() {
|
||||
a.jobs.In() <- func() {
|
||||
log.Info("Retrieving ACME certificates...")
|
||||
|
||||
a.deleteUnnecessaryDomains()
|
||||
|
||||
for i := 0; i < len(a.Domains); i++ {
|
||||
domain := a.Domains[i]
|
||||
|
||||
// check if cert isn't already loaded
|
||||
account := a.store.Get().(*Account)
|
||||
if _, exists := account.DomainsCertificate.exists(domain); !exists {
|
||||
var domains []string
|
||||
domains = append(domains, domain.Main)
|
||||
domains = append(domains, domain.SANs...)
|
||||
domains, err := a.getValidDomains(domains, true)
|
||||
if err != nil {
|
||||
log.Errorf("Error validating ACME certificate for domain %q: %s", domains, err)
|
||||
continue
|
||||
}
|
||||
|
||||
certificateResource, err := a.getDomainsCertificates(domains)
|
||||
if err != nil {
|
||||
log.Errorf("Error getting ACME certificate for domain %q: %s", domains, err)
|
||||
continue
|
||||
}
|
||||
|
||||
transaction, object, err := a.store.Begin()
|
||||
if err != nil {
|
||||
log.Errorf("Error creating ACME store transaction from domain %q: %s", domain, err)
|
||||
continue
|
||||
}
|
||||
|
||||
account = object.(*Account)
|
||||
_, err = account.DomainsCertificate.addCertificateForDomains(certificateResource, domain)
|
||||
if err != nil {
|
||||
log.Errorf("Error adding ACME certificate for domain %q: %s", domains, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if err = transaction.Commit(account); err != nil {
|
||||
log.Errorf("Error Saving ACME account %+v: %s", account, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("Retrieved ACME certificates")
|
||||
}
|
||||
}
|
||||
|
||||
func (a *ACME) renewCertificates() {
|
||||
a.jobs.In() <- func() {
|
||||
log.Info("Testing certificate renew...")
|
||||
account := a.store.Get().(*Account)
|
||||
for _, certificateResource := range account.DomainsCertificate.Certs {
|
||||
if certificateResource.needRenew() {
|
||||
log.Infof("Renewing certificate from LE : %+v", certificateResource.Domains)
|
||||
renewedACMECert, err := a.renewACMECertificate(certificateResource)
|
||||
if err != nil {
|
||||
log.Errorf("Error renewing certificate from LE: %v", err)
|
||||
continue
|
||||
}
|
||||
operation := func() error {
|
||||
return a.storeRenewedCertificate(certificateResource, renewedACMECert)
|
||||
}
|
||||
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 {
|
||||
log.Errorf("Datastore cannot sync: %v", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(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 flaeg.Duration) error {
|
||||
var err error
|
||||
if delay > 0 {
|
||||
log.Debugf("Delaying %d rather than validating DNS propagation", delay)
|
||||
acme.PreCheckDNS = func(_, _ string) (bool, error) {
|
||||
time.Sleep(time.Duration(delay))
|
||||
return true, nil
|
||||
}
|
||||
} else if delay < 0 {
|
||||
err = fmt.Errorf("invalid negative DelayBeforeCheck: %d", delay)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *ACME) buildACMEClient(account *Account) (*acme.Client, error) {
|
||||
log.Debug("Building ACME client...")
|
||||
caServer := "https://acme-v02.api.letsencrypt.org/directory"
|
||||
if len(a.CAServer) > 0 {
|
||||
caServer = a.CAServer
|
||||
}
|
||||
client, err := acme.NewClient(caServer, account, acme.RSA4096)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if a.DNSChallenge != nil && len(a.DNSChallenge.Provider) > 0 {
|
||||
log.Debugf("Using DNS Challenge provider: %s", a.DNSChallenge.Provider)
|
||||
|
||||
err = dnsOverrideDelay(a.DNSChallenge.DelayBeforeCheck)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var provider acme.ChallengeProvider
|
||||
provider, err = dns.NewDNSChallengeProviderByName(a.DNSChallenge.Provider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client.ExcludeChallenges([]acme.Challenge{acme.HTTP01})
|
||||
err = client.SetChallengeProvider(acme.DNS01, provider)
|
||||
} else if a.HTTPChallenge != nil && len(a.HTTPChallenge.EntryPoint) > 0 {
|
||||
log.Debug("Using HTTP Challenge provider.")
|
||||
client.ExcludeChallenges([]acme.Challenge{acme.DNS01})
|
||||
a.challengeHTTPProvider = &challengeHTTPProvider{store: a.store}
|
||||
err = client.SetChallengeProvider(acme.HTTP01, a.challengeHTTPProvider)
|
||||
} else {
|
||||
return nil, errors.New("ACME challenge not specified, please select HTTP or DNS Challenge")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (a *ACME) loadCertificateOnDemand(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
domain := types.CanonicalDomain(clientHello.ServerName)
|
||||
account := a.store.Get().(*Account)
|
||||
if certificateResource, ok := account.DomainsCertificate.getCertificateForDomain(domain); ok {
|
||||
return certificateResource.tlsCert, nil
|
||||
}
|
||||
certificate, err := a.getDomainsCertificates([]string{domain})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Debugf("Got certificate on demand for domain %s", domain)
|
||||
|
||||
transaction, object, err := a.store.Begin()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
account = object.(*Account)
|
||||
cert, err := account.DomainsCertificate.addCertificateForDomains(certificate, types.Domain{Main: domain})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = transaction.Commit(account); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cert.tlsCert, nil
|
||||
}
|
||||
|
||||
// LoadCertificateForDomains loads certificates from ACME for given domains
|
||||
func (a *ACME) LoadCertificateForDomains(domains []string) {
|
||||
a.jobs.In() <- func() {
|
||||
log.Debugf("LoadCertificateForDomains %v...", domains)
|
||||
|
||||
domains, err := a.getValidDomains(domains, false)
|
||||
if err != nil {
|
||||
log.Errorf("Error getting valid domain: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
operation := func() error {
|
||||
if a.client == nil {
|
||||
return errors.New("ACME client still not built")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
notify := func(err error, time time.Duration) {
|
||||
log.Errorf("Error getting ACME client: %v, retrying in %s", err, time)
|
||||
}
|
||||
ebo := backoff.NewExponentialBackOff()
|
||||
ebo.MaxElapsedTime = 30 * time.Second
|
||||
err = backoff.RetryNotify(safe.OperationWithRecover(operation), ebo, notify)
|
||||
if err != nil {
|
||||
log.Errorf("Error getting ACME client: %v", err)
|
||||
return
|
||||
}
|
||||
account := a.store.Get().(*Account)
|
||||
|
||||
// Check provided certificates
|
||||
uncheckedDomains := a.getUncheckedDomains(domains, account)
|
||||
if len(uncheckedDomains) == 0 {
|
||||
return
|
||||
}
|
||||
certificate, err := a.getDomainsCertificates(uncheckedDomains)
|
||||
if err != nil {
|
||||
log.Errorf("Error getting ACME certificates %+v : %v", uncheckedDomains, err)
|
||||
return
|
||||
}
|
||||
log.Debugf("Got certificate for domains %+v", uncheckedDomains)
|
||||
transaction, object, err := a.store.Begin()
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("Error creating transaction %+v : %v", uncheckedDomains, err)
|
||||
return
|
||||
}
|
||||
var domain types.Domain
|
||||
if len(uncheckedDomains) > 1 {
|
||||
domain = types.Domain{Main: uncheckedDomains[0], SANs: uncheckedDomains[1:]}
|
||||
} else {
|
||||
domain = types.Domain{Main: uncheckedDomains[0]}
|
||||
}
|
||||
account = object.(*Account)
|
||||
_, err = account.DomainsCertificate.addCertificateForDomains(certificate, domain)
|
||||
if err != nil {
|
||||
log.Errorf("Error adding ACME certificates %+v : %v", uncheckedDomains, err)
|
||||
return
|
||||
}
|
||||
if err = transaction.Commit(account); err != nil {
|
||||
log.Errorf("Error Saving ACME account %+v: %v", account, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
log.Debugf("Looking for provided certificate to validate %s...", domains)
|
||||
cert := searchProvidedCertificateForDomains(domains, a.TLSConfig.NameToCertificate)
|
||||
if cert == nil && a.dynamicCerts != nil && a.dynamicCerts.Get() != nil {
|
||||
cert = searchProvidedCertificateForDomains(domains, a.dynamicCerts.Get().(map[string]*tls.Certificate))
|
||||
}
|
||||
if cert == nil {
|
||||
log.Debugf("No provided certificate found for domains %s, get ACME certificate.", domains)
|
||||
}
|
||||
return cert
|
||||
}
|
||||
|
||||
func searchProvidedCertificateForDomains(domain string, certs map[string]*tls.Certificate) *tls.Certificate {
|
||||
// Use regex to test for provided certs that might have been added into TLSConfig
|
||||
for certDomains := range certs {
|
||||
domainChecked := false
|
||||
for _, certDomain := range strings.Split(certDomains, ",") {
|
||||
domainChecked = types.MatchDomain(domain, certDomain)
|
||||
if domainChecked {
|
||||
break
|
||||
}
|
||||
}
|
||||
if domainChecked {
|
||||
log.Debugf("Domain %q checked by provided certificate %q", domain, certDomains)
|
||||
return certs[certDomains]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get provided certificate which check a domains list (Main and SANs)
|
||||
// from static and dynamic provided certificates
|
||||
func (a *ACME) getUncheckedDomains(domains []string, account *Account) []string {
|
||||
log.Debugf("Looking for provided certificate to validate %s...", domains)
|
||||
allCerts := make(map[string]*tls.Certificate)
|
||||
|
||||
// Get static certificates
|
||||
for domains, certificate := range a.TLSConfig.NameToCertificate {
|
||||
allCerts[domains] = certificate
|
||||
}
|
||||
|
||||
// Get dynamic certificates
|
||||
if a.dynamicCerts != nil && a.dynamicCerts.Get() != nil {
|
||||
for domains, certificate := range a.dynamicCerts.Get().(map[string]*tls.Certificate) {
|
||||
allCerts[domains] = certificate
|
||||
}
|
||||
}
|
||||
|
||||
// Get ACME certificates
|
||||
if account != nil {
|
||||
for domains, certificate := range account.DomainsCertificate.toDomainsMap() {
|
||||
allCerts[domains] = certificate
|
||||
}
|
||||
}
|
||||
|
||||
// Get Configuration Domains
|
||||
for i := 0; i < len(a.Domains); i++ {
|
||||
allCerts[a.Domains[i].Main] = &tls.Certificate{}
|
||||
for _, san := range a.Domains[i].SANs {
|
||||
allCerts[san] = &tls.Certificate{}
|
||||
}
|
||||
}
|
||||
|
||||
return searchUncheckedDomains(domains, allCerts)
|
||||
}
|
||||
|
||||
func searchUncheckedDomains(domains []string, certs map[string]*tls.Certificate) []string {
|
||||
var uncheckedDomains []string
|
||||
for _, domainToCheck := range domains {
|
||||
if !isDomainAlreadyChecked(domainToCheck, certs) {
|
||||
uncheckedDomains = append(uncheckedDomains, domainToCheck)
|
||||
}
|
||||
}
|
||||
|
||||
if len(uncheckedDomains) == 0 {
|
||||
log.Debugf("No ACME certificate to generate for domains %q.", domains)
|
||||
} else {
|
||||
log.Debugf("Domains %q need ACME certificates generation for domains %q.", domains, strings.Join(uncheckedDomains, ","))
|
||||
}
|
||||
return uncheckedDomains
|
||||
}
|
||||
|
||||
func (a *ACME) getDomainsCertificates(domains []string) (*Certificate, error) {
|
||||
domains = fun.Map(types.CanonicalDomain, domains).([]string)
|
||||
log.Debugf("Loading ACME certificates %s...", domains)
|
||||
bundle := true
|
||||
|
||||
certificate, err := a.client.ObtainCertificate(domains, bundle, nil, OSCPMustStaple)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return nil, fmt.Errorf("cannot obtain certificates: %+v", err)
|
||||
}
|
||||
|
||||
log.Debugf("Loaded ACME certificates %s", domains)
|
||||
return &Certificate{
|
||||
Domain: certificate.Domain,
|
||||
CertURL: certificate.CertURL,
|
||||
CertStableURL: certificate.CertStableURL,
|
||||
PrivateKey: certificate.PrivateKey,
|
||||
Certificate: certificate.Certificate,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *ACME) runJobs() {
|
||||
safe.Go(func() {
|
||||
for job := range a.jobs.Out() {
|
||||
function := job.(func())
|
||||
function()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// getValidDomains checks if given domain is allowed to generate a ACME certificate and return it
|
||||
func (a *ACME) getValidDomains(domains []string, wildcardAllowed bool) ([]string, error) {
|
||||
// Check if the domains array is empty or contains only one empty value
|
||||
if len(domains) == 0 || (len(domains) == 1 && len(domains[0]) == 0) {
|
||||
return nil, errors.New("unable to generate a certificate when no domain is given")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(domains[0], "*") {
|
||||
if !wildcardAllowed {
|
||||
return nil, fmt.Errorf("unable to generate a wildcard certificate for domain %q from a 'Host' rule", strings.Join(domains, ","))
|
||||
}
|
||||
|
||||
if a.DNSChallenge == nil && len(a.DNSProvider) == 0 {
|
||||
return nil, fmt.Errorf("unable to generate a wildcard certificate for domain %q : ACME needs a DNSChallenge", strings.Join(domains, ","))
|
||||
}
|
||||
if strings.HasPrefix(domains[0], "*.*") {
|
||||
return nil, fmt.Errorf("unable to generate a wildcard certificate for domain %q : ACME does not allow '*.*' wildcard domain", strings.Join(domains, ","))
|
||||
}
|
||||
}
|
||||
for _, san := range domains[1:] {
|
||||
if strings.HasPrefix(san, "*") {
|
||||
return nil, fmt.Errorf("unable to generate a certificate for domains %q: SANs can not be a wildcard domain", strings.Join(domains, ","))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
domains = fun.Map(types.CanonicalDomain, domains).([]string)
|
||||
return domains, nil
|
||||
}
|
||||
|
||||
func isDomainAlreadyChecked(domainToCheck string, existentDomains map[string]*tls.Certificate) bool {
|
||||
for certDomains := range existentDomains {
|
||||
for _, certDomain := range strings.Split(certDomains, ",") {
|
||||
if types.MatchDomain(domainToCheck, certDomain) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// deleteUnnecessaryDomains deletes from the configuration :
|
||||
// - Duplicated domains
|
||||
// - Domains which are checked by wildcard domain
|
||||
func (a *ACME) deleteUnnecessaryDomains() {
|
||||
var newDomains []types.Domain
|
||||
|
||||
for idxDomainToCheck, domainToCheck := range a.Domains {
|
||||
keepDomain := true
|
||||
|
||||
for idxDomain, domain := range a.Domains {
|
||||
if idxDomainToCheck == idxDomain {
|
||||
continue
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(domain, domainToCheck) {
|
||||
if idxDomainToCheck > idxDomain {
|
||||
log.Warnf("The domain %v is duplicated in the configuration but will be process by ACME only once.", domainToCheck)
|
||||
keepDomain = false
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
var newDomainsToCheck []string
|
||||
|
||||
// Check if domains can be validated by the wildcard domain
|
||||
domainsMap := make(map[string]*tls.Certificate)
|
||||
domainsMap[domain.Main] = &tls.Certificate{}
|
||||
if len(domain.SANs) > 0 {
|
||||
domainsMap[strings.Join(domain.SANs, ",")] = &tls.Certificate{}
|
||||
}
|
||||
|
||||
for _, domainProcessed := range domainToCheck.ToStrArray() {
|
||||
if idxDomain < idxDomainToCheck && isDomainAlreadyChecked(domainProcessed, domainsMap) {
|
||||
// The domain is duplicated in a CN
|
||||
log.Warnf("Domain %q is duplicated in the configuration or validated by the domain %v. It will be processed once.", domainProcessed, domain)
|
||||
continue
|
||||
} else if domain.Main != domainProcessed && strings.HasPrefix(domain.Main, "*") && types.MatchDomain(domainProcessed, domain.Main) {
|
||||
// Check if a wildcard can validate the domain
|
||||
log.Warnf("Domain %q will not be processed by ACME provider because it is validated by the wildcard %q", domainProcessed, domain.Main)
|
||||
continue
|
||||
}
|
||||
newDomainsToCheck = append(newDomainsToCheck, domainProcessed)
|
||||
}
|
||||
|
||||
// Delete the domain if both Main and SANs can be validated by the wildcard domain
|
||||
// otherwise keep the unchecked values
|
||||
if newDomainsToCheck == nil {
|
||||
keepDomain = false
|
||||
break
|
||||
}
|
||||
domainToCheck.Set(newDomainsToCheck)
|
||||
}
|
||||
|
||||
if keepDomain {
|
||||
newDomains = append(newDomains, domainToCheck)
|
||||
}
|
||||
}
|
||||
|
||||
a.Domains = newDomains
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
{
|
||||
"Email": "test@traefik.io",
|
||||
"Registration": {
|
||||
"body": {
|
||||
"resource": "reg",
|
||||
"id": 3,
|
||||
"key": {
|
||||
"kty": "RSA",
|
||||
"n": "y5a71suIqvEtovDmDVQ3SSNagk5IVCFI_TvqWpEXSrdbcDE2C-PTEtEUJuLkYwygcpiWYbPmXgdS628vQCw5Uo4DeDyHiuysJOWBLaWow3p9goOdhnPbGBq0liIR9xXyRoctdipVk8UiO9scWsu4jMBM3sMr7_yBWPfYYiLEQmZGFO3iE7Oqr55h_kncHIj5lUQY1j_jkftqxlxUB5_0quyJ7l915j5QY--eY7h4GEhRvx0TlUpi-CnRtRblGeDDDilXZD6bQN2962WdKecsmRaYx-ttLz6jCPXz2VDJRWNcIS501ne2Zh3hzw_DS6IRd2GIia1Wg4sisi9epC9sumXPHi6xzR6-_i_nsFjdtTkUcV8HmorOYoc820KQVZaLScxa8e7-ixpOd6mr6AIbEf7dBAkb9f_iK3GwpqKD8yNcaj1EQgNSyJSjnKSulXI_GwkGnuXe00Qpb1a8ha5Z8yWg7XmZZnJyAZrmK60RfwRNQ1rO5ioerNUBJ2KYTYNzVjBdob9Ug6Cjh4bEKNNjqcbjQ50_Z97Vw40xzpDQ_fYllc6n92eSuv6olxFJTmK7EhHuanDzITngaqei3zL9RwQ7P-1jfEZ03qmGrQYYqXcsS46PQ8cE-frzY2mKp16pRNCG7-03gKVGV0JHyW1aYbevNUk7OumCAXhC2YOigBk",
|
||||
"e": "AQAB"
|
||||
},
|
||||
"contact": [
|
||||
"mailto:test@traefik.io"
|
||||
],
|
||||
"agreement": "http://boulder:4000/terms/v1"
|
||||
},
|
||||
"uri": "http://127.0.0.1:4000/acme/reg/3",
|
||||
"new_authzr_uri": "http://127.0.0.1:4000/acme/new-authz",
|
||||
"terms_of_service": "http://boulder:4000/terms/v1"
|
||||
},
|
||||
"PrivateKey": "MIIJJwIBAAKCAgEAy5a71suIqvEtovDmDVQ3SSNagk5IVCFI/TvqWpEXSrdbcDE2C+PTEtEUJuLkYwygcpiWYbPmXgdS628vQCw5Uo4DeDyHiuysJOWBLaWow3p9goOdhnPbGBq0liIR9xXyRoctdipVk8UiO9scWsu4jMBM3sMr7/yBWPfYYiLEQmZGFO3iE7Oqr55h/kncHIj5lUQY1j/jkftqxlxUB5/0quyJ7l915j5QY++eY7h4GEhRvx0TlUpi+CnRtRblGeDDDilXZD6bQN2962WdKecsmRaYx+ttLz6jCPXz2VDJRWNcIS501ne2Zh3hzw/DS6IRd2GIia1Wg4sisi9epC9sumXPHi6xzR6+/i/nsFjdtTkUcV8HmorOYoc820KQVZaLScxa8e7+ixpOd6mr6AIbEf7dBAkb9f/iK3GwpqKD8yNcaj1EQgNSyJSjnKSulXI/GwkGnuXe00Qpb1a8ha5Z8yWg7XmZZnJyAZrmK60RfwRNQ1rO5ioerNUBJ2KYTYNzVjBdob9Ug6Cjh4bEKNNjqcbjQ50/Z97Vw40xzpDQ/fYllc6n92eSuv6olxFJTmK7EhHuanDzITngaqei3zL9RwQ7P+1jfEZ03qmGrQYYqXcsS46PQ8cE+frzY2mKp16pRNCG7+03gKVGV0JHyW1aYbevNUk7OumCAXhC2YOigBkCAwEAAQKCAgA8XW1EuwTC6tAFSDhuK1JZNUpY6K05hMUHkQRj5jFpzgQmt/C2hc7H/YZkIVJmrA/G6sdsINNlffZwKH9yH6q/d6w/snLeFl7UcdhjmIL5sxAT6sKCY0fLVd/FxERfZvp3Pw2Tw+mr7v+/j7BQm6cU1M/2HRiiB9SydIqMTpKyvXB6NC6ceOFbQTL9GxlQvKyEPbS/kiH/3vRB7I5d1GfPZmNfcp6ark9X0my8VK4HRSo36H8t/OhrfLrZXvh/O82aHVf0OTv/d8AgU/jNu+XVXoXegUfWglQFDChJf1KuaE+g5w1tqgFDNgkGRD475soXA6xgZi0Iw/B9tN3zALzT4IiAW1q72feeTgKOMA2zGtKXxQZZSOV+DuWFZNz/tT7XqGQThqxM09CHv2WGOe80vobtegXYTUt90hysrqIZmBW5XYdzQlJh1KWTtfCaTrWd47kbGvhkEPc8aA3Ji4/AqfkVXiqwaLu+MSlgzPpRj7U7UAIDqnpZjgttgLp74Ujnk3bTaUzdyyNqYDBG3IFGr/Sv+2GQDAUn/PYRJKWr0BteqOzX9zvW3zY8g9CYVXfK/AW3RMWLV8ly6vH/gWqa9gEuzRNRlzjUU6/HCVbUx3UT8RMWH2TQ0uuQZr5JX1iTwjeeT0dEIly1NnRQC92wcrE4UUTBEF3krGVpDBf0AQKCAQEA4jB8w+2fwzbF8X+gCODcY7sTeJRunzGy+jbdaLkcThuylga+6W3ZgWx0BD30ql9K2mouCVu86fCTnBeXXEC3QoTdgw/EzJ83+4JU3QSDdzs9Ta9vLHyvrpUkQfZ8UZpeLLmFsmsBMbBbnfw0S1TzXDsgrAc+G4tia8nO/Iqu75kEMGzmHQAvmN3iSqc1aTS4qumbB19g+v+csq9NEht4F9jt39KotG+OD3MxCxtMu7vxAkJRjFFcgcbb2Rtqe/kQEKA1vLEAJg27lV4k8XibCSerVUR6IzT8WZHrNiXmpRguTLl2k8uFUdCOOx6aLGyRVJ6+8SgIsMR540vnxwQzEQKCAQEA5mu2wtWT19mvXopC3easPsXIPzc5oaRkqfWZYT1KHcVQ7NIXsE3vCjcf/3igZ8l/FVQ4G4fpk/GoTqlpV5Aq/JHCpVOR2O69uB+W4kWgliejpHvF9gszzAYnC8lIXqDbWiinBhmm3ii8sDGAoBaSDw5NMUq3mI+nd8zZ+jx1bLBczDafmQ0YKr8k0YaROxIgoBgDOQDdSqG387lwzpza2DKI5Al3HfS42zjT0RmBahPiuT2aEoUZmIYuvFY0fEjfkpbdvLyexHfZCILRUGlG1nAwASFg86lp+mFSBJ3E3cvbP0CpbFGxon5u4Ao3/7htoOh6huh7MQ91h41fv1hsiQKCAQAe7WRR4e7jYVzlbX7zV9Oqq0y5QwpxJ/mB7viNNiphn7Xmf5uhDU0dPjgK0HHgzdDNVpFe5DVLg4KbaDpg+dRU+xfSsNhG5kpgUGzMH67eIbJ7Kc64tX/MDkZ74nkTK1lPIjrer3TlV2jfjDmWR1JTPR51hzP9ziwx8tEjhM7woeqJuIoqUvkvHL+xV3WdIgFSFUkGVAtNpp/FauTN4gWktRupbAN3UH2LLUP6ccwnK0aD+Y9u8T0F3av33qDLvL1umIlgeI89pMkOXmYMwmHoeY0axpcwszECCkqwB7SmxEyoXv+Qq9ZZ3ntkKAYKpvmkKWSQUtoFWYgVBS727mMRAoIBABLdwusU/bPwuPEutObiWjwRiaHTbb6UbUGVQGe70vO5EjUxxorC9s2JUe9i+w9EakleyfFHIZLheHxoVp26yio/7QYIX6q5cYM/4uTH+qwQts9i6wSISkdsQYovguNsnEk3huVy+Dy8bSaoBvYUowTkkOF2Uq4FJRskBLz+ckbh8dcuqcaoUdA+Mk+NixqhE1bIYIssTPItZ5hnGJtyMGD/UkIJnF0ximk4r+8w/W2oDypHpvPZPg1E/1KgZE/Az7166NDpSL6haX3O6ECDPi+Uo/mTuBJ7TpgXm9WQ7WuTo3H8Y2LhFYBOhdmGPKuNeDxyjIW7R0rvDxp4MtzB6rECggEAJIl7/qp1lxUQPQJRTsEYBkOtdRw0IGG1Rcj0emhHaBN05c9opCy+Osb7mVeU5ZiULe5kD02phL+36pEumprz7QzN46Y5pZc8AQ2W/QkeL4Wo9U9QzczvQQzc1EqrBkzvQTZtBhn4DRzz0IuTn1beVyHtBZeNpBFgMQFv9VYQuUNwFoTOkkQrBRnYbXH6KEnhF3c/1Hzi4KHVdHdfZ3LH7KFQJ34xio0q2tWQSQYeybmwOXdd9sxpz/Y4KBS9fqm7UrwnPK8yuOc05HLEaws+1iam5YyJprlQo3mGKe0wRztwn44HDeQr70LlFm0lzigVAv0hSiWO1Q5hJL7nDu8m/Q==",
|
||||
"DomainsCertificate": {
|
||||
"Certs": [
|
||||
{
|
||||
"Domains": {
|
||||
"Main": "local1.com",
|
||||
"SANs": [
|
||||
"test1.local1.com",
|
||||
"test2.local1.com"
|
||||
]
|
||||
},
|
||||
"Certificate": {
|
||||
"Domain": "local1.com",
|
||||
"CertURL": "http://127.0.0.1:4000/acme/cert/ffc4f3f14def9ee6ec6a0522b5c0baa3379d",
|
||||
"CertStableURL": "",
|
||||
"PrivateKey": "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS1FJQkFBS0NBZ0VBdVNoTTR4enF6cE5YcFNaNnAvZnQrRmt5VmgyK1BSZXJUelV0OERRSng2UkVjQS9FCnN2RnNIVmNOSkZMS2twYTNlOEd3SUZBakJQNnJPK3hoR1JjWlJrdENON1gyOW5LZFhGbHZkYzJxd0hyTFF5WWkKTTB3ODhTck41VERiNi96TWU2dTB0dERiYWtDbDd6ZEJKUXJ6a1h5ZU1MeVkzTUs3aVkrMHpwL2JqMVhvbk5DdQpaQStkZ3hsMVNrV01DVUYvQk9HNWFyT1hwb0x4S0dQWGdzV3hOTVNLVmJKSHczL3ZqNTViZU92Um5lT3BNWlhvCmMwOWpZT3VBakNka1Z5czBSWHJLNWNCRDRMbVRXdnN4MFdTK2VMVHlGTTdQTHVZM3lEWkNNWEhjVmlqRHhnbFMKYjB1ZVRQcGFUWEQwYkxqZ0RNOUVEdE15ZEJzMUNPWlpPWG9ickN5Q2I1eWxTOFdVd1NzVXM1UldxZnlVbnAvcgpSNGx2c2RZOWRVZjRPdkNMVnJvWWk5NWFGc1Zxa0xLOExuL0Eyc3kxYWlDTnR4RmpKOXRXbWU0V0NhdzRoU0YvCkR4NWVNNWNYR2JSYXduVlZJQlZXeHhzNTBPMFJlUWRvbXBQZEFNS1RDWk9SRmxYaDdOWTdxQVdWRGtpdzhyam8Kekd3Ni9XdjlOR3hTNTliKzc0YVAxcjBxOTZ2RS9Rdi8zTCtjbjhiN0lBLytPYmFKdzhIT3RGbXc4RjBxQkN3MAprYWVVSloxb1JueGFYQUo4RHhHREpFOVdNUzh0QmJtVm16YkxoRkMzeDdVc0xGeTBrSzh1SFBFT3dQb2NKNUFUCkE1UHBvclNEMmFleHA0Z3VqYVp5c1JManpmY0dnaTdva0JFNlZVNWVqRE1iYS9lNERQNEJQUVg5VmtVQ0F3RUEKQVFLQ0FnQmZjMWdYcUp1ZmZMT3REcVlpbXh4UmIrSVVKT2NpWldaSndmZDVvY244NGtEcHFDZFZ2RUZvNnF4NgpzamQ5MURhb2xOUHdCSC9aSGxRMTR3aTNQNEluQzdzS0wwTXVEeTN5SXFUa0RPOWVwSzdPWWdVMWZyTFgvS0lCCjZlc2x2Ny9HYldFTzhhSjdKdktqM0U4NEFtcEg4UDgzenJIYTlJUnJTT3NEcmNNcEpEZHpSOXp1OW1IVDZMYmYKWC9UdC9KYTNkSW42YUxUZ0FSYkRKSjAvN0J3TFFOcXpqT0dUOWdzUWRhbGdMK2x5eEo4L1ViRndhRmVwNmgzdApvbzBHcHQ0ZWgwdTdueDhlNVd3Q2RnWmJsTnpnS3grMC9Gd3dLRHhQZVRFc2ZpOEJONmlkR2NjbVdzd3prTWdtCnJmbERaeGNSWTNRSlZIVHBCL0dTTWZXRFBPQ3dRdGltQk1WN3kxM2hPMTdPWXpSNDBMZnpUalJBbmtna2V2eWYKcFowb3dLR3o4QS9haHhRWWJmYVQ5VEhXV0wrYUpYeUhFanBKckp5aTg3UExVbzhsOFVydU56MDRWNXpLOFJPbgo2cG9EWmVtbm1EYWRlU09pK3hZRWlGT1NwSXNWbzlpcm9jUGFKN2YzYWpiNUU4RHpuN1o1MmhzL2R6akpLcFZJCm5mVDFkUU9SZEowSXRUNlRlQ2RTL0dpS25IS1RtNjR2T21IbmlJcm8rUGRhUmFjV0IrTUJ0VytRd0cyUStyRGkKc3g4NlpQbHRpTVpLMDZ5TVlyVHZUdGk2aFVGaUY5cWh4b3RGazdNQkNrZlIwYUVhaUREQUpKNm1jb1lpRUQ2QgpBVGJhVmpVaGNaUiswYkRST25PN0ozRk5rZmx3K2dMaVhvcXFRRW9pU2ZWb2h5SWY3UUtDQVFFQThjYTM5K0g4CjN3L2Qrcm0yUGNhM0RMQnBYaWU4Z3ZYcGpjazVYSkpvSGVmbnJjZWQrcFpXaTZEYncwYld0MEdtYkxmVjJNSlAKV2I1aTZzSXhmdkN3YlFqbHY0UnExMVA5ZEswT3poMnVpKzZ6cXVBMG5YTVcrN0lJS0cvdDhmS2NJZGRRNnRGcwpFclFVTFBDak56ODA2cHBiSlhPRmVvMW1BK293TGhHNlA3dDhCdlZHSk1NaTNxejNlSUNuVVE2eDNFY01ITXNuClhrM21DUzI1WUZaNk96cytFK254cGVraTAzZmQwblp3UE1jdElHZys1c3hleE9zREsrTHlvb2FqQnc5N0oyUzIKcUNNWXFtT0tLcmxEQ3Y1WmQ4dlZLN3hXVmpKRVhGTTNMZ2pieHBRcCtuVXNVVWxwS01LOVlGS0lRREl0RU9aMApWcWExTXJaOElzN1l5d0tDQVFFQXhBemZIa2pIVGlvTHdZbG5EcEk0MWlOTDh5Y0ZBallrTC94dWhPU2tlVkE4CjdRWDZPZUpDekR3Z0FUYXVqOWR6Y0wwby9yTndWV0xWcnQ3OXk3YnJvVDdFREZKWVNTY25GRXNMTlVWSXRncGkKckNSUXJTL1F2TkVGTmE5K0pRc1dmYkdBNHdIUTFaSjI4MFp1cWMvNlEyUi9kZVh3cUZBQVBHN2NIcEhHWlR6ZQoyRmFRUHFLRkV4WlEyZkpvRys0SVBRNHVQVERybXlGMmVUWXk2T3BaaDBHbWJRYlVTa1dFWDlQRmF1cHJIWVdGCk8wK25DaVVPNVRaMFZoaGR2dUNKMWdPclZHYzhBUlJtUVZ1aUNEWTZCaGlvVTU0ZmZsSXlDTXZ5a3MwcmRXZ3MKWVJ2TmN4TXNlRGJpTDRKSURkMHhiN1d4VUdmVjRVNHZPMks5Vms1N0x3S0NBUUVBMkd1eE1jcXd1RnRUc0tPYwpaaUFDcXZFZTRKRmhSVGtySHlnSW1MelZSaS9ZU3M1c3MycnZmWDA0T3N5bVZ0UUZUVHdoeUMzbktjWXFkVW52ClZGblBFMHJyblV2Qzk0elBUQ205SHZPaTBzK1JORndOdlFMUWgrME5NR1ZBOFZyaU44aXRQZ1RJWU5XaFdianQKNFA1TE45V0QwVHBmT1J4cFBRZmNxT0JsZjdjcmhtNzNvdUNwemZtMmE3OStCaWpKUFF5NzR1cFhDeXRmeHNlUApNSlU0Uk56NjdJaDFMclpKM2xGbDFvYitZT2xKazhDOHpZd1RLT0hWck9zeGxobyt4SXN2Q2t3MDFMelZ6Mi9hCnRmT3Y5NTlHSnQzbXE0ZWpJUFZPQy9iUlpmdTMvMEdSY2dpQTZ5SnpaM0VxWTVaOU1EbTU3VzdjcE5RRlRxZmEKNXEyUmtRS0NBUUErNGhZSzQ3TXg2aUNkTWxKaEJSdS82OUJucktOWm96NFdPalRFNFlXejk3MmpGU0Mrd2tsRQpzeUJjNDBvNGp4WFRHb2wwc04rZU03WndnY3dNTko3OXVHRXZ4cFhVMlA4YTdqc3BHaEVKZXVsTlo5U015R0orCnZkaWE4TEJZZDJiK2FCbjhOay9pd1Rqd0xTNC92NXI1Vk5uaFdpRElDK2tYZVVPWGRwQ1pWbDN3TEV2V0cxRHQKMzJHTmxzZzM5VENsVE5BZUJudjc1VTdYOEQrQ0gvRVpoa0E0aGxFL2hXN0JRZTczclRzd1creHhLc3BjWWFpVwpjdEg3NzVMYUw3Rm1lUVRTYk01OVZpcTZXZ2J0OVY3Rko5R09DSkQzZHF2ZjBITDlEVndjSzQ3WWt3OWlFc3RYCnY5cnEvREhhYUpGNzBGNlFlTTNNbDhSa212WTZJYkEzQW9JQkFRRGt6RmZLeG9HQ3dWUDlua3k4NmFQSjFvd2kKc2FDZEx6RjRWTENRZzkrUXJITzEyY0p5MFFQUnJ2cUQyMGp1cDFlOWJhWVZzbkdYc1FZTFg2NVR6UzJSSCtlSAp6S0NPTTdnMVE3djMxNWpjMDMvN1lQck4rb3RrV0VBOUkyaDZjUE1vY3c0aERTNk02OFlxQVlKTS9RclVhenZhCnhBTFJaZEVkQW1xWDA4VHhuY1hRUEVxYkk0ZnlSZ2pVM1BYR3RRaFFFbERpR2kwbThjQTJNTXdsR1RmbTdOSXgKaENjZ2ZkL296TEp2VUhiMkxLRi82cXEySmJVRHlOMkVoK0xSZUJjdnp6Y1grZE5MdGQxY0Uvcm1SM2hMbWxmNgo3KzRpTVMxK0t1eWV3VlJVUEE1c1F1aUYyVUVoeEs1MUpZK1FpOG9HbERKdGRrOXB3QlZNN1F0WW9KVEwKLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K",
|
||||
"Certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZvakNDQklxZ0F3SUJBZ0lUQVAvRTgvRk43NTdtN0dvRklyWEF1cU0zblRBTkJna3Foa2lHOXcwQkFRc0YKQURBZk1SMHdHd1lEVlFRRERCUm9NbkJ3ZVNCb01tTnJaWElnWm1GclpTQkRRVEFlRncweE9EQXhNVFV3TnpJNQpNREJhRncweE9EQTBNVFV3TnpJNU1EQmFNRVF4RXpBUkJnTlZCQU1UQ214dlkyRnNNUzVqYjIweExUQXJCZ05WCkJBVVRKR1ptWXpSbU0yWXhOR1JsWmpsbFpUWmxZelpoTURVeU1tSTFZekJpWVdFek16YzVaRENDQWlJd0RRWUoKS29aSWh2Y05BUUVCQlFBRGdnSVBBRENDQWdvQ2dnSUJBTGtvVE9NYzZzNlRWNlVtZXFmMzdmaFpNbFlkdmowWApxMDgxTGZBMENjZWtSSEFQeExMeGJCMVhEU1JTeXBLV3QzdkJzQ0JRSXdUK3F6dnNZUmtYR1VaTFFqZTE5dlp5Cm5WeFpiM1hOcXNCNnkwTW1Jak5NUFBFcXplVXcyK3Y4ekh1cnRMYlEyMnBBcGU4M1FTVUs4NUY4bmpDOG1OekMKdTRtUHRNNmYyNDlWNkp6UXJtUVBuWU1aZFVwRmpBbEJmd1RodVdxemw2YUM4U2hqMTRMRnNUVEVpbFd5UjhOLwo3NCtlVzNqcjBaM2pxVEdWNkhOUFkyRHJnSXduWkZjck5FVjZ5dVhBUStDNWsxcjdNZEZrdm5pMDhoVE96eTdtCk44ZzJRakZ4M0ZZb3c4WUpVbTlMbmt6NldrMXc5R3k0NEF6UFJBN1RNblFiTlFqbVdUbDZHNndzZ20rY3BVdkYKbE1FckZMT1VWcW44bEo2ZjYwZUpiN0hXUFhWSCtEcndpMWE2R0l2ZVdoYkZhcEN5dkM1L3dOck10V29namJjUgpZeWZiVnBudUZnbXNPSVVoZnc4ZVhqT1hGeG0wV3NKMVZTQVZWc2NiT2REdEVYa0hhSnFUM1FEQ2t3bVRrUlpWCjRleldPNmdGbFE1SXNQSzQ2TXhzT3Yxci9UUnNVdWZXL3UrR2o5YTlLdmVyeFAwTC85eS9uSi9HK3lBUC9qbTIKaWNQQnpyUlpzUEJkS2dRc05KR25sQ1dkYUVaOFdsd0NmQThSZ3lSUFZqRXZMUVc1bFpzMnk0UlF0OGUxTEN4Ywp0SkN2TGh6eERzRDZIQ2VRRXdPVDZhSzBnOW1uc2FlSUxvMm1jckVTNDgzM0JvSXU2SkFST2xWT1hvd3pHMnYzCnVBeitBVDBGL1ZaRkFnTUJBQUdqZ2dHd01JSUJyREFPQmdOVkhROEJBZjhFQkFNQ0JhQXdIUVlEVlIwbEJCWXcKRkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01Bd0dBMVVkRXdFQi93UUNNQUF3SFFZRFZSME9CQllFRk5LZQpBVUZYc2Z2N2lML0lYVVBXdzY2ZU5jQnhNQjhHQTFVZEl3UVlNQmFBRlB0NFR4TDVZQldETEo4WGZ6UVpzeTQyCjZrR0pNR1lHQ0NzR0FRVUZCd0VCQkZvd1dEQWlCZ2dyQmdFRkJRY3dBWVlXYUhSMGNEb3ZMekV5Tnk0d0xqQXUKTVRvME1EQXlMekF5QmdnckJnRUZCUWN3QW9ZbWFIUjBjRG92THpFeU55NHdMakF1TVRvME1EQXdMMkZqYldVdgphWE56ZFdWeUxXTmxjblF3T1FZRFZSMFJCREl3TUlJS2JHOWpZV3d4TG1OdmJZSVFkR1Z6ZERFdWJHOWpZV3d4CkxtTnZiWUlRZEdWemRESXViRzlqWVd3eExtTnZiVEFuQmdOVkhSOEVJREFlTUJ5Z0dxQVloaFpvZEhSd09pOHYKWlhoaGJYQnNaUzVqYjIwdlkzSnNNR0VHQTFVZElBUmFNRmd3Q0FZR1o0RU1BUUlCTUV3R0F5b0RCREJGTUNJRwpDQ3NHQVFVRkJ3SUJGaFpvZEhSd09pOHZaWGhoYlhCc1pTNWpiMjB2WTNCek1COEdDQ3NHQVFVRkJ3SUNNQk1NCkVVUnZJRmRvWVhRZ1ZHaHZkU0JYYVd4ME1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQ3A0Q2FxZlR4THNQTzQKS2JueDJZdEc4bTN3MC9keTVVR1VRNjZHbGxPVTk0L2I0MmNhbTRuNUZrTWlpZ01IaUx4c2JZVXh0cDZKQ3R5cQpLKzFNcDFWWEtSTTVKbFBTNWRIaWhxdHk1U3BrTUhjampwQSs3U2YyVWtoNmpKRWYxTUVJY2JnWnpJRk5IT0hYClVUUUppVFhKcno3blJDZnlQWFZtbWErUGtIRlU4R0VEVzJGOVptU1kzVFBiQWhiWkV2UkZubjUrR1lxbkZuancKWWw3Y0I2MXYwRzVpOGQwbnVvbTB4a2hiNTU3Y3BiZHhLblhsaFU4N2RZSTR5SUdPdUFGUWpYcXFXN2NIZCtXUQpWSDB2dFA3cEgrRmt2YnY4WkkxMHMrNU5ZcCtzZjFQZGQxekJsRmdNSGF3dnFFYUg3SU9sejdkajlCdmtVc0dpClhxQWVqQnFPCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVpakNDQTNLZ0F3SUJBZ0lDRWswd0RRWUpLb1pJaHZjTkFRRUxCUUF3S3pFcE1DY0dBMVVFQXd3Z1kyRmoKYTJ4cGJtY2dZM0o1Y0hSdlozSmhjR2hsY2lCbVlXdGxJRkpQVDFRd0hoY05NVFV4TURJeE1qQXhNVFV5V2hjTgpNakF4TURFNU1qQXhNVFV5V2pBZk1SMHdHd1lEVlFRREV4Um9ZWEJ3ZVNCb1lXTnJaWElnWm1GclpTQkRRVENDCkFTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTUlLUjNtYUJjVVNzbmNYWXpRVDEzRDUKTnIrWjNtTHhNTWgzVFVkdDZzQUNtcWJKMGJ0UmxnWGZNdE5MTTJPVTFJNmEzSnUrdElaU2RuMnYyMUpCd3Z4VQp6cFpRNHp5MmNpbUlpTVFEWkNRSEp3ekM5R1puOEhhVzA5MWl6OUgwR28zQTdXRFh3WU5tc2RMTlJpMDBvMTRVCmpvYVZxYVBzWXJaV3ZSS2FJUnFhVTBoSG1TMEFXd1FTdk4vOTNpTUlYdXlpd3l3bWt3S2JXbm54Q1EvZ3NjdEsKRlV0Y05yd0V4OVdnajZLbGh3RFR5STFRV1NCYnhWWU55VWdQRnpLeHJTbXdNTzB5TmZmN2hvK1FUOXg1K1kvNwpYRTU5UzRNYzRaWHhjWEtldy9nU2xOOVU1bXZUK0QyQmhEdGtDdXBkZnNaTkNRV3AyN0ErYi9EbXJGSTlOcXNDCkF3RUFBYU9DQWNJd2dnRytNQklHQTFVZEV3RUIvd1FJTUFZQkFmOENBUUF3UXdZRFZSMGVCRHd3T3FFNE1BYUMKQkM1dGFXd3dDb2NJQUFBQUFBQUFBQUF3SW9jZ0FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQQpBQUFBQUFBd0RnWURWUjBQQVFIL0JBUURBZ0dHTUg4R0NDc0dBUVVGQndFQkJITXdjVEF5QmdnckJnRUZCUWN3CkFZWW1hSFIwY0RvdkwybHpjbWN1ZEhKMWMzUnBaQzV2WTNOd0xtbGtaVzUwY25WemRDNWpiMjB3T3dZSUt3WUIKQlFVSE1BS0dMMmgwZEhBNkx5OWhjSEJ6TG1sa1pXNTBjblZ6ZEM1amIyMHZjbTl2ZEhNdlpITjBjbTl2ZEdOaAplRE11Y0Rkak1COEdBMVVkSXdRWU1CYUFGT21rUCs2ZXBlYnkxZGQ1WUR5VHBpNGtqcGVxTUZRR0ExVWRJQVJOCk1Fc3dDQVlHWjRFTUFRSUJNRDhHQ3lzR0FRUUJndDhUQVFFQk1EQXdMZ1lJS3dZQkJRVUhBZ0VXSW1oMGRIQTYKTHk5amNITXVjbTl2ZEMxNE1TNXNaWFJ6Wlc1amNubHdkQzV2Y21jd1BBWURWUjBmQkRVd016QXhvQytnTFlZcgphSFIwY0RvdkwyTnliQzVwWkdWdWRISjFjM1F1WTI5dEwwUlRWRkpQVDFSRFFWZ3pRMUpNTG1OeWJEQWRCZ05WCkhRNEVGZ1FVKzNoUEV2bGdGWU1zbnhkL05CbXpMamJxUVlrd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFBMFkKQWVMWE9rbHg0aGhDaWtVVWwrQmRuRmZuMWcwVzVBaVFMVk5JT0w2UG5xWHUwd2puaE55aHFkd25maFlNbm95NAppZFJoNGxCNnB6OEdmOXBubExkL0RuV1NWM2dTKy9JL21BbDFkQ2tLYnk2SDJWNzkwZTZJSG1JSzJLWW0zam0rClUrK0ZJZEdwQmRzUVRTZG1pWC9yQXl1eE1ETTBhZE1rTkJ3VGZRbVpRQ3o2bkdIdzFRY1NQWk12WnBzQzhTa3YKZWt6eHNqRjFvdE9yTVVQTlBRdnRUV3JWeDhHbFIycWZ4LzR4YlFhMXYyZnJOdkZCQ21PNTlnb3oram5XdmZUdApqMk5qd0RaN3ZsTUJzUG0xNmRiS1lDODQwdXZSb1pqeHFzZGMzQ2hDWmpxaW1GcWxORy94b1BBOCtkVGljWnpDClhFOWlqUEljdlc2eTFhYTNiR3c9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"ChallengeCerts": {}
|
||||
}
|
||||
@@ -1,818 +0,0 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"sort"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
acmeprovider "github.com/containous/traefik/provider/acme"
|
||||
"github.com/containous/traefik/tls/generate"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/xenolf/lego/acme"
|
||||
)
|
||||
|
||||
func TestDomainsSet(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
expected types.Domains
|
||||
}{
|
||||
{
|
||||
input: "",
|
||||
expected: types.Domains{},
|
||||
},
|
||||
{
|
||||
input: "foo1.com",
|
||||
expected: types.Domains{
|
||||
types.Domain{Main: "foo1.com"},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "foo2.com,bar.net",
|
||||
expected: types.Domains{
|
||||
types.Domain{
|
||||
Main: "foo2.com",
|
||||
SANs: []string{"bar.net"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "foo3.com,bar1.net,bar2.net,bar3.net",
|
||||
expected: types.Domains{
|
||||
types.Domain{
|
||||
Main: "foo3.com",
|
||||
SANs: []string{"bar1.net", "bar2.net", "bar3.net"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.input, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
domains := types.Domains{}
|
||||
domains.Set(test.input)
|
||||
assert.Exactly(t, test.expected, domains)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDomainsSetAppend(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
expected types.Domains
|
||||
}{
|
||||
{
|
||||
input: "",
|
||||
expected: types.Domains{},
|
||||
},
|
||||
{
|
||||
input: "foo1.com",
|
||||
expected: types.Domains{
|
||||
types.Domain{Main: "foo1.com"},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "foo2.com,bar.net",
|
||||
expected: types.Domains{
|
||||
types.Domain{Main: "foo1.com"},
|
||||
types.Domain{
|
||||
Main: "foo2.com",
|
||||
SANs: []string{"bar.net"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
input: "foo3.com,bar1.net,bar2.net,bar3.net",
|
||||
expected: types.Domains{
|
||||
types.Domain{Main: "foo1.com"},
|
||||
types.Domain{
|
||||
Main: "foo2.com",
|
||||
SANs: []string{"bar.net"},
|
||||
},
|
||||
types.Domain{
|
||||
Main: "foo3.com",
|
||||
SANs: []string{"bar1.net", "bar2.net", "bar3.net"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// append to
|
||||
domains := types.Domains{}
|
||||
for _, test := range testCases {
|
||||
t.Run(test.input, func(t *testing.T) {
|
||||
|
||||
domains.Set(test.input)
|
||||
assert.Exactly(t, test.expected, domains)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCertificatesRenew(t *testing.T) {
|
||||
foo1Cert, foo1Key, _ := generate.KeyPair("foo1.com", time.Now())
|
||||
foo2Cert, foo2Key, _ := generate.KeyPair("foo2.com", time.Now())
|
||||
|
||||
domainsCertificates := DomainsCertificates{
|
||||
lock: sync.RWMutex{},
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Domains: types.Domain{
|
||||
Main: "foo1.com"},
|
||||
Certificate: &Certificate{
|
||||
Domain: "foo1.com",
|
||||
CertURL: "url",
|
||||
CertStableURL: "url",
|
||||
PrivateKey: foo1Key,
|
||||
Certificate: foo1Cert,
|
||||
},
|
||||
},
|
||||
{
|
||||
Domains: types.Domain{
|
||||
Main: "foo2.com"},
|
||||
Certificate: &Certificate{
|
||||
Domain: "foo2.com",
|
||||
CertURL: "url",
|
||||
CertStableURL: "url",
|
||||
PrivateKey: foo2Key,
|
||||
Certificate: foo2Cert,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
foo1Cert, foo1Key, _ = generate.KeyPair("foo1.com", time.Now())
|
||||
newCertificate := &Certificate{
|
||||
Domain: "foo1.com",
|
||||
CertURL: "url",
|
||||
CertStableURL: "url",
|
||||
PrivateKey: foo1Key,
|
||||
Certificate: foo1Cert,
|
||||
}
|
||||
|
||||
err := domainsCertificates.renewCertificates(newCertificate, types.Domain{Main: "foo1.com"})
|
||||
if err != nil {
|
||||
t.Errorf("Error in renewCertificates :%v", err)
|
||||
}
|
||||
|
||||
if len(domainsCertificates.Certs) != 2 {
|
||||
t.Errorf("Expected domainsCertificates length %d %+v\nGot %+v", 2, domainsCertificates.Certs, len(domainsCertificates.Certs))
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(domainsCertificates.Certs[0].Certificate, newCertificate) {
|
||||
t.Errorf("Expected new certificate %+v \nGot %+v", newCertificate, domainsCertificates.Certs[0].Certificate)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveDuplicates(t *testing.T) {
|
||||
now := time.Now()
|
||||
fooCert, fooKey, _ := generate.KeyPair("foo.com", now)
|
||||
foo24Cert, foo24Key, _ := generate.KeyPair("foo.com", now.Add(24*time.Hour))
|
||||
foo48Cert, foo48Key, _ := generate.KeyPair("foo.com", now.Add(48*time.Hour))
|
||||
barCert, barKey, _ := generate.KeyPair("bar.com", now)
|
||||
domainsCertificates := DomainsCertificates{
|
||||
lock: sync.RWMutex{},
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Domains: types.Domain{
|
||||
Main: "foo.com"},
|
||||
Certificate: &Certificate{
|
||||
Domain: "foo.com",
|
||||
CertURL: "url",
|
||||
CertStableURL: "url",
|
||||
PrivateKey: foo24Key,
|
||||
Certificate: foo24Cert,
|
||||
},
|
||||
},
|
||||
{
|
||||
Domains: types.Domain{
|
||||
Main: "foo.com"},
|
||||
Certificate: &Certificate{
|
||||
Domain: "foo.com",
|
||||
CertURL: "url",
|
||||
CertStableURL: "url",
|
||||
PrivateKey: foo48Key,
|
||||
Certificate: foo48Cert,
|
||||
},
|
||||
},
|
||||
{
|
||||
Domains: types.Domain{
|
||||
Main: "foo.com"},
|
||||
Certificate: &Certificate{
|
||||
Domain: "foo.com",
|
||||
CertURL: "url",
|
||||
CertStableURL: "url",
|
||||
PrivateKey: fooKey,
|
||||
Certificate: fooCert,
|
||||
},
|
||||
},
|
||||
{
|
||||
Domains: types.Domain{
|
||||
Main: "bar.com"},
|
||||
Certificate: &Certificate{
|
||||
Domain: "bar.com",
|
||||
CertURL: "url",
|
||||
CertStableURL: "url",
|
||||
PrivateKey: barKey,
|
||||
Certificate: barCert,
|
||||
},
|
||||
},
|
||||
{
|
||||
Domains: types.Domain{
|
||||
Main: "foo.com"},
|
||||
Certificate: &Certificate{
|
||||
Domain: "foo.com",
|
||||
CertURL: "url",
|
||||
CertStableURL: "url",
|
||||
PrivateKey: foo48Key,
|
||||
Certificate: foo48Cert,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
domainsCertificates.Init()
|
||||
|
||||
if len(domainsCertificates.Certs) != 2 {
|
||||
t.Errorf("Expected domainsCertificates length %d %+v\nGot %+v", 2, domainsCertificates.Certs, len(domainsCertificates.Certs))
|
||||
}
|
||||
|
||||
for _, cert := range domainsCertificates.Certs {
|
||||
switch cert.Domains.Main {
|
||||
case "bar.com":
|
||||
continue
|
||||
case "foo.com":
|
||||
if !cert.tlsCert.Leaf.NotAfter.Equal(now.Add(48 * time.Hour).Truncate(1 * time.Second)) {
|
||||
t.Errorf("Bad expiration %s date for domain %+v, now %s", cert.tlsCert.Leaf.NotAfter.String(), cert, now.Add(48*time.Hour).Truncate(1*time.Second).String())
|
||||
}
|
||||
default:
|
||||
t.Errorf("Unknown domain %+v", cert)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoPreCheckOverride(t *testing.T) {
|
||||
acme.PreCheckDNS = nil // Irreversable - but not expecting real calls into this during testing process
|
||||
err := dnsOverrideDelay(0)
|
||||
if err != nil {
|
||||
t.Errorf("Error in dnsOverrideDelay :%v", err)
|
||||
}
|
||||
if acme.PreCheckDNS != nil {
|
||||
t.Error("Unexpected change to acme.PreCheckDNS when leaving DNS verification as is.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSillyPreCheckOverride(t *testing.T) {
|
||||
err := dnsOverrideDelay(-5)
|
||||
if err == nil {
|
||||
t.Error("Missing expected error in dnsOverrideDelay!")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreCheckOverride(t *testing.T) {
|
||||
acme.PreCheckDNS = nil // Irreversable - but not expecting real calls into this during testing process
|
||||
err := dnsOverrideDelay(5)
|
||||
if err != nil {
|
||||
t.Errorf("Error in dnsOverrideDelay :%v", err)
|
||||
}
|
||||
if acme.PreCheckDNS == nil {
|
||||
t.Error("No change to acme.PreCheckDNS when meant to be adding enforcing override function.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAcmeClientCreation(t *testing.T) {
|
||||
acme.PreCheckDNS = nil // Irreversable - but not expecting real calls into this during testing process
|
||||
// Lengthy setup to avoid external web requests - oh for easier golang testing!
|
||||
account := &Account{Email: "f@f"}
|
||||
account.PrivateKey, _ = base64.StdEncoding.DecodeString(`
|
||||
MIIBPAIBAAJBAMp2Ni92FfEur+CAvFkgC12LT4l9D53ApbBpDaXaJkzzks+KsLw9zyAxvlrfAyTCQ
|
||||
7tDnEnIltAXyQ0uOFUUdcMCAwEAAQJAK1FbipATZcT9cGVa5x7KD7usytftLW14heQUPXYNV80r/3
|
||||
lmnpvjL06dffRpwkYeN8DATQF/QOcy3NNNGDw/4QIhAPAKmiZFxA/qmRXsuU8Zhlzf16WrNZ68K64
|
||||
asn/h3qZrAiEA1+wFR3WXCPIolOvd7AHjfgcTKQNkoMPywU4FYUNQ1AkCIQDv8yk0qPjckD6HVCPJ
|
||||
llJh9MC0svjevGtNlxJoE3lmEQIhAKXy1wfZ32/XtcrnENPvi6lzxI0T94X7s5pP3aCoPPoJAiEAl
|
||||
cijFkALeQp/qyeXdFld2v9gUN3eCgljgcl0QweRoIc=---`)
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(`{
|
||||
"GPHhmRVEDas": "https://community.letsencrypt.org/t/adding-random-entries-to-the-directory/33417",
|
||||
"keyChange": "https://foo/acme/key-change",
|
||||
"meta": {
|
||||
"termsOfService": "https://boulder:4431/terms/v7"
|
||||
},
|
||||
"newAccount": "https://foo/acme/new-acct",
|
||||
"newNonce": "https://foo/acme/new-nonce",
|
||||
"newOrder": "https://foo/acme/new-order",
|
||||
"revokeCert": "https://foo/acme/revoke-cert"
|
||||
}`))
|
||||
}))
|
||||
defer ts.Close()
|
||||
a := ACME{DNSChallenge: &acmeprovider.DNSChallenge{Provider: "manual", DelayBeforeCheck: 10}, CAServer: ts.URL}
|
||||
|
||||
client, err := a.buildACMEClient(account)
|
||||
if err != nil {
|
||||
t.Errorf("Error in buildACMEClient: %v", err)
|
||||
}
|
||||
if client == nil {
|
||||
t.Error("No client from buildACMEClient!")
|
||||
}
|
||||
if acme.PreCheckDNS == nil {
|
||||
t.Error("No change to acme.PreCheckDNS when meant to be adding enforcing override function.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAcme_getUncheckedCertificates(t *testing.T) {
|
||||
mm := make(map[string]*tls.Certificate)
|
||||
mm["*.containo.us"] = &tls.Certificate{}
|
||||
mm["traefik.acme.io"] = &tls.Certificate{}
|
||||
|
||||
a := ACME{TLSConfig: &tls.Config{NameToCertificate: mm}}
|
||||
|
||||
domains := []string{"traefik.containo.us", "trae.containo.us"}
|
||||
uncheckedDomains := a.getUncheckedDomains(domains, nil)
|
||||
assert.Empty(t, uncheckedDomains)
|
||||
domains = []string{"traefik.acme.io", "trae.acme.io"}
|
||||
uncheckedDomains = a.getUncheckedDomains(domains, nil)
|
||||
assert.Len(t, uncheckedDomains, 1)
|
||||
domainsCertificates := DomainsCertificates{Certs: []*DomainsCertificate{
|
||||
{
|
||||
tlsCert: &tls.Certificate{},
|
||||
Domains: types.Domain{
|
||||
Main: "*.acme.wtf",
|
||||
SANs: []string{"trae.acme.io"},
|
||||
},
|
||||
},
|
||||
}}
|
||||
account := Account{DomainsCertificate: domainsCertificates}
|
||||
uncheckedDomains = a.getUncheckedDomains(domains, &account)
|
||||
assert.Empty(t, uncheckedDomains)
|
||||
}
|
||||
|
||||
func TestAcme_getProvidedCertificate(t *testing.T) {
|
||||
mm := make(map[string]*tls.Certificate)
|
||||
mm["*.containo.us"] = &tls.Certificate{}
|
||||
mm["traefik.acme.io"] = &tls.Certificate{}
|
||||
|
||||
a := ACME{TLSConfig: &tls.Config{NameToCertificate: mm}}
|
||||
|
||||
domain := "traefik.containo.us"
|
||||
certificate := a.getProvidedCertificate(domain)
|
||||
assert.NotNil(t, certificate)
|
||||
domain = "trae.acme.io"
|
||||
certificate = a.getProvidedCertificate(domain)
|
||||
assert.Nil(t, certificate)
|
||||
}
|
||||
|
||||
func TestAcme_getValidDomain(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
domains []string
|
||||
wildcardAllowed bool
|
||||
dnsChallenge *acmeprovider.DNSChallenge
|
||||
expectedErr string
|
||||
expectedDomains []string
|
||||
}{
|
||||
{
|
||||
desc: "valid wildcard",
|
||||
domains: []string{"*.traefik.wtf"},
|
||||
dnsChallenge: &acmeprovider.DNSChallenge{},
|
||||
wildcardAllowed: true,
|
||||
expectedErr: "",
|
||||
expectedDomains: []string{"*.traefik.wtf"},
|
||||
},
|
||||
{
|
||||
desc: "no wildcard",
|
||||
domains: []string{"traefik.wtf", "foo.traefik.wtf"},
|
||||
dnsChallenge: &acmeprovider.DNSChallenge{},
|
||||
expectedErr: "",
|
||||
wildcardAllowed: true,
|
||||
expectedDomains: []string{"traefik.wtf", "foo.traefik.wtf"},
|
||||
},
|
||||
{
|
||||
desc: "unauthorized wildcard",
|
||||
domains: []string{"*.traefik.wtf"},
|
||||
dnsChallenge: &acmeprovider.DNSChallenge{},
|
||||
wildcardAllowed: false,
|
||||
expectedErr: "unable to generate a wildcard certificate for domain \"*.traefik.wtf\" from a 'Host' rule",
|
||||
expectedDomains: nil,
|
||||
},
|
||||
{
|
||||
desc: "no domain",
|
||||
domains: []string{},
|
||||
dnsChallenge: nil,
|
||||
wildcardAllowed: true,
|
||||
expectedErr: "unable to generate a certificate when no domain is given",
|
||||
expectedDomains: nil,
|
||||
},
|
||||
{
|
||||
desc: "no DNSChallenge",
|
||||
domains: []string{"*.traefik.wtf", "foo.traefik.wtf"},
|
||||
dnsChallenge: nil,
|
||||
wildcardAllowed: true,
|
||||
expectedErr: "unable to generate a wildcard certificate for domain \"*.traefik.wtf,foo.traefik.wtf\" : ACME needs a DNSChallenge",
|
||||
expectedDomains: nil,
|
||||
},
|
||||
{
|
||||
desc: "unauthorized wildcard with SAN",
|
||||
domains: []string{"*.*.traefik.wtf", "foo.traefik.wtf"},
|
||||
dnsChallenge: &acmeprovider.DNSChallenge{},
|
||||
wildcardAllowed: true,
|
||||
expectedErr: "unable to generate a wildcard certificate for domain \"*.*.traefik.wtf,foo.traefik.wtf\" : ACME does not allow '*.*' wildcard domain",
|
||||
expectedDomains: nil,
|
||||
},
|
||||
{
|
||||
desc: "wildcard with SANs",
|
||||
domains: []string{"*.traefik.wtf", "traefik.wtf"},
|
||||
dnsChallenge: &acmeprovider.DNSChallenge{},
|
||||
wildcardAllowed: true,
|
||||
expectedErr: "",
|
||||
expectedDomains: []string{"*.traefik.wtf", "traefik.wtf"},
|
||||
},
|
||||
{
|
||||
desc: "unexpected SANs",
|
||||
domains: []string{"*.traefik.wtf", "*.acme.wtf"},
|
||||
dnsChallenge: &acmeprovider.DNSChallenge{},
|
||||
wildcardAllowed: true,
|
||||
expectedErr: "unable to generate a certificate for domains \"*.traefik.wtf,*.acme.wtf\": SANs can not be a wildcard domain",
|
||||
expectedDomains: nil,
|
||||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
a := ACME{}
|
||||
if test.dnsChallenge != nil {
|
||||
a.DNSChallenge = test.dnsChallenge
|
||||
}
|
||||
domains, err := a.getValidDomains(test.domains, test.wildcardAllowed)
|
||||
|
||||
if len(test.expectedErr) > 0 {
|
||||
assert.EqualError(t, err, test.expectedErr, "Unexpected error.")
|
||||
} else {
|
||||
assert.Equal(t, len(test.expectedDomains), len(domains), "Unexpected domains.")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAcme_getCertificateForDomain(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
domain string
|
||||
dc *DomainsCertificates
|
||||
expected *DomainsCertificate
|
||||
expectedFound bool
|
||||
}{
|
||||
{
|
||||
desc: "non-wildcard exact match",
|
||||
domain: "foo.traefik.wtf",
|
||||
dc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Domains: types.Domain{
|
||||
Main: "foo.traefik.wtf",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &DomainsCertificate{
|
||||
Domains: types.Domain{
|
||||
Main: "foo.traefik.wtf",
|
||||
},
|
||||
},
|
||||
expectedFound: true,
|
||||
},
|
||||
{
|
||||
desc: "non-wildcard no match",
|
||||
domain: "bar.traefik.wtf",
|
||||
dc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Domains: types.Domain{
|
||||
Main: "foo.traefik.wtf",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
expectedFound: false,
|
||||
},
|
||||
{
|
||||
desc: "wildcard match",
|
||||
domain: "foo.traefik.wtf",
|
||||
dc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Domains: types.Domain{
|
||||
Main: "*.traefik.wtf",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &DomainsCertificate{
|
||||
Domains: types.Domain{
|
||||
Main: "*.traefik.wtf",
|
||||
},
|
||||
},
|
||||
expectedFound: true,
|
||||
},
|
||||
{
|
||||
desc: "wildcard no match",
|
||||
domain: "foo.traefik.wtf",
|
||||
dc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Domains: types.Domain{
|
||||
Main: "*.bar.traefik.wtf",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
expectedFound: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
got, found := test.dc.getCertificateForDomain(test.domain)
|
||||
assert.Equal(t, test.expectedFound, found)
|
||||
assert.Equal(t, test.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemoveEmptyCertificates(t *testing.T) {
|
||||
now := time.Now()
|
||||
fooCert, fooKey, _ := generate.KeyPair("foo.com", now)
|
||||
acmeCert, acmeKey, _ := generate.KeyPair("acme.wtf", now.Add(24*time.Hour))
|
||||
barCert, barKey, _ := generate.KeyPair("bar.com", now)
|
||||
testCases := []struct {
|
||||
desc string
|
||||
dc *DomainsCertificates
|
||||
expectedDc *DomainsCertificates
|
||||
}{
|
||||
{
|
||||
desc: "No empty certificate",
|
||||
dc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: fooCert,
|
||||
PrivateKey: fooKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "foo.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: acmeCert,
|
||||
PrivateKey: acmeKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "acme.wtf",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: barCert,
|
||||
PrivateKey: barKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "bar.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedDc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: fooCert,
|
||||
PrivateKey: fooKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "foo.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: acmeCert,
|
||||
PrivateKey: acmeKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "acme.wtf",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: barCert,
|
||||
PrivateKey: barKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "bar.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "First certificate is nil",
|
||||
dc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Domains: types.Domain{
|
||||
Main: "foo.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: acmeCert,
|
||||
PrivateKey: acmeKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "acme.wtf",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: barCert,
|
||||
PrivateKey: barKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "bar.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedDc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: acmeCert,
|
||||
PrivateKey: acmeKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "acme.wtf",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: nil,
|
||||
PrivateKey: barKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "bar.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Last certificate is empty",
|
||||
dc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: fooCert,
|
||||
PrivateKey: fooKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "foo.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: acmeCert,
|
||||
PrivateKey: acmeKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "acme.wtf",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{},
|
||||
Domains: types.Domain{
|
||||
Main: "bar.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedDc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: fooCert,
|
||||
PrivateKey: fooKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "foo.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: acmeCert,
|
||||
PrivateKey: acmeKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "acme.wtf",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "First and last certificates are nil or empty",
|
||||
dc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Domains: types.Domain{
|
||||
Main: "foo.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: acmeCert,
|
||||
PrivateKey: acmeKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "acme.wtf",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{},
|
||||
Domains: types.Domain{
|
||||
Main: "bar.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedDc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Certificate: &Certificate{
|
||||
Certificate: acmeCert,
|
||||
PrivateKey: acmeKey,
|
||||
},
|
||||
Domains: types.Domain{
|
||||
Main: "acme.wtf",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "All certificates are nil or empty",
|
||||
dc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{
|
||||
{
|
||||
Domains: types.Domain{
|
||||
Main: "foo.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
Domains: types.Domain{
|
||||
Main: "foo24.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
Certificate: &Certificate{},
|
||||
Domains: types.Domain{
|
||||
Main: "bar.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedDc: &DomainsCertificates{
|
||||
Certs: []*DomainsCertificate{},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
a := &Account{DomainsCertificate: *test.dc}
|
||||
a.Init()
|
||||
|
||||
assert.Equal(t, len(test.expectedDc.Certs), len(a.DomainsCertificate.Certs))
|
||||
sort.Sort(&a.DomainsCertificate)
|
||||
sort.Sort(test.expectedDc)
|
||||
for key, value := range test.expectedDc.Certs {
|
||||
assert.Equal(t, value.Domains.Main, a.DomainsCertificate.Certs[key].Domains.Main)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/cenk/backoff"
|
||||
"github.com/containous/traefik/cluster"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/xenolf/lego/acme"
|
||||
)
|
||||
|
||||
var _ acme.ChallengeProviderTimeout = (*challengeHTTPProvider)(nil)
|
||||
|
||||
type challengeHTTPProvider struct {
|
||||
store cluster.Store
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
func (c *challengeHTTPProvider) getTokenValue(token, domain string) []byte {
|
||||
log.Debugf("Looking for an existing ACME challenge for token %v...", token)
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
account := c.store.Get().(*Account)
|
||||
if account.HTTPChallenge == nil {
|
||||
return []byte{}
|
||||
}
|
||||
var result []byte
|
||||
operation := func() error {
|
||||
var ok bool
|
||||
if result, ok = account.HTTPChallenge[token][domain]; !ok {
|
||||
return fmt.Errorf("cannot find challenge for token %v", token)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
notify := func(err error, time time.Duration) {
|
||||
log.Errorf("Error getting challenge for token retrying in %s", time)
|
||||
}
|
||||
ebo := backoff.NewExponentialBackOff()
|
||||
ebo.MaxElapsedTime = 60 * time.Second
|
||||
err := backoff.RetryNotify(safe.OperationWithRecover(operation), ebo, notify)
|
||||
if err != nil {
|
||||
log.Errorf("Error getting challenge for token: %v", err)
|
||||
return []byte{}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (c *challengeHTTPProvider) Present(domain, token, keyAuth string) error {
|
||||
log.Debugf("Challenge Present %s", domain)
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
transaction, object, err := c.store.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
account := object.(*Account)
|
||||
if account.HTTPChallenge == nil {
|
||||
account.HTTPChallenge = map[string]map[string][]byte{}
|
||||
}
|
||||
if _, ok := account.HTTPChallenge[token]; !ok {
|
||||
account.HTTPChallenge[token] = map[string][]byte{}
|
||||
}
|
||||
account.HTTPChallenge[token][domain] = []byte(keyAuth)
|
||||
return transaction.Commit(account)
|
||||
}
|
||||
|
||||
func (c *challengeHTTPProvider) CleanUp(domain, token, keyAuth string) error {
|
||||
log.Debugf("Challenge CleanUp %s", domain)
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
transaction, object, err := c.store.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
account := object.(*Account)
|
||||
if _, ok := account.HTTPChallenge[token]; ok {
|
||||
if _, domainOk := account.HTTPChallenge[token][domain]; domainOk {
|
||||
delete(account.HTTPChallenge[token], domain)
|
||||
}
|
||||
if len(account.HTTPChallenge[token]) == 0 {
|
||||
delete(account.HTTPChallenge, token)
|
||||
}
|
||||
}
|
||||
return transaction.Commit(account)
|
||||
}
|
||||
|
||||
func (c *challengeHTTPProvider) Timeout() (timeout, interval time.Duration) {
|
||||
return 60 * time.Second, 5 * time.Second
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/provider/acme"
|
||||
)
|
||||
|
||||
// LocalStore is a store using a file as storage
|
||||
type LocalStore struct {
|
||||
file string
|
||||
}
|
||||
|
||||
// NewLocalStore create a LocalStore
|
||||
func NewLocalStore(file string) *LocalStore {
|
||||
return &LocalStore{
|
||||
file: file,
|
||||
}
|
||||
}
|
||||
|
||||
// Get loads file into store and returns the Account
|
||||
func (s *LocalStore) Get() (*Account, error) {
|
||||
account := &Account{}
|
||||
|
||||
hasData, err := acme.CheckFile(s.file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if hasData {
|
||||
f, err := os.Open(s.file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
file, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(file, &account); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return account, nil
|
||||
}
|
||||
|
||||
// ConvertToNewFormat converts old acme.json format to the new one and store the result into the file (used for the backward compatibility)
|
||||
func ConvertToNewFormat(fileName string) {
|
||||
localStore := acme.NewLocalStore(fileName)
|
||||
|
||||
storeAccount, err := localStore.GetAccount()
|
||||
if err != nil {
|
||||
log.Errorf("Failed to read new account, ACME data conversion is not available : %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
storeCertificates, err := localStore.GetCertificates()
|
||||
if err != nil {
|
||||
log.Errorf("Failed to read new certificates, ACME data conversion is not available : %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if storeAccount == nil {
|
||||
localStore := NewLocalStore(fileName)
|
||||
|
||||
account, err := localStore.Get()
|
||||
if err != nil {
|
||||
log.Errorf("Failed to read old account, ACME data conversion is not available : %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Convert ACME data from old to new format
|
||||
newAccount := &acme.Account{}
|
||||
if account != nil && len(account.Email) > 0 {
|
||||
err = backupACMEFile(fileName, account)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to create a backup for the V1 formatted ACME file: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = account.RemoveAccountV1Values()
|
||||
if err != nil {
|
||||
log.Errorf("Unable to remove ACME Account V1 values during format conversion: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
newAccount = &acme.Account{
|
||||
PrivateKey: account.PrivateKey,
|
||||
Registration: account.Registration,
|
||||
Email: account.Email,
|
||||
}
|
||||
|
||||
var newCertificates []*acme.Certificate
|
||||
for _, cert := range account.DomainsCertificate.Certs {
|
||||
newCertificates = append(newCertificates, &acme.Certificate{
|
||||
Certificate: cert.Certificate.Certificate,
|
||||
Key: cert.Certificate.PrivateKey,
|
||||
Domain: cert.Domains,
|
||||
})
|
||||
}
|
||||
|
||||
// If account is in the old format, storeCertificates is nil or empty and has to be initialized
|
||||
storeCertificates = newCertificates
|
||||
}
|
||||
|
||||
// Store the data in new format into the file even if account is nil
|
||||
// to delete Account in ACME v1 format and keeping the certificates
|
||||
newLocalStore := acme.NewLocalStore(fileName)
|
||||
newLocalStore.SaveDataChan <- &acme.StoredData{Account: newAccount, Certificates: storeCertificates}
|
||||
}
|
||||
}
|
||||
|
||||
func backupACMEFile(originalFileName string, account interface{}) error {
|
||||
// write account to file
|
||||
data, err := json.MarshalIndent(account, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(originalFileName+".bak", data, 0600)
|
||||
}
|
||||
|
||||
// FromNewToOldFormat converts new acme account to the old one (used for the backward compatibility)
|
||||
func FromNewToOldFormat(fileName string) (*Account, error) {
|
||||
localStore := acme.NewLocalStore(fileName)
|
||||
|
||||
storeAccount, err := localStore.GetAccount()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
storeCertificates, err := localStore.GetCertificates()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert ACME Account from new to old format
|
||||
// (Needed by the KV stores)
|
||||
var account *Account
|
||||
if storeAccount != nil {
|
||||
account = &Account{
|
||||
Email: storeAccount.Email,
|
||||
PrivateKey: storeAccount.PrivateKey,
|
||||
Registration: storeAccount.Registration,
|
||||
DomainsCertificate: DomainsCertificates{},
|
||||
}
|
||||
}
|
||||
|
||||
// Convert ACME Certificates from new to old format
|
||||
// (Needed by the KV stores)
|
||||
if len(storeCertificates) > 0 {
|
||||
// Account can be nil if data are migrated from new format
|
||||
// with a ACME V1 Account
|
||||
if account == nil {
|
||||
account = &Account{}
|
||||
}
|
||||
for _, cert := range storeCertificates {
|
||||
_, err := account.DomainsCertificate.addCertificateForDomains(&Certificate{
|
||||
Domain: cert.Domain.Main,
|
||||
Certificate: cert.Certificate,
|
||||
PrivateKey: cert.Key,
|
||||
}, cert.Domain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return account, nil
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
acmeFile := "./acme_example.json"
|
||||
|
||||
folder, prefix := filepath.Split(acmeFile)
|
||||
tmpFile, err := ioutil.TempFile(folder, prefix)
|
||||
defer os.Remove(tmpFile.Name())
|
||||
|
||||
assert.NoError(t, err)
|
||||
|
||||
fileContent, err := ioutil.ReadFile(acmeFile)
|
||||
assert.NoError(t, err)
|
||||
|
||||
tmpFile.Write(fileContent)
|
||||
|
||||
localStore := NewLocalStore(tmpFile.Name())
|
||||
account, err := localStore.Get()
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Len(t, account.DomainsCertificate.Certs, 1)
|
||||
}
|
||||
@@ -1,665 +0,0 @@
|
||||
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"
|
||||
acmeprovider "github.com/containous/traefik/provider/acme"
|
||||
"github.com/containous/traefik/provider/boltdb"
|
||||
"github.com/containous/traefik/provider/consul"
|
||||
"github.com/containous/traefik/provider/consulcatalog"
|
||||
"github.com/containous/traefik/provider/docker"
|
||||
"github.com/containous/traefik/provider/dynamodb"
|
||||
"github.com/containous/traefik/provider/ecs"
|
||||
"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": {
|
||||
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": {
|
||||
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: []types.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",
|
||||
DNSChallenge: &acmeprovider.DNSChallenge{Provider: "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 = &consulcatalog.Provider{
|
||||
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: flaeg.Duration(30 * time.Second),
|
||||
RefreshSeconds: flaeg.Duration(30 * time.Second),
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
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, request.Header.Get("X-Forwarded-Prefix")+"/dashboard/", 302)
|
||||
})
|
||||
|
||||
router.Methods(http.MethodGet).
|
||||
Path("/dashboard/status").
|
||||
HandlerFunc(func(response http.ResponseWriter, request *http.Request) {
|
||||
http.Redirect(response, request, "/dashboard/", 302)
|
||||
})
|
||||
|
||||
router.Methods(http.MethodGet).
|
||||
PathPrefix("/dashboard/").
|
||||
Handler(http.StripPrefix("/dashboard/", http.FileServer(&assetfs.AssetFS{Asset: genstatic.Asset, AssetInfo: genstatic.AssetInfo, AssetDir: genstatic.AssetDir, Prefix: "static"})))
|
||||
}
|
||||
46
api/debug.go
@@ -1,46 +0,0 @@
|
||||
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
@@ -1,250 +0,0 @@
|
||||
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 `json:"-"`
|
||||
StatsRecorder *middlewares.StatsRecorder `json:"-"`
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,25 @@
|
||||
FROM golang:1.10-alpine
|
||||
FROM golang:1.12-alpine
|
||||
|
||||
RUN apk --update upgrade \
|
||||
&& apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
&& apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar ca-certificates tzdata \
|
||||
&& update-ca-certificates \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
RUN go get github.com/containous/go-bindata/... \
|
||||
&& go get github.com/golang/lint/golint \
|
||||
&& go get github.com/kisielk/errcheck \
|
||||
&& go get github.com/client9/misspell/cmd/misspell
|
||||
# Download golangci-lint and misspell binary to bin folder in $GOPATH
|
||||
RUN curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.15.0 \
|
||||
&& go get github.com/client9/misspell/cmd/misspell
|
||||
|
||||
# Download goreleaser binary to bin folder in $GOPATH
|
||||
RUN curl -sfL https://install.goreleaser.com/github.com/goreleaser/goreleaser.sh | sh
|
||||
|
||||
# Which docker version to test on
|
||||
ARG DOCKER_VERSION=17.03.2
|
||||
ARG DEP_VERSION=0.4.1
|
||||
ARG DEP_VERSION=0.5.0
|
||||
|
||||
# Download go-bindata binary to bin folder in $GOPATH
|
||||
RUN mkdir -p /usr/local/bin \
|
||||
&& curl -fsSL -o /usr/local/bin/go-bindata https://github.com/containous/go-bindata/releases/download/v1.0.0/go-bindata \
|
||||
&& chmod +x /usr/local/bin/go-bindata
|
||||
|
||||
# Download dep binary to bin folder in $GOPATH
|
||||
RUN mkdir -p /usr/local/bin \
|
||||
|
||||
@@ -1,247 +0,0 @@
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/abronan/valkeyrie/store"
|
||||
"github.com/cenk/backoff"
|
||||
"github.com/containous/staert"
|
||||
"github.com/containous/traefik/job"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
// Metadata stores Object plus metadata
|
||||
type Metadata struct {
|
||||
object Object
|
||||
Object []byte
|
||||
Lock string
|
||||
}
|
||||
|
||||
// NewMetadata returns new Metadata
|
||||
func NewMetadata(object Object) *Metadata {
|
||||
return &Metadata{object: object}
|
||||
}
|
||||
|
||||
// Marshall marshalls object
|
||||
func (m *Metadata) Marshall() error {
|
||||
var err error
|
||||
m.Object, err = json.Marshal(m.object)
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *Metadata) unmarshall() error {
|
||||
if len(m.Object) == 0 {
|
||||
return nil
|
||||
}
|
||||
return json.Unmarshal(m.Object, m.object)
|
||||
}
|
||||
|
||||
// Listener is called when Object has been changed in KV store
|
||||
type Listener func(Object) error
|
||||
|
||||
var _ Store = (*Datastore)(nil)
|
||||
|
||||
// Datastore holds a struct synced in a KV store
|
||||
type Datastore struct {
|
||||
kv staert.KvSource
|
||||
ctx context.Context
|
||||
localLock *sync.RWMutex
|
||||
meta *Metadata
|
||||
lockKey string
|
||||
listener Listener
|
||||
}
|
||||
|
||||
// NewDataStore creates a Datastore
|
||||
func NewDataStore(ctx context.Context, kvSource staert.KvSource, object Object, listener Listener) (*Datastore, error) {
|
||||
datastore := Datastore{
|
||||
kv: kvSource,
|
||||
ctx: ctx,
|
||||
meta: &Metadata{object: object},
|
||||
lockKey: kvSource.Prefix + "/lock",
|
||||
localLock: &sync.RWMutex{},
|
||||
listener: listener,
|
||||
}
|
||||
err := datastore.watchChanges()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &datastore, nil
|
||||
}
|
||||
|
||||
func (d *Datastore) watchChanges() error {
|
||||
stopCh := make(chan struct{})
|
||||
kvCh, err := d.kv.Watch(d.lockKey, stopCh, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
safe.Go(func() {
|
||||
ctx, cancel := context.WithCancel(d.ctx)
|
||||
operation := func() error {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
stopCh <- struct{}{}
|
||||
return nil
|
||||
case _, ok := <-kvCh:
|
||||
if !ok {
|
||||
cancel()
|
||||
return err
|
||||
}
|
||||
err = d.reload()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.listener != nil {
|
||||
err := d.listener(d.meta.object)
|
||||
if err != nil {
|
||||
log.Errorf("Error calling datastore listener: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
notify := func(err error, time time.Duration) {
|
||||
log.Errorf("Error in watch datastore: %+v, retrying in %s", err, time)
|
||||
}
|
||||
err := backoff.RetryNotify(safe.OperationWithRecover(operation), job.NewBackOff(backoff.NewExponentialBackOff()), notify)
|
||||
if err != nil {
|
||||
log.Errorf("Error in watch datastore: %v", err)
|
||||
}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Datastore) reload() error {
|
||||
log.Debug("Datastore reload")
|
||||
_, err := d.Load()
|
||||
return err
|
||||
}
|
||||
|
||||
// Begin creates a transaction with the KV store.
|
||||
func (d *Datastore) Begin() (Transaction, Object, error) {
|
||||
id := uuid.NewV4().String()
|
||||
log.Debugf("Transaction %s begins", id)
|
||||
remoteLock, err := d.kv.NewLock(d.lockKey, &store.LockOptions{TTL: 20 * time.Second, Value: []byte(id)})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
stopCh := make(chan struct{})
|
||||
ctx, cancel := context.WithCancel(d.ctx)
|
||||
var errLock error
|
||||
go func() {
|
||||
_, errLock = remoteLock.Lock(stopCh)
|
||||
cancel()
|
||||
}()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if errLock != nil {
|
||||
return nil, nil, errLock
|
||||
}
|
||||
case <-d.ctx.Done():
|
||||
stopCh <- struct{}{}
|
||||
return nil, nil, d.ctx.Err()
|
||||
}
|
||||
|
||||
// we got the lock! Now make sure we are synced with KV store
|
||||
operation := func() error {
|
||||
meta := d.get()
|
||||
if meta.Lock != id {
|
||||
return fmt.Errorf("Object lock value: expected %s, got %s", id, meta.Lock)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
notify := func(err error, time time.Duration) {
|
||||
log.Errorf("Datastore sync error: %v, retrying in %s", err, time)
|
||||
err = d.reload()
|
||||
if err != nil {
|
||||
log.Errorf("Error reloading: %+v", err)
|
||||
}
|
||||
}
|
||||
ebo := backoff.NewExponentialBackOff()
|
||||
ebo.MaxElapsedTime = 60 * time.Second
|
||||
err = backoff.RetryNotify(safe.OperationWithRecover(operation), ebo, notify)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Datastore cannot sync: %v", err)
|
||||
}
|
||||
|
||||
// we synced with KV store, we can now return Setter
|
||||
return &datastoreTransaction{
|
||||
Datastore: d,
|
||||
remoteLock: remoteLock,
|
||||
id: id,
|
||||
}, d.meta.object, nil
|
||||
}
|
||||
|
||||
func (d *Datastore) get() *Metadata {
|
||||
d.localLock.RLock()
|
||||
defer d.localLock.RUnlock()
|
||||
return d.meta
|
||||
}
|
||||
|
||||
// Load load atomically a struct from the KV store
|
||||
func (d *Datastore) Load() (Object, error) {
|
||||
d.localLock.Lock()
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = d.meta.unmarshall()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.meta.object, nil
|
||||
}
|
||||
|
||||
// Get atomically a struct from the KV store
|
||||
func (d *Datastore) Get() Object {
|
||||
d.localLock.RLock()
|
||||
defer d.localLock.RUnlock()
|
||||
return d.meta.object
|
||||
}
|
||||
|
||||
var _ Transaction = (*datastoreTransaction)(nil)
|
||||
|
||||
type datastoreTransaction struct {
|
||||
*Datastore
|
||||
remoteLock store.Locker
|
||||
dirty bool
|
||||
id string
|
||||
}
|
||||
|
||||
// Commit allows to set an object in the KV store
|
||||
func (s *datastoreTransaction) Commit(object Object) error {
|
||||
s.localLock.Lock()
|
||||
defer s.localLock.Unlock()
|
||||
if s.dirty {
|
||||
return fmt.Errorf("Transaction already used, please begin a new one")
|
||||
}
|
||||
s.Datastore.meta.object = object
|
||||
err := s.Datastore.meta.Marshall()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Marshall error: %s", err)
|
||||
}
|
||||
err = s.kv.StoreConfig(s.Datastore.meta)
|
||||
if err != nil {
|
||||
return fmt.Errorf("StoreConfig error: %s", err)
|
||||
}
|
||||
|
||||
err = s.remoteLock.Unlock()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unlock error: %s", err)
|
||||
}
|
||||
|
||||
s.dirty = true
|
||||
log.Debugf("Transaction committed %s", s.id)
|
||||
return nil
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/cenk/backoff"
|
||||
"github.com/containous/mux"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/docker/leadership"
|
||||
"github.com/unrolled/render"
|
||||
)
|
||||
|
||||
var templatesRenderer = render.New(render.Options{
|
||||
Directory: "nowhere",
|
||||
})
|
||||
|
||||
// Leadership allows leadership election using a KV store
|
||||
type Leadership struct {
|
||||
*safe.Pool
|
||||
*types.Cluster
|
||||
candidate *leadership.Candidate
|
||||
leader *safe.Safe
|
||||
listeners []LeaderListener
|
||||
}
|
||||
|
||||
// NewLeadership creates a leadership
|
||||
func NewLeadership(ctx context.Context, cluster *types.Cluster) *Leadership {
|
||||
return &Leadership{
|
||||
Pool: safe.NewPool(ctx),
|
||||
Cluster: cluster,
|
||||
candidate: leadership.NewCandidate(cluster.Store, cluster.Store.Prefix+"/leader", cluster.Node, 20*time.Second),
|
||||
listeners: []LeaderListener{},
|
||||
leader: safe.New(false),
|
||||
}
|
||||
}
|
||||
|
||||
// LeaderListener is called when leadership has changed
|
||||
type LeaderListener func(elected bool) error
|
||||
|
||||
// Participate tries to be a leader
|
||||
func (l *Leadership) Participate(pool *safe.Pool) {
|
||||
pool.GoCtx(func(ctx context.Context) {
|
||||
log.Debugf("Node %s running for election", l.Cluster.Node)
|
||||
defer log.Debugf("Node %s no more running for election", l.Cluster.Node)
|
||||
backOff := backoff.NewExponentialBackOff()
|
||||
operation := func() error {
|
||||
return l.run(ctx, l.candidate)
|
||||
}
|
||||
|
||||
notify := func(err error, time time.Duration) {
|
||||
log.Errorf("Leadership election error %+v, retrying in %s", err, time)
|
||||
}
|
||||
err := backoff.RetryNotify(safe.OperationWithRecover(operation), backOff, notify)
|
||||
if err != nil {
|
||||
log.Errorf("Cannot elect leadership %+v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// AddListener adds a leadership listener
|
||||
func (l *Leadership) AddListener(listener LeaderListener) {
|
||||
l.listeners = append(l.listeners, listener)
|
||||
}
|
||||
|
||||
// Resign resigns from being a leader
|
||||
func (l *Leadership) Resign() {
|
||||
l.candidate.Resign()
|
||||
log.Infof("Node %s resigned", l.Cluster.Node)
|
||||
}
|
||||
|
||||
func (l *Leadership) run(ctx context.Context, candidate *leadership.Candidate) error {
|
||||
electedCh, errCh := candidate.RunForElection()
|
||||
for {
|
||||
select {
|
||||
case elected := <-electedCh:
|
||||
l.onElection(elected)
|
||||
case err := <-errCh:
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
l.candidate.Resign()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Leadership) onElection(elected bool) {
|
||||
if elected {
|
||||
log.Infof("Node %s elected leader ♚", l.Cluster.Node)
|
||||
l.leader.Set(true)
|
||||
l.Start()
|
||||
} else {
|
||||
log.Infof("Node %s elected worker ♝", l.Cluster.Node)
|
||||
l.leader.Set(false)
|
||||
l.Stop()
|
||||
}
|
||||
for _, listener := range l.listeners {
|
||||
err := listener(elected)
|
||||
if err != nil {
|
||||
log.Errorf("Error calling Leadership listener: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type leaderResponse struct {
|
||||
Leader bool `json:"leader"`
|
||||
}
|
||||
|
||||
func (l *Leadership) getLeaderHandler(response http.ResponseWriter, request *http.Request) {
|
||||
leader := &leaderResponse{Leader: l.IsLeader()}
|
||||
|
||||
status := http.StatusOK
|
||||
if !leader.Leader {
|
||||
// Set status to be `429`, as this will typically cause load balancers to stop sending requests to the instance without removing them from rotation.
|
||||
status = http.StatusTooManyRequests
|
||||
}
|
||||
|
||||
err := templatesRenderer.JSON(response, status, leader)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// IsLeader returns true if current node is leader
|
||||
func (l *Leadership) IsLeader() bool {
|
||||
return l.leader.Get().(bool)
|
||||
}
|
||||
|
||||
// AddRoutes add dashboard routes on a router
|
||||
func (l *Leadership) AddRoutes(router *mux.Router) {
|
||||
// Expose cluster leader
|
||||
router.Methods(http.MethodGet).Path("/api/cluster/leader").HandlerFunc(l.getLeaderHandler)
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package cluster
|
||||
|
||||
// Object is the struct to store
|
||||
type Object interface{}
|
||||
|
||||
// Store is a generic interface to represents a storage
|
||||
type Store interface {
|
||||
Load() (Object, error)
|
||||
Get() Object
|
||||
Begin() (Transaction, Object, error)
|
||||
}
|
||||
|
||||
// Transaction allows to set a struct in the KV store
|
||||
type Transaction interface {
|
||||
Commit(object Object) error
|
||||
}
|
||||
171
cmd/bug/bug.go
@@ -1,171 +0,0 @@
|
||||
package bug
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"text/template"
|
||||
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/traefik/anonymize"
|
||||
"github.com/containous/traefik/cmd"
|
||||
"github.com/containous/traefik/cmd/version"
|
||||
)
|
||||
|
||||
const (
|
||||
bugTracker = "https://github.com/containous/traefik/issues/new"
|
||||
bugTemplate = `<!--
|
||||
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
|
||||
|
||||
The issue tracker is for reporting bugs and feature requests only.
|
||||
For end-user related support questions, refer to one of the following:
|
||||
|
||||
- Stack Overflow (using the "traefik" tag): https://stackoverflow.com/questions/tagged/traefik
|
||||
- the Traefik community Slack channel: https://traefik.herokuapp.com
|
||||
|
||||
-->
|
||||
|
||||
### 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?
|
||||
|
||||
- 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.
|
||||
- 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
|
||||
|
||||
-->
|
||||
|
||||
|
||||
### What did you expect to see?
|
||||
|
||||
|
||||
|
||||
### What did you see instead?
|
||||
|
||||
|
||||
|
||||
### Output of ` + "`" + `traefik version` + "`" + `: (_What version of Traefik are you using?_)
|
||||
|
||||
` + "```" + `
|
||||
{{.Version}}
|
||||
` + "```" + `
|
||||
|
||||
### What is your environment & configuration (arguments, toml, provider, platform, ...)?
|
||||
|
||||
` + "```" + `json
|
||||
{{.Configuration}}
|
||||
` + "```" + `
|
||||
|
||||
<!--
|
||||
Add more configuration information here.
|
||||
-->
|
||||
|
||||
### If applicable, please paste the log output at DEBUG level (` + "`" + `--logLevel=DEBUG` + "`" + ` switch)
|
||||
|
||||
` + "```" + `
|
||||
(paste your output here)
|
||||
` + "```" + `
|
||||
|
||||
`
|
||||
)
|
||||
|
||||
// NewCmd builds a new Bug command
|
||||
func NewCmd(traefikConfiguration *cmd.TraefikConfiguration, traefikPointersConfiguration *cmd.TraefikConfiguration) *flaeg.Command {
|
||||
|
||||
//version Command init
|
||||
return &flaeg.Command{
|
||||
Name: "bug",
|
||||
Description: `Report an issue on Traefik bugtracker`,
|
||||
Config: traefikConfiguration,
|
||||
DefaultPointersConfig: traefikPointersConfiguration,
|
||||
Run: runCmd(traefikConfiguration),
|
||||
Metadata: map[string]string{
|
||||
"parseAllSources": "true",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func runCmd(traefikConfiguration *cmd.TraefikConfiguration) func() error {
|
||||
return func() error {
|
||||
|
||||
body, err := createReport(traefikConfiguration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sendReport(body)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func createReport(traefikConfiguration *cmd.TraefikConfiguration) (string, error) {
|
||||
var versionPrint bytes.Buffer
|
||||
if err := version.GetPrint(&versionPrint); 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: versionPrint.String(),
|
||||
Configuration: config,
|
||||
}
|
||||
|
||||
var bug bytes.Buffer
|
||||
if err := tmpl.Execute(&bug, v); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return bug.String(), nil
|
||||
}
|
||||
|
||||
func sendReport(body string) {
|
||||
URL := bugTracker + "?body=" + url.QueryEscape(body)
|
||||
if err := openBrowser(URL); err != nil {
|
||||
fmt.Printf("Please file a new issue at %s using this template:\n\n", bugTracker)
|
||||
fmt.Print(body)
|
||||
}
|
||||
}
|
||||
|
||||
func openBrowser(URL string) error {
|
||||
var err error
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
err = exec.Command("xdg-open", URL).Start()
|
||||
case "windows":
|
||||
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", URL).Start()
|
||||
case "darwin":
|
||||
err = exec.Command("open", URL).Start()
|
||||
default:
|
||||
err = fmt.Errorf("unsupported platform")
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package bug
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/containous/traefik/anonymize"
|
||||
"github.com/containous/traefik/cmd"
|
||||
"github.com/containous/traefik/configuration"
|
||||
"github.com/containous/traefik/provider/file"
|
||||
"github.com/containous/traefik/tls"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_createReport(t *testing.T) {
|
||||
traefikConfiguration := &cmd.TraefikConfiguration{
|
||||
ConfigFile: "FOO",
|
||||
GlobalConfiguration: configuration.GlobalConfiguration{
|
||||
EntryPoints: configuration.EntryPoints{
|
||||
"goo": &configuration.EntryPoint{
|
||||
Address: "hoo.bar",
|
||||
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"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
File: &file.Provider{
|
||||
Directory: "BAR",
|
||||
},
|
||||
RootCAs: tls.RootCAs{"fllf"},
|
||||
},
|
||||
}
|
||||
|
||||
report, err := createReport(traefikConfiguration)
|
||||
assert.NoError(t, err, report)
|
||||
|
||||
// exported anonymous configuration
|
||||
assert.NotContains(t, "web Basic Users ", report)
|
||||
assert.NotContains(t, "foo Digest Users ", report)
|
||||
assert.NotContains(t, "hoo.bar", report)
|
||||
}
|
||||
|
||||
func Test_anonymize_traefikConfiguration(t *testing.T) {
|
||||
traefikConfiguration := &cmd.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)
|
||||
}
|
||||
@@ -3,321 +3,31 @@ package cmd
|
||||
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/middlewares/tracing"
|
||||
"github.com/containous/traefik/middlewares/tracing/jaeger"
|
||||
"github.com/containous/traefik/middlewares/tracing/zipkin"
|
||||
"github.com/containous/traefik/ping"
|
||||
"github.com/containous/traefik/provider/boltdb"
|
||||
"github.com/containous/traefik/provider/consul"
|
||||
"github.com/containous/traefik/provider/consulcatalog"
|
||||
"github.com/containous/traefik/provider/docker"
|
||||
"github.com/containous/traefik/provider/dynamodb"
|
||||
"github.com/containous/traefik/provider/ecs"
|
||||
"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"
|
||||
"github.com/containous/traefik/pkg/config/static"
|
||||
"github.com/containous/traefik/pkg/types"
|
||||
)
|
||||
|
||||
// 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"`
|
||||
// TraefikCmdConfiguration wraps the static configuration and extra parameters.
|
||||
type TraefikCmdConfiguration struct {
|
||||
static.Configuration `export:"true"`
|
||||
// ConfigFile is the path to the configuration file.
|
||||
ConfigFile string `description:"Configuration file to use. If specified all other flags are ignored." 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},
|
||||
EntryPoint: configuration.DefaultInternalEntryPointName,
|
||||
},
|
||||
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 consulcatalog.Provider
|
||||
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.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.RefreshSeconds = flaeg.Duration(30 * time.Second)
|
||||
|
||||
// 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: "",
|
||||
Filters: &types.AccessLogFilters{},
|
||||
Fields: &types.AccessLogFields{
|
||||
DefaultMode: types.AccessLogKeep,
|
||||
Headers: &types.FieldHeaders{
|
||||
DefaultMode: types.AccessLogKeep,
|
||||
// NewTraefikConfiguration creates a TraefikCmdConfiguration with default values.
|
||||
func NewTraefikConfiguration() *TraefikCmdConfiguration {
|
||||
return &TraefikCmdConfiguration{
|
||||
Configuration: static.Configuration{
|
||||
Global: &static.Global{
|
||||
CheckNewVersion: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// 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 Tracing
|
||||
defaultTracing := tracing.Tracing{
|
||||
Backend: "jaeger",
|
||||
ServiceName: "traefik",
|
||||
Jaeger: &jaeger.Config{
|
||||
SamplingServerURL: "http://localhost:5778/sampling",
|
||||
SamplingType: "const",
|
||||
SamplingParam: 1.0,
|
||||
LocalAgentHostPort: "127.0.0.1:6831",
|
||||
},
|
||||
Zipkin: &zipkin.Config{
|
||||
HTTPEndpoint: "http://localhost:9411/api/v1/spans",
|
||||
SameSpan: false,
|
||||
ID128Bit: true,
|
||||
Debug: false,
|
||||
},
|
||||
}
|
||||
|
||||
// default LifeCycle
|
||||
defaultLifeCycle := configuration.LifeCycle{
|
||||
GraceTimeOut: flaeg.Duration(configuration.DefaultGraceTimeout),
|
||||
}
|
||||
|
||||
// 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: configuration.DefaultInternalEntryPointName,
|
||||
},
|
||||
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,
|
||||
Tracing: &defaultTracing,
|
||||
}
|
||||
|
||||
return &TraefikConfiguration{
|
||||
GlobalConfiguration: defaultConfiguration,
|
||||
}
|
||||
}
|
||||
|
||||
// NewTraefikConfiguration creates a TraefikConfiguration with default values
|
||||
func NewTraefikConfiguration() *TraefikConfiguration {
|
||||
return &TraefikConfiguration{
|
||||
GlobalConfiguration: configuration.GlobalConfiguration{
|
||||
AccessLogsFile: "",
|
||||
TraefikLogsFile: "",
|
||||
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),
|
||||
EntryPoints: make(static.EntryPoints),
|
||||
Providers: &static.Providers{
|
||||
ProvidersThrottleDuration: types.Duration(2 * time.Second),
|
||||
},
|
||||
LifeCycle: &configuration.LifeCycle{
|
||||
GraceTimeOut: flaeg.Duration(configuration.DefaultGraceTimeout),
|
||||
ServersTransport: &static.ServersTransport{
|
||||
MaxIdleConnsPerHost: 200,
|
||||
},
|
||||
CheckNewVersion: true,
|
||||
},
|
||||
ConfigFile: "",
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// ContextWithSignal create a context cancelled when SIGINT or SIGTERM are notified
|
||||
// ContextWithSignal creates a context canceled when SIGINT or SIGTERM are notified
|
||||
func ContextWithSignal(ctx context.Context) context.Context {
|
||||
newCtx, cancel := context.WithCancel(ctx)
|
||||
signals := make(chan os.Signal)
|
||||
|
||||
@@ -1,41 +1,40 @@
|
||||
package healthcheck
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/traefik/cmd"
|
||||
"github.com/containous/traefik/configuration"
|
||||
"github.com/containous/traefik/pkg/cli"
|
||||
"github.com/containous/traefik/pkg/config/static"
|
||||
)
|
||||
|
||||
// NewCmd builds a new HealthCheck command
|
||||
func NewCmd(traefikConfiguration *cmd.TraefikConfiguration, traefikPointersConfiguration *cmd.TraefikConfiguration) *flaeg.Command {
|
||||
return &flaeg.Command{
|
||||
Name: "healthcheck",
|
||||
Description: `Calls traefik /ping to check health (web provider must be enabled)`,
|
||||
Config: traefikConfiguration,
|
||||
DefaultPointersConfig: traefikPointersConfiguration,
|
||||
Run: runCmd(traefikConfiguration),
|
||||
Metadata: map[string]string{
|
||||
"parseAllSources": "true",
|
||||
},
|
||||
// NewCmd builds a new HealthCheck command.
|
||||
func NewCmd(traefikConfiguration *static.Configuration, loaders []cli.ResourceLoader) *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "healthcheck",
|
||||
Description: `Calls Traefik /ping to check the health of Traefik (the API must be enabled).`,
|
||||
Configuration: traefikConfiguration,
|
||||
Run: runCmd(traefikConfiguration),
|
||||
Resources: loaders,
|
||||
}
|
||||
}
|
||||
|
||||
func runCmd(traefikConfiguration *cmd.TraefikConfiguration) func() error {
|
||||
return func() error {
|
||||
traefikConfiguration.GlobalConfiguration.SetEffectiveConfiguration(traefikConfiguration.ConfigFile)
|
||||
func runCmd(traefikConfiguration *static.Configuration) func(_ []string) error {
|
||||
return func(_ []string) error {
|
||||
traefikConfiguration.SetEffectiveConfiguration("")
|
||||
|
||||
resp, errPing := Do(traefikConfiguration.GlobalConfiguration)
|
||||
resp, errPing := Do(*traefikConfiguration)
|
||||
if resp != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
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)
|
||||
@@ -47,27 +46,29 @@ func runCmd(traefikConfiguration *cmd.TraefikConfiguration) func() error {
|
||||
}
|
||||
|
||||
// Do try to do a healthcheck
|
||||
func Do(globalConfiguration configuration.GlobalConfiguration) (*http.Response, error) {
|
||||
if globalConfiguration.Ping == nil {
|
||||
func Do(staticConfiguration static.Configuration) (*http.Response, error) {
|
||||
if staticConfiguration.Ping == nil {
|
||||
return nil, errors.New("please enable `ping` to use health check")
|
||||
}
|
||||
pingEntryPoint, ok := globalConfiguration.EntryPoints[globalConfiguration.Ping.EntryPoint]
|
||||
|
||||
pingEntryPoint, ok := staticConfiguration.EntryPoints[staticConfiguration.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
|
||||
}
|
||||
|
||||
// FIXME Handle TLS on ping etc...
|
||||
// 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")
|
||||
}
|
||||
|
||||
@@ -1,192 +0,0 @@
|
||||
package storeconfig
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
stdlog "log"
|
||||
"os"
|
||||
|
||||
"github.com/abronan/valkeyrie/store"
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/staert"
|
||||
"github.com/containous/traefik/acme"
|
||||
"github.com/containous/traefik/cluster"
|
||||
"github.com/containous/traefik/cmd"
|
||||
"github.com/containous/traefik/log"
|
||||
)
|
||||
|
||||
// NewCmd builds a new StoreConfig command
|
||||
func NewCmd(traefikConfiguration *cmd.TraefikConfiguration, traefikPointersConfiguration *cmd.TraefikConfiguration) *flaeg.Command {
|
||||
return &flaeg.Command{
|
||||
Name: "storeconfig",
|
||||
Description: `Store the static traefik configuration into a Key-value stores. Traefik will not start.`,
|
||||
Config: traefikConfiguration,
|
||||
DefaultPointersConfig: traefikPointersConfiguration,
|
||||
Metadata: map[string]string{
|
||||
"parseAllSources": "true",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Run store config in KV
|
||||
func Run(kv *staert.KvSource, traefikConfiguration *cmd.TraefikConfiguration) func() error {
|
||||
return func() error {
|
||||
if kv == nil {
|
||||
return fmt.Errorf("error using command storeconfig, no Key-value store defined")
|
||||
}
|
||||
|
||||
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.BuildConfiguration()
|
||||
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 {
|
||||
account := &acme.Account{}
|
||||
|
||||
// Migrate ACME data from file to KV store if needed
|
||||
if len(traefikConfiguration.GlobalConfiguration.ACME.StorageFile) > 0 {
|
||||
account, err = migrateACMEData(traefikConfiguration.GlobalConfiguration.ACME.StorageFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Store the ACME Account into the KV Store
|
||||
meta := cluster.NewMetadata(account)
|
||||
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
|
||||
return kv.Delete(kv.Prefix + "/acme/storagefile")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// migrateACMEData allows migrating data from acme.json file to KV store in function of the file format
|
||||
func migrateACMEData(fileName string) (*acme.Account, error) {
|
||||
|
||||
f, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
file, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check if the storage file is not empty before to get data
|
||||
account := &acme.Account{}
|
||||
if len(file) > 0 {
|
||||
accountFromNewFormat, err := acme.FromNewToOldFormat(fileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if accountFromNewFormat == nil {
|
||||
// convert ACME json file to KV store (used for backward compatibility)
|
||||
localStore := acme.NewLocalStore(fileName)
|
||||
|
||||
account, err = localStore.Get()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = account.RemoveAccountV1Values()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
account = accountFromNewFormat
|
||||
}
|
||||
} else {
|
||||
log.Warnf("No data will be imported from the storageFile %q because it is empty.", fileName)
|
||||
}
|
||||
|
||||
err = account.Init()
|
||||
return account, err
|
||||
}
|
||||
|
||||
// CreateKvSource creates KvSource
|
||||
// TLS support is enable for Consul and Etcd backends
|
||||
func CreateKvSource(traefikConfiguration *cmd.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
|
||||
}
|
||||
@@ -3,254 +3,231 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
fmtlog "log"
|
||||
"fmt"
|
||||
stdlog "log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cenk/backoff"
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/staert"
|
||||
"github.com/containous/traefik/autogen/genstatic"
|
||||
"github.com/containous/traefik/cmd"
|
||||
"github.com/containous/traefik/cmd/bug"
|
||||
"github.com/containous/traefik/cmd/healthcheck"
|
||||
"github.com/containous/traefik/cmd/storeconfig"
|
||||
cmdVersion "github.com/containous/traefik/cmd/version"
|
||||
"github.com/containous/traefik/collector"
|
||||
"github.com/containous/traefik/configuration"
|
||||
"github.com/containous/traefik/job"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/provider/acme"
|
||||
"github.com/containous/traefik/provider/ecs"
|
||||
"github.com/containous/traefik/provider/kubernetes"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/containous/traefik/server"
|
||||
"github.com/containous/traefik/server/uuid"
|
||||
traefiktls "github.com/containous/traefik/tls"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/containous/traefik/version"
|
||||
"github.com/containous/traefik/pkg/cli"
|
||||
"github.com/containous/traefik/pkg/collector"
|
||||
"github.com/containous/traefik/pkg/config"
|
||||
"github.com/containous/traefik/pkg/config/static"
|
||||
"github.com/containous/traefik/pkg/log"
|
||||
"github.com/containous/traefik/pkg/provider/aggregator"
|
||||
"github.com/containous/traefik/pkg/safe"
|
||||
"github.com/containous/traefik/pkg/server"
|
||||
"github.com/containous/traefik/pkg/server/router"
|
||||
traefiktls "github.com/containous/traefik/pkg/tls"
|
||||
"github.com/containous/traefik/pkg/version"
|
||||
"github.com/coreos/go-systemd/daemon"
|
||||
"github.com/ogier/pflag"
|
||||
assetfs "github.com/elazarl/go-bindata-assetfs"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/vulcand/oxy/roundrobin"
|
||||
)
|
||||
|
||||
func init() {
|
||||
goDebug := os.Getenv("GODEBUG")
|
||||
if len(goDebug) > 0 {
|
||||
goDebug += ","
|
||||
}
|
||||
os.Setenv("GODEBUG", goDebug+"tls13=1")
|
||||
}
|
||||
|
||||
func main() {
|
||||
// traefik config inits
|
||||
traefikConfiguration := cmd.NewTraefikConfiguration()
|
||||
traefikPointersConfiguration := cmd.NewTraefikDefaultPointersConfiguration()
|
||||
tConfig := cmd.NewTraefikConfiguration()
|
||||
|
||||
// traefik Command init
|
||||
traefikCmd := &flaeg.Command{
|
||||
loaders := []cli.ResourceLoader{&cli.FileLoader{}, &cli.EnvLoader{}, &cli.FlagLoader{}}
|
||||
|
||||
cmdTraefik := &cli.Command{
|
||||
Name: "traefik",
|
||||
Description: `traefik is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
|
||||
Description: `Traefik is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
|
||||
Complete documentation is available at https://traefik.io`,
|
||||
Config: traefikConfiguration,
|
||||
DefaultPointersConfig: traefikPointersConfiguration,
|
||||
Run: func() error {
|
||||
runCmd(&traefikConfiguration.GlobalConfiguration, traefikConfiguration.ConfigFile)
|
||||
return nil
|
||||
Configuration: tConfig,
|
||||
Resources: loaders,
|
||||
Run: func(_ []string) error {
|
||||
return runCmd(&tConfig.Configuration, cli.GetConfigFile(loaders))
|
||||
},
|
||||
}
|
||||
|
||||
// storeconfig Command init
|
||||
storeConfigCmd := storeconfig.NewCmd(traefikConfiguration, traefikPointersConfiguration)
|
||||
|
||||
// init flaeg source
|
||||
f := flaeg.New(traefikCmd, os.Args[1:])
|
||||
// add custom parsers
|
||||
f.AddParser(reflect.TypeOf(configuration.EntryPoints{}), &configuration.EntryPoints{})
|
||||
f.AddParser(reflect.TypeOf(configuration.DefaultEntryPoints{}), &configuration.DefaultEntryPoints{})
|
||||
f.AddParser(reflect.TypeOf(traefiktls.RootCAs{}), &traefiktls.RootCAs{})
|
||||
f.AddParser(reflect.TypeOf(types.Constraints{}), &types.Constraints{})
|
||||
f.AddParser(reflect.TypeOf(kubernetes.Namespaces{}), &kubernetes.Namespaces{})
|
||||
f.AddParser(reflect.TypeOf(ecs.Clusters{}), &ecs.Clusters{})
|
||||
f.AddParser(reflect.TypeOf([]types.Domain{}), &types.Domains{})
|
||||
f.AddParser(reflect.TypeOf(types.Buckets{}), &types.Buckets{})
|
||||
f.AddParser(reflect.TypeOf(types.StatusCodes{}), &types.StatusCodes{})
|
||||
f.AddParser(reflect.TypeOf(types.FieldNames{}), &types.FieldNames{})
|
||||
f.AddParser(reflect.TypeOf(types.FieldHeaderNames{}), &types.FieldHeaderNames{})
|
||||
|
||||
// add commands
|
||||
f.AddCommand(cmdVersion.NewCmd())
|
||||
f.AddCommand(bug.NewCmd(traefikConfiguration, traefikPointersConfiguration))
|
||||
f.AddCommand(storeConfigCmd)
|
||||
f.AddCommand(healthcheck.NewCmd(traefikConfiguration, traefikPointersConfiguration))
|
||||
|
||||
usedCmd, err := f.GetCommand()
|
||||
err := cmdTraefik.AddCommand(healthcheck.NewCmd(&tConfig.Configuration, loaders))
|
||||
if err != nil {
|
||||
fmtlog.Println(err)
|
||||
stdlog.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if _, err := f.Parse(usedCmd); err != nil {
|
||||
if err == pflag.ErrHelp {
|
||||
os.Exit(0)
|
||||
}
|
||||
fmtlog.Printf("Error parsing command: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// staert init
|
||||
s := staert.NewStaert(traefikCmd)
|
||||
// init TOML source
|
||||
toml := staert.NewTomlSource("traefik", []string{traefikConfiguration.ConfigFile, "/etc/traefik/", "$HOME/.traefik/", "."})
|
||||
|
||||
// add sources to staert
|
||||
s.AddSource(toml)
|
||||
s.AddSource(f)
|
||||
if _, err := s.LoadConfig(); err != nil {
|
||||
fmtlog.Printf("Error reading TOML config file %s : %s\n", toml.ConfigFileUsed(), err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
traefikConfiguration.ConfigFile = toml.ConfigFileUsed()
|
||||
|
||||
kv, err := storeconfig.CreateKvSource(traefikConfiguration)
|
||||
err = cmdTraefik.AddCommand(cmdVersion.NewCmd())
|
||||
if err != nil {
|
||||
fmtlog.Printf("Error creating kv store: %s\n", err)
|
||||
stdlog.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
storeConfigCmd.Run = storeconfig.Run(kv, traefikConfiguration)
|
||||
|
||||
// if a KV Store is enable and no sub-command called in args
|
||||
if kv != nil && usedCmd == traefikCmd {
|
||||
if traefikConfiguration.Cluster == nil {
|
||||
traefikConfiguration.Cluster = &types.Cluster{Node: uuid.Get()}
|
||||
}
|
||||
if traefikConfiguration.Cluster.Store == nil {
|
||||
traefikConfiguration.Cluster.Store = &types.Store{Prefix: kv.Prefix, Store: kv.Store}
|
||||
}
|
||||
s.AddSource(kv)
|
||||
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)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.Run(); err != nil {
|
||||
fmtlog.Printf("Error running traefik: %s\n", err)
|
||||
err = cli.Execute(cmdTraefik)
|
||||
if err != nil {
|
||||
stdlog.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func runCmd(globalConfiguration *configuration.GlobalConfiguration, configFile string) {
|
||||
configureLogging(globalConfiguration)
|
||||
|
||||
if len(configFile) > 0 {
|
||||
log.Infof("Using TOML configuration file %s", configFile)
|
||||
}
|
||||
func runCmd(staticConfiguration *static.Configuration, configFile string) error {
|
||||
configureLogging(staticConfiguration)
|
||||
|
||||
http.DefaultTransport.(*http.Transport).Proxy = http.ProxyFromEnvironment
|
||||
|
||||
if globalConfiguration.AllowMinWeightZero {
|
||||
roundrobin.SetDefaultWeight(0)
|
||||
if err := roundrobin.SetDefaultWeight(0); err != nil {
|
||||
log.WithoutContext().Errorf("Could not set roundrobin default weight: %v", err)
|
||||
}
|
||||
|
||||
globalConfiguration.SetEffectiveConfiguration(configFile)
|
||||
globalConfiguration.ValidateConfiguration()
|
||||
staticConfiguration.SetEffectiveConfiguration(configFile)
|
||||
staticConfiguration.ValidateConfiguration()
|
||||
|
||||
jsonConf, _ := json.Marshal(globalConfiguration)
|
||||
log.Infof("Traefik version %s built on %s", version.Version, version.BuildDate)
|
||||
log.WithoutContext().Infof("Traefik version %s built on %s", version.Version, version.BuildDate)
|
||||
|
||||
if globalConfiguration.CheckNewVersion {
|
||||
jsonConf, err := json.Marshal(staticConfiguration)
|
||||
if err != nil {
|
||||
log.WithoutContext().Errorf("Could not marshal static configuration: %v", err)
|
||||
log.WithoutContext().Debugf("Static configuration loaded [struct] %#v", staticConfiguration)
|
||||
} else {
|
||||
log.WithoutContext().Debugf("Static configuration loaded %s", string(jsonConf))
|
||||
}
|
||||
|
||||
if staticConfiguration.API != nil && staticConfiguration.API.Dashboard {
|
||||
staticConfiguration.API.DashboardAssets = &assetfs.AssetFS{Asset: genstatic.Asset, AssetInfo: genstatic.AssetInfo, AssetDir: genstatic.AssetDir, Prefix: "static"}
|
||||
}
|
||||
|
||||
if staticConfiguration.Global.CheckNewVersion {
|
||||
checkNewVersion()
|
||||
}
|
||||
|
||||
stats(globalConfiguration)
|
||||
stats(staticConfiguration)
|
||||
|
||||
log.Debugf("Global configuration loaded %s", string(jsonConf))
|
||||
if acme.IsEnabled() {
|
||||
store := acme.NewLocalStore(acme.Get().Storage)
|
||||
acme.Get().Store = &store
|
||||
providerAggregator := aggregator.NewProviderAggregator(*staticConfiguration.Providers)
|
||||
|
||||
acmeProvider, err := staticConfiguration.InitACMEProvider()
|
||||
if err != nil {
|
||||
log.WithoutContext().Errorf("Unable to initialize ACME provider: %v", err)
|
||||
} else if acmeProvider != nil {
|
||||
if err := providerAggregator.AddProvider(acmeProvider); err != nil {
|
||||
log.WithoutContext().Errorf("Unable to add ACME provider to the providers list: %v", err)
|
||||
acmeProvider = nil
|
||||
}
|
||||
}
|
||||
svr := server.NewServer(*globalConfiguration, configuration.NewProviderAggregator(globalConfiguration))
|
||||
if acme.IsEnabled() && acme.Get().OnHostRule {
|
||||
acme.Get().SetConfigListenerChan(make(chan types.Configuration))
|
||||
svr.AddListener(acme.Get().ListenConfiguration)
|
||||
|
||||
serverEntryPointsTCP := make(server.TCPEntryPoints)
|
||||
for entryPointName, config := range staticConfiguration.EntryPoints {
|
||||
ctx := log.With(context.Background(), log.Str(log.EntryPointName, entryPointName))
|
||||
serverEntryPointsTCP[entryPointName], err = server.NewTCPEntryPoint(ctx, config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while building entryPoint %s: %v", entryPointName, err)
|
||||
}
|
||||
serverEntryPointsTCP[entryPointName].RouteAppenderFactory = router.NewRouteAppenderFactory(*staticConfiguration, entryPointName, acmeProvider)
|
||||
|
||||
}
|
||||
|
||||
tlsManager := traefiktls.NewManager()
|
||||
|
||||
if acmeProvider != nil {
|
||||
acmeProvider.SetTLSManager(tlsManager)
|
||||
if acmeProvider.TLSChallenge != nil &&
|
||||
acmeProvider.HTTPChallenge == nil &&
|
||||
acmeProvider.DNSChallenge == nil {
|
||||
tlsManager.TLSAlpnGetter = acmeProvider.GetTLSALPNCertificate
|
||||
}
|
||||
}
|
||||
|
||||
svr := server.NewServer(*staticConfiguration, providerAggregator, serverEntryPointsTCP, tlsManager)
|
||||
|
||||
if acmeProvider != nil && acmeProvider.OnHostRule {
|
||||
acmeProvider.SetConfigListenerChan(make(chan config.Configuration))
|
||||
svr.AddListener(acmeProvider.ListenConfiguration)
|
||||
}
|
||||
ctx := cmd.ContextWithSignal(context.Background())
|
||||
svr.StartWithContext(ctx)
|
||||
|
||||
if staticConfiguration.Ping != nil {
|
||||
staticConfiguration.Ping.WithContext(ctx)
|
||||
}
|
||||
|
||||
svr.Start(ctx)
|
||||
defer svr.Close()
|
||||
|
||||
sent, err := daemon.SdNotify(false, "READY=1")
|
||||
if !sent && err != nil {
|
||||
log.Error("Fail to notify", err)
|
||||
log.WithoutContext().Errorf("Failed to notify: %v", err)
|
||||
}
|
||||
|
||||
t, err := daemon.SdWatchdogEnabled(false)
|
||||
if err != nil {
|
||||
log.Error("Problem with watchdog", err)
|
||||
log.WithoutContext().Errorf("Could not enable Watchdog: %v", err)
|
||||
} else if t != 0 {
|
||||
// Send a ping each half time given
|
||||
t = t / 2
|
||||
log.Info("Watchdog activated with timer each ", t)
|
||||
t /= 2
|
||||
log.WithoutContext().Infof("Watchdog activated with timer duration %s", t)
|
||||
safe.Go(func() {
|
||||
tick := time.Tick(t)
|
||||
for range tick {
|
||||
_, errHealthCheck := healthcheck.Do(*globalConfiguration)
|
||||
if globalConfiguration.Ping == nil || errHealthCheck == nil {
|
||||
resp, errHealthCheck := healthcheck.Do(*staticConfiguration)
|
||||
if resp != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
|
||||
if staticConfiguration.Ping == nil || errHealthCheck == nil {
|
||||
if ok, _ := daemon.SdNotify(false, "WATCHDOG=1"); !ok {
|
||||
log.Error("Fail to tick watchdog")
|
||||
log.WithoutContext().Error("Fail to tick watchdog")
|
||||
}
|
||||
} else {
|
||||
log.Error(errHealthCheck)
|
||||
log.WithoutContext().Error(errHealthCheck)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
svr.Wait()
|
||||
log.Info("Shutting down")
|
||||
log.WithoutContext().Info("Shutting down")
|
||||
logrus.Exit(0)
|
||||
return nil
|
||||
}
|
||||
|
||||
func configureLogging(globalConfiguration *configuration.GlobalConfiguration) {
|
||||
func configureLogging(staticConfiguration *static.Configuration) {
|
||||
// configure default log flags
|
||||
fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags)
|
||||
stdlog.SetFlags(stdlog.Lshortfile | stdlog.LstdFlags)
|
||||
|
||||
// configure log level
|
||||
// an explicitly defined log level always has precedence. if none is
|
||||
// given and debug mode is disabled, the default is ERROR, and DEBUG
|
||||
// otherwise.
|
||||
levelStr := strings.ToLower(globalConfiguration.LogLevel)
|
||||
var levelStr string
|
||||
if staticConfiguration.Log != nil {
|
||||
levelStr = strings.ToLower(staticConfiguration.Log.Level)
|
||||
}
|
||||
if levelStr == "" {
|
||||
levelStr = "error"
|
||||
if globalConfiguration.Debug {
|
||||
if staticConfiguration.Global.Debug {
|
||||
levelStr = "debug"
|
||||
}
|
||||
}
|
||||
level, err := logrus.ParseLevel(levelStr)
|
||||
if err != nil {
|
||||
log.Error("Error getting level", err)
|
||||
log.WithoutContext().Errorf("Error getting level: %v", 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
|
||||
var logFile string
|
||||
if staticConfiguration.Log != nil && len(staticConfiguration.Log.FilePath) > 0 {
|
||||
logFile = staticConfiguration.Log.FilePath
|
||||
}
|
||||
|
||||
// configure log format
|
||||
var formatter logrus.Formatter
|
||||
if globalConfiguration.TraefikLog != nil && globalConfiguration.TraefikLog.Format == "json" {
|
||||
if staticConfiguration.Log != nil && staticConfiguration.Log.Format == "json" {
|
||||
formatter = &logrus.JSONFormatter{}
|
||||
} else {
|
||||
disableColors := len(logFile) > 0
|
||||
@@ -262,17 +239,17 @@ func configureLogging(globalConfiguration *configuration.GlobalConfiguration) {
|
||||
dir := filepath.Dir(logFile)
|
||||
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
log.Errorf("Failed to create log path %s: %s", dir, err)
|
||||
log.WithoutContext().Errorf("Failed to create log path %s: %s", dir, err)
|
||||
}
|
||||
|
||||
err = log.OpenFile(logFile)
|
||||
logrus.RegisterExitHandler(func() {
|
||||
if err := log.CloseFile(); err != nil {
|
||||
log.Error("Error closing log", err)
|
||||
log.WithoutContext().Errorf("Error while closing log: %v", err)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("Error opening file", err)
|
||||
log.WithoutContext().Errorf("Error while opening log file %s: %v", logFile, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -286,17 +263,25 @@ func checkNewVersion() {
|
||||
})
|
||||
}
|
||||
|
||||
func stats(globalConfiguration *configuration.GlobalConfiguration) {
|
||||
if globalConfiguration.SendAnonymousUsage {
|
||||
log.Info(`
|
||||
func stats(staticConfiguration *static.Configuration) {
|
||||
if staticConfiguration.Global.SendAnonymousUsage == nil {
|
||||
log.WithoutContext().Error(`
|
||||
You haven't specified the sendAnonymousUsage option, it will be enabled by default.
|
||||
`)
|
||||
sendAnonymousUsage := true
|
||||
staticConfiguration.Global.SendAnonymousUsage = &sendAnonymousUsage
|
||||
}
|
||||
|
||||
if *staticConfiguration.Global.SendAnonymousUsage {
|
||||
log.WithoutContext().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/basics/#collected-data
|
||||
`)
|
||||
collect(globalConfiguration)
|
||||
collect(staticConfiguration)
|
||||
} else {
|
||||
log.Info(`
|
||||
log.WithoutContext().Info(`
|
||||
Stats collection is disabled.
|
||||
Help us improve Traefik by turning this feature on :)
|
||||
More details on: https://docs.traefik.io/basics/#collected-data
|
||||
@@ -304,12 +289,12 @@ More details on: https://docs.traefik.io/basics/#collected-data
|
||||
}
|
||||
}
|
||||
|
||||
func collect(globalConfiguration *configuration.GlobalConfiguration) {
|
||||
func collect(staticConfiguration *static.Configuration) {
|
||||
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)
|
||||
if err := collector.Collect(staticConfiguration); err != nil {
|
||||
log.WithoutContext().Debug(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"runtime"
|
||||
"text/template"
|
||||
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/traefik/version"
|
||||
"github.com/containous/traefik/pkg/cli"
|
||||
"github.com/containous/traefik/pkg/version"
|
||||
)
|
||||
|
||||
var versionTemplate = `Version: {{.Version}}
|
||||
@@ -18,19 +18,17 @@ Built: {{.BuildTime}}
|
||||
OS/Arch: {{.Os}}/{{.Arch}}`
|
||||
|
||||
// NewCmd builds a new Version command
|
||||
func NewCmd() *flaeg.Command {
|
||||
return &flaeg.Command{
|
||||
Name: "version",
|
||||
Description: `Print version`,
|
||||
Config: struct{}{},
|
||||
DefaultPointersConfig: struct{}{},
|
||||
Run: func() error {
|
||||
func NewCmd() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "version",
|
||||
Description: `Shows the current Traefik version.`,
|
||||
Configuration: nil,
|
||||
Run: func(_ []string) error {
|
||||
if err := GetPrint(os.Stdout); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Print("\n")
|
||||
return nil
|
||||
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
package collector
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/containous/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}
|
||||
}
|
||||
@@ -1,505 +0,0 @@
|
||||
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/middlewares/tracing"
|
||||
"github.com/containous/traefik/middlewares/tracing/jaeger"
|
||||
"github.com/containous/traefik/middlewares/tracing/zipkin"
|
||||
"github.com/containous/traefik/ping"
|
||||
acmeprovider "github.com/containous/traefik/provider/acme"
|
||||
"github.com/containous/traefik/provider/boltdb"
|
||||
"github.com/containous/traefik/provider/consul"
|
||||
"github.com/containous/traefik/provider/consulcatalog"
|
||||
"github.com/containous/traefik/provider/docker"
|
||||
"github.com/containous/traefik/provider/dynamodb"
|
||||
"github.com/containous/traefik/provider/ecs"
|
||||
"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
|
||||
|
||||
// DefaultAcmeCAServer is the default ACME API endpoint
|
||||
DefaultAcmeCAServer = "https://acme-v02.api.letsencrypt.org/directory"
|
||||
)
|
||||
|
||||
// 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"`
|
||||
Tracing *tracing.Tracing `description:"OpenTracing configuration" export:"true"`
|
||||
LogLevel string `short:"l" description:"Log level" export:"true"`
|
||||
EntryPoints EntryPoints `description:"Entrypoints definition using format: --entryPoints='Name:http Address::8000 Redirect.EntryPoint:https' --entryPoints='Name:https Address::4442 TLS:tests/traefik.crt,tests/traefik.key;prod/traefik.crt,prod/traefik.key'" export:"true"`
|
||||
Cluster *types.Cluster `description:"Enable clustering" export:"true"`
|
||||
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"`
|
||||
AllowMinWeightZero bool `description:"Allow weight to take 0 as minimum real value." export:"true"` // Deprecated
|
||||
Web *WebCompatibility `description:"(Deprecated) Enable Web backend with default settings" export:"true"` // Deprecated
|
||||
Docker *docker.Provider `description:"Enable Docker backend with default settings" export:"true"`
|
||||
File *file.Provider `description:"Enable File backend with default settings" export:"true"`
|
||||
Marathon *marathon.Provider `description:"Enable Marathon backend with default settings" export:"true"`
|
||||
Consul *consul.Provider `description:"Enable Consul backend with default settings" export:"true"`
|
||||
ConsulCatalog *consulcatalog.Provider `description:"Enable Consul catalog backend with default settings" export:"true"`
|
||||
Etcd *etcd.Provider `description:"Enable Etcd backend with default settings" export:"true"`
|
||||
Zookeeper *zk.Provider `description:"Enable Zookeeper backend with default settings" export:"true"`
|
||||
Boltdb *boltdb.Provider `description:"Enable Boltdb backend with default settings" export:"true"`
|
||||
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:"(Deprecated) Web administration port" export:"true"`
|
||||
CertFile string `description:"(Deprecated) SSL certificate" export:"true"`
|
||||
KeyFile string `description:"(Deprecated) SSL certificate" export:"true"`
|
||||
ReadOnly bool `description:"(Deprecated) Enable read only API" export:"true"`
|
||||
Statistics *types.Statistics `description:"(Deprecated) Enable more detailed statistics" export:"true"`
|
||||
Metrics *types.Metrics `description:"(Deprecated) Enable a metrics exporter" export:"true"`
|
||||
Path string `description:"(Deprecated) Root path for dashboard and API" export:"true"`
|
||||
Auth *types.Auth `export:"true"`
|
||||
Debug bool `export:"true"`
|
||||
}
|
||||
|
||||
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"}
|
||||
}
|
||||
}
|
||||
|
||||
for entryPointName := range gc.EntryPoints {
|
||||
entryPoint := gc.EntryPoints[entryPointName]
|
||||
// ForwardedHeaders must be remove in the next breaking version
|
||||
if entryPoint.ForwardedHeaders == nil {
|
||||
entryPoint.ForwardedHeaders = &ForwardedHeaders{Insecure: true}
|
||||
}
|
||||
|
||||
if len(entryPoint.WhitelistSourceRange) > 0 {
|
||||
log.Warnf("Deprecated configuration found: %s. Please use %s.", "whiteListSourceRange", "whiteList.sourceRange")
|
||||
|
||||
if entryPoint.WhiteList == nil {
|
||||
entryPoint.WhiteList = &types.WhiteList{
|
||||
SourceRange: entryPoint.WhitelistSourceRange,
|
||||
}
|
||||
entryPoint.WhitelistSourceRange = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure LifeCycle isn't nil to spare nil checks elsewhere.
|
||||
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.Docker != nil {
|
||||
if len(gc.Docker.Filename) != 0 && gc.Docker.TemplateVersion != 2 {
|
||||
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
|
||||
gc.Docker.TemplateVersion = 1
|
||||
} else {
|
||||
gc.Docker.TemplateVersion = 2
|
||||
}
|
||||
}
|
||||
|
||||
if gc.Marathon != nil {
|
||||
if len(gc.Marathon.Filename) != 0 && gc.Marathon.TemplateVersion != 2 {
|
||||
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
|
||||
gc.Marathon.TemplateVersion = 1
|
||||
} else {
|
||||
gc.Marathon.TemplateVersion = 2
|
||||
}
|
||||
}
|
||||
|
||||
if gc.Mesos != nil {
|
||||
if len(gc.Mesos.Filename) != 0 && gc.Mesos.TemplateVersion != 2 {
|
||||
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
|
||||
gc.Mesos.TemplateVersion = 1
|
||||
} else {
|
||||
gc.Mesos.TemplateVersion = 2
|
||||
}
|
||||
}
|
||||
|
||||
if gc.Eureka != nil {
|
||||
if gc.Eureka.Delay != 0 {
|
||||
log.Warn("Delay has been deprecated -- please use RefreshSeconds")
|
||||
gc.Eureka.RefreshSeconds = gc.Eureka.Delay
|
||||
}
|
||||
}
|
||||
|
||||
if gc.ECS != nil {
|
||||
if len(gc.ECS.Filename) != 0 && gc.ECS.TemplateVersion != 2 {
|
||||
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
|
||||
gc.ECS.TemplateVersion = 1
|
||||
} else {
|
||||
gc.ECS.TemplateVersion = 2
|
||||
}
|
||||
}
|
||||
|
||||
if gc.ConsulCatalog != nil {
|
||||
if len(gc.ConsulCatalog.Filename) != 0 && gc.ConsulCatalog.TemplateVersion != 2 {
|
||||
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
|
||||
gc.ConsulCatalog.TemplateVersion = 1
|
||||
} else {
|
||||
gc.ConsulCatalog.TemplateVersion = 2
|
||||
}
|
||||
}
|
||||
|
||||
if gc.Rancher != nil {
|
||||
if len(gc.Rancher.Filename) != 0 && gc.Rancher.TemplateVersion != 2 {
|
||||
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
|
||||
gc.Rancher.TemplateVersion = 1
|
||||
} else {
|
||||
gc.Rancher.TemplateVersion = 2
|
||||
}
|
||||
|
||||
// Ensure backwards compatibility for now
|
||||
if len(gc.Rancher.AccessKey) > 0 ||
|
||||
len(gc.Rancher.Endpoint) > 0 ||
|
||||
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.Web != nil && (gc.Web.Path == "" || !strings.HasSuffix(gc.Web.Path, "/")) {
|
||||
gc.Web.Path += "/"
|
||||
}
|
||||
|
||||
if gc.File != nil {
|
||||
gc.File.TraefikFile = configFile
|
||||
}
|
||||
|
||||
gc.initACMEProvider()
|
||||
gc.initTracing()
|
||||
}
|
||||
|
||||
func (gc *GlobalConfiguration) initTracing() {
|
||||
if gc.Tracing != nil {
|
||||
switch gc.Tracing.Backend {
|
||||
case jaeger.Name:
|
||||
if gc.Tracing.Jaeger == nil {
|
||||
gc.Tracing.Jaeger = &jaeger.Config{
|
||||
SamplingServerURL: "http://localhost:5778/sampling",
|
||||
SamplingType: "const",
|
||||
SamplingParam: 1.0,
|
||||
LocalAgentHostPort: "127.0.0.1:6831",
|
||||
}
|
||||
}
|
||||
if gc.Tracing.Zipkin != nil {
|
||||
log.Warn("Zipkin configuration will be ignored")
|
||||
gc.Tracing.Zipkin = nil
|
||||
}
|
||||
case zipkin.Name:
|
||||
if gc.Tracing.Zipkin == nil {
|
||||
gc.Tracing.Zipkin = &zipkin.Config{
|
||||
HTTPEndpoint: "http://localhost:9411/api/v1/spans",
|
||||
SameSpan: false,
|
||||
ID128Bit: true,
|
||||
Debug: false,
|
||||
}
|
||||
}
|
||||
if gc.Tracing.Jaeger != nil {
|
||||
log.Warn("Jaeger configuration will be ignored")
|
||||
gc.Tracing.Jaeger = nil
|
||||
}
|
||||
default:
|
||||
log.Warnf("Unknown tracer %q", gc.Tracing.Backend)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (gc *GlobalConfiguration) initACMEProvider() {
|
||||
if gc.ACME != nil {
|
||||
gc.ACME.CAServer = getSafeACMECAServer(gc.ACME.CAServer)
|
||||
|
||||
if gc.ACME.DNSChallenge != nil && gc.ACME.HTTPChallenge != nil {
|
||||
log.Warn("Unable to use DNS challenge and HTTP challenge at the same time. Fallback to DNS challenge.")
|
||||
gc.ACME.HTTPChallenge = nil
|
||||
}
|
||||
|
||||
// TODO: to remove in the future
|
||||
if len(gc.ACME.StorageFile) > 0 && len(gc.ACME.Storage) == 0 {
|
||||
log.Warn("ACME.StorageFile is deprecated, use ACME.Storage instead")
|
||||
gc.ACME.Storage = gc.ACME.StorageFile
|
||||
}
|
||||
|
||||
if len(gc.ACME.DNSProvider) > 0 {
|
||||
log.Warn("ACME.DNSProvider is deprecated, use ACME.DNSChallenge instead")
|
||||
gc.ACME.DNSChallenge = &acmeprovider.DNSChallenge{Provider: gc.ACME.DNSProvider, DelayBeforeCheck: gc.ACME.DelayDontCheckDNS}
|
||||
}
|
||||
|
||||
if gc.ACME.OnDemand {
|
||||
log.Warn("ACME.OnDemand is deprecated")
|
||||
}
|
||||
|
||||
// TODO: Remove when Provider ACME will replace totally ACME
|
||||
// If provider file, use Provider ACME instead of ACME
|
||||
if gc.Cluster == nil {
|
||||
acmeprovider.Get().Configuration = &acmeprovider.Configuration{
|
||||
OnHostRule: gc.ACME.OnHostRule,
|
||||
OnDemand: gc.ACME.OnDemand,
|
||||
Email: gc.ACME.Email,
|
||||
Storage: gc.ACME.Storage,
|
||||
HTTPChallenge: gc.ACME.HTTPChallenge,
|
||||
DNSChallenge: gc.ACME.DNSChallenge,
|
||||
Domains: gc.ACME.Domains,
|
||||
ACMELogging: gc.ACME.ACMELogging,
|
||||
CAServer: gc.ACME.CAServer,
|
||||
EntryPoint: gc.ACME.EntryPoint,
|
||||
}
|
||||
gc.ACME = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getSafeACMECAServer(caServerSrc string) string {
|
||||
if len(caServerSrc) == 0 {
|
||||
return DefaultAcmeCAServer
|
||||
}
|
||||
|
||||
if strings.HasPrefix(caServerSrc, "https://acme-v01.api.letsencrypt.org") {
|
||||
caServer := strings.Replace(caServerSrc, "v01", "v02", 1)
|
||||
log.Warnf("The CA server %[1]q refers to a v01 endpoint of the ACME API, please change to %[2]q. Fallback to %[2]q.", caServerSrc, caServer)
|
||||
return caServer
|
||||
}
|
||||
|
||||
if strings.HasPrefix(caServerSrc, "https://acme-staging.api.letsencrypt.org") {
|
||||
caServer := strings.Replace(caServerSrc, "https://acme-staging.api.letsencrypt.org", "https://acme-staging-v02.api.letsencrypt.org", 1)
|
||||
log.Warnf("The CA server %[1]q refers to a v01 endpoint of the ACME API, please change to %[2]q. Fallback to %[2]q.", caServerSrc, caServer)
|
||||
return caServer
|
||||
}
|
||||
|
||||
return caServerSrc
|
||||
}
|
||||
|
||||
// ValidateConfiguration validate that configuration is coherent
|
||||
func (gc *GlobalConfiguration) ValidateConfiguration() {
|
||||
if gc.ACME != nil {
|
||||
if _, ok := gc.EntryPoints[gc.ACME.EntryPoint]; !ok {
|
||||
log.Fatalf("Unknown entrypoint %q for ACME configuration", gc.ACME.EntryPoint)
|
||||
} else {
|
||||
if gc.EntryPoints[gc.ACME.EntryPoint].TLS == nil {
|
||||
log.Fatalf("Entrypoint %q has no TLS configuration for ACME configuration", gc.ACME.EntryPoint)
|
||||
}
|
||||
}
|
||||
} else if acmeprovider.IsEnabled() {
|
||||
if _, ok := gc.EntryPoints[acmeprovider.Get().EntryPoint]; !ok {
|
||||
log.Fatalf("Unknown entrypoint %q for provider ACME configuration", acmeprovider.Get().EntryPoint)
|
||||
} else {
|
||||
if gc.EntryPoints[acmeprovider.Get().EntryPoint].TLS == nil {
|
||||
log.Fatalf("Entrypoint %q has no TLS configuration for provider ACME configuration", acmeprovider.Get().EntryPoint)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 *dep
|
||||
}
|
||||
|
||||
// SetValue sets the EntryPoints map with val
|
||||
func (dep *DefaultEntryPoints) SetValue(val interface{}) {
|
||||
*dep = val.(DefaultEntryPoints)
|
||||
}
|
||||
|
||||
// Type is type of the struct
|
||||
func (dep *DefaultEntryPoints) Type() string {
|
||||
return "defaultentrypoints"
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/traefik/middlewares/tracing"
|
||||
"github.com/containous/traefik/middlewares/tracing/jaeger"
|
||||
"github.com/containous/traefik/middlewares/tracing/zipkin"
|
||||
"github.com/containous/traefik/provider"
|
||||
"github.com/containous/traefik/provider/file"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const defaultConfigFile = "traefik.toml"
|
||||
|
||||
func TestSetEffectiveConfigurationGraceTimeout(t *testing.T) {
|
||||
testCases := []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 testCases {
|
||||
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)
|
||||
|
||||
assert.Equal(t, test.wantGraceTimeout, time.Duration(gc.LifeCycle.GraceTimeOut))
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetEffectiveConfigurationFileProviderFilename(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
fileProvider *file.Provider
|
||||
wantFileProviderFilename string
|
||||
wantFileProviderTraefikFile string
|
||||
}{
|
||||
{
|
||||
desc: "no filename for file provider given",
|
||||
fileProvider: &file.Provider{},
|
||||
wantFileProviderFilename: "",
|
||||
wantFileProviderTraefikFile: defaultConfigFile,
|
||||
},
|
||||
{
|
||||
desc: "filename for file provider given",
|
||||
fileProvider: &file.Provider{BaseProvider: provider.BaseProvider{Filename: "other.toml"}},
|
||||
wantFileProviderFilename: "other.toml",
|
||||
wantFileProviderTraefikFile: defaultConfigFile,
|
||||
},
|
||||
{
|
||||
desc: "directory for file provider given",
|
||||
fileProvider: &file.Provider{Directory: "/"},
|
||||
wantFileProviderFilename: "",
|
||||
wantFileProviderTraefikFile: defaultConfigFile,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
gc := &GlobalConfiguration{
|
||||
File: test.fileProvider,
|
||||
}
|
||||
|
||||
gc.SetEffectiveConfiguration(defaultConfigFile)
|
||||
|
||||
assert.Equal(t, test.wantFileProviderFilename, gc.File.Filename)
|
||||
assert.Equal(t, test.wantFileProviderTraefikFile, gc.File.TraefikFile)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetEffectiveConfigurationTracing(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
tracing *tracing.Tracing
|
||||
expected *tracing.Tracing
|
||||
}{
|
||||
{
|
||||
desc: "no tracing configuration",
|
||||
tracing: &tracing.Tracing{},
|
||||
expected: &tracing.Tracing{},
|
||||
},
|
||||
{
|
||||
desc: "tracing bad backend name",
|
||||
tracing: &tracing.Tracing{
|
||||
Backend: "powpow",
|
||||
},
|
||||
expected: &tracing.Tracing{
|
||||
Backend: "powpow",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "tracing jaeger backend name",
|
||||
tracing: &tracing.Tracing{
|
||||
Backend: "jaeger",
|
||||
Zipkin: &zipkin.Config{
|
||||
HTTPEndpoint: "http://localhost:9411/api/v1/spans",
|
||||
SameSpan: false,
|
||||
ID128Bit: true,
|
||||
Debug: false,
|
||||
},
|
||||
},
|
||||
expected: &tracing.Tracing{
|
||||
Backend: "jaeger",
|
||||
Jaeger: &jaeger.Config{
|
||||
SamplingServerURL: "http://localhost:5778/sampling",
|
||||
SamplingType: "const",
|
||||
SamplingParam: 1.0,
|
||||
LocalAgentHostPort: "127.0.0.1:6831",
|
||||
},
|
||||
Zipkin: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "tracing zipkin backend name",
|
||||
tracing: &tracing.Tracing{
|
||||
Backend: "zipkin",
|
||||
Jaeger: &jaeger.Config{
|
||||
SamplingServerURL: "http://localhost:5778/sampling",
|
||||
SamplingType: "const",
|
||||
SamplingParam: 1.0,
|
||||
LocalAgentHostPort: "127.0.0.1:6831",
|
||||
},
|
||||
},
|
||||
expected: &tracing.Tracing{
|
||||
Backend: "zipkin",
|
||||
Jaeger: nil,
|
||||
Zipkin: &zipkin.Config{
|
||||
HTTPEndpoint: "http://localhost:9411/api/v1/spans",
|
||||
SameSpan: false,
|
||||
ID128Bit: true,
|
||||
Debug: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "tracing zipkin backend name value override",
|
||||
tracing: &tracing.Tracing{
|
||||
Backend: "zipkin",
|
||||
Jaeger: &jaeger.Config{
|
||||
SamplingServerURL: "http://localhost:5778/sampling",
|
||||
SamplingType: "const",
|
||||
SamplingParam: 1.0,
|
||||
LocalAgentHostPort: "127.0.0.1:6831",
|
||||
},
|
||||
Zipkin: &zipkin.Config{
|
||||
HTTPEndpoint: "http://powpow:9411/api/v1/spans",
|
||||
SameSpan: true,
|
||||
ID128Bit: true,
|
||||
Debug: true,
|
||||
},
|
||||
},
|
||||
expected: &tracing.Tracing{
|
||||
Backend: "zipkin",
|
||||
Jaeger: nil,
|
||||
Zipkin: &zipkin.Config{
|
||||
HTTPEndpoint: "http://powpow:9411/api/v1/spans",
|
||||
SameSpan: true,
|
||||
ID128Bit: true,
|
||||
Debug: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
gc := &GlobalConfiguration{
|
||||
Tracing: test.tracing,
|
||||
}
|
||||
|
||||
gc.SetEffectiveConfiguration(defaultConfigFile)
|
||||
|
||||
assert.Equal(t, test.expected, gc.Tracing)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,266 +0,0 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/tls"
|
||||
"github.com/containous/traefik/types"
|
||||
)
|
||||
|
||||
// EntryPoint holds an entry point configuration of the reverse proxy (ip, port, TLS...)
|
||||
type EntryPoint struct {
|
||||
Address string
|
||||
TLS *tls.TLS `export:"true"`
|
||||
Redirect *types.Redirect `export:"true"`
|
||||
Auth *types.Auth `export:"true"`
|
||||
WhitelistSourceRange []string // Deprecated
|
||||
WhiteList *types.WhiteList `export:"true"`
|
||||
Compress bool `export:"true"`
|
||||
ProxyProtocol *ProxyProtocol `export:"true"`
|
||||
ForwardedHeaders *ForwardedHeaders `export:"true"`
|
||||
}
|
||||
|
||||
// ProxyProtocol contains Proxy-Protocol configuration
|
||||
type ProxyProtocol struct {
|
||||
Insecure bool `export:"true"`
|
||||
TrustedIPs []string
|
||||
}
|
||||
|
||||
// ForwardedHeaders Trust client forwarding headers
|
||||
type ForwardedHeaders struct {
|
||||
Insecure bool `export:"true"`
|
||||
TrustedIPs []string
|
||||
}
|
||||
|
||||
// EntryPoints holds entry points configuration of the reverse proxy (ip, port, TLS...)
|
||||
type EntryPoints map[string]*EntryPoint
|
||||
|
||||
// String is the method to format the flag's value, part of the flag.Value interface.
|
||||
// The String method's output will be used in diagnostics.
|
||||
func (ep EntryPoints) String() string {
|
||||
return fmt.Sprintf("%+v", map[string]*EntryPoint(ep))
|
||||
}
|
||||
|
||||
// Get return the EntryPoints map
|
||||
func (ep *EntryPoints) Get() interface{} {
|
||||
return *ep
|
||||
}
|
||||
|
||||
// SetValue sets the EntryPoints map with val
|
||||
func (ep *EntryPoints) SetValue(val interface{}) {
|
||||
*ep = val.(EntryPoints)
|
||||
}
|
||||
|
||||
// Type is type of the struct
|
||||
func (ep *EntryPoints) Type() string {
|
||||
return "entrypoints"
|
||||
}
|
||||
|
||||
// Set is the method to set the flag value, part of the flag.Value interface.
|
||||
// Set's argument is a string to be parsed to set the flag.
|
||||
// It's a comma-separated list, so we split it.
|
||||
func (ep *EntryPoints) Set(value string) error {
|
||||
result := parseEntryPointsConfiguration(value)
|
||||
|
||||
var whiteListSourceRange []string
|
||||
if len(result["whitelistsourcerange"]) > 0 {
|
||||
whiteListSourceRange = strings.Split(result["whitelistsourcerange"], ",")
|
||||
}
|
||||
|
||||
compress := toBool(result, "compress")
|
||||
|
||||
configTLS, err := makeEntryPointTLS(result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
(*ep)[result["name"]] = &EntryPoint{
|
||||
Address: result["address"],
|
||||
TLS: configTLS,
|
||||
Auth: makeEntryPointAuth(result),
|
||||
Redirect: makeEntryPointRedirect(result),
|
||||
Compress: compress,
|
||||
WhitelistSourceRange: whiteListSourceRange,
|
||||
WhiteList: makeWhiteList(result),
|
||||
ProxyProtocol: makeEntryPointProxyProtocol(result),
|
||||
ForwardedHeaders: makeEntryPointForwardedHeaders(result),
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeWhiteList(result map[string]string) *types.WhiteList {
|
||||
var wl *types.WhiteList
|
||||
if rawRange, ok := result["whitelist_sourcerange"]; ok {
|
||||
wl = &types.WhiteList{
|
||||
SourceRange: strings.Split(rawRange, ","),
|
||||
UseXForwardedFor: toBool(result, "whitelist_usexforwardedfor"),
|
||||
}
|
||||
}
|
||||
return wl
|
||||
}
|
||||
|
||||
func makeEntryPointAuth(result map[string]string) *types.Auth {
|
||||
var basic *types.Basic
|
||||
if v, ok := result["auth_basic_users"]; ok {
|
||||
basic = &types.Basic{
|
||||
Users: strings.Split(v, ","),
|
||||
}
|
||||
}
|
||||
|
||||
var digest *types.Digest
|
||||
if v, ok := result["auth_digest_users"]; ok {
|
||||
digest = &types.Digest{
|
||||
Users: strings.Split(v, ","),
|
||||
}
|
||||
}
|
||||
|
||||
var forward *types.Forward
|
||||
if address, ok := result["auth_forward_address"]; ok {
|
||||
var clientTLS *types.ClientTLS
|
||||
|
||||
cert := result["auth_forward_tls_cert"]
|
||||
key := result["auth_forward_tls_key"]
|
||||
insecureSkipVerify := toBool(result, "auth_forward_tls_insecureskipverify")
|
||||
|
||||
if len(cert) > 0 && len(key) > 0 || insecureSkipVerify {
|
||||
clientTLS = &types.ClientTLS{
|
||||
CA: result["auth_forward_tls_ca"],
|
||||
CAOptional: toBool(result, "auth_forward_tls_caoptional"),
|
||||
Cert: cert,
|
||||
Key: key,
|
||||
InsecureSkipVerify: insecureSkipVerify,
|
||||
}
|
||||
}
|
||||
|
||||
forward = &types.Forward{
|
||||
Address: address,
|
||||
TLS: clientTLS,
|
||||
TrustForwardHeader: toBool(result, "auth_forward_trustforwardheader"),
|
||||
}
|
||||
}
|
||||
|
||||
var auth *types.Auth
|
||||
if basic != nil || digest != nil || forward != nil {
|
||||
auth = &types.Auth{
|
||||
Basic: basic,
|
||||
Digest: digest,
|
||||
Forward: forward,
|
||||
HeaderField: result["auth_headerfield"],
|
||||
}
|
||||
}
|
||||
|
||||
return auth
|
||||
}
|
||||
|
||||
func makeEntryPointProxyProtocol(result map[string]string) *ProxyProtocol {
|
||||
var proxyProtocol *ProxyProtocol
|
||||
|
||||
ppTrustedIPs := result["proxyprotocol_trustedips"]
|
||||
if len(result["proxyprotocol_insecure"]) > 0 || len(ppTrustedIPs) > 0 {
|
||||
proxyProtocol = &ProxyProtocol{
|
||||
Insecure: toBool(result, "proxyprotocol_insecure"),
|
||||
}
|
||||
if len(ppTrustedIPs) > 0 {
|
||||
proxyProtocol.TrustedIPs = strings.Split(ppTrustedIPs, ",")
|
||||
}
|
||||
}
|
||||
|
||||
if proxyProtocol != nil && proxyProtocol.Insecure {
|
||||
log.Warn("ProxyProtocol.Insecure:true is dangerous. Please use 'ProxyProtocol.TrustedIPs:IPs' and remove 'ProxyProtocol.Insecure:true'")
|
||||
}
|
||||
|
||||
return proxyProtocol
|
||||
}
|
||||
|
||||
func makeEntryPointForwardedHeaders(result map[string]string) *ForwardedHeaders {
|
||||
// TODO must be changed to false by default in the next breaking version.
|
||||
forwardedHeaders := &ForwardedHeaders{Insecure: true}
|
||||
if _, ok := result["forwardedheaders_insecure"]; ok {
|
||||
forwardedHeaders.Insecure = toBool(result, "forwardedheaders_insecure")
|
||||
}
|
||||
|
||||
fhTrustedIPs := result["forwardedheaders_trustedips"]
|
||||
if len(fhTrustedIPs) > 0 {
|
||||
// TODO must be removed in the next breaking version.
|
||||
forwardedHeaders.Insecure = toBool(result, "forwardedheaders_insecure")
|
||||
forwardedHeaders.TrustedIPs = strings.Split(fhTrustedIPs, ",")
|
||||
}
|
||||
|
||||
return forwardedHeaders
|
||||
}
|
||||
|
||||
func makeEntryPointRedirect(result map[string]string) *types.Redirect {
|
||||
var redirect *types.Redirect
|
||||
|
||||
if len(result["redirect_entrypoint"]) > 0 || len(result["redirect_regex"]) > 0 || len(result["redirect_replacement"]) > 0 {
|
||||
redirect = &types.Redirect{
|
||||
EntryPoint: result["redirect_entrypoint"],
|
||||
Regex: result["redirect_regex"],
|
||||
Replacement: result["redirect_replacement"],
|
||||
Permanent: toBool(result, "redirect_permanent"),
|
||||
}
|
||||
}
|
||||
|
||||
return redirect
|
||||
}
|
||||
|
||||
func makeEntryPointTLS(result map[string]string) (*tls.TLS, error) {
|
||||
var configTLS *tls.TLS
|
||||
|
||||
if len(result["tls"]) > 0 {
|
||||
certs := tls.Certificates{}
|
||||
if err := certs.Set(result["tls"]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
configTLS = &tls.TLS{
|
||||
Certificates: certs,
|
||||
}
|
||||
} else if len(result["tls_acme"]) > 0 {
|
||||
configTLS = &tls.TLS{
|
||||
Certificates: tls.Certificates{},
|
||||
}
|
||||
}
|
||||
|
||||
if len(result["ca"]) > 0 {
|
||||
files := strings.Split(result["ca"], ",")
|
||||
optional := toBool(result, "ca_optional")
|
||||
configTLS.ClientCA = tls.ClientCA{
|
||||
Files: files,
|
||||
Optional: optional,
|
||||
}
|
||||
}
|
||||
|
||||
return configTLS, nil
|
||||
}
|
||||
|
||||
func parseEntryPointsConfiguration(raw string) map[string]string {
|
||||
sections := strings.Fields(raw)
|
||||
|
||||
config := make(map[string]string)
|
||||
for _, part := range sections {
|
||||
field := strings.SplitN(part, ":", 2)
|
||||
name := strings.ToLower(strings.Replace(field[0], ".", "_", -1))
|
||||
if len(field) > 1 {
|
||||
config[name] = field[1]
|
||||
} else {
|
||||
if strings.EqualFold(name, "TLS") {
|
||||
config["tls_acme"] = "TLS"
|
||||
} else {
|
||||
config[name] = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
func toBool(conf map[string]string, key string) bool {
|
||||
if val, ok := conf[key]; ok {
|
||||
return strings.EqualFold(val, "true") ||
|
||||
strings.EqualFold(val, "enable") ||
|
||||
strings.EqualFold(val, "on")
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -1,467 +0,0 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/containous/traefik/tls"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_parseEntryPointsConfiguration(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
value string
|
||||
expectedResult map[string]string
|
||||
}{
|
||||
{
|
||||
name: "all parameters",
|
||||
value: "Name:foo " +
|
||||
"Address::8000 " +
|
||||
"TLS:goo,gii " +
|
||||
"TLS " +
|
||||
"CA:car " +
|
||||
"CA.Optional:true " +
|
||||
"Redirect.EntryPoint:https " +
|
||||
"Redirect.Regex:http://localhost/(.*) " +
|
||||
"Redirect.Replacement:http://mydomain/$1 " +
|
||||
"Redirect.Permanent:true " +
|
||||
"Compress:true " +
|
||||
"ProxyProtocol.TrustedIPs:192.168.0.1 " +
|
||||
"ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24 " +
|
||||
"Auth.Basic.Users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0 " +
|
||||
"Auth.Digest.Users:test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e " +
|
||||
"Auth.HeaderField:X-WebAuth-User " +
|
||||
"Auth.Forward.Address:https://authserver.com/auth " +
|
||||
"Auth.Forward.TrustForwardHeader:true " +
|
||||
"Auth.Forward.TLS.CA:path/to/local.crt " +
|
||||
"Auth.Forward.TLS.CAOptional:true " +
|
||||
"Auth.Forward.TLS.Cert:path/to/foo.cert " +
|
||||
"Auth.Forward.TLS.Key:path/to/foo.key " +
|
||||
"Auth.Forward.TLS.InsecureSkipVerify:true " +
|
||||
"WhiteListSourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " +
|
||||
"whiteList.sourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " +
|
||||
"whiteList.useXForwardedFor:true ",
|
||||
expectedResult: map[string]string{
|
||||
"address": ":8000",
|
||||
"auth_basic_users": "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
"auth_digest_users": "test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e",
|
||||
"auth_forward_address": "https://authserver.com/auth",
|
||||
"auth_forward_tls_ca": "path/to/local.crt",
|
||||
"auth_forward_tls_caoptional": "true",
|
||||
"auth_forward_tls_cert": "path/to/foo.cert",
|
||||
"auth_forward_tls_insecureskipverify": "true",
|
||||
"auth_forward_tls_key": "path/to/foo.key",
|
||||
"auth_forward_trustforwardheader": "true",
|
||||
"auth_headerfield": "X-WebAuth-User",
|
||||
"ca": "car",
|
||||
"ca_optional": "true",
|
||||
"compress": "true",
|
||||
"forwardedheaders_trustedips": "10.0.0.3/24,20.0.0.3/24",
|
||||
"name": "foo",
|
||||
"proxyprotocol_trustedips": "192.168.0.1",
|
||||
"redirect_entrypoint": "https",
|
||||
"redirect_permanent": "true",
|
||||
"redirect_regex": "http://localhost/(.*)",
|
||||
"redirect_replacement": "http://mydomain/$1",
|
||||
"tls": "goo,gii",
|
||||
"tls_acme": "TLS",
|
||||
"whitelistsourcerange": "10.42.0.0/16,152.89.1.33/32,afed:be44::/16",
|
||||
"whitelist_sourcerange": "10.42.0.0/16,152.89.1.33/32,afed:be44::/16",
|
||||
"whitelist_usexforwardedfor": "true",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "compress on",
|
||||
value: "name:foo Compress:on",
|
||||
expectedResult: map[string]string{
|
||||
"name": "foo",
|
||||
"compress": "on",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TLS",
|
||||
value: "Name:foo TLS:goo TLS",
|
||||
expectedResult: map[string]string{
|
||||
"name": "foo",
|
||||
"tls": "goo",
|
||||
"tls_acme": "TLS",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
conf := parseEntryPointsConfiguration(test.value)
|
||||
|
||||
assert.Len(t, conf, len(test.expectedResult))
|
||||
assert.Equal(t, test.expectedResult, conf)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_toBool(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
value string
|
||||
key string
|
||||
expectedBool bool
|
||||
}{
|
||||
{
|
||||
name: "on",
|
||||
value: "on",
|
||||
key: "foo",
|
||||
expectedBool: true,
|
||||
},
|
||||
{
|
||||
name: "true",
|
||||
value: "true",
|
||||
key: "foo",
|
||||
expectedBool: true,
|
||||
},
|
||||
{
|
||||
name: "enable",
|
||||
value: "enable",
|
||||
key: "foo",
|
||||
expectedBool: true,
|
||||
},
|
||||
{
|
||||
name: "arbitrary string",
|
||||
value: "bar",
|
||||
key: "foo",
|
||||
expectedBool: false,
|
||||
},
|
||||
{
|
||||
name: "no existing entry",
|
||||
value: "bar",
|
||||
key: "fii",
|
||||
expectedBool: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
conf := map[string]string{
|
||||
"foo": test.value,
|
||||
}
|
||||
|
||||
result := toBool(conf, test.key)
|
||||
|
||||
assert.Equal(t, test.expectedBool, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEntryPoints_Set(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
expression string
|
||||
expectedEntryPointName string
|
||||
expectedEntryPoint *EntryPoint
|
||||
}{
|
||||
{
|
||||
name: "all parameters camelcase",
|
||||
expression: "Name:foo " +
|
||||
"Address::8000 " +
|
||||
"TLS:goo,gii;foo,fii " +
|
||||
"TLS " +
|
||||
"CA:car " +
|
||||
"CA.Optional:true " +
|
||||
"Redirect.EntryPoint:https " +
|
||||
"Redirect.Regex:http://localhost/(.*) " +
|
||||
"Redirect.Replacement:http://mydomain/$1 " +
|
||||
"Redirect.Permanent:true " +
|
||||
"Compress:true " +
|
||||
"ProxyProtocol.TrustedIPs:192.168.0.1 " +
|
||||
"ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24 " +
|
||||
"Auth.Basic.Users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0 " +
|
||||
"Auth.Digest.Users:test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e " +
|
||||
"Auth.HeaderField:X-WebAuth-User " +
|
||||
"Auth.Forward.Address:https://authserver.com/auth " +
|
||||
"Auth.Forward.TrustForwardHeader:true " +
|
||||
"Auth.Forward.TLS.CA:path/to/local.crt " +
|
||||
"Auth.Forward.TLS.CAOptional:true " +
|
||||
"Auth.Forward.TLS.Cert:path/to/foo.cert " +
|
||||
"Auth.Forward.TLS.Key:path/to/foo.key " +
|
||||
"Auth.Forward.TLS.InsecureSkipVerify:true " +
|
||||
"WhiteListSourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " +
|
||||
"whiteList.sourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " +
|
||||
"whiteList.useXForwardedFor:true ",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
Address: ":8000",
|
||||
TLS: &tls.TLS{
|
||||
Certificates: tls.Certificates{
|
||||
{
|
||||
CertFile: tls.FileOrContent("goo"),
|
||||
KeyFile: tls.FileOrContent("gii"),
|
||||
},
|
||||
{
|
||||
CertFile: tls.FileOrContent("foo"),
|
||||
KeyFile: tls.FileOrContent("fii"),
|
||||
},
|
||||
},
|
||||
ClientCA: tls.ClientCA{
|
||||
Files: []string{"car"},
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
Redirect: &types.Redirect{
|
||||
EntryPoint: "https",
|
||||
Regex: "http://localhost/(.*)",
|
||||
Replacement: "http://mydomain/$1",
|
||||
Permanent: true,
|
||||
},
|
||||
Auth: &types.Auth{
|
||||
Basic: &types.Basic{
|
||||
Users: types.Users{
|
||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
},
|
||||
},
|
||||
Digest: &types.Digest{
|
||||
Users: types.Users{
|
||||
"test:traefik:a2688e031edb4be6a3797f3882655c05",
|
||||
"test2:traefik:518845800f9e2bfb1f1f740ec24f074e",
|
||||
},
|
||||
},
|
||||
Forward: &types.Forward{
|
||||
Address: "https://authserver.com/auth",
|
||||
TLS: &types.ClientTLS{
|
||||
CA: "path/to/local.crt",
|
||||
CAOptional: true,
|
||||
Cert: "path/to/foo.cert",
|
||||
Key: "path/to/foo.key",
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
TrustForwardHeader: true,
|
||||
},
|
||||
HeaderField: "X-WebAuth-User",
|
||||
},
|
||||
WhitelistSourceRange: []string{
|
||||
"10.42.0.0/16",
|
||||
"152.89.1.33/32",
|
||||
"afed:be44::/16",
|
||||
},
|
||||
WhiteList: &types.WhiteList{
|
||||
SourceRange: []string{
|
||||
"10.42.0.0/16",
|
||||
"152.89.1.33/32",
|
||||
"afed:be44::/16",
|
||||
},
|
||||
UseXForwardedFor: true,
|
||||
},
|
||||
Compress: true,
|
||||
ProxyProtocol: &ProxyProtocol{
|
||||
Insecure: false,
|
||||
TrustedIPs: []string{"192.168.0.1"},
|
||||
},
|
||||
ForwardedHeaders: &ForwardedHeaders{
|
||||
Insecure: false,
|
||||
TrustedIPs: []string{
|
||||
"10.0.0.3/24",
|
||||
"20.0.0.3/24",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "all parameters lowercase",
|
||||
expression: "Name:foo " +
|
||||
"address::8000 " +
|
||||
"tls:goo,gii;foo,fii " +
|
||||
"tls " +
|
||||
"ca:car " +
|
||||
"ca.Optional:true " +
|
||||
"redirect.entryPoint:https " +
|
||||
"redirect.regex:http://localhost/(.*) " +
|
||||
"redirect.replacement:http://mydomain/$1 " +
|
||||
"redirect.permanent:true " +
|
||||
"compress:true " +
|
||||
"whiteListSourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " +
|
||||
"proxyProtocol.TrustedIPs:192.168.0.1 " +
|
||||
"forwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24 " +
|
||||
"auth.basic.users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0 " +
|
||||
"auth.digest.users:test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e " +
|
||||
"auth.headerField:X-WebAuth-User " +
|
||||
"auth.forward.address:https://authserver.com/auth " +
|
||||
"auth.forward.trustForwardHeader:true " +
|
||||
"auth.forward.tls.ca:path/to/local.crt " +
|
||||
"auth.forward.tls.caOptional:true " +
|
||||
"auth.forward.tls.cert:path/to/foo.cert " +
|
||||
"auth.forward.tls.key:path/to/foo.key " +
|
||||
"auth.forward.tls.insecureSkipVerify:true ",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
Address: ":8000",
|
||||
TLS: &tls.TLS{
|
||||
Certificates: tls.Certificates{
|
||||
{
|
||||
CertFile: tls.FileOrContent("goo"),
|
||||
KeyFile: tls.FileOrContent("gii"),
|
||||
},
|
||||
{
|
||||
CertFile: tls.FileOrContent("foo"),
|
||||
KeyFile: tls.FileOrContent("fii"),
|
||||
},
|
||||
},
|
||||
ClientCA: tls.ClientCA{
|
||||
Files: []string{"car"},
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
Redirect: &types.Redirect{
|
||||
EntryPoint: "https",
|
||||
Regex: "http://localhost/(.*)",
|
||||
Replacement: "http://mydomain/$1",
|
||||
Permanent: true,
|
||||
},
|
||||
Auth: &types.Auth{
|
||||
Basic: &types.Basic{
|
||||
Users: types.Users{
|
||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
},
|
||||
},
|
||||
Digest: &types.Digest{
|
||||
Users: types.Users{
|
||||
"test:traefik:a2688e031edb4be6a3797f3882655c05",
|
||||
"test2:traefik:518845800f9e2bfb1f1f740ec24f074e",
|
||||
},
|
||||
},
|
||||
Forward: &types.Forward{
|
||||
Address: "https://authserver.com/auth",
|
||||
TLS: &types.ClientTLS{
|
||||
CA: "path/to/local.crt",
|
||||
CAOptional: true,
|
||||
Cert: "path/to/foo.cert",
|
||||
Key: "path/to/foo.key",
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
TrustForwardHeader: true,
|
||||
},
|
||||
HeaderField: "X-WebAuth-User",
|
||||
},
|
||||
WhitelistSourceRange: []string{
|
||||
"10.42.0.0/16",
|
||||
"152.89.1.33/32",
|
||||
"afed:be44::/16",
|
||||
},
|
||||
Compress: true,
|
||||
ProxyProtocol: &ProxyProtocol{
|
||||
Insecure: false,
|
||||
TrustedIPs: []string{"192.168.0.1"},
|
||||
},
|
||||
ForwardedHeaders: &ForwardedHeaders{
|
||||
Insecure: false,
|
||||
TrustedIPs: []string{
|
||||
"10.0.0.3/24",
|
||||
"20.0.0.3/24",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "default",
|
||||
expression: "Name:foo",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ForwardedHeaders insecure true",
|
||||
expression: "Name:foo ForwardedHeaders.Insecure:true",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ForwardedHeaders insecure false",
|
||||
expression: "Name:foo ForwardedHeaders.Insecure:false",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: false},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ForwardedHeaders TrustedIPs",
|
||||
expression: "Name:foo ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
ForwardedHeaders: &ForwardedHeaders{
|
||||
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProxyProtocol insecure true",
|
||||
expression: "Name:foo ProxyProtocol.Insecure:true",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
ProxyProtocol: &ProxyProtocol{Insecure: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProxyProtocol insecure false",
|
||||
expression: "Name:foo ProxyProtocol.Insecure:false",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
ProxyProtocol: &ProxyProtocol{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProxyProtocol TrustedIPs",
|
||||
expression: "Name:foo ProxyProtocol.TrustedIPs:10.0.0.3/24,20.0.0.3/24",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
ProxyProtocol: &ProxyProtocol{
|
||||
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "compress on",
|
||||
expression: "Name:foo Compress:on",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
Compress: true,
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "compress true",
|
||||
expression: "Name:foo Compress:true",
|
||||
expectedEntryPointName: "foo",
|
||||
expectedEntryPoint: &EntryPoint{
|
||||
Compress: true,
|
||||
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
eps := EntryPoints{}
|
||||
err := eps.Set(test.expression)
|
||||
require.NoError(t, err)
|
||||
|
||||
ep := eps[test.expectedEntryPointName]
|
||||
assert.EqualValues(t, test.expectedEntryPoint, ep)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
|
||||
"github.com/containous/traefik/acme"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/provider"
|
||||
acmeprovider "github.com/containous/traefik/provider/acme"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/containous/traefik/types"
|
||||
)
|
||||
|
||||
type providerAggregator struct {
|
||||
providers []provider.Provider
|
||||
}
|
||||
|
||||
// NewProviderAggregator return an aggregate of all the providers configured in GlobalConfiguration
|
||||
func NewProviderAggregator(gc *GlobalConfiguration) provider.Provider {
|
||||
provider := providerAggregator{}
|
||||
if gc.Docker != nil {
|
||||
provider.providers = append(provider.providers, gc.Docker)
|
||||
}
|
||||
if gc.Marathon != nil {
|
||||
provider.providers = append(provider.providers, gc.Marathon)
|
||||
}
|
||||
if gc.File != nil {
|
||||
provider.providers = append(provider.providers, gc.File)
|
||||
}
|
||||
if gc.Rest != nil {
|
||||
provider.providers = append(provider.providers, gc.Rest)
|
||||
}
|
||||
if gc.Consul != nil {
|
||||
provider.providers = append(provider.providers, gc.Consul)
|
||||
}
|
||||
if gc.ConsulCatalog != nil {
|
||||
provider.providers = append(provider.providers, gc.ConsulCatalog)
|
||||
}
|
||||
if gc.Etcd != nil {
|
||||
provider.providers = append(provider.providers, gc.Etcd)
|
||||
}
|
||||
if gc.Zookeeper != nil {
|
||||
provider.providers = append(provider.providers, gc.Zookeeper)
|
||||
}
|
||||
if gc.Boltdb != nil {
|
||||
provider.providers = append(provider.providers, gc.Boltdb)
|
||||
}
|
||||
if gc.Kubernetes != nil {
|
||||
provider.providers = append(provider.providers, gc.Kubernetes)
|
||||
}
|
||||
if gc.Mesos != nil {
|
||||
provider.providers = append(provider.providers, gc.Mesos)
|
||||
}
|
||||
if gc.Eureka != nil {
|
||||
provider.providers = append(provider.providers, gc.Eureka)
|
||||
}
|
||||
if gc.ECS != nil {
|
||||
provider.providers = append(provider.providers, gc.ECS)
|
||||
}
|
||||
if gc.Rancher != nil {
|
||||
provider.providers = append(provider.providers, gc.Rancher)
|
||||
}
|
||||
if gc.DynamoDB != nil {
|
||||
provider.providers = append(provider.providers, gc.DynamoDB)
|
||||
}
|
||||
if gc.ServiceFabric != nil {
|
||||
provider.providers = append(provider.providers, gc.ServiceFabric)
|
||||
}
|
||||
if acmeprovider.IsEnabled() {
|
||||
provider.providers = append(provider.providers, acmeprovider.Get())
|
||||
acme.ConvertToNewFormat(acmeprovider.Get().Storage)
|
||||
}
|
||||
if len(provider.providers) == 1 {
|
||||
return provider.providers[0]
|
||||
}
|
||||
return provider
|
||||
}
|
||||
|
||||
func (p providerAggregator) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
|
||||
for _, p := range p.providers {
|
||||
providerType := reflect.TypeOf(p)
|
||||
jsonConf, err := json.Marshal(p)
|
||||
if err != nil {
|
||||
log.Debugf("Unable to marshal provider conf %v with error: %v", providerType, err)
|
||||
}
|
||||
log.Infof("Starting provider %v %s", providerType, jsonConf)
|
||||
currentProvider := p
|
||||
safe.Go(func() {
|
||||
err := currentProvider.Provide(configurationChan, pool, constraints)
|
||||
if err != nil {
|
||||
log.Errorf("Error starting provider %v: %s", providerType, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
1374
contrib/grafana/traefik-kubernetes.json
Normal file
1055
contrib/grafana/traefik.json
Normal file
@@ -1,170 +0,0 @@
|
||||
#!/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 '.Account.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 '.Certificates[].Domain.Main' ${acmefile}); do
|
||||
# Traefik stores a cert bundle for each domain. Within this cert
|
||||
# bundle there is both proper the certificate and the Let's Encrypt CA
|
||||
echo "Extracting cert bundle for ${domain}"
|
||||
cert=$(jq -e -r --arg domain "$domain" '.Certificates[] |
|
||||
select (.Domain.Main == $domain )| .Certificate' ${acmefile}) || bad_acme
|
||||
echo "${cert}" | ${CMD_DECODE_BASE64} > "${cdir}/${domain}.crt"
|
||||
|
||||
echo "Extracting private key for ${domain}"
|
||||
key=$(jq -e -r --arg domain "$domain" '.Certificates[] |
|
||||
select (.Domain.Main == $domain )| .Key' ${acmefile}) || bad_acme
|
||||
echo "${key}" | ${CMD_DECODE_BASE64} > "${pdir}/${domain}.key"
|
||||
done
|
||||
@@ -1,11 +1,41 @@
|
||||
[Unit]
|
||||
Description=Traefik
|
||||
Documentation=https://docs.traefik.io
|
||||
#After=network-online.target
|
||||
#AssertFileIsExecutable=/usr/bin/traefik
|
||||
#AssertPathExists=/etc/traefik/traefik.toml
|
||||
|
||||
[Service]
|
||||
# Run traefik as its own user (create new user with: useradd -r -s /bin/false -U -M traefik)
|
||||
#User=traefik
|
||||
#AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
|
||||
# configure service behavior
|
||||
Type=notify
|
||||
ExecStart=/usr/bin/traefik --configFile=/etc/traefik.toml
|
||||
#ExecStart=/usr/bin/traefik --configFile=/etc/traefik/traefik.toml
|
||||
Restart=always
|
||||
WatchdogSec=1s
|
||||
|
||||
# lock down system access
|
||||
# prohibit any operating system and configuration modification
|
||||
#ProtectSystem=strict
|
||||
# create separate, new (and empty) /tmp and /var/tmp filesystems
|
||||
#PrivateTmp=true
|
||||
# make /home directories inaccessible
|
||||
#ProtectHome=true
|
||||
# turns off access to physical devices (/dev/...)
|
||||
#PrivateDevices=true
|
||||
# make kernel settings (procfs and sysfs) read-only
|
||||
#ProtectKernelTunables=true
|
||||
# make cgroups /sys/fs/cgroup read-only
|
||||
#ProtectControlGroups=true
|
||||
|
||||
# allow writing of acme.json
|
||||
#ReadWritePaths=/etc/traefik/acme.json
|
||||
# depending on log and entrypoint configuration, you may need to allow writing to other paths, too
|
||||
|
||||
# limit number of processes in this unit
|
||||
#LimitNPROC=1
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
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
|
||||
1
docs/.dockerignore
Normal file
@@ -0,0 +1 @@
|
||||
site/
|
||||
10
docs/.markdownlint.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"no-hard-tabs": false,
|
||||
"MD007": { "indent": 4 },
|
||||
"MD009": false,
|
||||
"MD013": false,
|
||||
"MD026": false,
|
||||
"MD033": false,
|
||||
"MD034": false,
|
||||
"MD036": false
|
||||
}
|
||||
52
docs/Makefile
Normal file
@@ -0,0 +1,52 @@
|
||||
|
||||
#######
|
||||
# This Makefile contains all targets related to the documentation
|
||||
#######
|
||||
|
||||
DOCS_VERIFY_SKIP ?= false
|
||||
DOCS_LINT_SKIP ?= false
|
||||
|
||||
TRAEFIK_DOCS_BUILD_IMAGE ?= traefik-docs
|
||||
TRAEFIK_DOCS_CHECK_IMAGE ?= $(TRAEFIK_DOCS_BUILD_IMAGE)-check
|
||||
|
||||
SITE_DIR := $(CURDIR)/site
|
||||
|
||||
DOCKER_RUN_DOC_PORT := 8000
|
||||
DOCKER_RUN_DOC_MOUNTS := -v $(CURDIR):/mkdocs
|
||||
DOCKER_RUN_DOC_OPTS := --rm $(DOCKER_RUN_DOC_MOUNTS) -p $(DOCKER_RUN_DOC_PORT):8000
|
||||
|
||||
# Default: generates the documentation into $(SITE_DIR)
|
||||
docs: docs-clean docs-image docs-lint docs-build docs-verify
|
||||
|
||||
# Writer Mode: build and serve docs on http://localhost:8000 with livereload
|
||||
docs-serve: docs-image
|
||||
docker run $(DOCKER_RUN_DOC_OPTS) $(TRAEFIK_DOCS_BUILD_IMAGE) mkdocs serve
|
||||
|
||||
# Utilities Targets for each step
|
||||
docs-image:
|
||||
docker build -t $(TRAEFIK_DOCS_BUILD_IMAGE) -f docs.Dockerfile ./
|
||||
|
||||
docs-build: docs-image
|
||||
docker run $(DOCKER_RUN_DOC_OPTS) $(TRAEFIK_DOCS_BUILD_IMAGE) sh -c "mkdocs build \
|
||||
&& chown -R $(shell id -u):$(shell id -g) ./site"
|
||||
|
||||
docs-verify: docs-build
|
||||
@if [ "$(DOCS_VERIFY_SKIP)" != "true" ]; then \
|
||||
docker build -t $(TRAEFIK_DOCS_CHECK_IMAGE) -f check.Dockerfile ./; \
|
||||
docker run --rm -v $(CURDIR):/app $(TRAEFIK_DOCS_CHECK_IMAGE) /verify.sh; \
|
||||
else \
|
||||
echo "DOCS_VERIFY_SKIP is true: no verification done."; \
|
||||
fi
|
||||
|
||||
docs-lint:
|
||||
@if [ "$(DOCS_LINT_SKIP)" != "true" ]; then \
|
||||
docker build -t $(TRAEFIK_DOCS_CHECK_IMAGE) -f check.Dockerfile ./ && \
|
||||
docker run --rm -v $(CURDIR):/app $(TRAEFIK_DOCS_CHECK_IMAGE) /lint.sh; \
|
||||
else \
|
||||
echo "DOCS_LINT_SKIP is true: no linting done."; \
|
||||
fi
|
||||
|
||||
docs-clean:
|
||||
rm -rf $(SITE_DIR)
|
||||
|
||||
.PHONY: all docs-verify docs docs-clean docs-build docs-lint
|
||||
739
docs/basics.md
@@ -1,739 +0,0 @@
|
||||
# Basics
|
||||
|
||||
## Concepts
|
||||
|
||||
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.
|
||||
> If you want your users to access some of your microservices from the Internet, you will have to use a reverse proxy and configure it using virtual hosts or prefix paths:
|
||||
|
||||
> - domain `api.domain.com` will point the microservice `api` 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
|
||||
|
||||
> 
|
||||
|
||||
Let's zoom on Træfik and have an overview of its internal architecture:
|
||||
|
||||
|
||||

|
||||
|
||||
- Incoming requests end on [entrypoints](#entrypoints), as the name suggests, they are the network entry points into Træfik (listening port, SSL, traffic redirection...).
|
||||
- Traffic is then forwarded to a matching [frontend](#frontends). A frontend defines routes from [entrypoints](#entrypoints) to [backends](#backends).
|
||||
Routes are created using requests fields (`Host`, `Path`, `Headers`...) and can match or not a request.
|
||||
- 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.
|
||||
|
||||
### Entrypoints
|
||||
|
||||
Entrypoints are the network entry points into Træfik.
|
||||
They can be defined using:
|
||||
|
||||
- a port (80, 443...)
|
||||
- SSL (Certificates, Keys, authentication with a client certificate signed by a trusted CA...)
|
||||
- redirection to another entrypoint (redirect `HTTP` to `HTTPS`)
|
||||
|
||||
Here is an example of entrypoints definition:
|
||||
|
||||
```toml
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
[entryPoints.http.redirect]
|
||||
entryPoint = "https"
|
||||
[entryPoints.https]
|
||||
address = ":443"
|
||||
[entryPoints.https.tls]
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
certFile = "tests/traefik.crt"
|
||||
keyFile = "tests/traefik.key"
|
||||
```
|
||||
|
||||
- Two entrypoints are defined `http` and `https`.
|
||||
- `http` listens on port `80` and `https` on port `443`.
|
||||
- We enable SSL on `https` by giving a certificate and a key.
|
||||
- We also redirect all the traffic from entrypoint `http` to `https`.
|
||||
|
||||
And here is another example with client certificate authentication:
|
||||
|
||||
```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 = "tests/traefik.crt"
|
||||
keyFile = "tests/traefik.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.
|
||||
- It is possible to have multiple CA:s in the same file or keep them in separate files.
|
||||
|
||||
### Frontends
|
||||
|
||||
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.
|
||||
|
||||
#### Modifiers
|
||||
|
||||
Modifier rules only modify the request. They do not have any impact on routing decisions being made.
|
||||
|
||||
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.
|
||||
- `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
|
||||
|
||||
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 `;` (semicolon) in order to enable ALL semantics (i.e., forward a request if all rules match).
|
||||
|
||||
Following is the list of existing matcher rules along with examples:
|
||||
|
||||
| Matcher | Description |
|
||||
|------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `Headers: Content-Type, application/json` | Match HTTP header. It accepts a comma-separated key/value pair where both key and value must be literals. |
|
||||
| `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. |
|
||||
| `HostRegexp: traefik.io, {subdomain:[a-z]+}.traefik.io` | Match request host. It accepts a sequence of literal and regular expression hosts. |
|
||||
| `Method: GET, POST, PUT` | Match request HTTP method. It accepts a sequence of HTTP methods. |
|
||||
| `Path: /products/, /articles/{category}/{id:[0-9]+}` | Match exact request path. It accepts a sequence of literal and regular expression 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. |
|
||||
| `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. |
|
||||
| `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]+}`).
|
||||
|
||||
!!! 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.
|
||||
|
||||
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.
|
||||
|
||||
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 `*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 `/`.
|
||||
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.
|
||||
|
||||
#### Examples
|
||||
|
||||
Here is an example of frontends definition:
|
||||
|
||||
```toml
|
||||
[frontends]
|
||||
[frontends.frontend1]
|
||||
backend = "backend2"
|
||||
[frontends.frontend1.routes.test_1]
|
||||
rule = "Host:test.localhost,test2.localhost"
|
||||
[frontends.frontend2]
|
||||
backend = "backend1"
|
||||
passHostHeader = true
|
||||
passTLSCert = true
|
||||
priority = 10
|
||||
entrypoints = ["https"] # overrides defaultEntryPoints
|
||||
[frontends.frontend2.routes.test_1]
|
||||
rule = "HostRegexp:localhost,{subdomain:[a-z]+}.localhost"
|
||||
[frontends.frontend3]
|
||||
backend = "backend2"
|
||||
[frontends.frontend3.routes.test_1]
|
||||
rule = "Host:test3.localhost;Path:/test"
|
||||
```
|
||||
|
||||
- Three frontends are defined: `frontend1`, `frontend2` and `frontend3`
|
||||
- `frontend1` will forward the traffic to the `backend2` if the rule `Host:test.localhost,test2.localhost` is matched
|
||||
- `frontend2` will forward the traffic to the `backend1` if the rule `HostRegexp:localhost,{subdomain:[a-z]+}.localhost` is matched (forwarding client `Host` header to the backend)
|
||||
- `frontend3` will forward the traffic to the `backend2` if the rules `Host:test3.localhost` **AND** `Path:/test` are matched
|
||||
|
||||
#### Combining multiple rules
|
||||
|
||||
As seen in the previous example, you can combine multiple rules.
|
||||
In TOML file, you can use multiple routes:
|
||||
|
||||
```toml
|
||||
[frontends.frontend3]
|
||||
backend = "backend2"
|
||||
[frontends.frontend3.routes.test_1]
|
||||
rule = "Host:test3.localhost"
|
||||
[frontends.frontend3.routes.test_2]
|
||||
rule = "Path:/test"
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```toml
|
||||
[frontends.frontend3]
|
||||
backend = "backend2"
|
||||
[frontends.frontend3.routes.test_1]
|
||||
rule = "Host:test3.localhost;Path:/test"
|
||||
```
|
||||
|
||||
Finally, you can create a rule to bind multiple domains or Path to a frontend, using the `,` separator:
|
||||
|
||||
```toml
|
||||
[frontends.frontend2]
|
||||
[frontends.frontend2.routes.test_1]
|
||||
rule = "Host:test1.localhost,test2.localhost"
|
||||
[frontends.frontend3]
|
||||
backend = "backend2"
|
||||
[frontends.frontend3.routes.test_1]
|
||||
rule = "Path:/test1,/test2"
|
||||
```
|
||||
|
||||
#### Rules Order
|
||||
|
||||
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.
|
||||
|
||||
- `PathStrip`
|
||||
- `PathStripRegex`
|
||||
- `PathPrefixStrip`
|
||||
- `PathPrefixStripRegex`
|
||||
|
||||
`Modifiers` will be applied in a pre-determined order regardless of their order in the `rule` configuration section.
|
||||
|
||||
1. `PathStrip`
|
||||
2. `PathPrefixStrip`
|
||||
3. `PathStripRegex`
|
||||
4. `PathPrefixStripRegex`
|
||||
5. `AddPrefix`
|
||||
6. `ReplacePath`
|
||||
|
||||
#### Priorities
|
||||
|
||||
By default, routes will be sorted (in descending order) using rules length (to avoid path overlap):
|
||||
`PathPrefix:/foo;Host:foo.com` (length == 28) will be matched before `PathPrefixStrip:/foobar` (length == 23) will be matched before `PathPrefix:/foo,/bar` (length == 20).
|
||||
|
||||
You can customize priority by frontend. The priority value override the rule length during sorting:
|
||||
|
||||
```toml
|
||||
[frontends]
|
||||
[frontends.frontend1]
|
||||
backend = "backend1"
|
||||
priority = 20
|
||||
passHostHeader = true
|
||||
[frontends.frontend1.routes.test_1]
|
||||
rule = "PathPrefix:/to"
|
||||
[frontends.frontend2]
|
||||
backend = "backend2"
|
||||
passHostHeader = true
|
||||
[frontends.frontend2.routes.test_1]
|
||||
rule = "PathPrefix:/toto"
|
||||
```
|
||||
|
||||
Here, `frontend1` will be matched before `frontend2` (`20 > 16`).
|
||||
|
||||
#### 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` 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` header removed from the request, and the `X-Custom-Response-Header` header removed from 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.
|
||||
|
||||
#### Servers
|
||||
|
||||
Servers are simply defined using a `url`. You can also apply a custom `weight` to each server (this will be used by load-balancing).
|
||||
|
||||
!!! note
|
||||
Paths in `url` are ignored. Use `Modifier` to specify paths instead.
|
||||
|
||||
Here is an example of backends and servers definition:
|
||||
|
||||
```toml
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
# ...
|
||||
[backends.backend1.servers.server1]
|
||||
url = "http://172.17.0.2:80"
|
||||
weight = 10
|
||||
[backends.backend1.servers.server2]
|
||||
url = "http://172.17.0.3:80"
|
||||
weight = 1
|
||||
[backends.backend2]
|
||||
# ...
|
||||
[backends.backend2.servers.server1]
|
||||
url = "http://172.17.0.4:80"
|
||||
weight = 1
|
||||
[backends.backend2.servers.server2]
|
||||
url = "http://172.17.0.5:80"
|
||||
weight = 2
|
||||
```
|
||||
|
||||
- Two backends are defined: `backend1` and `backend2`
|
||||
- `backend1` will forward the traffic to two servers: `http://172.17.0.2:80"` with weight `10` and `http://172.17.0.3:80` with weight `1`.
|
||||
- `backend2` will forward the traffic to two servers: `http://172.17.0.4:80"` with weight `1` and `http://172.17.0.5:80` with weight `2`.
|
||||
|
||||
#### Load-balancing
|
||||
|
||||
Various methods of load-balancing are supported:
|
||||
|
||||
- `wrr`: Weighted Round Robin.
|
||||
- `drr`: Dynamic Round Robin: increases weights on servers that perform better than others.
|
||||
It also rolls back to original weights if the servers have changed.
|
||||
|
||||
#### Circuit breakers
|
||||
|
||||
A circuit breaker can also be applied to a backend, preventing high loads on failing servers.
|
||||
Initial state is Standby. CB observes the statistics and does not modify the request.
|
||||
In case the condition matches, CB enters Tripped state, where it responds with predefined code or redirects to another frontend.
|
||||
Once Tripped timer expires, CB enters Recovering state and resets all stats.
|
||||
In case the condition does not match and recovery timer expires, CB enters Standby state.
|
||||
|
||||
It can be configured using:
|
||||
|
||||
- Methods: `LatencyAtQuantileMS`, `NetworkErrorRatio`, `ResponseCodeRatio`
|
||||
- Operators: `AND`, `OR`, `EQ`, `NEQ`, `LT`, `LE`, `GT`, `GE`
|
||||
|
||||
For example:
|
||||
|
||||
- `NetworkErrorRatio() > 0.5`: watch error ratio over 10 second sliding window for a frontend.
|
||||
- `LatencyAtQuantileMS(50.0) > 50`: watch latency at quantile in milliseconds.
|
||||
- `ResponseCodeRatio(500, 600, 0, 600) > 0.5`: ratio of response codes in ranges [500-600) and [0-600).
|
||||
|
||||
Here is an example of backends and servers definition:
|
||||
|
||||
```toml
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.circuitbreaker]
|
||||
expression = "NetworkErrorRatio() > 0.5"
|
||||
[backends.backend1.servers.server1]
|
||||
url = "http://172.17.0.2:80"
|
||||
weight = 10
|
||||
[backends.backend1.servers.server2]
|
||||
url = "http://172.17.0.3:80"
|
||||
weight = 1
|
||||
```
|
||||
|
||||
- `backend1` will forward the traffic to two servers: `http://172.17.0.2:80"` with weight `10` and `http://172.17.0.3:80` with weight `1` using default `wrr` load-balancing strategy.
|
||||
- a circuit breaker is added on `backend1` using the expression `NetworkErrorRatio() > 0.5`: watch error ratio over 10 second sliding window
|
||||
|
||||
#### Maximum connections
|
||||
|
||||
To proactively prevent backends from being overwhelmed with high load, a maximum connection limit can also be applied to each backend.
|
||||
|
||||
Maximum connections can be configured by specifying an integer value for `maxconn.amount` and `maxconn.extractorfunc` which is a strategy used to determine how to categorize requests in order to evaluate the maximum connections.
|
||||
|
||||
For example:
|
||||
```toml
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.maxconn]
|
||||
amount = 10
|
||||
extractorfunc = "request.host"
|
||||
# ...
|
||||
```
|
||||
|
||||
- `backend1` will return `HTTP code 429 Too Many Requests` if there are already 10 requests in progress for the same Host header.
|
||||
- Another possible value for `extractorfunc` is `client.ip` which will categorize requests based on client source ip.
|
||||
- Lastly `extractorfunc` can take the value of `request.header.ANY_HEADER` which will categorize requests based on `ANY_HEADER` that you provide.
|
||||
|
||||
#### Sticky sessions
|
||||
|
||||
Sticky sessions 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:
|
||||
|
||||
```toml
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.loadbalancer]
|
||||
sticky = true
|
||||
```
|
||||
|
||||
#### Health Check
|
||||
|
||||
A health check can be configured in order to remove a backend from LB rotation as long as it keeps returning HTTP status codes other than `200 OK` to HTTP GET requests periodically carried out by Traefik.
|
||||
The check is defined by a path appended to the backend URL and an interval (given in a format understood by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration)) specifying how often the health check should be executed (the default being 30 seconds).
|
||||
Each backend must respond to the health check within 5 seconds.
|
||||
By default, the port of the backend server is used, however, this may be overridden.
|
||||
|
||||
A recovering backend returning 200 OK responses again is being returned to the
|
||||
LB rotation pool.
|
||||
|
||||
For example:
|
||||
```toml
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.healthcheck]
|
||||
path = "/health"
|
||||
interval = "10s"
|
||||
```
|
||||
|
||||
To use a different port for the healthcheck:
|
||||
```toml
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.healthcheck]
|
||||
path = "/health"
|
||||
interval = "10s"
|
||||
port = 8080
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Træfik's configuration has two parts:
|
||||
|
||||
- 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
|
||||
|
||||
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.
|
||||
Each item takes precedence over the item below it:
|
||||
|
||||
- [Key-value store](/basics/#key-value-stores)
|
||||
- [Arguments](/basics/#arguments)
|
||||
- [Configuration file](/basics/#configuration-file)
|
||||
- Default
|
||||
|
||||
It means that arguments override configuration file, and key-value store overrides arguments.
|
||||
|
||||
!!! 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:
|
||||
|
||||
- `/etc/traefik/`
|
||||
- `$HOME/.traefik/`
|
||||
- `.` _the working directory_
|
||||
|
||||
You can override this by setting a `configFile` argument:
|
||||
|
||||
```bash
|
||||
traefik --configFile=foo/bar/myconfigfile.toml
|
||||
```
|
||||
|
||||
Please refer to the [global configuration](/configuration/commons) section to get documentation on it.
|
||||
|
||||
#### Arguments
|
||||
|
||||
Each argument (and command) is described in the help section:
|
||||
|
||||
```bash
|
||||
traefik --help
|
||||
```
|
||||
|
||||
Note that all default values will be displayed as well.
|
||||
|
||||
#### Key-value stores
|
||||
|
||||
Træfik supports several Key-value stores:
|
||||
|
||||
- [Consul](https://consul.io)
|
||||
- [etcd](https://coreos.com/etcd/)
|
||||
- [ZooKeeper](https://zookeeper.apache.org/)
|
||||
- [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.
|
||||
|
||||
### Dynamic Træfik configuration
|
||||
|
||||
The dynamic configuration concerns :
|
||||
|
||||
- [Frontends](/basics/#frontends)
|
||||
- [Backends](/basics/#backends)
|
||||
- [Servers](/basics/#servers)
|
||||
- HTTPS Certificates
|
||||
|
||||
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.
|
||||
Routes to services will be created and updated instantly at any changes.
|
||||
|
||||
Please refer to the [configuration backends](/configuration/commons) section to get documentation on it.
|
||||
|
||||
## Commands
|
||||
|
||||
### traefik
|
||||
|
||||
Usage:
|
||||
```bash
|
||||
traefik [command] [--flag=flag_argument]
|
||||
```
|
||||
|
||||
List of Træfik available commands with description :
|
||||
|
||||
- `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.
|
||||
|
||||
All those related flags will be displayed with :
|
||||
|
||||
```bash
|
||||
traefik [command] --help
|
||||
```
|
||||
|
||||
Each command is described at the beginning of the help section:
|
||||
|
||||
```bash
|
||||
traefik --help
|
||||
|
||||
# or
|
||||
|
||||
docker run traefik[:version] --help
|
||||
# ex: docker run traefik:1.5 --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"
|
||||
|
||||
[api]
|
||||
|
||||
[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"
|
||||
|
||||
[api]
|
||||
|
||||
[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
|
||||
```
|
||||
@@ -1,214 +0,0 @@
|
||||
# Benchmarks
|
||||
|
||||
## Configuration
|
||||
|
||||
I would like to thanks [vincentbernat](https://github.com/vincentbernat) from [exoscale.ch](https://www.exoscale.ch) who kindly provided the infrastructure needed for the benchmarks.
|
||||
|
||||
I used 4 VMs for the tests with the following configuration:
|
||||
|
||||
- 32 GB RAM
|
||||
- 8 CPU Cores
|
||||
- 10 GB SSD
|
||||
- Ubuntu 14.04 LTS 64-bit
|
||||
|
||||
## Setup
|
||||
|
||||
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)
|
||||
3. Two VMs for 2 backend servers in go [whoami](https://github.com/emilevauge/whoamI/)
|
||||
|
||||
Each VM has been tuned using the following limits:
|
||||
|
||||
```bash
|
||||
sysctl -w fs.file-max="9999999"
|
||||
sysctl -w fs.nr_open="9999999"
|
||||
sysctl -w net.core.netdev_max_backlog="4096"
|
||||
sysctl -w net.core.rmem_max="16777216"
|
||||
sysctl -w net.core.somaxconn="65535"
|
||||
sysctl -w net.core.wmem_max="16777216"
|
||||
sysctl -w net.ipv4.ip_local_port_range="1025 65535"
|
||||
sysctl -w net.ipv4.tcp_fin_timeout="30"
|
||||
sysctl -w net.ipv4.tcp_keepalive_time="30"
|
||||
sysctl -w net.ipv4.tcp_max_syn_backlog="20480"
|
||||
sysctl -w net.ipv4.tcp_max_tw_buckets="400000"
|
||||
sysctl -w net.ipv4.tcp_no_metrics_save="1"
|
||||
sysctl -w net.ipv4.tcp_syn_retries="2"
|
||||
sysctl -w net.ipv4.tcp_synack_retries="2"
|
||||
sysctl -w net.ipv4.tcp_tw_recycle="1"
|
||||
sysctl -w net.ipv4.tcp_tw_reuse="1"
|
||||
sysctl -w vm.min_free_kbytes="65536"
|
||||
sysctl -w vm.overcommit_memory="1"
|
||||
ulimit -n 9999999
|
||||
```
|
||||
|
||||
### Nginx
|
||||
|
||||
Here is the config Nginx file use `/etc/nginx/nginx.conf`:
|
||||
|
||||
```
|
||||
user www-data;
|
||||
worker_processes auto;
|
||||
worker_rlimit_nofile 200000;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 10000;
|
||||
use epoll;
|
||||
multi_accept on;
|
||||
}
|
||||
|
||||
http {
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 300;
|
||||
keepalive_requests 10000;
|
||||
types_hash_max_size 2048;
|
||||
|
||||
open_file_cache max=200000 inactive=300s;
|
||||
open_file_cache_valid 300s;
|
||||
open_file_cache_min_uses 2;
|
||||
open_file_cache_errors on;
|
||||
|
||||
server_tokens off;
|
||||
dav_methods off;
|
||||
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
access_log /var/log/nginx/access.log combined;
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
|
||||
gzip off;
|
||||
gzip_vary off;
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
include /etc/nginx/sites-enabled/*.conf;
|
||||
}
|
||||
```
|
||||
|
||||
Here is the Nginx vhost file used:
|
||||
|
||||
```
|
||||
upstream whoami {
|
||||
server IP-whoami1:80;
|
||||
server IP-whoami2:80;
|
||||
keepalive 300;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 8001;
|
||||
server_name test.traefik;
|
||||
access_log off;
|
||||
error_log /dev/null crit;
|
||||
if ($host != "test.traefik") {
|
||||
return 404;
|
||||
}
|
||||
location / {
|
||||
proxy_pass http://whoami;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Traefik
|
||||
|
||||
Here is the `traefik.toml` file used:
|
||||
|
||||
```toml
|
||||
maxIdleConnsPerHost = 100000
|
||||
defaultEntryPoints = ["http"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":8000"
|
||||
|
||||
[file]
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.servers.server1]
|
||||
url = "http://IP-whoami1:80"
|
||||
weight = 1
|
||||
[backends.backend1.servers.server2]
|
||||
url = "http://IP-whoami2:80"
|
||||
weight = 1
|
||||
|
||||
[frontends]
|
||||
[frontends.frontend1]
|
||||
backend = "backend1"
|
||||
[frontends.frontend1.routes.test_1]
|
||||
rule = "Host: test.traefik"
|
||||
```
|
||||
|
||||
## Results
|
||||
|
||||
### whoami:
|
||||
```shell
|
||||
wrk -t20 -c1000 -d60s -H "Host: test.traefik" --latency http://IP-whoami:80/bench
|
||||
Running 1m test @ http://IP-whoami:80/bench
|
||||
20 threads and 1000 connections
|
||||
Thread Stats Avg Stdev Max +/- Stdev
|
||||
Latency 70.28ms 134.72ms 1.91s 89.94%
|
||||
Req/Sec 2.92k 742.42 8.78k 68.80%
|
||||
Latency Distribution
|
||||
50% 10.63ms
|
||||
75% 75.64ms
|
||||
90% 205.65ms
|
||||
99% 668.28ms
|
||||
3476705 requests in 1.00m, 384.61MB read
|
||||
Socket errors: connect 0, read 0, write 0, timeout 103
|
||||
Requests/sec: 57894.35
|
||||
Transfer/sec: 6.40MB
|
||||
```
|
||||
|
||||
### nginx:
|
||||
```shell
|
||||
wrk -t20 -c1000 -d60s -H "Host: test.traefik" --latency http://IP-nginx:8001/bench
|
||||
Running 1m test @ http://IP-nginx:8001/bench
|
||||
20 threads and 1000 connections
|
||||
Thread Stats Avg Stdev Max +/- Stdev
|
||||
Latency 101.25ms 180.09ms 1.99s 89.34%
|
||||
Req/Sec 1.69k 567.69 9.39k 72.62%
|
||||
Latency Distribution
|
||||
50% 15.46ms
|
||||
75% 129.11ms
|
||||
90% 302.44ms
|
||||
99% 846.59ms
|
||||
2018427 requests in 1.00m, 298.36MB read
|
||||
Socket errors: connect 0, read 0, write 0, timeout 90
|
||||
Requests/sec: 33591.67
|
||||
Transfer/sec: 4.97MB
|
||||
```
|
||||
|
||||
### Traefik:
|
||||
|
||||
```shell
|
||||
wrk -t20 -c1000 -d60s -H "Host: test.traefik" --latency http://IP-traefik:8000/bench
|
||||
Running 1m test @ http://IP-traefik:8000/bench
|
||||
20 threads and 1000 connections
|
||||
Thread Stats Avg Stdev Max +/- Stdev
|
||||
Latency 91.72ms 150.43ms 2.00s 90.50%
|
||||
Req/Sec 1.43k 266.37 2.97k 69.77%
|
||||
Latency Distribution
|
||||
50% 19.74ms
|
||||
75% 121.98ms
|
||||
90% 237.39ms
|
||||
99% 687.49ms
|
||||
1705073 requests in 1.00m, 188.63MB read
|
||||
Socket errors: connect 0, read 0, write 0, timeout 7
|
||||
Requests/sec: 28392.44
|
||||
Transfer/sec: 3.14MB
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
|
||||
Traefik is obviously slower than Nginx, but not so much: Traefik can serve 28392 requests/sec and Nginx 33591 requests/sec which gives a ratio of 85%.
|
||||
Not bad for young project :) !
|
||||
|
||||
Some areas of possible improvements:
|
||||
|
||||
- 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)
|
||||
|
||||
43
docs/check.Dockerfile
Normal file
@@ -0,0 +1,43 @@
|
||||
|
||||
FROM alpine:3.9 as alpine
|
||||
|
||||
# The "build-dependencies" virtual package provides build tools for html-proofer installation.
|
||||
# It compile ruby-nokogiri, because alpine native version is always out of date
|
||||
# This virtual package is cleaned at the end.
|
||||
RUN apk --no-cache --no-progress add \
|
||||
libcurl \
|
||||
ruby \
|
||||
ruby-bigdecimal \
|
||||
ruby-etc \
|
||||
ruby-ffi \
|
||||
ruby-json \
|
||||
&& apk add --no-cache --virtual build-dependencies \
|
||||
build-base \
|
||||
libcurl \
|
||||
libxml2-dev \
|
||||
libxslt-dev \
|
||||
ruby-dev \
|
||||
&& gem install --no-document html-proofer -v 3.10.2 \
|
||||
&& apk del build-dependencies
|
||||
|
||||
# After Ruby, some NodeJS YAY!
|
||||
RUN apk --no-cache --no-progress add \
|
||||
git \
|
||||
nodejs \
|
||||
npm \
|
||||
&& npm install markdownlint@0.12.0 markdownlint-cli@0.13.0 --global
|
||||
|
||||
# Finally the shell tools we need for later
|
||||
# tini helps to terminate properly all the parallelized tasks when sending CTRL-C
|
||||
RUN apk --no-cache --no-progress add \
|
||||
ca-certificates \
|
||||
curl \
|
||||
tini
|
||||
|
||||
COPY ./scripts/verify.sh /verify.sh
|
||||
COPY ./scripts/lint.sh /lint.sh
|
||||
|
||||
WORKDIR /app
|
||||
VOLUME ["/tmp","/app"]
|
||||
|
||||
ENTRYPOINT ["/sbin/tini","-g","sh"]
|
||||
@@ -1,423 +0,0 @@
|
||||
# ACME (Let's Encrypt) Configuration
|
||||
|
||||
See [Let's Encrypt examples](/user-guide/examples/#lets-encrypt-support) and [Docker & Let's Encrypt user guide](/user-guide/docker-and-lets-encrypt) as well.
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml
|
||||
# Sample entrypoint configuration when using ACME.
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
[entryPoints.https]
|
||||
address = ":443"
|
||||
[entryPoints.https.tls]
|
||||
```
|
||||
|
||||
```toml
|
||||
# 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 apply certificates to.
|
||||
#
|
||||
# Required
|
||||
#
|
||||
entryPoint = "https"
|
||||
|
||||
# Deprecated, replaced by [acme.dnsChallenge].
|
||||
#
|
||||
# Optional.
|
||||
#
|
||||
# dnsProvider = "digitalocean"
|
||||
|
||||
# Deprecated, replaced by [acme.dnsChallenge.delayBeforeCheck].
|
||||
#
|
||||
# Optional
|
||||
# Default: 0
|
||||
#
|
||||
# delayDontCheckDNS = 0
|
||||
|
||||
# If true, display debug log messages from the acme client library.
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
# acmeLogging = true
|
||||
|
||||
# Deprecated. Enable on demand certificate generation.
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
# onDemand = true
|
||||
|
||||
# Enable certificate generation on frontends host rules.
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
# onHostRule = true
|
||||
|
||||
# CA server to use.
|
||||
# Uncomment the line to use Let's Encrypt's staging server,
|
||||
# leave commented to go to prod.
|
||||
#
|
||||
# Optional
|
||||
# Default: "https://acme-v02.api.letsencrypt.org/directory"
|
||||
#
|
||||
# caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
|
||||
# Domains list.
|
||||
# Only domains defined here can generate wildcard certificates.
|
||||
#
|
||||
# [[acme.domains]]
|
||||
# main = "local1.com"
|
||||
# sans = ["test1.local1.com", "test2.local1.com"]
|
||||
# [[acme.domains]]
|
||||
# main = "local2.com"
|
||||
# [[acme.domains]]
|
||||
# main = "*.local3.com"
|
||||
# sans = ["local3.com", "test1.test1.local3.com"]
|
||||
|
||||
# Use a HTTP-01 ACME challenge.
|
||||
#
|
||||
# Optional (but recommended)
|
||||
#
|
||||
[acme.httpChallenge]
|
||||
|
||||
# EntryPoint to use for the HTTP-01 challenges.
|
||||
#
|
||||
# Required
|
||||
#
|
||||
entryPoint = "http"
|
||||
|
||||
# Use a DNS-01 ACME challenge rather than HTTP-01 challenge.
|
||||
# Note: mandatory for wildcard certificate generation.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# [acme.dnsChallenge]
|
||||
|
||||
# DNS provider used.
|
||||
#
|
||||
# Required
|
||||
#
|
||||
# provider = "digitalocean"
|
||||
|
||||
# By default, the provider will verify the TXT DNS challenge record before letting ACME verify.
|
||||
# If delayBeforeCheck is greater than zero, this check is delayed for the configured duration in seconds.
|
||||
# Useful if internal networks block external DNS queries.
|
||||
#
|
||||
# Optional
|
||||
# Default: 0
|
||||
#
|
||||
# delayBeforeCheck = 0
|
||||
```
|
||||
|
||||
### `caServer`
|
||||
|
||||
The CA server to use.
|
||||
|
||||
This example shows the usage of Let's Encrypt's staging server:
|
||||
|
||||
```toml
|
||||
[acme]
|
||||
# ...
|
||||
caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
# ...
|
||||
```
|
||||
|
||||
### `dnsChallenge`
|
||||
|
||||
Use the `DNS-01` challenge to generate and renew ACME certificates by provisioning a DNS record.
|
||||
|
||||
```toml
|
||||
[acme]
|
||||
# ...
|
||||
[acme.dnsChallenge]
|
||||
provider = "digitalocean"
|
||||
delayBeforeCheck = 0
|
||||
# ...
|
||||
```
|
||||
|
||||
#### `delayBeforeCheck`
|
||||
|
||||
By default, the `provider` will verify the TXT DNS challenge record before letting ACME verify.
|
||||
If `delayBeforeCheck` is greater than zero, this check is delayed for the configured duration in seconds.
|
||||
|
||||
Useful if internal networks block external DNS queries.
|
||||
|
||||
!!! note
|
||||
A `provider` is mandatory.
|
||||
|
||||
#### `provider`
|
||||
|
||||
Here is a list of supported `provider`s, that can automate the DNS verification, along with the required environment variables and their [wildcard & root domain support](/configuration/acme/#wildcard-domains) for each. Do not hesitate to complete it.
|
||||
|
||||
| Provider Name | Provider Code | Environment Variables | Wildcard & Root Domain Support |
|
||||
|--------------------------------------------------------|----------------|-----------------------------------------------------------------------------------------------------------------------------|--------------------------------|
|
||||
| [Auroradns](https://www.pcextreme.com/aurora/dns) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` | Not tested yet |
|
||||
| [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP` | Not tested yet |
|
||||
| [Blue Cat](https://www.bluecatnetworks.com/) | `bluecat` | `BLUECAT_SERVER_URL`, `BLUECAT_USER_NAME`, `BLUECAT_PASSWORD`, `BLUECAT_CONFIG_NAME`, `BLUECAT_DNS_VIEW` | Not tested yet |
|
||||
| [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CLOUDFLARE_EMAIL`, `CLOUDFLARE_API_KEY` - The `Global API Key` needs to be used, not the `Origin CA Key` | YES |
|
||||
| [CloudXNS](https://www.cloudxns.net) | `cloudxns` | `CLOUDXNS_API_KEY`, `CLOUDXNS_SECRET_KEY` | Not tested yet |
|
||||
| [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` | YES |
|
||||
| [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` | Not tested yet |
|
||||
| [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` | Not tested yet |
|
||||
| [DNSPod](http://www.dnspod.net/) | `dnspod` | `DNSPOD_API_KEY` | Not tested yet |
|
||||
| [Duck DNS](https://www.duckdns.org/) | `duckdns` | `DUCKDNS_TOKEN` | Not tested yet |
|
||||
| [Dyn](https://dyn.com) | `dyn` | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` | Not tested yet |
|
||||
| External Program | `exec` | `EXEC_PATH` | Not tested yet |
|
||||
| [Exoscale](https://www.exoscale.ch) | `exoscale` | `EXOSCALE_API_KEY`, `EXOSCALE_API_SECRET`, `EXOSCALE_ENDPOINT` | Not tested yet |
|
||||
| [Fast DNS](https://www.akamai.com/) | `fastdns` | `AKAMAI_CLIENT_TOKEN`, `AKAMAI_CLIENT_SECRET`, `AKAMAI_ACCESS_TOKEN` | Not tested yet |
|
||||
| [Gandi](https://www.gandi.net) | `gandi` | `GANDI_API_KEY` | Not tested yet |
|
||||
| [Gandi V5](http://doc.livedns.gandi.net) | `gandiv5` | `GANDIV5_API_KEY` | Not tested yet |
|
||||
| [Glesys](https://glesys.com/) | `glesys` | `GLESYS_API_USER`, `GLESYS_API_KEY`, `GLESYS_DOMAIN` | Not tested yet |
|
||||
| [GoDaddy](https://godaddy.com/domains) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` | Not tested yet |
|
||||
| [Google Cloud DNS](https://cloud.google.com/dns/docs/) | `gcloud` | `GCE_PROJECT`, `GCE_SERVICE_ACCOUNT_FILE` | YES |
|
||||
| [Lightsail](https://aws.amazon.com/lightsail/) | `lightsail` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `DNS_ZONE` | Not tested yet |
|
||||
| [Linode](https://www.linode.com) | `linode` | `LINODE_API_KEY` | Not tested yet |
|
||||
| manual | - | none, but you need to run Træfik interactively, turn on `acmeLogging` to see instructions and press <kbd>Enter</kbd>. | YES |
|
||||
| [Namecheap](https://www.namecheap.com) | `namecheap` | `NAMECHEAP_API_USER`, `NAMECHEAP_API_KEY` | Not tested yet |
|
||||
| [name.com](https://www.name.com/) | `namedotcom` | `NAMECOM_USERNAME`, `NAMECOM_API_TOKEN`, `NAMECOM_SERVER` | Not tested yet |
|
||||
| [Ns1](https://ns1.com/) | `ns1` | `NS1_API_KEY` | Not tested yet |
|
||||
| [Open Telekom Cloud](https://cloud.telekom.de/en/) | `otc` | `OTC_DOMAIN_NAME`, `OTC_USER_NAME`, `OTC_PASSWORD`, `OTC_PROJECT_NAME`, `OTC_IDENTITY_ENDPOINT` | Not tested yet |
|
||||
| [OVH](https://www.ovh.com) | `ovh` | `OVH_ENDPOINT`, `OVH_APPLICATION_KEY`, `OVH_APPLICATION_SECRET`, `OVH_CONSUMER_KEY` | YES |
|
||||
| [PowerDNS](https://www.powerdns.com) | `pdns` | `PDNS_API_KEY`, `PDNS_API_URL` | Not tested yet |
|
||||
| [Rackspace](https://www.rackspace.com/cloud/dns) | `rackspace` | `RACKSPACE_USER`, `RACKSPACE_API_KEY` | Not tested yet |
|
||||
| [RFC2136](https://tools.ietf.org/html/rfc2136) | `rfc2136` | `RFC2136_TSIG_KEY`, `RFC2136_TSIG_SECRET`, `RFC2136_TSIG_ALGORITHM`, `RFC2136_NAMESERVER` | Not tested yet |
|
||||
| [Route 53](https://aws.amazon.com/route53/) | `route53` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`, `AWS_HOSTED_ZONE_ID` or a configured user/instance IAM profile. | YES |
|
||||
| [VULTR](https://www.vultr.com) | `vultr` | `VULTR_API_KEY` | Not tested yet |
|
||||
|
||||
### `domains`
|
||||
|
||||
You can provide SANs (alternative domains) to each main domain.
|
||||
All domains must have A/AAAA records pointing to Træfik.
|
||||
Each domain & SAN will lead to a certificate request.
|
||||
|
||||
```toml
|
||||
[acme]
|
||||
# ...
|
||||
[[acme.domains]]
|
||||
main = "local1.com"
|
||||
sans = ["test1.local1.com", "test2.local1.com"]
|
||||
[[acme.domains]]
|
||||
main = "local2.com"
|
||||
[[acme.domains]]
|
||||
main = "*.local3.com"
|
||||
sans = ["local3.com", "test1.test1.local3.com"]
|
||||
# ...
|
||||
```
|
||||
|
||||
!!! warning
|
||||
Take note that Let's Encrypt applies [rate limiting](https://letsencrypt.org/docs/rate-limits).
|
||||
|
||||
!!! note
|
||||
Wildcard certificates can only be verified through a `DNS-01` challenge.
|
||||
|
||||
#### Wildcard Domains
|
||||
|
||||
[ACME V2](https://community.letsencrypt.org/t/acme-v2-and-wildcard-certificate-support-is-live/55579) allows wildcard certificate support.
|
||||
As described in [Let's Encrypt's post](https://community.letsencrypt.org/t/staging-endpoint-for-acme-v2/49605) wildcard certificates can only be generated through a [`DNS-01` challenge](/configuration/acme/#dnschallenge).
|
||||
|
||||
```toml
|
||||
[acme]
|
||||
# ...
|
||||
[[acme.domains]]
|
||||
main = "*.local1.com"
|
||||
sans = ["local1.com"]
|
||||
# ...
|
||||
```
|
||||
|
||||
It is not possible to request a double wildcard certificate for a domain (for example `*.*.local.com`).
|
||||
Due to ACME limitation it is not possible to define wildcards in SANs (alternative domains). Thus, the wildcard domain has to be defined as a main domain.
|
||||
Most likely the root domain should receive a certificate too, so it needs to be specified as SAN and 2 `DNS-01` challenges are executed.
|
||||
In this case the generated DNS TXT record for both domains is the same.
|
||||
Eventhough this behaviour is [DNS RFC](https://community.letsencrypt.org/t/wildcard-issuance-two-txt-records-for-the-same-name/54528/2) compliant, it can lead to problems as all DNS providers keep DNS records cached for a certain time (TTL) and this TTL can be superior to the challenge timeout making the `DNS-01` challenge fail.
|
||||
The Træfik ACME client library [LEGO](https://github.com/xenolf/lego) supports some but not all DNS providers to work around this issue.
|
||||
The [`provider` table](/configuration/acme/#provider) indicates if they allow generating certificates for a wildcard domain and its root domain.
|
||||
|
||||
### `httpChallenge`
|
||||
|
||||
Use the `HTTP-01` challenge to generate and renew ACME certificates by provisioning a HTTP resource under a well-known URI.
|
||||
|
||||
Redirection is fully compatible with the `HTTP-01` challenge.
|
||||
|
||||
```toml
|
||||
[acme]
|
||||
# ...
|
||||
entryPoint = "https"
|
||||
[acme.httpChallenge]
|
||||
entryPoint = "http"
|
||||
```
|
||||
|
||||
!!! note
|
||||
If the `HTTP-01` challenge is used, `acme.httpChallenge.entryPoint` has to be defined and reachable by Let's Encrypt through port 80.
|
||||
This is a Let's Encrypt limitation as described on the [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72).
|
||||
|
||||
#### `entryPoint`
|
||||
|
||||
Specify the entryPoint to use during the challenges.
|
||||
|
||||
```toml
|
||||
defaultEntryPoints = ["http", "https"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
[entryPoints.https]
|
||||
address = ":443"
|
||||
[entryPoints.https.tls]
|
||||
# ...
|
||||
|
||||
[acme]
|
||||
# ...
|
||||
entryPoint = "https"
|
||||
[acme.httpChallenge]
|
||||
entryPoint = "http"
|
||||
```
|
||||
|
||||
!!! note
|
||||
`acme.httpChallenge.entryPoint` has to be reachable through port 80. It's a Let's Encrypt limitation as described on the [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72).
|
||||
|
||||
### `onDemand` (Deprecated)
|
||||
|
||||
!!! danger "DEPRECATED"
|
||||
This option is deprecated.
|
||||
|
||||
```toml
|
||||
[acme]
|
||||
# ...
|
||||
onDemand = true
|
||||
# ...
|
||||
```
|
||||
|
||||
Enable on demand certificate generation.
|
||||
|
||||
This will request certificates from Let's Encrypt during the first TLS handshake for host names that do not yet have certificates.
|
||||
|
||||
!!! warning
|
||||
TLS handshakes are slow when requesting a host name certificate for the first time. This can lead to DoS attacks!
|
||||
|
||||
!!! warning
|
||||
Take note that Let's Encrypt applies [rate limiting](https://letsencrypt.org/docs/rate-limits).
|
||||
|
||||
### `onHostRule`
|
||||
|
||||
```toml
|
||||
[acme]
|
||||
# ...
|
||||
onHostRule = true
|
||||
# ...
|
||||
```
|
||||
|
||||
Enable certificate generation on frontend `Host` rules (for frontends wired to the `acme.entryPoint`).
|
||||
|
||||
This will request a certificate from Let's Encrypt for each frontend with a Host rule.
|
||||
|
||||
For example, the rule `Host:test1.traefik.io,test2.traefik.io` will request a certificate with main domain `test1.traefik.io` and SAN `test2.traefik.io`.
|
||||
|
||||
!!! warning
|
||||
`onHostRule` option can not be used to generate wildcard certificates.
|
||||
Refer to [wildcard generation](/configuration/acme/#wildcard-domain) for further information.
|
||||
|
||||
### `storage`
|
||||
|
||||
The `storage` option sets the location where your ACME certificates are saved to.
|
||||
|
||||
```toml
|
||||
[acme]
|
||||
# ...
|
||||
storage = "acme.json"
|
||||
# ...
|
||||
```
|
||||
|
||||
The value can refer to two kinds of storage:
|
||||
|
||||
- a JSON file
|
||||
- a KV store entry
|
||||
|
||||
!!! danger "DEPRECATED"
|
||||
`storage` replaces `storageFile` which is deprecated.
|
||||
|
||||
!!! note
|
||||
During migration to a KV store use both `storageFile` and `storage` to migrate ACME certificates too. See [`storeconfig` subcommand](/user-guide/kv-config/#store-configuration-in-key-value-store) for further information.
|
||||
|
||||
#### As a File
|
||||
|
||||
ACME certificates can be stored in a JSON file that needs to have file mode `600`.
|
||||
|
||||
In Docker you can either mount the JSON file or the folder containing it:
|
||||
|
||||
```bash
|
||||
docker run -v "/my/host/acme.json:acme.json" traefik
|
||||
```
|
||||
```bash
|
||||
docker run -v "/my/host/acme:/etc/traefik/acme" traefik
|
||||
```
|
||||
|
||||
!!! warning
|
||||
This file cannot be shared across multiple instances of Træfik at the same time. Please use a [KV Store entry](/configuration/acme/#as-a-key-value-store-entry) instead.
|
||||
|
||||
#### As a Key Value Store Entry
|
||||
|
||||
ACME certificates can be stored in a KV Store entry. This kind of storage is **mandatory in cluster mode**.
|
||||
|
||||
```toml
|
||||
storage = "traefik/acme/account"
|
||||
```
|
||||
|
||||
Because KV stores (like Consul) have limited entry size the certificates list is compressed before it is saved as KV store entry.
|
||||
|
||||
!!! note
|
||||
It is possible to store up to approximately 100 ACME certificates in Consul.
|
||||
|
||||
#### ACME v2 Migration
|
||||
|
||||
During migration from ACME v1 to ACME v2, using a storage file, a backup of the original file is created in the same place as the latter (with a `.bak` extension).
|
||||
|
||||
For example: if `acme.storage`'s value is `/etc/traefik/acme/acme.json`, the backup file will be `/etc/traefik/acme/acme.json.bak`.
|
||||
|
||||
!!! note
|
||||
When Træfik is launched in a container, the storage file's parent directory needs to be mounted to be able to access the backup file on the host.
|
||||
Otherwise the backup file will be deleted when the container is stopped. Træfik will only generate it once!
|
||||
|
||||
### `dnsProvider` (Deprecated)
|
||||
|
||||
!!! danger "DEPRECATED"
|
||||
This option is deprecated. Please use [dnsChallenge.provider](/configuration/acme/#provider) instead.
|
||||
|
||||
### `delayDontCheckDNS` (Deprecated)
|
||||
|
||||
!!! danger "DEPRECATED"
|
||||
This option is deprecated. Please use [dnsChallenge.delayBeforeCheck](/configuration/acme/#dnschallenge) instead.
|
||||
|
||||
## Fallbacks
|
||||
|
||||
If Let's Encrypt is not reachable, these certificates will be used:
|
||||
|
||||
1. ACME certificates already generated before downtime
|
||||
1. Expired ACME certificates
|
||||
1. Provided certificates
|
||||
|
||||
!!! note
|
||||
For new (sub)domains which need Let's Encrypt authentification, the default Træfik certificate will be used until Træfik is restarted.
|
||||
@@ -1,328 +0,0 @@
|
||||
# API Definition
|
||||
|
||||
## Configuration
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
For more customization, see [entry points](/configuration/entrypoints/) documentation and [examples](/user-guide/examples/#ping-health-check).
|
||||
|
||||
## Web UI
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## API
|
||||
|
||||
| Path | Method | Description |
|
||||
|-----------------------------------------------------------------|------------------|-------------------------------------------|
|
||||
| `/` | `GET` | Provides a simple HTML frontend of Træfik |
|
||||
| `/cluster/leader` | `GET` | JSON leader true/false response |
|
||||
| `/health` | `GET` | JSON health metrics |
|
||||
| `/api` | `GET` | Configuration for all providers |
|
||||
| `/api/providers` | `GET` | Providers |
|
||||
| `/api/providers/{provider}` | `GET`, `PUT` | Get or update provider (1) |
|
||||
| `/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 |
|
||||
|
||||
<1> See [Rest](/configuration/backends/rest/#api) for more information.
|
||||
|
||||
!!! 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`.
|
||||
|
||||
### Address / Port
|
||||
|
||||
You can define a custom address/port like this:
|
||||
|
||||
```toml
|
||||
defaultEntryPoints = ["http"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
|
||||
[entryPoints.foo]
|
||||
address = ":8082"
|
||||
|
||||
[entryPoints.bar]
|
||||
address = ":8083"
|
||||
|
||||
[ping]
|
||||
entryPoint = "foo"
|
||||
|
||||
[api]
|
||||
entryPoint = "bar"
|
||||
```
|
||||
|
||||
In the above example, you would access a regular path, administration panel, and health-check as follows:
|
||||
|
||||
* Regular path: `http://hostname:80/path`
|
||||
* Admin Panel: `http://hostname:8083/`
|
||||
* Ping URL: `http://hostname:8082/ping`
|
||||
|
||||
In the above example, it is _very_ important to create a named dedicated entry point, and do **not** include it in `defaultEntryPoints`.
|
||||
Otherwise, you are likely to expose _all_ services via that entry point.
|
||||
|
||||
### Custom Path
|
||||
|
||||
You can define a custom path like this:
|
||||
|
||||
```toml
|
||||
defaultEntryPoints = ["http"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
|
||||
[entryPoints.foo]
|
||||
address = ":8080"
|
||||
|
||||
[entryPoints.bar]
|
||||
address = ":8081"
|
||||
|
||||
# Activate API and Dashboard
|
||||
[api]
|
||||
entryPoint = "bar"
|
||||
dashboard = true
|
||||
|
||||
[file]
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.servers.server1]
|
||||
url = "http://127.0.0.1:8081"
|
||||
|
||||
[frontends]
|
||||
[frontends.frontend1]
|
||||
entryPoints = ["foo"]
|
||||
backend = "backend1"
|
||||
[frontends.frontend1.routes.test_1]
|
||||
rule = "PathPrefixStrip:/yourprefix;PathPrefix:/yourprefix"
|
||||
```
|
||||
|
||||
### Authentication
|
||||
|
||||
You can define the authentication like this:
|
||||
|
||||
```toml
|
||||
defaultEntryPoints = ["http"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
|
||||
[entryPoints.foo]
|
||||
address=":8080"
|
||||
[entryPoints.foo.auth]
|
||||
[entryPoints.foo.auth.basic]
|
||||
users = [
|
||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
]
|
||||
|
||||
[api]
|
||||
entrypoint="foo"
|
||||
```
|
||||
|
||||
For more information, see [entry points](/configuration/entrypoints/) .
|
||||
|
||||
### Provider call example
|
||||
|
||||
```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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Cluster Leadership
|
||||
|
||||
```shell
|
||||
curl -s "http://localhost:8080/cluster/leader" | jq .
|
||||
```
|
||||
```shell
|
||||
< HTTP/1.1 200 OK
|
||||
< Content-Type: application/json; charset=UTF-8
|
||||
< Date: xxx
|
||||
< Content-Length: 15
|
||||
```
|
||||
If the given node is not a cluster leader, an HTTP status of `429-Too-Many-Requests` will be returned.
|
||||
```json
|
||||
{
|
||||
// current leadership status of the queried node
|
||||
"leader": true
|
||||
}
|
||||
```
|
||||
|
||||
### Health
|
||||
|
||||
```shell
|
||||
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 --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 |
|
||||
@@ -1,59 +0,0 @@
|
||||
# BoltDB Provider
|
||||
|
||||
Træfik can be configured to use BoltDB as a provider.
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# BoltDB Provider
|
||||
################################################################
|
||||
|
||||
# Enable BoltDB Provider.
|
||||
[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 [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
@@ -1,61 +0,0 @@
|
||||
# Consul Key-Value Provider
|
||||
|
||||
Træfik can be configured to use Consul as a provider.
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Consul KV Provider
|
||||
################################################################
|
||||
|
||||
# Enable Consul KV Provider.
|
||||
[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 [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
||||
Please refer to the [Key Value storage structure](/user-guide/kv-config/#key-value-storage-structure) section to get documentation on Traefik KV structure.
|
||||
@@ -1,183 +0,0 @@
|
||||
# Consul Catalog Provider
|
||||
|
||||
Træfik can be configured to use service discovery catalog of Consul as a provider.
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Consul Catalog Provider
|
||||
################################################################
|
||||
|
||||
# Enable Consul Catalog Provider.
|
||||
[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}}"
|
||||
|
||||
# Enable Consul catalog TLS connection.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# [consulCatalog.tls]
|
||||
# ca = "/etc/ssl/ca.crt"
|
||||
# cert = "/etc/ssl/consul.crt"
|
||||
# key = "/etc/ssl/consul.key"
|
||||
# insecureSkipVerify = true
|
||||
|
||||
# Override default configuration template.
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# filename = "consulcatalog.tmpl"
|
||||
|
||||
# Override template version
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
# - "1": previous template version (must be used only with older custom templates, see "filename")
|
||||
# - "2": current template version (must be used to force template version when "filename" is used)
|
||||
#
|
||||
# templateVersion = 2
|
||||
```
|
||||
|
||||
This provider will create routes matching on hostname based on the service name used in Consul.
|
||||
|
||||
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
||||
## Tags
|
||||
|
||||
Additional settings can be defined using Consul Catalog tags.
|
||||
|
||||
!!! note
|
||||
The default prefix is `traefik`.
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `<prefix>.enable=false` | Disable this container in Træfik. |
|
||||
| `<prefix>.protocol=https` | Override the default `http` protocol. |
|
||||
| `<prefix>.weight=10` | Assign this weight to the container. |
|
||||
| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `<prefix>.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend. ex: `NetworkErrorRatio() > 0.` |
|
||||
| `<prefix>.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. |
|
||||
| `<prefix>.backend.healthcheck.port=8080` | Allow to use a different port for the health check. |
|
||||
| `<prefix>.backend.healthcheck.interval=1s` | Define the health check interval. |
|
||||
| `<prefix>.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm. |
|
||||
| `<prefix>.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions. |
|
||||
| `<prefix>.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions. |
|
||||
| `<prefix>.backend.loadbalancer.sticky=true` | Enable backend sticky sessions. (DEPRECATED) |
|
||||
| `<prefix>.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `<prefix>.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `<prefix>.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
|
||||
| `<prefix>.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `<prefix>.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `<prefix>.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `<prefix>.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `<prefix>.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
|
||||
| `<prefix>.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. |
|
||||
| `<prefix>.frontend.priority=10` | Override default frontend priority. |
|
||||
| `<prefix>.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `<prefix>.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `<prefix>.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `<prefix>.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `<prefix>.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS). |
|
||||
| `<prefix>.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `<prefix>.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `<prefix>.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
||||
| `<prefix>.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{{.ServiceName}}.{{.Domain}}`. |
|
||||
| `<prefix>.frontend.whiteList.sourceRange=RANGE` | List of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `<prefix>.frontend.whiteList.useXForwardedFor=true` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
|
||||
### Custom Headers
|
||||
|
||||
!!! note
|
||||
The default prefix is `traefik`.
|
||||
|
||||
| Label | Description |
|
||||
|--------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `<prefix>.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `<prefix>.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
|
||||
### Security Headers
|
||||
|
||||
!!! note
|
||||
The default prefix is `traefik`.
|
||||
|
||||
| Label | Description |
|
||||
|-----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `<prefix>.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
|
||||
| `<prefix>.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
|
||||
| `<prefix>.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
|
||||
| `<prefix>.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
|
||||
| `<prefix>.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
|
||||
| `<prefix>.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
|
||||
| `<prefix>.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
|
||||
| `<prefix>.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
|
||||
| `<prefix>.frontend.headers.hostsProxyHeaders=EXPR` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
|
||||
| `<prefix>.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
| `<prefix>.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
|
||||
| `<prefix>.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
|
||||
| `<prefix>.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
|
||||
| `<prefix>.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `<prefix>.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `<prefix>.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `<prefix>.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `<prefix>.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `<prefix>.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
|
||||
### Examples
|
||||
|
||||
If you want that Træfik uses Consul tags correctly you need to defined them like that:
|
||||
|
||||
```js
|
||||
traefik.enable=true
|
||||
traefik.tags=api
|
||||
traefik.tags=external
|
||||
```
|
||||
|
||||
If the prefix defined in Træfik configuration is `bla`, tags need to be defined like that:
|
||||
|
||||
```js
|
||||
bla.enable=true
|
||||
bla.tags=api
|
||||
bla.tags=external
|
||||
```
|
||||
@@ -1,357 +0,0 @@
|
||||
|
||||
# Docker Provider
|
||||
|
||||
Træfik can be configured to use Docker as a provider.
|
||||
|
||||
## Docker
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Docker Provider
|
||||
################################################################
|
||||
|
||||
# Enable Docker Provider.
|
||||
[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"
|
||||
|
||||
# Override template version
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
# - "1": previous template version (must be used only with older custom templates, see "filename")
|
||||
# - "2": current template version (must be used to force template version when "filename" is used)
|
||||
#
|
||||
# templateVersion = 2
|
||||
|
||||
# Expose containers by default in Traefik.
|
||||
# If set to false, containers that don't have `traefik.enable=true` will be ignored.
|
||||
#
|
||||
# Optional
|
||||
# Default: true
|
||||
#
|
||||
exposedByDefault = true
|
||||
|
||||
# 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 [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
||||
|
||||
## Docker Swarm Mode
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Docker Swarm Mode Provider
|
||||
################################################################
|
||||
|
||||
# Enable Docker Provider.
|
||||
[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"
|
||||
|
||||
# Override template version
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
# - "1": previous template version (must be used only with older custom templates, see "filename")
|
||||
# - "2": current template version (must be used to force template version when "filename" is used)
|
||||
#
|
||||
# templateVersion = 2
|
||||
|
||||
# Expose services by default in Traefik.
|
||||
#
|
||||
# Optional
|
||||
# Default: true
|
||||
#
|
||||
exposedByDefault = false
|
||||
|
||||
# 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 [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
||||
## Labels: overriding default behavior
|
||||
|
||||
### Using Docker with Swarm Mode
|
||||
|
||||
If you use a compose file with the Swarm mode, labels should be defined in the `deploy` part of your service.
|
||||
This behavior is only enabled for docker-compose version 3+ ([Compose file reference](https://docs.docker.com/compose/compose-file/#labels-1)).
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
services:
|
||||
whoami:
|
||||
deploy:
|
||||
labels:
|
||||
traefik.docker.network: traefik
|
||||
```
|
||||
|
||||
### Using Docker Compose
|
||||
|
||||
If you are intending to use only Docker Compose commands (e.g. `docker-compose up --scale whoami=2 -d`), labels should be under your service, otherwise they will be ignored.
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
services:
|
||||
whoami:
|
||||
labels:
|
||||
traefik.docker.network: traefik
|
||||
```
|
||||
|
||||
### On Containers
|
||||
|
||||
Labels can be used on containers to override default behavior.
|
||||
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.docker.network` | Set the docker network to use for connections to this container. [1] |
|
||||
| `traefik.domain` | Default domain used for frontend rules. |
|
||||
| `traefik.enable=false` | Disable this container in Træfik |
|
||||
| `traefik.port=80` | Register this port. Useful when the container exposes multiples ports. |
|
||||
| `traefik.protocol=https` | Override the default `http` protocol |
|
||||
| `traefik.weight=10` | Assign this weight to the container |
|
||||
| `traefik.backend=foo` | Give the name `foo` to the generated backend for this container. |
|
||||
| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. |
|
||||
| `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.interval=1s` | Define the health check interval. |
|
||||
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) |
|
||||
| `traefik.backend.loadbalancer.swarm=true` | Use Swarm's inbuilt load balancer (only relevant under Swarm Mode). |
|
||||
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` [2] |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. |
|
||||
| `traefik.frontend.priority=10` | Override default frontend priority |
|
||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) |
|
||||
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
||||
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{containerName}.{domain}` or `Host:{service}.{project_name}.{domain}` if you are using `docker-compose`. |
|
||||
| `traefik.frontend.whiteList.sourceRange=RANGE` | List of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access.<br>If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.frontend.whiteList.useXForwardedFor=true` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
|
||||
[1] `traefik.docker.network`:
|
||||
If a container is linked to several networks, be sure to set the proper network name (you can check with `docker inspect <container_id>`) otherwise it will randomly pick one (depending on how docker is returning them).
|
||||
For instance when deploying docker `stack` from compose files, the compose defined networks will be prefixed with the `stack` name.
|
||||
Or if your service references external network use it's name instead.
|
||||
|
||||
[2] `traefik.frontend.auth.basic=EXPR`:
|
||||
To create `user:password` pair, it's possible to use this command `echo $(htpasswd -nb user password) | sed -e s/\\$/\\$\\$/g`.
|
||||
The result will be `user:$$apr1$$9Cv/OMGj$$ZomWQzuQbL.3TRCS81A1g/`, note additional symbol `$` makes escaping.
|
||||
|
||||
#### Custom Headers
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
|
||||
#### Security Headers
|
||||
|
||||
| Label | Description |
|
||||
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
|
||||
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
|
||||
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
|
||||
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
|
||||
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
|
||||
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
|
||||
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
|
||||
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
|
||||
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
|
||||
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
|
||||
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
|
||||
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
|
||||
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
|
||||
### On containers with Multiple Ports (segment labels)
|
||||
|
||||
Segment labels are used to define routes to a container exposing multiple ports.
|
||||
A segment is a group of labels that apply to a port exposed by a container.
|
||||
You can define as many segments as ports exposed in a container.
|
||||
|
||||
Segment labels override the default behavior.
|
||||
|
||||
| Label | Description |
|
||||
|---------------------------------------------------------------------------|-------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
||||
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
||||
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
||||
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
||||
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
||||
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
|
||||
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
|
||||
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
|
||||
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
|
||||
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
|
||||
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
|
||||
|
||||
#### Custom Headers
|
||||
|
||||
| Label | Description |
|
||||
|----------------------------------------------------------------------|----------------------------------------------------------|
|
||||
| `traefik.<segment_name>.frontend.headers.customRequestHeaders=EXPR ` | Same as `traefik.frontend.headers.customRequestHeaders` |
|
||||
| `traefik.<segment_name>.frontend.headers.customResponseHeaders=EXPR` | Same as `traefik.frontend.headers.customResponseHeaders` |
|
||||
|
||||
#### Security Headers
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------------------------|--------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.frontend.headers.allowedHosts=EXPR` | Same as `traefik.frontend.headers.allowedHosts` |
|
||||
| `traefik.<segment_name>.frontend.headers.browserXSSFilter=true` | Same as `traefik.frontend.headers.browserXSSFilter` |
|
||||
| `traefik.<segment_name>.frontend.headers.contentSecurityPolicy=VALUE` | Same as `traefik.frontend.headers.contentSecurityPolicy` |
|
||||
| `traefik.<segment_name>.frontend.headers.contentTypeNosniff=true` | Same as `traefik.frontend.headers.contentTypeNosniff` |
|
||||
| `traefik.<segment_name>.frontend.headers.customBrowserXSSValue=VALUE` | Same as `traefik.frontend.headers.customBrowserXSSValue` |
|
||||
| `traefik.<segment_name>.frontend.headers.customFrameOptionsValue=VALUE` | Same as `traefik.frontend.headers.customFrameOptionsValue` |
|
||||
| `traefik.<segment_name>.frontend.headers.forceSTSHeader=false` | Same as `traefik.frontend.headers.forceSTSHeader` |
|
||||
| `traefik.<segment_name>.frontend.headers.frameDeny=false` | Same as `traefik.frontend.headers.frameDeny` |
|
||||
| `traefik.<segment_name>.frontend.headers.hostsProxyHeaders=EXPR` | Same as `traefik.frontend.headers.hostsProxyHeaders` |
|
||||
| `traefik.<segment_name>.frontend.headers.isDevelopment=false` | Same as `traefik.frontend.headers.isDevelopment` |
|
||||
| `traefik.<segment_name>.frontend.headers.publicKey=VALUE` | Same as `traefik.frontend.headers.publicKey` |
|
||||
| `traefik.<segment_name>.frontend.headers.referrerPolicy=VALUE` | Same as `traefik.frontend.headers.referrerPolicy` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLRedirect=true` | Same as `traefik.frontend.headers.SSLRedirect` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLTemporaryRedirect=true` | Same as `traefik.frontend.headers.SSLTemporaryRedirect` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLHost=HOST` | Same as `traefik.frontend.headers.SSLHost` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLProxyHeaders=EXPR` | Same as `traefik.frontend.headers.SSLProxyHeaders=EXPR` |
|
||||
| `traefik.<segment_name>.frontend.headers.STSSeconds=315360000` | Same as `traefik.frontend.headers.STSSeconds=315360000` |
|
||||
| `traefik.<segment_name>.frontend.headers.STSIncludeSubdomains=true` | Same as `traefik.frontend.headers.STSIncludeSubdomains=true` |
|
||||
| `traefik.<segment_name>.frontend.headers.STSPreload=true` | Same as `traefik.frontend.headers.STSPreload=true` |
|
||||
|
||||
!!! note
|
||||
If a label is defined both as a `container label` and a `segment label` (for example `traefik.<segment_name>.port=PORT` and `traefik.port=PORT` ), the `segment label` is used to defined the `<segment_name>` property (`port` in the example).
|
||||
|
||||
It's possible to mix `container labels` and `segment labels`, in this case `container labels` are used as default value for missing `segment labels` but no frontends are going to be created with the `container labels`.
|
||||
|
||||
More details in this [example](/user-guide/docker-and-lets-encrypt/#labels).
|
||||
|
||||
!!! warning
|
||||
When running inside a container, Træfik will need network access through:
|
||||
|
||||
`docker network connect <network> <traefik-container>`
|
||||
@@ -1,70 +0,0 @@
|
||||
# DynamoDB Provider
|
||||
|
||||
Træfik can be configured to use Amazon DynamoDB as a provider.
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# DynamoDB Provider
|
||||
################################################################
|
||||
|
||||
# Enable DynamoDB Provider.
|
||||
[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
|
||||
|
||||
# Access Key ID to use when connecting to AWS.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
accessKeyID = "abc"
|
||||
|
||||
# Secret Access Key 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.
|
||||
@@ -1,209 +0,0 @@
|
||||
# ECS Provider
|
||||
|
||||
Træfik can be configured to use Amazon ECS as a provider.
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# ECS Provider
|
||||
################################################################
|
||||
|
||||
# Enable ECS Provider.
|
||||
[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.
|
||||
# Can be overridden by setting the "traefik.domain" label.
|
||||
#
|
||||
# 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"
|
||||
|
||||
# Access Key ID to use when connecting to AWS.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
accessKeyID = "abc"
|
||||
|
||||
# Secret Access Key to use when connecting to AWS.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
secretAccessKey = "123"
|
||||
|
||||
# Override default configuration template.
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# filename = "ecs.tmpl"
|
||||
|
||||
# Override template version
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
# - "1": previous template version (must be used only with older custom templates, see "filename")
|
||||
# - "2": current template version (must be used to force template version when "filename" is used)
|
||||
#
|
||||
# templateVersion = 2
|
||||
```
|
||||
|
||||
If `accessKeyID`/`secretAccessKey` is not given credentials will be resolved in the following order:
|
||||
|
||||
- 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.domain` | Default domain used for frontend rules. |
|
||||
| `traefik.enable=false` | Disable this container in Træfik |
|
||||
| `traefik.port=80` | Override the default `port` value. Overrides `NetworkBindings` from Docker Container |
|
||||
| `traefik.protocol=https` | Override the default `http` protocol |
|
||||
| `traefik.weight=10` | Assign this weight to the container |
|
||||
| `traefik.backend=foo` | Give the name `foo` to the generated backend for this container. |
|
||||
| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. |
|
||||
| `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.interval=1s` | Define the health check interval. (Default: 30s) |
|
||||
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) |
|
||||
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. |
|
||||
| `traefik.frontend.priority=10` | Override default frontend priority |
|
||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) |
|
||||
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
||||
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{instance_name}.{domain}`. |
|
||||
| `traefik.frontend.whiteList.sourceRange=RANGE` | List of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.frontend.whiteList.useXForwardedFor=true` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
|
||||
### Custom Headers
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
|
||||
### Security Headers
|
||||
|
||||
| Label | Description |
|
||||
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
|
||||
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
|
||||
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
|
||||
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
|
||||
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
|
||||
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
|
||||
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
|
||||
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
|
||||
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
|
||||
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
|
||||
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
|
||||
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
|
||||
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
@@ -1,75 +0,0 @@
|
||||
# Etcd Provider
|
||||
|
||||
Træfik can be configured to use Etcd as a provider.
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Etcd Provider
|
||||
################################################################
|
||||
|
||||
# Enable Etcd Provider.
|
||||
[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 [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
||||
Please refer to the [Key Value storage structure](/user-guide/kv-config/#key-value-storage-structure) section to get documentation on Traefik KV structure.
|
||||
|
||||
!!! 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.
|
||||
@@ -1,32 +0,0 @@
|
||||
# Eureka Provider
|
||||
|
||||
Træfik can be configured to use Eureka as a provider.
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Eureka Provider
|
||||
################################################################
|
||||
|
||||
# Enable Eureka Provider.
|
||||
[eureka]
|
||||
|
||||
# Eureka server endpoint.
|
||||
#
|
||||
# Required
|
||||
#
|
||||
endpoint = "http://my.eureka.server/eureka"
|
||||
|
||||
# Override default configuration time between refresh.
|
||||
#
|
||||
# Optional
|
||||
# Default: 30s
|
||||
#
|
||||
refreshSeconds = "1m"
|
||||
|
||||
# Override default configuration template.
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# filename = "eureka.tmpl"
|
||||
```
|
||||
@@ -1,303 +0,0 @@
|
||||
# File Provider
|
||||
|
||||
Træfik can be configured with a file.
|
||||
|
||||
## Reference
|
||||
|
||||
```toml
|
||||
[file]
|
||||
|
||||
# Backends
|
||||
[backends]
|
||||
|
||||
[backends.backend1]
|
||||
|
||||
[backends.backend1.servers]
|
||||
[backends.backend1.servers.server0]
|
||||
url = "http://10.10.10.1:80"
|
||||
weight = 1
|
||||
[backends.backend1.servers.server1]
|
||||
url = "http://10.10.10.2:80"
|
||||
weight = 2
|
||||
# ...
|
||||
|
||||
[backends.backend1.circuitBreaker]
|
||||
expression = "NetworkErrorRatio() > 0.5"
|
||||
|
||||
[backends.backend1.loadBalancer]
|
||||
method = "drr"
|
||||
[backends.backend1.loadBalancer.stickiness]
|
||||
cookieName = "foobar"
|
||||
|
||||
[backends.backend1.maxConn]
|
||||
amount = 10
|
||||
extractorfunc = "request.host"
|
||||
|
||||
[backends.backend1.healthCheck]
|
||||
path = "/health"
|
||||
port = 88
|
||||
interval = "30s"
|
||||
|
||||
[backends.backend2]
|
||||
# ...
|
||||
|
||||
# Frontends
|
||||
[frontends]
|
||||
|
||||
[frontends.frontend1]
|
||||
entryPoints = ["http", "https"]
|
||||
backend = "backend1"
|
||||
passHostHeader = true
|
||||
passTLSCert = true
|
||||
priority = 42
|
||||
basicAuth = [
|
||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
]
|
||||
|
||||
[frontends.frontend1.whiteList]
|
||||
sourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
|
||||
useXForwardedFor = true
|
||||
|
||||
[frontends.frontend1.routes]
|
||||
[frontends.frontend1.routes.route0]
|
||||
rule = "Host:test.localhost"
|
||||
[frontends.frontend1.routes.Route1]
|
||||
rule = "Method:GET"
|
||||
# ...
|
||||
|
||||
[frontends.frontend1.headers]
|
||||
allowedHosts = ["foobar", "foobar"]
|
||||
hostsProxyHeaders = ["foobar", "foobar"]
|
||||
SSLRedirect = true
|
||||
SSLTemporaryRedirect = true
|
||||
SSLHost = "foobar"
|
||||
STSSeconds = 42
|
||||
STSIncludeSubdomains = true
|
||||
STSPreload = true
|
||||
forceSTSHeader = true
|
||||
frameDeny = true
|
||||
customFrameOptionsValue = "foobar"
|
||||
contentTypeNosniff = true
|
||||
browserXSSFilter = true
|
||||
contentSecurityPolicy = "foobar"
|
||||
publicKey = "foobar"
|
||||
referrerPolicy = "foobar"
|
||||
isDevelopment = true
|
||||
[frontends.frontend1.headers.customRequestHeaders]
|
||||
X-Foo-Bar-01 = "foobar"
|
||||
X-Foo-Bar-02 = "foobar"
|
||||
# ...
|
||||
[frontends.frontend1.headers.customResponseHeaders]
|
||||
X-Foo-Bar-03 = "foobar"
|
||||
X-Foo-Bar-04 = "foobar"
|
||||
# ...
|
||||
[frontends.frontend1.headers.SSLProxyHeaders]
|
||||
X-Foo-Bar-05 = "foobar"
|
||||
X-Foo-Bar-06 = "foobar"
|
||||
# ...
|
||||
|
||||
[frontends.frontend1.errors]
|
||||
[frontends.frontend1.errors.errorPage0]
|
||||
status = ["500-599"]
|
||||
backend = "error"
|
||||
query = "/{status}.html"
|
||||
[frontends.frontend1.errors.errorPage1]
|
||||
status = ["404", "403"]
|
||||
backend = "error"
|
||||
query = "/{status}.html"
|
||||
# ...
|
||||
|
||||
[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
|
||||
# ...
|
||||
|
||||
[frontends.frontend1.redirect]
|
||||
entryPoint = "https"
|
||||
regex = "^http://localhost/(.*)"
|
||||
replacement = "http://mydomain/$1"
|
||||
permanent = true
|
||||
|
||||
[frontends.frontend2]
|
||||
# ...
|
||||
|
||||
# HTTPS certificates
|
||||
[[tls]]
|
||||
entryPoints = ["https"]
|
||||
[tls.certificate]
|
||||
certFile = "path/to/my.cert"
|
||||
keyFile = "path/to/my.key"
|
||||
|
||||
[[tls]]
|
||||
# ...
|
||||
```
|
||||
|
||||
## Configuration Mode
|
||||
|
||||
You have two choices:
|
||||
|
||||
- [Rules in Træfik configuration file](/configuration/backends/file/#rules-in-trfik-configuration-file)
|
||||
- [Rules in dedicated files](/configuration/backends/file/#rules-in-dedicated-files)
|
||||
|
||||
To enable the file backend, you must either pass the `--file` option to the Træfik binary or put the `[file]` section (with or without inner settings) in the configuration file.
|
||||
|
||||
The configuration file allows managing both backends/frontends and HTTPS certificates (which are not [Let's Encrypt](https://letsencrypt.org) certificates generated through Træfik).
|
||||
|
||||
TOML templating can be used if rules are not defined in the Træfik configuration file.
|
||||
|
||||
### Rules in Træfik Configuration File
|
||||
|
||||
Add your configuration at the end of the global configuration file `traefik.toml`:
|
||||
|
||||
```toml
|
||||
defaultEntryPoints = ["http", "https"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
# ...
|
||||
[entryPoints.https]
|
||||
# ...
|
||||
|
||||
[file]
|
||||
|
||||
# rules
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
# ...
|
||||
[backends.backend2]
|
||||
# ...
|
||||
|
||||
[frontends]
|
||||
[frontends.frontend1]
|
||||
# ...
|
||||
[frontends.frontend2]
|
||||
# ...
|
||||
[frontends.frontend3]
|
||||
# ...
|
||||
|
||||
# HTTPS certificate
|
||||
[[tls]]
|
||||
# ...
|
||||
|
||||
[[tls]]
|
||||
# ...
|
||||
```
|
||||
|
||||
!!! note
|
||||
If `tls.entryPoints` is not defined, the certificate is attached to all the `defaultEntryPoints` with a TLS configuration.
|
||||
|
||||
!!! note
|
||||
Adding certificates directly to the entryPoint is still maintained but certificates declared in this way cannot be managed dynamically.
|
||||
It's recommended to use the file provider to declare certificates.
|
||||
|
||||
!!! warning
|
||||
TOML templating cannot be used if rules are defined in the Træfik configuration file.
|
||||
|
||||
### Rules in Dedicated Files
|
||||
|
||||
Træfik allows defining rules in one or more separate files.
|
||||
|
||||
#### One Separate File
|
||||
|
||||
You have to specify the file path in the `file.filename` option.
|
||||
|
||||
```toml
|
||||
# traefik.toml
|
||||
defaultEntryPoints = ["http", "https"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
# ...
|
||||
[entryPoints.https]
|
||||
# ...
|
||||
|
||||
[file]
|
||||
filename = "rules.toml"
|
||||
watch = true
|
||||
```
|
||||
|
||||
The option `file.watch` allows Træfik to watch file changes automatically.
|
||||
|
||||
#### Multiple Separated Files
|
||||
|
||||
You could have multiple `.toml` files in a directory (and recursively in its sub-directories):
|
||||
|
||||
```toml
|
||||
[file]
|
||||
directory = "/path/to/config/"
|
||||
watch = true
|
||||
```
|
||||
|
||||
The option `file.watch` allows Træfik to watch file changes automatically.
|
||||
|
||||
#### Separate Files Content
|
||||
|
||||
If you are defining rules in one or more separate files, you can use two formats.
|
||||
|
||||
##### Simple Format
|
||||
|
||||
Backends, Frontends and TLS certificates are defined one at time, as described in the file `rules.toml`:
|
||||
|
||||
```toml
|
||||
# rules.toml
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
# ...
|
||||
[backends.backend2]
|
||||
# ...
|
||||
|
||||
[frontends]
|
||||
[frontends.frontend1]
|
||||
# ...
|
||||
[frontends.frontend2]
|
||||
# ...
|
||||
[frontends.frontend3]
|
||||
# ...
|
||||
|
||||
# HTTPS certificate
|
||||
[[tls]]
|
||||
# ...
|
||||
|
||||
[[tls]]
|
||||
# ...
|
||||
```
|
||||
|
||||
##### TOML Templating
|
||||
|
||||
!!! warning
|
||||
TOML templating can only be used **if rules are defined in one or more separate files**.
|
||||
Templating will not work in the Træfik configuration file.
|
||||
|
||||
Træfik allows using TOML templating.
|
||||
|
||||
Thus, it's possible to define easily lot of Backends, Frontends and TLS certificates as described in the file `template-rules.toml` :
|
||||
|
||||
```toml
|
||||
# template-rules.toml
|
||||
[backends]
|
||||
{{ range $i, $e := until 100 }}
|
||||
[backends.backend{{ $e }}]
|
||||
#...
|
||||
{{ end }}
|
||||
|
||||
[frontends]
|
||||
{{ range $i, $e := until 100 }}
|
||||
[frontends.frontend{{ $e }}]
|
||||
#...
|
||||
{{ end }}
|
||||
|
||||
|
||||
# HTTPS certificate
|
||||
{{ range $i, $e := until 100 }}
|
||||
[[tls]]
|
||||
#...
|
||||
{{ end }}
|
||||
```
|
||||
@@ -1,264 +0,0 @@
|
||||
# Kubernetes Ingress Provider
|
||||
|
||||
Træfik can be configured to use Kubernetes Ingress as a provider.
|
||||
|
||||
See also [Kubernetes user guide](/user-guide/kubernetes).
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Kubernetes Ingress Provider
|
||||
################################################################
|
||||
|
||||
# Enable Kubernetes Ingress Provider.
|
||||
[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 filter Ingress objects that should be processed.
|
||||
#
|
||||
# Optional
|
||||
# Default: empty (process all Ingresses)
|
||||
#
|
||||
# labelselector = "A and not B"
|
||||
|
||||
# Value of `kubernetes.io/ingress.class` annotation that identifies Ingress objects to be processed.
|
||||
# If the parameter is non-empty, only Ingresses containing an annotation with the same value are processed.
|
||||
# Otherwise, Ingresses missing the annotation, having an empty value, or the value `traefik` are processed.
|
||||
#
|
||||
# Note : `ingressClass` option must begin with the "traefik" prefix.
|
||||
#
|
||||
# Optional
|
||||
# Default: empty
|
||||
#
|
||||
# ingressClass = "traefik-internal"
|
||||
|
||||
# Disable PassHost Headers.
|
||||
#
|
||||
# Optional
|
||||
# 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 as URL.
|
||||
|
||||
When deployed into Kubernetes, Traefik will read the environment variables `KUBERNETES_SERVICE_HOST` and `KUBERNETES_SERVICE_PORT` to construct the endpoint.
|
||||
|
||||
The access token will be looked up in `/var/run/secrets/kubernetes.io/serviceaccount/token` and the SSL CA certificate in `/var/run/secrets/kubernetes.io/serviceaccount/ca.crt`.
|
||||
Both are provided mounted automatically when deployed inside Kubernetes.
|
||||
|
||||
The endpoint may be specified to override the environment variable values inside a cluster.
|
||||
|
||||
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 using the granted autentication and authorization of the associated kubeconfig.
|
||||
|
||||
### `labelselector`
|
||||
|
||||
By default, Traefik processes all Ingress objects in the configured namespaces.
|
||||
A label selector can be defined to filter on specific Ingress objects only.
|
||||
|
||||
See [label-selectors](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors) for details.
|
||||
|
||||
### TLS communication between Traefik and backend pods
|
||||
|
||||
Traefik automatically requests endpoint information based on the service provided in the ingress spec.
|
||||
Although traefik will connect directly to the endpoints (pods), it still checks the service port to see if TLS communication is required.
|
||||
If the service port defined in the ingress spec is 443, then the backend communication protocol is assumed to be TLS, and will connect via TLS automatically.
|
||||
|
||||
!!! note
|
||||
Please note that by enabling TLS communication between traefik and your pods, you will have to have trusted certificates that have the proper trust chain and IP subject name.
|
||||
If this is not an option, you may need to skip TLS certificate verification.
|
||||
See the [insecureSkipVerify](/configuration/commons/#main-section) setting for more details.
|
||||
|
||||
## Annotations
|
||||
|
||||
### General annotations
|
||||
|
||||
The following general annotations are applicable on the Ingress object:
|
||||
|
||||
| Annotation | Description |
|
||||
|---------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.ingress.kubernetes.io/buffering: <YML>` | (3) See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.ingress.kubernetes.io/error-pages: <YML>` | (1) See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.ingress.kubernetes.io/frontend-entry-points: http,https` | Override the default frontend endpoints. |
|
||||
| `traefik.ingress.kubernetes.io/pass-tls-cert: "true"` | Override the default frontend PassTLSCert value. Default: `false`. |
|
||||
| `traefik.ingress.kubernetes.io/preserve-host: "true"` | Forward client `Host` header to the backend. |
|
||||
| `traefik.ingress.kubernetes.io/priority: "3"` | Override the default frontend rule priority. |
|
||||
| `traefik.ingress.kubernetes.io/rate-limit: <YML>` | (2) See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.ingress.kubernetes.io/redirect-entry-point: https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS). |
|
||||
| `traefik.ingress.kubernetes.io/redirect-permanent: "true"` | Return 301 instead of 302. |
|
||||
| `traefik.ingress.kubernetes.io/redirect-regex: ^http://localhost/(.*)` | Redirect to another URL for that frontend. Must be set with `traefik.ingress.kubernetes.io/redirect-replacement`. |
|
||||
| `traefik.ingress.kubernetes.io/redirect-replacement: http://mydomain/$1` | Redirect to another URL for that frontend. Must be set with `traefik.ingress.kubernetes.io/redirect-regex`. |
|
||||
| `traefik.ingress.kubernetes.io/rewrite-target: /users` | Replaces each matched Ingress path with the specified one, and adds the old path to the `X-Replaced-Path` header. |
|
||||
| `traefik.ingress.kubernetes.io/rule-type: PathPrefixStrip` | Override the default frontend rule type. Default: `PathPrefix`. |
|
||||
| `traefik.ingress.kubernetes.io/whitelist-source-range: "1.2.3.0/24, fe80::/16"` | A comma-separated list of IP ranges permitted for access. all source IPs are permitted if the list is empty or a single range is ill-formatted. Please note, you may have to set `service.spec.externalTrafficPolicy` to the value `Local` to preserve the source IP of the request for filtering. Please see [this link](https://kubernetes.io/docs/tutorials/services/source-ip/) for more information.|
|
||||
| `traefik.ingress.kubernetes.io/app-root: "/index.html"` | Redirects all requests for `/` to the defined path. (4) |
|
||||
|
||||
<1> `traefik.ingress.kubernetes.io/error-pages` example:
|
||||
|
||||
```yaml
|
||||
foo:
|
||||
status:
|
||||
- "404"
|
||||
backend: bar
|
||||
query: /bar
|
||||
fii:
|
||||
status:
|
||||
- "503"
|
||||
- "500"
|
||||
backend: bar
|
||||
query: /bir
|
||||
```
|
||||
|
||||
<2> `traefik.ingress.kubernetes.io/rate-limit` example:
|
||||
|
||||
```yaml
|
||||
extractorfunc: client.ip
|
||||
rateset:
|
||||
bar:
|
||||
period: 3s
|
||||
average: 6
|
||||
burst: 9
|
||||
foo:
|
||||
period: 6s
|
||||
average: 12
|
||||
burst: 18
|
||||
```
|
||||
|
||||
<3> `traefik.ingress.kubernetes.io/buffering` example:
|
||||
|
||||
```yaml
|
||||
maxrequestbodybytes: 10485760
|
||||
memrequestbodybytes: 2097153
|
||||
maxresponsebodybytes: 10485761
|
||||
memresponsebodybytes: 2097152
|
||||
retryexpression: IsNetworkError() && Attempts() <= 2
|
||||
```
|
||||
|
||||
<4> `traefik.ingress.kubernetes.io/app-root`:
|
||||
Non-root paths will not be affected by this annotation and handled normally.
|
||||
This annotation may not be combined with the `ReplacePath` rule type or any other annotation leveraging that rule type.
|
||||
Trying to do so leads to an error and the corresponding Ingress object being ignored.
|
||||
|
||||
!!! note
|
||||
Please note that `traefik.ingress.kubernetes.io/redirect-regex` and `traefik.ingress.kubernetes.io/redirect-replacement` do not have to be set if `traefik.ingress.kubernetes.io/redirect-entry-point` is defined for the redirection (they will not be used in this case).
|
||||
|
||||
The following annotations are applicable on the Service object associated with a particular Ingress object:
|
||||
|
||||
| Annotation | Description |
|
||||
|--------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.backend.loadbalancer.sticky: "true"` | Enable backend sticky sessions (DEPRECATED). |
|
||||
| `traefik.ingress.kubernetes.io/affinity: "true"` | Enable backend sticky sessions. |
|
||||
| `traefik.ingress.kubernetes.io/circuit-breaker-expression: <expression>` | Set the circuit breaker expression for the backend. |
|
||||
| `traefik.ingress.kubernetes.io/load-balancer-method: drr` | Override the default `wrr` load balancer algorithm. |
|
||||
| `traefik.ingress.kubernetes.io/max-conn-amount: 10` | Set a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.ingress.kubernetes.io/max-conn-extractor-func: client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.ingress.kubernetes.io/session-cookie-name: <NAME>` | Manually set the cookie name for sticky sessions. |
|
||||
|
||||
!!! note
|
||||
`traefik.ingress.kubernetes.io/` and `ingress.kubernetes.io/` are supported prefixes.
|
||||
|
||||
### Custom Headers Annotations
|
||||
|
||||
| Annotation | Description |
|
||||
| ------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `ingress.kubernetes.io/custom-request-headers: EXPR` | Provides the container with custom request headers that will be appended to each request forwarded to the container. Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `ingress.kubernetes.io/custom-response-headers: EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client. Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
|
||||
### Security Headers Annotations
|
||||
|
||||
The following security annotations are applicable on the Ingress object:
|
||||
|
||||
| Annotation | Description |
|
||||
| ----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `ingress.kubernetes.io/allowed-hosts: EXPR` | Provides a list of allowed hosts that requests will be processed. Format: `Host1,Host2` |
|
||||
| `ingress.kubernetes.io/browser-xss-filter: "true"` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
|
||||
| `ingress.kubernetes.io/content-security-policy: VALUE` | Adds CSP Header with the custom value. |
|
||||
| `ingress.kubernetes.io/content-type-nosniff: "true"` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
|
||||
| `ingress.kubernetes.io/custom-browser-xss-value: VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
|
||||
| `ingress.kubernetes.io/custom-frame-options-value: VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
|
||||
| `ingress.kubernetes.io/force-hsts: "false"` | Adds the STS header to non-SSL requests. |
|
||||
| `ingress.kubernetes.io/frame-deny: "false"` | Adds the `X-Frame-Options` header with the value of `DENY`. |
|
||||
| `ingress.kubernetes.io/hsts-max-age: "315360000"` | Sets the max-age of the HSTS header. |
|
||||
| `ingress.kubernetes.io/hsts-include-subdomains: "true"` | Adds the IncludeSubdomains section of the STS header. |
|
||||
| `ingress.kubernetes.io/hsts-preload: "true"` | Adds the preload flag to the HSTS header. |
|
||||
| `ingress.kubernetes.io/is-development: "false"` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
| `ingress.kubernetes.io/proxy-headers: EXPR` | Provides a list of headers that the proxied hostname may be stored. Format: `HEADER1,HEADER2` |
|
||||
| `ingress.kubernetes.io/public-key: VALUE` | Adds pinned HTST public key header. |
|
||||
| `ingress.kubernetes.io/referrer-policy: VALUE` | Adds referrer policy header. |
|
||||
| `ingress.kubernetes.io/ssl-redirect: "true"` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
|
||||
| `ingress.kubernetes.io/ssl-temporary-redirect: "true"` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `ingress.kubernetes.io/ssl-host: HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `ingress.kubernetes.io/ssl-proxy-headers: EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`). Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
|
||||
### Authentication
|
||||
|
||||
Additional authentication annotations can be added to the Ingress object.
|
||||
The source of the authentication is a Secret object that contains the credentials.
|
||||
|
||||
| Annotation | Description |
|
||||
|-----------------------------------------------|-------------------------------------------------------------------------------------------------------------|
|
||||
| `ingress.kubernetes.io/auth-type: basic` | Contains the authentication type. The only permitted type is `basic`. |
|
||||
| `ingress.kubernetes.io/auth-secret: mysecret` | Name of Secret containing the username and password with access to the paths defined in the Ingress object. |
|
||||
|
||||
The secret must be created in the same namespace as the Ingress object.
|
||||
|
||||
The following limitations hold:
|
||||
|
||||
- The realm is not configurable; the only supported (and default) value is `traefik`.
|
||||
- The Secret must contain a single file only.
|
||||
|
||||
### TLS certificates management
|
||||
|
||||
TLS certificates can be managed in Secrets objects.
|
||||
More information are available in the [User Guide](/user-guide/kubernetes/#add-a-tls-certificate-to-the-ingress).
|
||||
|
||||
!!! note
|
||||
Only TLS certificates provided by users can be stored in Kubernetes Secrets.
|
||||
[Let's Encrypt](https://letsencrypt.org) certificates cannot be managed in Kubernets Secrets yet.
|
||||
@@ -1,313 +0,0 @@
|
||||
# Marathon Provider
|
||||
|
||||
Træfik can be configured to use Marathon as a provider.
|
||||
|
||||
See also [Marathon user guide](/user-guide/marathon).
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Mesos/Marathon Provider
|
||||
################################################################
|
||||
|
||||
# Enable Marathon Provider.
|
||||
[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"
|
||||
|
||||
# Override template version
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
# - "1": previous template version (must be used only with older custom templates, see "filename")
|
||||
# - "2": current template version (must be used to force template version when "filename" is used)
|
||||
#
|
||||
# templateVersion = 2
|
||||
|
||||
# Expose Marathon apps by default in Traefik.
|
||||
#
|
||||
# Optional
|
||||
# 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 [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
||||
## Labels: overriding default behavior
|
||||
|
||||
Marathon labels may be used to dynamically change the routing and forwarding behavior.
|
||||
|
||||
They may be specified on one of two levels: Application or service.
|
||||
|
||||
### Application Level
|
||||
|
||||
The following labels can be defined on Marathon applications. They adjust the behavior for the entire application.
|
||||
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.domain` | Default domain used for frontend rules. |
|
||||
| `traefik.enable=false` | Disable this container in Træfik |
|
||||
| `traefik.port=80` | Register this port. Useful when the container exposes multiples ports. |
|
||||
| `traefik.portIndex=1` | Register port by index in the application's ports array. Useful when the application exposes multiple ports. |
|
||||
| `traefik.protocol=https` | Override the default `http` protocol |
|
||||
| `traefik.weight=10` | Assign this weight to the container |
|
||||
| `traefik.backend=foo` | Give the name `foo` to the generated backend for this container. |
|
||||
| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. |
|
||||
| `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.interval=1s` | Define the health check interval. (Default: 30s) |
|
||||
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) |
|
||||
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. |
|
||||
| `traefik.frontend.priority=10` | Override default frontend priority |
|
||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) |
|
||||
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
||||
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{sub_domain}.{domain}`. |
|
||||
| `traefik.frontend.whiteList.sourceRange=RANGE` | List of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.frontend.whiteList.useXForwardedFor=true` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
|
||||
#### Custom Headers
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
|
|
||||
|
||||
#### Security Headers
|
||||
|
||||
| Label | Description |
|
||||
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
|
||||
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
|
||||
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
|
||||
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
|
||||
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
|
||||
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
|
||||
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
|
||||
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
|
||||
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
|
||||
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
|
||||
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
|
||||
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
|
||||
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
|
||||
### Applications with Multiple Ports (segment labels)
|
||||
|
||||
Segment labels are used to define routes to an application exposing multiple ports.
|
||||
A segment is a group of labels that apply to a port exposed by an application.
|
||||
You can define as many segments as ports exposed in an application.
|
||||
|
||||
Segment labels override the default behavior.
|
||||
|
||||
| Label | Description |
|
||||
|---------------------------------------------------------------------------|-------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||
| `traefik.<segment_name>.portIndex=1` | Same as `traefik.portIndex` |
|
||||
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
||||
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
||||
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
||||
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
||||
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
||||
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
|
||||
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
|
||||
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
|
||||
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
|
||||
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
|
||||
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
|
||||
|
||||
#### Custom Headers
|
||||
|
||||
| Label | Description |
|
||||
|----------------------------------------------------------------------|----------------------------------------------------------|
|
||||
| `traefik.<segment_name>.frontend.headers.customRequestHeaders=EXPR ` | Same as `traefik.frontend.headers.customRequestHeaders` |
|
||||
| `traefik.<segment_name>.frontend.headers.customResponseHeaders=EXPR` | Same as `traefik.frontend.headers.customResponseHeaders` |
|
||||
|
||||
#### Security Headers
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------------------------|--------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.frontend.headers.allowedHosts=EXPR` | Same as `traefik.frontend.headers.allowedHosts` |
|
||||
| `traefik.<segment_name>.frontend.headers.browserXSSFilter=true` | Same as `traefik.frontend.headers.browserXSSFilter` |
|
||||
| `traefik.<segment_name>.frontend.headers.contentSecurityPolicy=VALUE` | Same as `traefik.frontend.headers.contentSecurityPolicy` |
|
||||
| `traefik.<segment_name>.frontend.headers.contentTypeNosniff=true` | Same as `traefik.frontend.headers.contentTypeNosniff` |
|
||||
| `traefik.<segment_name>.frontend.headers.customBrowserXSSValue=VALUE` | Same as `traefik.frontend.headers.customBrowserXSSValue` |
|
||||
| `traefik.<segment_name>.frontend.headers.customFrameOptionsValue=VALUE` | Same as `traefik.frontend.headers.customFrameOptionsValue` |
|
||||
| `traefik.<segment_name>.frontend.headers.forceSTSHeader=false` | Same as `traefik.frontend.headers.forceSTSHeader` |
|
||||
| `traefik.<segment_name>.frontend.headers.frameDeny=false` | Same as `traefik.frontend.headers.frameDeny` |
|
||||
| `traefik.<segment_name>.frontend.headers.hostsProxyHeaders=EXPR` | Same as `traefik.frontend.headers.hostsProxyHeaders` |
|
||||
| `traefik.<segment_name>.frontend.headers.isDevelopment=false` | Same as `traefik.frontend.headers.isDevelopment` |
|
||||
| `traefik.<segment_name>.frontend.headers.publicKey=VALUE` | Same as `traefik.frontend.headers.publicKey` |
|
||||
| `traefik.<segment_name>.frontend.headers.referrerPolicy=VALUE` | Same as `traefik.frontend.headers.referrerPolicy` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLRedirect=true` | Same as `traefik.frontend.headers.SSLRedirect` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLTemporaryRedirect=true` | Same as `traefik.frontend.headers.SSLTemporaryRedirect` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLHost=HOST` | Same as `traefik.frontend.headers.SSLHost` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLProxyHeaders=EXPR` | Same as `traefik.frontend.headers.SSLProxyHeaders=EXPR` |
|
||||
| `traefik.<segment_name>.frontend.headers.STSSeconds=315360000` | Same as `traefik.frontend.headers.STSSeconds=315360000` |
|
||||
| `traefik.<segment_name>.frontend.headers.STSIncludeSubdomains=true` | Same as `traefik.frontend.headers.STSIncludeSubdomains=true` |
|
||||
| `traefik.<segment_name>.frontend.headers.STSPreload=true` | Same as `traefik.frontend.headers.STSPreload=true` |
|
||||
@@ -1,181 +0,0 @@
|
||||
# Mesos Generic Provider
|
||||
|
||||
Træfik can be configured to use Mesos as a provider.
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Mesos Provider
|
||||
################################################################
|
||||
|
||||
# Enable Mesos Provider.
|
||||
[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"
|
||||
|
||||
# Expose Mesos apps by default in Traefik.
|
||||
#
|
||||
# Optional
|
||||
# Default: true
|
||||
#
|
||||
# exposedByDefault = false
|
||||
|
||||
# Override default configuration template.
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# filename = "mesos.tmpl"
|
||||
|
||||
# Override template version
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
# - "1": previous template version (must be used only with older custom templates, see "filename")
|
||||
# - "2": current template version (must be used to force template version when "filename" is used)
|
||||
#
|
||||
# templateVersion = 2
|
||||
|
||||
# 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, netinfo).
|
||||
#
|
||||
# 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
|
||||
|
||||
```
|
||||
|
||||
## Labels: overriding default behavior
|
||||
|
||||
The following labels can be defined on Mesos tasks. They adjust the behavior for the entire application.
|
||||
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.domain` | Default domain used for frontend rules. |
|
||||
| `traefik.enable=false` | Disable this container in Træfik |
|
||||
| `traefik.port=80` | Register this port. Useful when the container exposes multiples ports. |
|
||||
| `traefik.portIndex=1` | Register port by index in the application's ports array. Useful when the application exposes multiple ports. |
|
||||
| `traefik.protocol=https` | Override the default `http` protocol |
|
||||
| `traefik.weight=10` | Assign this weight to the container |
|
||||
| `traefik.backend=foo` | Give the name `foo` to the generated backend for this container. |
|
||||
| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. |
|
||||
| `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.interval=1s` | Define the health check interval. (Default: 30s) |
|
||||
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
|
||||
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. |
|
||||
| `traefik.frontend.priority=10` | Override default frontend priority |
|
||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) |
|
||||
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
||||
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{discovery_name}.{domain}`. |
|
||||
| `traefik.frontend.whiteList.sourceRange=RANGE` | List of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.frontend.whiteList.useXForwardedFor=true` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
|
||||
### Custom Headers
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
|
||||
### Security Headers
|
||||
|
||||
| Label | Description |
|
||||
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
|
||||
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
|
||||
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
|
||||
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
|
||||
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
|
||||
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
|
||||
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
|
||||
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
|
||||
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
|
||||
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
|
||||
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
|
||||
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
|
||||
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
@@ -1,279 +0,0 @@
|
||||
# Rancher Provider
|
||||
|
||||
Træfik can be configured to use Rancher as a provider.
|
||||
|
||||
## Global Configuration
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Rancher Provider
|
||||
################################################################
|
||||
|
||||
# Enable Rancher Provider.
|
||||
[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
|
||||
|
||||
# Override default configuration template.
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# filename = "rancher.tmpl"
|
||||
|
||||
# Override template version
|
||||
# For advanced users :)
|
||||
#
|
||||
# Optional
|
||||
# - "1": previous template version (must be used only with older custom templates, see "filename")
|
||||
# - "2": current template version (must be used to force template version when "filename" is used)
|
||||
#
|
||||
# templateVersion = 2
|
||||
```
|
||||
|
||||
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
||||
## Rancher Metadata Service
|
||||
|
||||
```toml
|
||||
# Enable Rancher metadata service provider instead of the API
|
||||
# provider.
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
[rancher.metadata]
|
||||
|
||||
# Poll the Rancher metadata service for changes every `rancher.refreshSeconds`.
|
||||
# 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 provider.
|
||||
#
|
||||
# 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 behavior
|
||||
|
||||
### On Containers
|
||||
|
||||
Labels can be used on task containers to override default behavior:
|
||||
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.domain` | Default domain used for frontend rules. |
|
||||
| `traefik.enable=false` | Disable this container in Træfik |
|
||||
| `traefik.port=80` | Register this port. Useful when the container exposes multiples ports. |
|
||||
| `traefik.protocol=https` | Override the default `http` protocol |
|
||||
| `traefik.weight=10` | Assign this weight to the container |
|
||||
| `traefik.backend=foo` | Give the name `foo` to the generated backend for this container. |
|
||||
| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. |
|
||||
| `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.interval=1s` | Define the health check interval. |
|
||||
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) |
|
||||
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. |
|
||||
| `traefik.frontend.priority=10` | Override default frontend priority |
|
||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) |
|
||||
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
||||
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{service_name}.{stack_name}.{domain}`. |
|
||||
| `traefik.frontend.whiteList.sourceRange=RANGE` | List of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access.<br>If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.frontend.whiteList.useXForwardedFor=true` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
|
||||
#### Custom Headers
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
|
||||
#### Security Headers
|
||||
|
||||
| Label | Description |
|
||||
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
|
||||
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
|
||||
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
|
||||
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
|
||||
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
|
||||
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
|
||||
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
|
||||
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
|
||||
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
|
||||
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
|
||||
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
|
||||
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
|
||||
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
|
||||
### On containers with Multiple Ports (segment labels)
|
||||
|
||||
Segment labels are used to define routes to a container exposing multiple ports.
|
||||
A segment is a group of labels that apply to a port exposed by a container.
|
||||
You can define as many segments as ports exposed in a container.
|
||||
|
||||
Segment labels override the default behavior.
|
||||
|
||||
| Label | Description |
|
||||
|---------------------------------------------------------------------------|-------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
||||
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
||||
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
||||
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
||||
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
||||
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
|
||||
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
|
||||
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
|
||||
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
|
||||
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
|
||||
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
|
||||
|
||||
#### Custom Headers
|
||||
|
||||
| Label | Description |
|
||||
|----------------------------------------------------------------------|------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.frontend.headers.customRequestHeaders=EXPR ` | overrides `traefik.frontend.headers.customRequestHeaders` |
|
||||
| `traefik.<segment_name>.frontend.headers.customResponseHeaders=EXPR` | overrides `traefik.frontend.headers.customResponseHeaders` |
|
||||
|
||||
#### Security Headers
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------------------------|--------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.frontend.headers.allowedHosts=EXPR` | overrides `traefik.frontend.headers.allowedHosts` |
|
||||
| `traefik.<segment_name>.frontend.headers.browserXSSFilter=true` | overrides `traefik.frontend.headers.browserXSSFilter` |
|
||||
| `traefik.<segment_name>.frontend.headers.contentSecurityPolicy=VALUE` | overrides `traefik.frontend.headers.contentSecurityPolicy` |
|
||||
| `traefik.<segment_name>.frontend.headers.contentTypeNosniff=true` | overrides `traefik.frontend.headers.contentTypeNosniff` |
|
||||
| `traefik.<segment_name>.frontend.headers.customBrowserXSSValue=VALUE` | overrides `traefik.frontend.headers.customBrowserXSSValue` |
|
||||
| `traefik.<segment_name>.frontend.headers.customFrameOptionsValue=VALUE` | overrides `traefik.frontend.headers.customFrameOptionsValue` |
|
||||
| `traefik.<segment_name>.frontend.headers.forceSTSHeader=false` | overrides `traefik.frontend.headers.forceSTSHeader` |
|
||||
| `traefik.<segment_name>.frontend.headers.frameDeny=false` | overrides `traefik.frontend.headers.frameDeny` |
|
||||
| `traefik.<segment_name>.frontend.headers.hostsProxyHeaders=EXPR` | overrides `traefik.frontend.headers.hostsProxyHeaders` |
|
||||
| `traefik.<segment_name>.frontend.headers.isDevelopment=false` | overrides `traefik.frontend.headers.isDevelopment` |
|
||||
| `traefik.<segment_name>.frontend.headers.publicKey=VALUE` | overrides `traefik.frontend.headers.publicKey` |
|
||||
| `traefik.<segment_name>.frontend.headers.referrerPolicy=VALUE` | overrides `traefik.frontend.headers.referrerPolicy` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLRedirect=true` | overrides `traefik.frontend.headers.SSLRedirect` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLTemporaryRedirect=true` | overrides `traefik.frontend.headers.SSLTemporaryRedirect` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLHost=HOST` | overrides `traefik.frontend.headers.SSLHost` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLProxyHeaders=EXPR` | overrides `traefik.frontend.headers.SSLProxyHeaders` |
|
||||
| `traefik.<segment_name>.frontend.headers.STSSeconds=315360000` | overrides `traefik.frontend.headers.STSSeconds` |
|
||||
| `traefik.<segment_name>.frontend.headers.STSIncludeSubdomains=true` | overrides `traefik.frontend.headers.STSIncludeSubdomains` |
|
||||
| `traefik.<segment_name>.frontend.headers.STSPreload=true` | overrides `traefik.frontend.headers.STSPreload` |
|
||||
@@ -1,92 +0,0 @@
|
||||
# Rest Provider
|
||||
|
||||
Træfik can be configured:
|
||||
|
||||
- using a RESTful api.
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml
|
||||
# Enable REST Provider.
|
||||
[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/providers/rest"
|
||||
```
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,156 +0,0 @@
|
||||
# Azure Service Fabric Provider
|
||||
|
||||
Træfik can be configured to use Azure Service Fabric as a provider.
|
||||
|
||||
See [this repository for an example deployment package and further documentation.](https://aka.ms/traefikonsf)
|
||||
|
||||
## Azure Service Fabric
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Azure Service Fabric Provider
|
||||
################################################################
|
||||
|
||||
# Enable Azure Service Fabric Provider
|
||||
[serviceFabric]
|
||||
|
||||
# Azure Service Fabric Management Endpoint
|
||||
#
|
||||
# Required
|
||||
#
|
||||
clusterManagementUrl = "https://localhost:19080"
|
||||
|
||||
# Azure Service Fabric Management Endpoint API Version
|
||||
#
|
||||
# Required
|
||||
# Default: "3.0"
|
||||
#
|
||||
apiVersion = "3.0"
|
||||
|
||||
# Azure 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.enable">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 behavior.
|
||||
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.enable=false` | Disable this container in Træfik |
|
||||
| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.servicefabric.groupname` | Group all services with the same name into a single backend in Træfik |
|
||||
| `traefik.servicefabric.groupweight` | Set the weighting of the current services nodes in the backend group |
|
||||
| `traefik.servicefabric.enablelabeloverrides` | Toggle whether labels can be overridden using the Service Fabric Property Manager API |
|
||||
| `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. |
|
||||
| `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.interval=1s` | Define the health check interval. |
|
||||
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) |
|
||||
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.backend.weight=10` | Assign this weight to the container |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. |
|
||||
| `traefik.frontend.priority=10` | Override default frontend priority |
|
||||
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) |
|
||||
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. |
|
||||
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Defaults to SF address. |
|
||||
| `traefik.frontend.whiteList.sourceRange=RANGE` | List of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access.<br>If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.frontend.whiteList.useXForwardedFor=true` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
|
||||
### Custom Headers
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
|
||||
### Security Headers
|
||||
|
||||
| Label | Description |
|
||||
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
|
||||
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
|
||||
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
|
||||
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
|
||||
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
|
||||
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
|
||||
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
|
||||
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
|
||||
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
|
||||
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
|
||||
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
|
||||
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
|
||||
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
|
||||
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
|
||||
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
|
||||
@@ -1,482 +0,0 @@
|
||||
# Web Provider
|
||||
|
||||
!!! danger "DEPRECATED"
|
||||
The web provider is deprecated, please use the [api](/configuration/api.md), the [ping](/configuration/ping.md), the [metrics](/configuration/metrics) and the [rest](/configuration/backends/rest.md) provider.
|
||||
|
||||
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 Provider.
|
||||
[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
|
||||
|
||||
# Set the root path for webui and API
|
||||
#
|
||||
# Deprecated
|
||||
# Optional
|
||||
#
|
||||
# path = "/mypath"
|
||||
#
|
||||
```
|
||||
|
||||
## 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 Træfik 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 host name
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Deprecation compatibility
|
||||
|
||||
#### Address
|
||||
|
||||
As the web provider is deprecated, you can handle the `Address` option like this:
|
||||
|
||||
```toml
|
||||
defaultEntryPoints = ["http"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
|
||||
[entryPoints.foo]
|
||||
address = ":8082"
|
||||
|
||||
[entryPoints.bar]
|
||||
address = ":8083"
|
||||
|
||||
[ping]
|
||||
entryPoint = "foo"
|
||||
|
||||
[api]
|
||||
entryPoint = "bar"
|
||||
```
|
||||
|
||||
In the above example, you would access a regular path, administration panel, and health-check as follows:
|
||||
|
||||
* Regular path: `http://hostname:80/path`
|
||||
* Admin Panel: `http://hostname:8083/`
|
||||
* Ping URL: `http://hostname:8082/ping`
|
||||
|
||||
In the above example, it is _very_ important to create a named dedicated entry point, and do **not** include it in `defaultEntryPoints`.
|
||||
Otherwise, you are likely to expose _all_ services via that entry point.
|
||||
|
||||
#### Path
|
||||
|
||||
As the web provider is deprecated, you can handle the `Path` option like this:
|
||||
|
||||
```toml
|
||||
defaultEntryPoints = ["http"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
|
||||
[entryPoints.foo]
|
||||
address = ":8080"
|
||||
|
||||
[entryPoints.bar]
|
||||
address = ":8081"
|
||||
|
||||
# Activate API and Dashboard
|
||||
[api]
|
||||
entryPoint = "bar"
|
||||
dashboard = true
|
||||
|
||||
[file]
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.servers.server1]
|
||||
url = "http://127.0.0.1:8081"
|
||||
|
||||
[frontends]
|
||||
[frontends.frontend1]
|
||||
entryPoints = ["foo"]
|
||||
backend = "backend1"
|
||||
[frontends.frontend1.routes.test_1]
|
||||
rule = "PathPrefixStrip:/yourprefix;PathPrefix:/yourprefix"
|
||||
```
|
||||
|
||||
#### Authentication
|
||||
|
||||
As the web provider is deprecated, you can handle the `auth` option like this:
|
||||
|
||||
```toml
|
||||
defaultEntryPoints = ["http"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
|
||||
[entryPoints.foo]
|
||||
address=":8080"
|
||||
[entryPoints.foo.auth]
|
||||
[entryPoints.foo.auth.basic]
|
||||
users = [
|
||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
]
|
||||
|
||||
[api]
|
||||
entrypoint="foo"
|
||||
```
|
||||
|
||||
For more information, see [entry points](/configuration/entrypoints/) .
|
||||
@@ -1,61 +0,0 @@
|
||||
# Zookeeper Provider
|
||||
|
||||
Træfik can be configured to use Zookeeper as a provider.
|
||||
|
||||
```toml
|
||||
################################################################
|
||||
# Zookeeper Provider
|
||||
################################################################
|
||||
|
||||
# Enable Zookeeper Provider.
|
||||
[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 [provider-specific constraints section](/configuration/commons/#provider-specific).
|
||||
|
||||
Please refer to the [Key Value storage structure](/user-guide/kv-config/#key-value-storage-structure) section to get documentation on Traefik KV structure.
|
||||
@@ -1,471 +0,0 @@
|
||||
# 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.
|
||||
# The log level will be set to DEBUG unless `logLevel` is specified.
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
# debug = true
|
||||
|
||||
# Periodically check if a new version has been released.
|
||||
#
|
||||
# Optional
|
||||
# Default: true
|
||||
#
|
||||
# checkNewVersion = false
|
||||
|
||||
# Providers 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"]
|
||||
|
||||
# Allow the use of 0 as server weight.
|
||||
# - false: a weight 0 means internally a weight of 1.
|
||||
# - true: a weight 0 means internally a weight of 0 (a server with a weight of 0 is removed from the available servers).
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
# AllowMinWeightZero = true
|
||||
```
|
||||
|
||||
- `graceTimeOut`: Duration to give active requests a chance to finish before Traefik stops.
|
||||
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`: Providers throttle duration: minimum duration in seconds between 2 events from providers before applying a new configuration.
|
||||
It avoids unnecessary reloads if multiples events are sent in a short amount of time.
|
||||
Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw values (digits).
|
||||
If no units are provided, the value is parsed assuming seconds.
|
||||
|
||||
- `maxIdleConnsPerHost`: Controls the maximum idle (keep-alive) connections to keep per-host.
|
||||
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 providers.
|
||||
|
||||
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-*"]
|
||||
```
|
||||
|
||||
### provider-specific
|
||||
|
||||
Supported Providers:
|
||||
|
||||
- Docker
|
||||
- Consul K/V
|
||||
- BoltDB
|
||||
- Zookeeper
|
||||
- Etcd
|
||||
- Consul Catalog
|
||||
- Rancher
|
||||
- Marathon
|
||||
- Kubernetes (using a provider-specific mechanism based on label selectors)
|
||||
|
||||
```toml
|
||||
# Provider-specific constraint
|
||||
[consulCatalog]
|
||||
# ...
|
||||
constraints = ["tag==api"]
|
||||
|
||||
# Provider-specific constraint
|
||||
[marathon]
|
||||
# ...
|
||||
constraints = ["tag==api", "tag!=v*-beta"]
|
||||
```
|
||||
|
||||
|
||||
## 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`.
|
||||
|
||||
|
||||
## 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]
|
||||
# ...
|
||||
[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.
|
||||
|
||||
## Buffering
|
||||
|
||||
In some cases request/buffering can be enabled for a specific backend.
|
||||
By enabling this, Træfik will read the entire request into memory (possibly buffering large requests into disk) and will reject requests that are over a specified limit.
|
||||
This may help services deal with large data (multipart/form-data for example) more efficiently and should minimise time spent when sending data to a backend server.
|
||||
|
||||
For more information please check [oxy/buffer](http://godoc.org/github.com/vulcand/oxy/buffer) documentation.
|
||||
|
||||
Example configuration:
|
||||
|
||||
```toml
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.buffering]
|
||||
maxRequestBodyBytes = 10485760
|
||||
memRequestBodyBytes = 2097152
|
||||
maxResponseBodyBytes = 10485760
|
||||
memResponseBodyBytes = 2097152
|
||||
retryExpression = "IsNetworkError() && Attempts() <= 2"
|
||||
```
|
||||
|
||||
## Retry Configuration
|
||||
|
||||
```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 providers except: File Provider, Web Provider and DynamoDB Provider.
|
||||
|
||||
```toml
|
||||
[provider_name]
|
||||
|
||||
# Override default provider 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}}
|
||||
```
|
||||
@@ -1,421 +0,0 @@
|
||||
# Entry Points Definition
|
||||
|
||||
## Reference
|
||||
|
||||
### TOML
|
||||
|
||||
```toml
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
compress = true
|
||||
|
||||
[entryPoints.http.whitelist]
|
||||
sourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
|
||||
useXForwardedFor = true
|
||||
|
||||
[entryPoints.http.tls]
|
||||
minVersion = "VersionTLS12"
|
||||
cipherSuites = [
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_RSA_WITH_AES_256_GCM_SHA384"
|
||||
]
|
||||
[[entryPoints.http.tls.certificates]]
|
||||
certFile = "path/to/my.cert"
|
||||
keyFile = "path/to/my.key"
|
||||
[[entryPoints.http.tls.certificates]]
|
||||
certFile = "path/to/other.cert"
|
||||
keyFile = "path/to/other.key"
|
||||
# ...
|
||||
[entryPoints.http.tls.clientCA]
|
||||
files = ["path/to/ca1.crt", "path/to/ca2.crt"]
|
||||
optional = false
|
||||
|
||||
[entryPoints.http.redirect]
|
||||
entryPoint = "https"
|
||||
regex = "^http://localhost/(.*)"
|
||||
replacement = "http://mydomain/$1"
|
||||
permanent = true
|
||||
|
||||
[entryPoints.http.auth]
|
||||
headerField = "X-WebAuth-User"
|
||||
[entryPoints.http.auth.basic]
|
||||
users = [
|
||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
]
|
||||
usersFile = "/path/to/.htpasswd"
|
||||
[entryPoints.http.auth.digest]
|
||||
users = [
|
||||
"test:traefik:a2688e031edb4be6a3797f3882655c05",
|
||||
"test2:traefik:518845800f9e2bfb1f1f740ec24f074e",
|
||||
]
|
||||
usersFile = "/path/to/.htdigest"
|
||||
[entryPoints.http.auth.forward]
|
||||
address = "https://authserver.com/auth"
|
||||
trustForwardHeader = true
|
||||
[entryPoints.http.auth.forward.tls]
|
||||
ca = [ "path/to/local.crt"]
|
||||
caOptional = true
|
||||
cert = "path/to/foo.cert"
|
||||
key = "path/to/foo.key"
|
||||
insecureSkipVerify = true
|
||||
|
||||
[entryPoints.http.proxyProtocol]
|
||||
insecure = true
|
||||
trustedIPs = ["10.10.10.1", "10.10.10.2"]
|
||||
|
||||
[entryPoints.http.forwardedHeaders]
|
||||
trustedIPs = ["10.10.10.1", "10.10.10.2"]
|
||||
|
||||
[entryPoints.https]
|
||||
# ...
|
||||
```
|
||||
|
||||
### CLI
|
||||
|
||||
For more information about the CLI, see the documentation about [Traefik command](/basics/#traefik).
|
||||
|
||||
```shell
|
||||
--entryPoints='Name:http Address::80'
|
||||
--entryPoints='Name:https Address::443 TLS'
|
||||
```
|
||||
|
||||
!!! note
|
||||
Whitespace is used as option separator and `,` is used as value separator for the list.
|
||||
The names of the options are case-insensitive.
|
||||
|
||||
In compose file the entrypoint syntax is different:
|
||||
|
||||
```yaml
|
||||
traefik:
|
||||
image: traefik
|
||||
command:
|
||||
- --defaultentrypoints=powpow
|
||||
- "--entryPoints=Name:powpow Address::42 Compress:true"
|
||||
```
|
||||
or
|
||||
```yaml
|
||||
traefik:
|
||||
image: traefik
|
||||
command: --defaultentrypoints=powpow --entryPoints='Name:powpow Address::42 Compress:true'
|
||||
```
|
||||
|
||||
#### All available options:
|
||||
|
||||
```ini
|
||||
Name:foo
|
||||
Address::80
|
||||
TLS:/my/path/foo.cert,/my/path/foo.key;/my/path/goo.cert,/my/path/goo.key;/my/path/hoo.cert,/my/path/hoo.key
|
||||
TLS
|
||||
CA:car
|
||||
CA.Optional:true
|
||||
Redirect.EntryPoint:https
|
||||
Redirect.Regex:http://localhost/(.*)
|
||||
Redirect.Replacement:http://mydomain/$1
|
||||
Redirect.Permanent:true
|
||||
Compress:true
|
||||
WhiteList.SourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16
|
||||
WhiteList.UseXForwardedFor:true
|
||||
ProxyProtocol.TrustedIPs:192.168.0.1
|
||||
ProxyProtocol.Insecure:true
|
||||
ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24
|
||||
Auth.Basic.Users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0
|
||||
Auth.Digest.Users:test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e
|
||||
Auth.HeaderField:X-WebAuth-User
|
||||
Auth.Forward.Address:https://authserver.com/auth
|
||||
Auth.Forward.TrustForwardHeader:true
|
||||
Auth.Forward.TLS.CA:path/to/local.crt
|
||||
Auth.Forward.TLS.CAOptional:true
|
||||
Auth.Forward.TLS.Cert:path/to/foo.cert
|
||||
Auth.Forward.TLS.Key:path/to/foo.key
|
||||
Auth.Forward.TLS.InsecureSkipVerify:true
|
||||
```
|
||||
|
||||
## Basic
|
||||
|
||||
```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).
|
||||
|
||||
Care should be taken when defining replacement expand variables: `$1x` is equivalent to `${1x}`, not `${1}x` (see [Regexp.Expand](https://golang.org/pkg/regexp/#Regexp.Expand)), so use `${1}` syntax.
|
||||
|
||||
Regular expressions and replacements can be tested using online tools such as [Go Playground](https://play.golang.org/p/mWU9p-wk2ru) or the [Regex101](https://regex101.com/r/58sIgx/2).
|
||||
|
||||
## TLS
|
||||
|
||||
### Static Certificates
|
||||
|
||||
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.
|
||||
|
||||
|
||||
### Dynamic Certificates
|
||||
|
||||
If you need to add or remove TLS certificates while Traefik is started, Dynamic TLS certificates are supported using the [file provider](/configuration/backends/file).
|
||||
|
||||
|
||||
## 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 them.
|
||||
|
||||
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 them.
|
||||
|
||||
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.digest]
|
||||
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 authentication 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_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"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.
|
||||
|
||||
## White Listing
|
||||
|
||||
To enable IP white listing at the entry point level.
|
||||
|
||||
```toml
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
|
||||
[entryPoints.http.whiteList]
|
||||
sourceRange = ["127.0.0.1/32", "192.168.1.7"]
|
||||
# useXForwardedFor = true
|
||||
```
|
||||
|
||||
## 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"]
|
||||
```
|
||||
@@ -1,252 +0,0 @@
|
||||
# Logs Definition
|
||||
|
||||
## Reference
|
||||
|
||||
### TOML
|
||||
|
||||
```toml
|
||||
logLevel = "INFO"
|
||||
|
||||
[traefikLog]
|
||||
filePath = "/path/to/traefik.log"
|
||||
format = "json"
|
||||
|
||||
[accessLog]
|
||||
filePath = "/path/to/access.log"
|
||||
format = "json"
|
||||
|
||||
[accessLog.filters]
|
||||
statusCodes = ["200", "300-302"]
|
||||
retryAttempts = true
|
||||
|
||||
[accessLog.fields]
|
||||
defaultMode = "keep"
|
||||
[accessLog.fields.names]
|
||||
"ClientUsername" = "drop"
|
||||
# ...
|
||||
|
||||
[accessLog.fields.headers]
|
||||
defaultMode = "keep"
|
||||
[accessLog.fields.headers.names]
|
||||
"User-Agent" = "redact"
|
||||
"Authorization" = "drop"
|
||||
"Content-Type" = "keep"
|
||||
# ...
|
||||
```
|
||||
|
||||
### CLI
|
||||
|
||||
For more information about the CLI, see the documentation about [Traefik command](/basics/#traefik).
|
||||
|
||||
```shell
|
||||
--logLevel="DEBUG"
|
||||
--traefikLog.filePath="/path/to/traefik.log"
|
||||
--traefikLog.format="json"
|
||||
--accessLog.filePath="/path/to/access.log"
|
||||
--accessLog.format="json"
|
||||
--accessLog.filters.statusCodes="200,300-302"
|
||||
--accessLog.filters.retryAttempts="true"
|
||||
--accessLog.fields.defaultMode="keep"
|
||||
--accessLog.fields.names="Username=drop Hostname=drop"
|
||||
--accessLog.fields.headers.defaultMode="keep"
|
||||
--accessLog.fields.headers.names="User-Agent=redact Authorization=drop Content-Type=keep"
|
||||
```
|
||||
|
||||
|
||||
## Traefik Logs
|
||||
|
||||
By default the Traefik log is written to stdout in text format.
|
||||
|
||||
To write the logs into a log file specify the `filePath`:
|
||||
```toml
|
||||
[traefikLog]
|
||||
filePath = "/path/to/traefik.log"
|
||||
```
|
||||
|
||||
To write JSON format logs, specify `json` as the format:
|
||||
```toml
|
||||
[traefikLog]
|
||||
filePath = "/path/to/traefik.log"
|
||||
format = "json"
|
||||
```
|
||||
|
||||
|
||||
Deprecated way (before 1.4):
|
||||
|
||||
!!! danger "DEPRECATED"
|
||||
`traefikLogsFile` is deprecated, use [traefikLog](/configuration/logs/#traefik-logs) instead.
|
||||
|
||||
```toml
|
||||
# Traefik logs file
|
||||
# If not defined, logs to stdout
|
||||
#
|
||||
# DEPRECATED - see [traefikLog] lower down
|
||||
# In case both traefikLogsFile and traefikLog.filePath are specified, the latter will take precedence.
|
||||
# Optional
|
||||
#
|
||||
traefikLogsFile = "log/traefik.log"
|
||||
```
|
||||
|
||||
To customize the log level:
|
||||
```toml
|
||||
# Log level
|
||||
#
|
||||
# Optional
|
||||
# Default: "ERROR"
|
||||
#
|
||||
# Accepted values, in order of severity: "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "PANIC"
|
||||
# Messages at and above the selected level will be logged.
|
||||
#
|
||||
logLevel = "ERROR"
|
||||
```
|
||||
|
||||
|
||||
## Access Logs
|
||||
|
||||
Access logs are written when `[accessLog]` is defined.
|
||||
By default it will write to stdout and produce logs in the textual Common Log Format (CLF), extended with additional fields.
|
||||
|
||||
To enable access logs using the default settings just add the `[accessLog]` entry:
|
||||
```toml
|
||||
[accessLog]
|
||||
```
|
||||
|
||||
To write the logs into a log file specify the `filePath`:
|
||||
```toml
|
||||
[accessLog]
|
||||
filePath = "/path/to/access.log"
|
||||
```
|
||||
|
||||
To write JSON format logs, specify `json` as the format:
|
||||
```toml
|
||||
[accessLog]
|
||||
filePath = "/path/to/access.log"
|
||||
format = "json"
|
||||
```
|
||||
|
||||
To filter logs you can specify a set of filters which are logically "OR-connected". Thus, specifying multiple filters will keep more access logs than specifying only one:
|
||||
```toml
|
||||
[accessLog]
|
||||
filePath = "/path/to/access.log"
|
||||
format = "json"
|
||||
|
||||
[accessLog.filters]
|
||||
|
||||
# statusCodes keep access logs with status codes in the specified range
|
||||
#
|
||||
# Optional
|
||||
# Default: []
|
||||
#
|
||||
statusCodes = ["200", "300-302"]
|
||||
|
||||
# retryAttempts keep access logs when at least one retry happened
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
retryAttempts = true
|
||||
```
|
||||
|
||||
To customize logs format:
|
||||
```toml
|
||||
[accessLog]
|
||||
filePath = "/path/to/access.log"
|
||||
format = "json"
|
||||
|
||||
[accessLog.filters]
|
||||
|
||||
# statusCodes keep only access logs with status codes in the specified range
|
||||
#
|
||||
# Optional
|
||||
# Default: []
|
||||
#
|
||||
statusCodes = ["200", "300-302"]
|
||||
|
||||
[accessLog.fields]
|
||||
|
||||
# defaultMode
|
||||
#
|
||||
# Optional
|
||||
# Default: "keep"
|
||||
#
|
||||
# Accepted values "keep", "drop"
|
||||
#
|
||||
defaultMode = "keep"
|
||||
|
||||
# Fields map which is used to override fields defaultMode
|
||||
[accessLog.fields.names]
|
||||
"ClientUsername" = "drop"
|
||||
# ...
|
||||
|
||||
[accessLog.fields.headers]
|
||||
# defaultMode
|
||||
#
|
||||
# Optional
|
||||
# Default: "keep"
|
||||
#
|
||||
# Accepted values "keep", "drop", "redact"
|
||||
#
|
||||
defaultMode = "keep"
|
||||
# Fields map which is used to override headers defaultMode
|
||||
[accessLog.fields.headers.names]
|
||||
"User-Agent" = "redact"
|
||||
"Authorization" = "drop"
|
||||
"Content-Type" = "keep"
|
||||
# ...
|
||||
```
|
||||
|
||||
#### List of all available fields
|
||||
|
||||
```ini
|
||||
StartUTC
|
||||
StartLocal
|
||||
Duration
|
||||
FrontendName
|
||||
BackendName
|
||||
BackendURL
|
||||
BackendAddr
|
||||
ClientAddr
|
||||
ClientHost
|
||||
ClientPort
|
||||
ClientUsername
|
||||
RequestAddr
|
||||
RequestHost
|
||||
RequestPort
|
||||
RequestMethod
|
||||
RequestPath
|
||||
RequestProtocol
|
||||
RequestLine
|
||||
RequestContentSize
|
||||
OriginDuration
|
||||
OriginContentSize
|
||||
OriginStatus
|
||||
OriginStatusLine
|
||||
DownstreamStatus
|
||||
DownstreamStatusLine
|
||||
DownstreamContentSize
|
||||
RequestCount
|
||||
GzipRatio
|
||||
Overhead
|
||||
RetryAttempts
|
||||
```
|
||||
|
||||
Deprecated way (before 1.4):
|
||||
|
||||
!!! danger "DEPRECATED"
|
||||
`accessLogsFile` is deprecated, use [accessLog](/configuration/logs/#access-logs) instead.
|
||||
|
||||
```toml
|
||||
# Access logs file
|
||||
#
|
||||
# DEPRECATED - see [accessLog]
|
||||
#
|
||||
accessLogsFile = "log/access.log"
|
||||
```
|
||||
|
||||
## Log Rotation
|
||||
|
||||
Traefik will close and reopen its log files, assuming they're configured, on receipt of a USR1 signal.
|
||||
This allows the logs to be rotated and processed by an external program, such as `logrotate`.
|
||||
|
||||
!!! note
|
||||
This does not work on Windows due to the lack of USR signals.
|
||||
@@ -1,126 +0,0 @@
|
||||
# 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
|
||||
|
||||
# ...
|
||||
```
|
||||
@@ -1,91 +0,0 @@
|
||||
# Ping Definition
|
||||
|
||||
## Configuration
|
||||
|
||||
```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.
|
||||
|
||||
## Examples
|
||||
|
||||
The `/ping` health-check URL is enabled with the command-line `--ping` or config file option `[ping]`.
|
||||
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 administration 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 entry point
|
||||
* Enable `/ping` on a dedicated port
|
||||
|
||||
### Ping health check on a regular entry point
|
||||
|
||||
To proxy `/ping` from a regular entry point to the administration one without exposing the panel, do the following:
|
||||
|
||||
```toml
|
||||
defaultEntryPoints = ["http"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
|
||||
[ping]
|
||||
entryPoint = "http"
|
||||
|
||||
```
|
||||
|
||||
The above link `ping` on the `http` entry point and then expose it on port `80`
|
||||
|
||||
### Enable ping health check on dedicated port
|
||||
|
||||
If you do not want to or cannot expose the health-check on a regular entry point - e.g. your security rules do not allow it, or you have a conflicting path - then you can enable health-check on its own entry point.
|
||||
Use the following configuration:
|
||||
|
||||
```toml
|
||||
defaultEntryPoints = ["http"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
[entryPoints.ping]
|
||||
address = ":8082"
|
||||
|
||||
[ping]
|
||||
entryPoint = "ping"
|
||||
```
|
||||
|
||||
The above is similar to the previous example, but instead of enabling `/ping` on the _default_ entry point, we enable it on a _dedicated_ entry point.
|
||||
|
||||
In the above example, you would access a regular path and health-check as follows:
|
||||
|
||||
* Regular path: `http://hostname:80/foo`
|
||||
* 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 entry point, and do **not** include it in `defaultEntryPoints`.
|
||||
Otherwise, you are likely to expose _all_ services via this entry point.
|
||||
|
||||
### Using ping for external Load-balancer rotation health check
|
||||
|
||||
If you are running traefik behind a external Load-balancer, and want to configure rotation health check on the Load-balancer to take a traefik instance out of rotation gracefully, you can configure [lifecycle.requestAcceptGraceTimeout](/configuration/commons.md#life-cycle) and the ping endpoint will return `503` response on traefik server termination, so that the Load-balancer can take the terminating traefik instance out of rotation, before it stops responding.
|
||||
@@ -1,100 +0,0 @@
|
||||
# Tracing
|
||||
|
||||
Tracing system allows developers to visualize call flows in there infrastructures.
|
||||
|
||||
We use [OpenTracing](http://opentracing.io). It is an open standard designed for distributed tracing.
|
||||
|
||||
Træfik supports two backends: Jaeger and Zipkin.
|
||||
|
||||
## Jaeger
|
||||
|
||||
```toml
|
||||
# Tracing definition
|
||||
[tracing]
|
||||
# Backend name used to send tracing data
|
||||
#
|
||||
# Default: "jaeger"
|
||||
#
|
||||
backend = "jaeger"
|
||||
|
||||
# Service name used in Jaeger backend
|
||||
#
|
||||
# Default: "traefik"
|
||||
#
|
||||
serviceName = "traefik"
|
||||
|
||||
[tracing.jaeger]
|
||||
# Sampling Server URL is the address of jaeger-agent's HTTP sampling server
|
||||
#
|
||||
# Default: "http://localhost:5778/sampling"
|
||||
#
|
||||
samplingServerURL = "http://localhost:5778/sampling"
|
||||
|
||||
# Sampling Type specifies the type of the sampler: const, probabilistic, rateLimiting
|
||||
#
|
||||
# Default: "const"
|
||||
#
|
||||
samplingType = "const"
|
||||
|
||||
# Sampling Param is a value passed to the sampler.
|
||||
# Valid values for Param field are:
|
||||
# - for "const" sampler, 0 or 1 for always false/true respectively
|
||||
# - for "probabilistic" sampler, a probability between 0 and 1
|
||||
# - for "rateLimiting" sampler, the number of spans per second
|
||||
#
|
||||
# Default: 1.0
|
||||
#
|
||||
samplingParam = 1.0
|
||||
|
||||
# Local Agent Host Port instructs reporter to send spans to jaeger-agent at this address
|
||||
#
|
||||
# Default: "127.0.0.1:6831"
|
||||
#
|
||||
localAgentHostPort = "127.0.0.1:6831"
|
||||
```
|
||||
|
||||
!!! warning
|
||||
Træfik is only able to send data over compact thrift protocol to the [Jaeger agent](https://www.jaegertracing.io/docs/deployment/#agent).
|
||||
|
||||
## Zipkin
|
||||
|
||||
```toml
|
||||
# Tracing definition
|
||||
[tracing]
|
||||
# Backend name used to send tracing data
|
||||
#
|
||||
# Default: "jaeger"
|
||||
#
|
||||
backend = "zipkin"
|
||||
|
||||
# Service name used in Zipkin backend
|
||||
#
|
||||
# Default: "traefik"
|
||||
#
|
||||
serviceName = "traefik"
|
||||
|
||||
[tracing.zipkin]
|
||||
# Zipking HTTP endpoint used to send data
|
||||
#
|
||||
# Default: "http://localhost:9411/api/v1/spans"
|
||||
#
|
||||
httpEndpoint = "http://localhost:9411/api/v1/spans"
|
||||
|
||||
# Enable Zipkin debug
|
||||
#
|
||||
# Default: false
|
||||
#
|
||||
debug = false
|
||||
|
||||
# Use ZipKin SameSpan RPC style traces
|
||||
#
|
||||
# Default: false
|
||||
#
|
||||
sameSpan = false
|
||||
|
||||
# Use ZipKin 128 bit root span IDs
|
||||
#
|
||||
# Default: true
|
||||
#
|
||||
id128Bit = true
|
||||
```
|
||||
BIN
docs/content/assets/img/architecture-overview.png
Normal file
|
After Width: | Height: | Size: 361 KiB |
|
Before Width: | Height: | Size: 208 KiB After Width: | Height: | Size: 208 KiB |
|
Before Width: | Height: | Size: 274 KiB After Width: | Height: | Size: 274 KiB |
BIN
docs/content/assets/img/entrypoints.png
Normal file
|
After Width: | Height: | Size: 376 KiB |
BIN
docs/content/assets/img/middleware/addprefix.png
Normal file
|
After Width: | Height: | Size: 42 KiB |