Compare commits
1322 Commits
v2.0.0-alp
...
v2.6.7
Author | SHA1 | Date | |
---|---|---|---|
|
5aa1220e5a | ||
|
c1919c6b24 | ||
|
6349e2e28c | ||
|
e642365613 | ||
|
575d4ab431 | ||
|
d134a993d0 | ||
|
32920ca65c | ||
|
3ac708ddcb | ||
|
0dac0c3a5b | ||
|
9810120aff | ||
|
a34e1c0747 | ||
|
c29ed24a06 | ||
|
ff5cd9b592 | ||
|
af855ef7b4 | ||
|
6559d63d3c | ||
|
4758cc0c8e | ||
|
e4ed829661 | ||
|
2968e5b61b | ||
|
6c2eb6eef3 | ||
|
95257d2ee1 | ||
|
55addfefc8 | ||
|
0ecd85cc66 | ||
|
a9fe3f98c5 | ||
|
77b2a88819 | ||
|
44621ad28c | ||
|
232e2c1e7d | ||
|
ad3625bef3 | ||
|
7c4bf602f0 | ||
|
ffdd693ff6 | ||
|
85b0a47fe8 | ||
|
78822a8015 | ||
|
55cef21fbe | ||
|
2691ac1307 | ||
|
a51851247e | ||
|
0e532a3634 | ||
|
c9daf16388 | ||
|
b22945e185 | ||
|
4b755dc58d | ||
|
0f29e893f4 | ||
|
e3adf93a74 | ||
|
81f88dd998 | ||
|
b6bfa905db | ||
|
16d7b89cb1 | ||
|
9398222db7 | ||
|
ba2d09f6fb | ||
|
7243e65b51 | ||
|
23a6602cbf | ||
|
822b94c45d | ||
|
0a776c3fd5 | ||
|
d7378a96ad | ||
|
db4c6111fd | ||
|
b02c651961 | ||
|
0617a1b0e0 | ||
|
06749e71f2 | ||
|
a1e766e180 | ||
|
b3de9a040b | ||
|
a59dbc4c79 | ||
|
40deefa868 | ||
|
491de0cf64 | ||
|
27a7563e33 | ||
|
819de02101 | ||
|
ce851a5929 | ||
|
7e390ef516 | ||
|
fb23bd5d26 | ||
|
6974f54bfd | ||
|
371b6e3c86 | ||
|
9e96089da6 | ||
|
84a0810546 | ||
|
d9fbb5e25c | ||
|
e97aa6515b | ||
|
6bcfba43c8 | ||
|
0c83ee736c | ||
|
4da33c2bc2 | ||
|
2d56be0ebb | ||
|
6742dd8454 | ||
|
3ac755bd2f | ||
|
3ed72c4e46 | ||
|
477fa15859 | ||
|
390eb9cb61 | ||
|
5a1c936ede | ||
|
47ad6538f1 | ||
|
9be44d8330 | ||
|
a4b354b33f | ||
|
a70b864c55 | ||
|
3bd5fc0f90 | ||
|
aabfb792af | ||
|
e5e48d1cc1 | ||
|
42a110dd69 | ||
|
64af364b02 | ||
|
cf14b8fa92 | ||
|
e7dc6ec025 | ||
|
f29e311b73 | ||
|
a914ce2bd2 | ||
|
b42a7c89e7 | ||
|
67483c1b17 | ||
|
4071f1e7f2 | ||
|
577709fff3 | ||
|
8cd45476ac | ||
|
cf14504fd5 | ||
|
b84829336d | ||
|
d969e59911 | ||
|
936b6148ff | ||
|
a9776ceafc | ||
|
e471239955 | ||
|
2e8156bfaa | ||
|
48ce6c32c1 | ||
|
4990239855 | ||
|
5e2c929322 | ||
|
2b5355c849 | ||
|
f21f71786a | ||
|
fc7f109cb2 | ||
|
a711f0d037 | ||
|
98fc6ca441 | ||
|
c10f1a3a36 | ||
|
da092e653d | ||
|
bf29417136 | ||
|
79a14ce992 | ||
|
99ce26f7b1 | ||
|
16250361c3 | ||
|
be44385b42 | ||
|
54c77ecb54 | ||
|
a30f0dcabd | ||
|
efef7dce4f | ||
|
1c9e4c6050 | ||
|
89cd9e8ddd | ||
|
92093a8c09 | ||
|
d970813c20 | ||
|
f69982aa9d | ||
|
82fdc569c2 | ||
|
def0c1a526 | ||
|
93de7cf0c0 | ||
|
ef2d03d96e | ||
|
321c9421ea | ||
|
5a225b4196 | ||
|
95fabeae73 | ||
|
525a6cf5b2 | ||
|
27ec0912d5 | ||
|
83a7f10c75 | ||
|
0a5c9095ac | ||
|
0a31225e65 | ||
|
db4a92d877 | ||
|
9df053e3f5 | ||
|
1f17731369 | ||
|
8e32d1913b | ||
|
e10a82a501 | ||
|
ce47f200d5 | ||
|
95dc43ce4a | ||
|
d91eefa74f | ||
|
ffdfc13461 | ||
|
a13b03ef3d | ||
|
69d504c905 | ||
|
bda7e025a2 | ||
|
596f04eae8 | ||
|
b39d226fb8 | ||
|
20dfb91948 | ||
|
e033355225 | ||
|
56ed45ae70 | ||
|
d3ff0c2cd4 | ||
|
566b205758 | ||
|
b537ccdb0c | ||
|
d9b8435a7d | ||
|
c0ba4d177f | ||
|
7377ab7b95 | ||
|
207ac94ed0 | ||
|
fe32a7e584 | ||
|
25e12aee14 | ||
|
85dd45cb81 | ||
|
32340252b2 | ||
|
5d716f0149 | ||
|
918a343557 | ||
|
969dd088a2 | ||
|
89001ae9a4 | ||
|
c99221fa34 | ||
|
9ef3fc84f9 | ||
|
d28bcf24e5 | ||
|
8d739c411b | ||
|
46c1600ada | ||
|
126b32c579 | ||
|
380514941c | ||
|
61ceb7a32c | ||
|
07a3c37a23 | ||
|
c7e13eb082 | ||
|
6906a022ca | ||
|
8f0832d340 | ||
|
bda0dba131 | ||
|
76867e39ea | ||
|
6f8e8ea252 | ||
|
8e7881094f | ||
|
7d09132a5c | ||
|
6f4a7fb604 | ||
|
6e28db513c | ||
|
2084201c8f | ||
|
70359e5d27 | ||
|
a72d124551 | ||
|
7ff13c3e3e | ||
|
55360c1eaf | ||
|
60ff50a675 | ||
|
ba3967aa16 | ||
|
fffa413121 | ||
|
c011bdfdd8 | ||
|
4235cef1b2 | ||
|
871e04cb12 | ||
|
287cebb498 | ||
|
6c8d200373 | ||
|
0ac6f80b50 | ||
|
2b73860ea5 | ||
|
ddcb003b3b | ||
|
be52c5abb1 | ||
|
f81ceaef8a | ||
|
eb6c5fc34d | ||
|
4fc16f26a3 | ||
|
234d35f592 | ||
|
352a72a5d7 | ||
|
4d1ce986a6 | ||
|
531a8ff248 | ||
|
2644c1f598 | ||
|
fa53f7ec85 | ||
|
e05574af58 | ||
|
fcfc976b13 | ||
|
78180a5fa7 | ||
|
3445abe7ac | ||
|
e0b442a48b | ||
|
bd1c84755b | ||
|
a7194e96e0 | ||
|
2bd60f9e60 | ||
|
35a40c8727 | ||
|
7f62667569 | ||
|
fd4ba585ee | ||
|
81eb46e36d | ||
|
b7700e77bf | ||
|
e73dd31619 | ||
|
187ec26d8e | ||
|
ef9b79f85c | ||
|
32d88a977d | ||
|
547c380961 | ||
|
848e23b489 | ||
|
d63cb1b4d6 | ||
|
c45de0d8bc | ||
|
5c18967f06 | ||
|
e78f172f02 | ||
|
4fc077a5d2 | ||
|
7f307d60c4 | ||
|
b386964abc | ||
|
817ac8f256 | ||
|
c76d58d532 | ||
|
4b456f3b76 | ||
|
319e3065f0 | ||
|
a48a8a97a1 | ||
|
8be434aaad | ||
|
d9fc775084 | ||
|
f25139424a | ||
|
2d95c37ea4 | ||
|
e12630ef06 | ||
|
48bd279311 | ||
|
36ffdf548d | ||
|
a5b169c563 | ||
|
bc5e621683 | ||
|
1e69939532 | ||
|
d8156ef625 | ||
|
c2c4dc9b58 | ||
|
ffd4e207a4 | ||
|
bd3271aff0 | ||
|
0664f5a9ca | ||
|
c515ace328 | ||
|
8d4620dc53 | ||
|
16f65f669b | ||
|
2a2f7f783f | ||
|
6ae50389e6 | ||
|
87fd51d7ec | ||
|
7e43e5615e | ||
|
985f8778e9 | ||
|
3a180e2afc | ||
|
2f47bb0df6 | ||
|
7e0f0d9d11 | ||
|
e1f5866989 | ||
|
3c1ed0d9b2 | ||
|
10ab39c33b | ||
|
3072354ca5 | ||
|
14499cd6e5 | ||
|
5d3dc3348e | ||
|
ca2ff214c4 | ||
|
f8db285d5d | ||
|
1f880662d6 | ||
|
febab86682 | ||
|
8070dfef45 | ||
|
fc69f882c5 | ||
|
838a8e18d3 | ||
|
5e3e47b484 | ||
|
6d8512bda0 | ||
|
cd68cbd3ea | ||
|
55845c95bb | ||
|
a243ac4dde | ||
|
a01cbb42c7 | ||
|
b5da5760a2 | ||
|
c190b160e9 | ||
|
ce2e02b690 | ||
|
5dab09c42b | ||
|
03b08d67f0 | ||
|
5841c9a7a5 | ||
|
ed9b1bea3f | ||
|
dca348359b | ||
|
cf0759a48f | ||
|
c9df233d24 | ||
|
99a23b0414 | ||
|
95e0633b2f | ||
|
5ca210fa60 | ||
|
2ccdc419d0 | ||
|
9af0e705a5 | ||
|
0a3e40332a | ||
|
a758d18e51 | ||
|
f15d05b22f | ||
|
fc9f41b955 | ||
|
fd1eae4f07 | ||
|
51ee77b96f | ||
|
b03c5ff5ce | ||
|
521fed1fea | ||
|
679def0151 | ||
|
2560626419 | ||
|
e5024d5d0a | ||
|
c10c7619d3 | ||
|
dd04c432e9 | ||
|
b1fd3b8fc7 | ||
|
456df0fc19 | ||
|
526f493e12 | ||
|
5632ee6378 | ||
|
1680f00091 | ||
|
376b6f90d9 | ||
|
21c0195d29 | ||
|
56f845c71a | ||
|
d6d639d4d7 | ||
|
e1e1fd640c | ||
|
2408eeceba | ||
|
6ae194934d | ||
|
63ef0f1cee | ||
|
de2437cfec | ||
|
32e08f3510 | ||
|
40f21f41e1 | ||
|
ee12424795 | ||
|
0b48d5d0d2 | ||
|
080cf98e51 | ||
|
dc8d5ef744 | ||
|
70a02158e5 | ||
|
ab71dad51a | ||
|
0624cefc10 | ||
|
56b26421a5 | ||
|
ea8ba87aeb | ||
|
08b258a2cb | ||
|
ac486d3d1d | ||
|
e096bf6b62 | ||
|
e28b33b53b | ||
|
5814ba5322 | ||
|
be81ce244e | ||
|
d3a3aeb0fc | ||
|
fe6acdf4d2 | ||
|
702e0a461a | ||
|
46d6da4fce | ||
|
aa61835b78 | ||
|
2a1e46c8b6 | ||
|
cb4fb973b2 | ||
|
513f6e9a68 | ||
|
ad980334d1 | ||
|
d13d078351 | ||
|
947798b44c | ||
|
ed427616d4 | ||
|
297921182c | ||
|
31a5f3591f | ||
|
32655b5b16 | ||
|
8947f85ddd | ||
|
a513a05b7a | ||
|
1e716a93ff | ||
|
06fc2c505f | ||
|
6fcea91d1f | ||
|
93d099a2f0 | ||
|
29908098e4 | ||
|
e5983d96f7 | ||
|
08e6ae07af | ||
|
49b46a9a3f | ||
|
36c316f39c | ||
|
7e76abc067 | ||
|
702e301990 | ||
|
b1e11f3e88 | ||
|
09d5f59701 | ||
|
3c8675bb8b | ||
|
71ca237478 | ||
|
0e4b6d36fd | ||
|
e898080460 | ||
|
bdba7d3adf | ||
|
606b43dc51 | ||
|
2e7833df49 | ||
|
ec0d03658d | ||
|
992d4c1b94 | ||
|
d2d7cf14e5 | ||
|
e658712d53 | ||
|
40cd6ada4f | ||
|
c843c182e4 | ||
|
c35a8bdb15 | ||
|
dd0701dd16 | ||
|
32500773b8 | ||
|
e7d3f4316f | ||
|
438eec720a | ||
|
4b38d7368f | ||
|
dce6a86900 | ||
|
dc9c558c06 | ||
|
b8a466c571 | ||
|
bae28c5f57 | ||
|
1b21f0723f | ||
|
911c439858 | ||
|
f81f85cea2 | ||
|
1325cc5cd0 | ||
|
951d61bfcd | ||
|
0937cba870 | ||
|
5597d7633d | ||
|
502c88ee3f | ||
|
5ef6297daa | ||
|
9e33e23b8b | ||
|
16d00ccffb | ||
|
d211437d6c | ||
|
7996a42f76 | ||
|
f482e5e84a | ||
|
447c3567b4 | ||
|
3c5e6fe7f8 | ||
|
bf4a578bbb | ||
|
4cabea069d | ||
|
c53033a778 | ||
|
ea8642e2a1 | ||
|
73cea2d303 | ||
|
96a3468791 | ||
|
2065f4c003 | ||
|
9a931e4dc9 | ||
|
49ec62c757 | ||
|
a371f971fb | ||
|
5f9a84fc8b | ||
|
2461e36ed4 | ||
|
1305bf49a5 | ||
|
da0a16e122 | ||
|
fb10687168 | ||
|
f0d78471af | ||
|
a90b2a672e | ||
|
2bbb6fc427 | ||
|
2747e240c1 | ||
|
4b370930b5 | ||
|
c74918321d | ||
|
b05a5c818d | ||
|
41d22ef17e | ||
|
bbee63fcf3 | ||
|
b1ddd0e038 | ||
|
8c5dc3b5cb | ||
|
afa05329d9 | ||
|
dbbff393e1 | ||
|
f742671bbe | ||
|
0dae829080 | ||
|
e62a00a3f5 | ||
|
ab4c93dd2f | ||
|
ed5321999c | ||
|
fb21e3bb5c | ||
|
3595292f7f | ||
|
47fb6e036a | ||
|
92886c46ea | ||
|
83fa3f4cc8 | ||
|
c24f75ce0b | ||
|
63929b0341 | ||
|
fc7ec17905 | ||
|
e5a01c7cc8 | ||
|
0509b6fdb9 | ||
|
60d87f3c64 | ||
|
5d800ba5fe | ||
|
759d17547a | ||
|
d4f0a9ff62 | ||
|
c4fa96c41e | ||
|
f54136b602 | ||
|
5dd1728bf8 | ||
|
da1c9f48b7 | ||
|
0ec0e37532 | ||
|
544dc2eaa5 | ||
|
a3327c4430 | ||
|
f8ae972e70 | ||
|
3ff83fc1f8 | ||
|
63f65e5b2a | ||
|
3140a4e0cd | ||
|
31038e0e12 | ||
|
ac8e47579b | ||
|
ec0075e0d0 | ||
|
7900d266b1 | ||
|
c21597c593 | ||
|
ea418aa7d8 | ||
|
5487015a83 | ||
|
418cccd307 | ||
|
2a0760412c | ||
|
eebbe64b36 | ||
|
42d8e6d60d | ||
|
7ba907f261 | ||
|
c72769e2ea | ||
|
02d856b8a5 | ||
|
0d15ac8861 | ||
|
134a767a7f | ||
|
7403b6fb82 | ||
|
64a65cadf3 | ||
|
121eaced49 | ||
|
a488430f23 | ||
|
b5db753e11 | ||
|
b0aa27db31 | ||
|
512ed086bd | ||
|
76e35a09b7 | ||
|
d2c1d39d42 | ||
|
e9cccf6504 | ||
|
1c505903ff | ||
|
53ed8e04ae | ||
|
2112de6f15 | ||
|
be0845af02 | ||
|
f83a57b3da | ||
|
08264749f0 | ||
|
a75819cae3 | ||
|
9fb32a47ca | ||
|
4f43c9ebb4 | ||
|
9177982334 | ||
|
84b125bdde | ||
|
52eeff9f9f | ||
|
0fcccd35ff | ||
|
598dcf6b62 | ||
|
459200dd01 | ||
|
af22cabc6f | ||
|
920e82f11a | ||
|
520fcf82ae | ||
|
9bdf9e1e02 | ||
|
3a45f05e36 | ||
|
8e3e387be7 | ||
|
267d0b7b5a | ||
|
74d1d55051 | ||
|
3a8cb3f010 | ||
|
f5b290b093 | ||
|
d38d11f02e | ||
|
af04e92cf2 | ||
|
4ea1c98ac9 | ||
|
05333b9579 | ||
|
49cdb67ddc | ||
|
b5198e63c4 | ||
|
db007efe00 | ||
|
699cf71652 | ||
|
a0c02f62a3 | ||
|
ff7b814edc | ||
|
015f24a901 | ||
|
4fccde84bd | ||
|
ea459e9af0 | ||
|
2dd5a53db2 | ||
|
fc97ea7ee0 | ||
|
582d2540af | ||
|
6ad79dcd45 | ||
|
721896ba70 | ||
|
228270414c | ||
|
2683df7b5b | ||
|
3e61d1f233 | ||
|
04c07227f2 | ||
|
2e8d99c5b8 | ||
|
c07301473b | ||
|
b1ba42410b | ||
|
b80f89e3db | ||
|
edb15a9346 | ||
|
714a4d4f2d | ||
|
5c853766e8 | ||
|
3567ae88ad | ||
|
afcec56be4 | ||
|
d2435cf43b | ||
|
556f7608db | ||
|
a4df4b028e | ||
|
63683d35fc | ||
|
495344591f | ||
|
4e508499da | ||
|
326be29568 | ||
|
e4a3df3516 | ||
|
3506cbd5e9 | ||
|
ab13019bde | ||
|
ddc663eac0 | ||
|
fc7002fbab | ||
|
f2e53a3569 | ||
|
c5b4e589ff | ||
|
5e63ab619e | ||
|
c9bbfa1272 | ||
|
050968cbac | ||
|
8ca0d804d8 | ||
|
54e5a3607e | ||
|
cd947ae822 | ||
|
2477e18c87 | ||
|
ef08e8b8a0 | ||
|
f59bf16e82 | ||
|
118c31eb8d | ||
|
476f16f0aa | ||
|
b40d35b779 | ||
|
8e016cf672 | ||
|
7e482e9f8b | ||
|
6445befe87 | ||
|
86c099d629 | ||
|
79af433381 | ||
|
c0f1e74bed | ||
|
9df89e66e3 | ||
|
660375d6e4 | ||
|
498e8545b6 | ||
|
230c2e5cc2 | ||
|
3e60863e2d | ||
|
4592626bbb | ||
|
b980c87eff | ||
|
0f7c322623 | ||
|
76f42a3013 | ||
|
93b3d601d5 | ||
|
56329e89bb | ||
|
5c8b8149eb | ||
|
6075f7e8fd | ||
|
ddf53494f0 | ||
|
cd1f03d4f4 | ||
|
8474a61f21 | ||
|
4ad0ab5433 | ||
|
66d151df77 | ||
|
2045b250fd | ||
|
1dbee90d34 | ||
|
eb7a6d925b | ||
|
3678bd5a93 | ||
|
2d1a973ee5 | ||
|
322f7b2ad4 | ||
|
41aa2672cd | ||
|
f3090a452a | ||
|
52790d3c37 | ||
|
3677252e17 | ||
|
235d1d655d | ||
|
29bd6faa18 | ||
|
69c0f38305 | ||
|
0399d0c4d6 | ||
|
3db47f0adc | ||
|
483e2c43cf | ||
|
3e3b7238e0 | ||
|
532b5865de | ||
|
54b94f29e1 | ||
|
b67a7215f6 | ||
|
e424cc7608 | ||
|
229008e76a | ||
|
584f4bc596 | ||
|
1502d20def | ||
|
eecc2f4dd7 | ||
|
6fc110a71a | ||
|
ca6b46533a | ||
|
a1fe29347a | ||
|
449afea4fc | ||
|
6e5dd35ee3 | ||
|
0d5d14d41a | ||
|
3a42e457cf | ||
|
5b05c990b0 | ||
|
9df0a6208b | ||
|
3214904cc7 | ||
|
ec775a016a | ||
|
a2ca235fee | ||
|
de458b7357 | ||
|
7c039ca223 | ||
|
3942962ef5 | ||
|
675655d437 | ||
|
dafb14ff37 | ||
|
fc52d1cfba | ||
|
fdf2a68a11 | ||
|
3908ef611a | ||
|
e63db782c1 | ||
|
a6c6127e33 | ||
|
207d0bec78 | ||
|
1443c8d4c6 | ||
|
a136c46148 | ||
|
bbbc18fd84 | ||
|
2c7f6e4def | ||
|
dcd0cda0c6 | ||
|
ff16925f63 | ||
|
0b7aaa3643 | ||
|
44a244b1cb | ||
|
1dc6f39b55 | ||
|
45f52ca29c | ||
|
fae2d93525 | ||
|
25b74ce1f3 | ||
|
4957e498af | ||
|
54ca1abd2b | ||
|
8f2951b275 | ||
|
720bef97e6 | ||
|
c42f1b7a50 | ||
|
0186c31d59 | ||
|
58bf1a2ca5 | ||
|
4a31544024 | ||
|
cb6ec507e2 | ||
|
1ef93fead7 | ||
|
285ded6e49 | ||
|
6e4f5821dc | ||
|
a3df5b9a94 | ||
|
04f0ebf776 | ||
|
0e97a3becd | ||
|
77a0cef9ce | ||
|
143e9b6f9c | ||
|
06dcf8d8aa | ||
|
c315b4e064 | ||
|
73ca7ad0c1 | ||
|
d7f517fbf5 | ||
|
b10cb84f33 | ||
|
a55f0cabdd | ||
|
d73c7ccf50 | ||
|
2b35397169 | ||
|
416c367778 | ||
|
a20e90aa17 | ||
|
d698eba1e7 | ||
|
fe8e9414cf | ||
|
ed216bea4d | ||
|
3350b56057 | ||
|
4d71f682b3 | ||
|
607cda779d | ||
|
b61de07ca0 | ||
|
295ed76a1a | ||
|
7669f41e8e | ||
|
8da051789f | ||
|
30e0778ed2 | ||
|
7b1a256546 | ||
|
cc4879fb76 | ||
|
7c54a45950 | ||
|
73513f8371 | ||
|
dabf69abc7 | ||
|
8d3d5c068c | ||
|
cb1d0441e9 | ||
|
8d827f98da | ||
|
e5e46bf4ed | ||
|
9f32292473 | ||
|
7affeae480 | ||
|
b0f7b71453 | ||
|
c0c540dc09 | ||
|
7694ff1761 | ||
|
0d902671e5 | ||
|
fb90a7889a | ||
|
48c73d6a34 | ||
|
12e462f383 | ||
|
b7fe55b6be | ||
|
a1270d6cc7 | ||
|
f874c389bd | ||
|
8c5846c478 | ||
|
dce807a329 | ||
|
7928e6d0cd | ||
|
a98b726263 | ||
|
42ec4e4e98 | ||
|
635e3fb9a8 | ||
|
5f0b6fde92 | ||
|
04257afab7 | ||
|
b673969a0f | ||
|
c52c40f061 | ||
|
abdb5cc6cb | ||
|
4a6817c64b | ||
|
328611c619 | ||
|
f12c27aa7c | ||
|
e22c62baba | ||
|
6b1158235e | ||
|
efcaf64a43 | ||
|
f120301bc8 | ||
|
4da63c9237 | ||
|
97294df84f | ||
|
de42fc10b5 | ||
|
e5c6b0d4ea | ||
|
7c7ca7ef2b | ||
|
a813d32c53 | ||
|
2f18e20cb0 | ||
|
2ce2d63bda | ||
|
367e797d5f | ||
|
4fcf7bf2de | ||
|
e1d51b51f2 | ||
|
40b4032ea0 | ||
|
756aa82aa9 | ||
|
fe5a4a26f8 | ||
|
2171cb7f3d | ||
|
f55a09862e | ||
|
d0b21efd36 | ||
|
daf4258472 | ||
|
619bc95b2b | ||
|
76c2fa6d9a | ||
|
77bf3ac6ce | ||
|
0d7761f097 | ||
|
6c08d0b20b | ||
|
148400ae0a | ||
|
ac1657d86e | ||
|
332c314d53 | ||
|
5c8d386881 | ||
|
6f749c6414 | ||
|
a6b6e1d101 | ||
|
aa68cc2e63 | ||
|
5560ab28f2 | ||
|
f624449ccb | ||
|
69de5bb828 | ||
|
b54412e82e | ||
|
dd19fc3f3e | ||
|
dd436a689f | ||
|
ee06778cc2 | ||
|
b0c7fad81b | ||
|
0c28630948 | ||
|
198320be8a | ||
|
da8451c637 | ||
|
f54b8d8847 | ||
|
f4fb758629 | ||
|
b40fa61783 | ||
|
94cd9e5337 | ||
|
15c9fc4051 | ||
|
2b28607a4e | ||
|
683d5d5a48 | ||
|
4f92ef5fa9 | ||
|
44221fba49 | ||
|
63d7ed74f1 | ||
|
9012f2d6b1 | ||
|
09224e4b04 | ||
|
668e6fd610 | ||
|
62c3025a76 | ||
|
6e92c20edb | ||
|
60de577a5f | ||
|
af58faafae | ||
|
5adf74e6ce | ||
|
f4007a342c | ||
|
672234aaea | ||
|
f19eebd3cc | ||
|
37fb5298a0 | ||
|
4280af4844 | ||
|
d67e06037e | ||
|
4ce90a7eb4 | ||
|
4408c634b0 | ||
|
df351511de | ||
|
3b85dc9618 | ||
|
e511cfe2e4 | ||
|
d0f8c1834d | ||
|
d02bb28920 | ||
|
99861ac808 | ||
|
13ebd2c4e4 | ||
|
16c4807162 | ||
|
11aa4a6be0 | ||
|
cf7f0f878a | ||
|
09c07f45ee | ||
|
b5d205b78c | ||
|
ad6bf936d5 | ||
|
a6040c623b | ||
|
93a7af270f | ||
|
082fb166a2 | ||
|
dccc075f2c | ||
|
5fdec48854 | ||
|
fb51ebcba6 | ||
|
67e17def56 | ||
|
353bd3d06f | ||
|
a7495f711b | ||
|
e9d0a16a3b | ||
|
5072735866 | ||
|
1746ed6e1c | ||
|
664cd940c5 | ||
|
389536aff0 | ||
|
f6c6c2b2c0 | ||
|
18d90ecd96 | ||
|
70fdfeb926 | ||
|
8c271cf40c | ||
|
665aeb34b2 | ||
|
98f304f8b0 | ||
|
7a5d2a3bd9 | ||
|
f4d62d3342 | ||
|
54df7b0a3c | ||
|
9795a7c4a9 | ||
|
1557fda588 | ||
|
1e7f34c271 | ||
|
d71e8ab7c9 | ||
|
3b4c8ba439 | ||
|
336dd1d5ba | ||
|
a474e196ea | ||
|
101aefbfe8 | ||
|
e04ebaa364 | ||
|
bb4de11c51 | ||
|
a20a5f1a44 | ||
|
aab7043d45 | ||
|
ee6d28b25e | ||
|
ef504f3eba | ||
|
86407871e6 | ||
|
76bb2ef60c | ||
|
beec65938e | ||
|
1c764052f7 | ||
|
d501c0786f | ||
|
322c329c6f | ||
|
7c430e5c9d | ||
|
94b2b6393f | ||
|
4a1d20e8a3 | ||
|
8762e5160d | ||
|
c33348e80c | ||
|
0c90f6afa2 | ||
|
115d42e0f0 | ||
|
6e43ab5897 | ||
|
8988c8f9af | ||
|
aa21351d0d | ||
|
97109db82b | ||
|
8bb625adb7 | ||
|
ea2d65f8bb | ||
|
1cf09d91bb | ||
|
cf2b97b656 | ||
|
2e8cbd81b4 | ||
|
b498c7bcbb | ||
|
e78843bdca | ||
|
2eaf3136f9 | ||
|
6b6ab9fe6d | ||
|
f35b9a4509 | ||
|
349ce004f8 | ||
|
1b63c95c4e | ||
|
c80d53e7e5 | ||
|
eb2028e0fa | ||
|
03689251c5 | ||
|
85c08312be | ||
|
16288d171c | ||
|
87044c54f4 | ||
|
a4e8d3cb36 | ||
|
dce6356d75 | ||
|
c24e74efe3 | ||
|
60e247862a | ||
|
c796cd2250 | ||
|
c296a4a967 | ||
|
24192a3797 | ||
|
f84d947115 | ||
|
9544dece07 | ||
|
6c4d7fd377 | ||
|
8d467ddd61 | ||
|
db28ee1ff7 | ||
|
e378cb410c | ||
|
144eee7fbf | ||
|
72e702a15a | ||
|
6b7be462b8 | ||
|
4329d393e6 | ||
|
4f52691f71 | ||
|
c132d71684 | ||
|
8410f61c73 | ||
|
cac76a182e | ||
|
5b0e93552c | ||
|
5eebd04d43 | ||
|
6f4aefffe7 | ||
|
377c219fd9 | ||
|
da3d814c8b | ||
|
4461ecfed1 | ||
|
bd676922c3 | ||
|
49356cadd4 | ||
|
c02f222005 | ||
|
d3977ce40e | ||
|
7283d7eb2f | ||
|
48252d284e | ||
|
807dc46ad0 | ||
|
0837ec9b70 | ||
|
b380522df8 | ||
|
c127d34d32 | ||
|
bc0b97d5d8 | ||
|
431abe79f3 | ||
|
125470f110 | ||
|
4f669bdd66 | ||
|
8930236396 | ||
|
b3c9a50ead | ||
|
4d0aee67be | ||
|
b501c6d5bf | ||
|
7dcee38b21 | ||
|
903c63ac13 | ||
|
a98c9f99d1 | ||
|
7f085df240 | ||
|
b5ae141fb6 | ||
|
7eb866ffee | ||
|
61e59d74e0 | ||
|
5f50d2e230 | ||
|
3f1484480e | ||
|
2d3fc613ec | ||
|
e2982185d6 | ||
|
bdf4c6723f | ||
|
1d4f10bead | ||
|
aac3e2d4fb | ||
|
87dd6badac | ||
|
1b6c7af3eb | ||
|
5c091a1871 | ||
|
fb3839e096 | ||
|
eef3ca0295 | ||
|
c9dc0226fd | ||
|
1a7a3a4233 | ||
|
d2e458f673 | ||
|
e0f265db15 | ||
|
39a3cefc21 | ||
|
89db08eb93 | ||
|
f40cf2cd8e | ||
|
50bb69b796 | ||
|
a7d7c2b98b | ||
|
8dfc0d9dda | ||
|
0e6dce7093 | ||
|
ddbf4470a1 | ||
|
829649e905 | ||
|
bc063ad773 | ||
|
ef38810425 | ||
|
5ccca8d708 | ||
|
89919dbe36 | ||
|
ecd51a1428 | ||
|
4cb9eec257 | ||
|
78097b96c9 | ||
|
2af8589afd | ||
|
cf1ace3a73 | ||
|
efcc9d51d4 | ||
|
9b9f4be6a4 | ||
|
a87c104172 | ||
|
028683666d | ||
|
b2c59be8de | ||
|
2685e06528 | ||
|
a99673122e | ||
|
ba49012447 | ||
|
fe8b090911 | ||
|
c4a38de007 | ||
|
407eda0ba0 | ||
|
5b1dc0bfbd | ||
|
772b260b37 | ||
|
bd75eddc8e | ||
|
00db3a0922 | ||
|
2bcc1b7fb4 | ||
|
433c848c8d | ||
|
abdb3b9475 | ||
|
9761161163 | ||
|
e5104021b1 | ||
|
9ef4f47ba0 | ||
|
3bbc88f89a | ||
|
bfa61c8f67 | ||
|
3bdeb75cc2 | ||
|
ca9eaf383a | ||
|
42a8d84a1f | ||
|
3fd330c2fb | ||
|
8f340afca1 | ||
|
e28d9426b9 | ||
|
b3078b75cd | ||
|
424b97994e | ||
|
f30a52c2dc | ||
|
1db22f4a1b | ||
|
424e2a9439 | ||
|
2ee2e29262 | ||
|
7afd2dbd20 | ||
|
cdb2446e32 | ||
|
ac8c9215cd | ||
|
dfca01e469 | ||
|
ca1d980746 | ||
|
587d3f9012 | ||
|
e30ab07439 | ||
|
e6e026f420 | ||
|
2036518813 | ||
|
7536f5e83c | ||
|
229402594f | ||
|
97873ddb5d | ||
|
dbf303d5d6 | ||
|
7346b3e326 | ||
|
93cf947e2a | ||
|
c37ad5c8bf | ||
|
5a3e325742 | ||
|
c5ec12cd56 | ||
|
3410541a2f | ||
|
80a68de91b | ||
|
1f39083555 | ||
|
5f8fb6c226 | ||
|
d66dd01438 | ||
|
6d3bad1ae0 | ||
|
8b8b1427f6 | ||
|
e2d971f20e | ||
|
9d17e8826b | ||
|
531c581cd5 | ||
|
f790b9aa54 | ||
|
8f000423ed | ||
|
4990f6c22d | ||
|
d447a50b73 | ||
|
cbecfad4df | ||
|
770a7f11a7 | ||
|
27a65f8745 | ||
|
5cd06c03f0 | ||
|
43e5092c46 | ||
|
a239e3fba6 | ||
|
743d772a80 | ||
|
1f734630b9 | ||
|
355fe6195e | ||
|
d22bd5b42d | ||
|
5327ce543b | ||
|
3747eb59ea | ||
|
2b00ab3432 | ||
|
a6cdd701e2 | ||
|
c8984e6a6a | ||
|
9179aa52cf | ||
|
2042fdf3bd | ||
|
d1c3372dc4 | ||
|
3884a68889 | ||
|
0ec84ec597 | ||
|
6a9d21e9aa | ||
|
a829d44b51 | ||
|
554e3e9e6e | ||
|
904b3b5b0b | ||
|
14bdc0e57a | ||
|
02bdc1dcb9 | ||
|
7be2db6e86 | ||
|
b586ae2f25 | ||
|
d0ed814669 | ||
|
8492a702b2 | ||
|
0048156379 | ||
|
cb3328dca3 | ||
|
e7b7ae94b0 | ||
|
17ce295c30 | ||
|
4e9166759d | ||
|
d5e3bb1b6d | ||
|
7e4e5ec6e4 | ||
|
f2656e62dc | ||
|
83de97e547 | ||
|
b552efe770 | ||
|
1663c7c8e7 | ||
|
1a6bef1a7e | ||
|
ff31e75ccc | ||
|
c87a37f804 | ||
|
76ead096aa | ||
|
668ff71470 | ||
|
538d5e8be4 | ||
|
b2b142a037 | ||
|
3ebed4ff40 | ||
|
a2cd69b654 | ||
|
cfc14671ed | ||
|
ed4b2f74ff | ||
|
dd53be7a1b | ||
|
c83d7916c9 | ||
|
0865962f8d | ||
|
9691085bc2 | ||
|
b243d1c599 | ||
|
db6e404bda | ||
|
6f63e24dbb | ||
|
0082fe8173 | ||
|
06d37b2a94 | ||
|
48f11900d3 | ||
|
230cd28ac9 | ||
|
86261f2b0a | ||
|
30ad00fa65 | ||
|
33a1499bdd | ||
|
211fa18ac2 | ||
|
4c5250e850 | ||
|
788024685f | ||
|
b5f07d2995 | ||
|
8d7af21ff3 | ||
|
dce9278193 | ||
|
c6e783e7c3 | ||
|
c8fa059064 | ||
|
29efac3e5e | ||
|
027d313df5 | ||
|
ea78808e74 | ||
|
6f6f999129 | ||
|
b16ebd529b | ||
|
25deecd405 | ||
|
2471f893e7 | ||
|
17480abe85 | ||
|
bfde17b4d7 | ||
|
76263a9610 | ||
|
855468e776 | ||
|
beceea9421 | ||
|
dabc139fab | ||
|
41aea2e336 | ||
|
f929346c18 | ||
|
e699662b1e | ||
|
90057318c8 | ||
|
6f2eaf3009 | ||
|
e8fc16dc09 | ||
|
0f1911ba68 | ||
|
94699fbe00 | ||
|
a380317e2c | ||
|
64bcdd4398 | ||
|
56e0580aa5 | ||
|
7f0c9c239e | ||
|
e0a1592e6e | ||
|
3d784a14f9 | ||
|
47a9b086ea | ||
|
e70c8a7b46 | ||
|
673351d821 | ||
|
4b966f1f82 | ||
|
93626de01c | ||
|
7847b7685d | ||
|
255e88fbf6 | ||
|
685c6dc00c | ||
|
8e18d37b3d | ||
|
b4c7b90c9e | ||
|
b55be9fdea | ||
|
401b3afa3b | ||
|
7fa3537015 | ||
|
149ed91afb | ||
|
887826ee68 | ||
|
7357d5eae2 | ||
|
e4e2a188c5 | ||
|
e40e3af760 | ||
|
24a2788081 | ||
|
1388266102 | ||
|
43af0b051f | ||
|
6e8138e19b | ||
|
fb8edd86d5 | ||
|
34be181706 | ||
|
fcc1109e76 | ||
|
2b828765e3 | ||
|
25f4c23ab2 | ||
|
be90b20a5d | ||
|
232c113dae | ||
|
605a9b2817 | ||
|
d044c0f4cc | ||
|
1959e1fd44 | ||
|
6712423dd1 | ||
|
3689990bd5 | ||
|
81a1f618f9 | ||
|
b77bb690de | ||
|
f843f260ee | ||
|
770b3739e0 | ||
|
261e7c1744 | ||
|
10acbb8d92 | ||
|
a917115a85 | ||
|
b8ed6f1588 | ||
|
3ed57e01a6 | ||
|
cb7c5a8ca1 | ||
|
07eb9c5970 | ||
|
306e5081d9 | ||
|
259c7adc81 | ||
|
af9762cf32 | ||
|
17554202f6 | ||
|
0d9cf697fa | ||
|
df0dd2f5e6 | ||
|
38508f9a9c | ||
|
b113972bcf | ||
|
72e67bf4e9 | ||
|
a20a6636b4 | ||
|
da8aa2d8e4 | ||
|
602a2ea541 | ||
|
fd24b1898e | ||
|
89150e1164 | ||
|
e1831c4c60 | ||
|
4ec90c5c0d | ||
|
a8c73f7baf | ||
|
6fed76a687 | ||
|
84de444325 | ||
|
0fbd87ca87 | ||
|
99797502eb | ||
|
16bd0b9ca8 | ||
|
5fdfa963f4 | ||
|
1d86e71331 | ||
|
9e3f549341 | ||
|
2895ad21f3 | ||
|
5731ae7f47 | ||
|
51f7d9a07f | ||
|
6be390c795 | ||
|
0f32de4aa2 | ||
|
5d01452648 | ||
|
51b0508512 | ||
|
4c5e7a238d | ||
|
f327b7b499 | ||
|
306e86c9c6 | ||
|
9024f1b444 | ||
|
fc26e8c194 | ||
|
ffd8e5667c | ||
|
9299c3abc7 | ||
|
88ebac942e | ||
|
63a07fe6cf | ||
|
c2d440a914 | ||
|
2b5c7f9e91 | ||
|
91e63dea47 | ||
|
cd164de776 | ||
|
c0ef5ce512 | ||
|
7c852fbf33 | ||
|
28500989bc | ||
|
75c99a0491 | ||
|
8b4ba3cb67 | ||
|
3ef2971c3f | ||
|
a5aa8c6006 | ||
|
022d14abe1 | ||
|
1800b0b69c | ||
|
c39a550b00 | ||
|
092aa8fa6d | ||
|
f75f73f3d2 | ||
|
e3627e9cba | ||
|
d5f4934acf | ||
|
693bd7e110 | ||
|
4d8dcdc623 | ||
|
8e97af8dc3 | ||
|
4dc448056c | ||
|
68c349bbfa | ||
|
75aedc8e94 | ||
|
8b08f89d2c | ||
|
889b38f75a | ||
|
a17ac23457 | ||
|
6fdd48509e | ||
|
62800116d3 | ||
|
1bccbf061b | ||
|
093658836e | ||
|
f49800e56a | ||
|
e478dbeb85 | ||
|
51486b18fa | ||
|
48d98dcf45 | ||
|
2c7cfd1c68 | ||
|
7a4b4c941c | ||
|
608ccb0ca1 | ||
|
3f6ea04048 | ||
|
74c5ec70a9 | ||
|
c8bf8e896a | ||
|
09cc1161c9 | ||
|
8ab33db51a | ||
|
cc4258bf9d | ||
|
0ee5d3d83f | ||
|
c39aa5e857 | ||
|
39aae4167e | ||
|
9db9143366 | ||
|
06df6017df | ||
|
49814b92fe | ||
|
260b5d6b0d | ||
|
4360ca14c1 | ||
|
c7d336f958 | ||
|
f6436663eb | ||
|
84d7c65039 | ||
|
4245096be4 | ||
|
c9b2a07bc7 | ||
|
e69d4cba88 | ||
|
96962dd21f | ||
|
36d48224b5 | ||
|
15b5433f1a | ||
|
53779d6ceb | ||
|
e7e268b3bd | ||
|
ca2f76fe1f | ||
|
4d44ab9628 | ||
|
dd62051e6c | ||
|
fdb1701d1b | ||
|
80b35575df | ||
|
69cf05df9a | ||
|
69a1817c3f | ||
|
a918dcd5a4 | ||
|
adc9a65ae3 | ||
|
1e779f7135 | ||
|
fe68e9e243 | ||
|
890d02638b | ||
|
e9792b446f | ||
|
4012599264 | ||
|
429b1d8574 | ||
|
a34876d700 | ||
|
68ecf78f0e | ||
|
11f04a453e | ||
|
7baa752a9d |
@@ -1,3 +1,5 @@
|
||||
dist/
|
||||
!dist/traefik
|
||||
site/
|
||||
vendor/
|
||||
.idea/
|
||||
|
24
.github/CODEOWNERS
vendored
@@ -1,24 +0,0 @@
|
||||
provider/kubernetes/** @containous/kubernetes
|
||||
provider/rancher/** @containous/rancher
|
||||
provider/marathon/** @containous/marathon
|
||||
provider/docker/** @containous/docker
|
||||
|
||||
docs/user-guide/kubernetes.md @containous/kubernetes
|
||||
docs/user-guide/marathon.md @containous/marathon
|
||||
docs/user-guide/swarm.md @containous/docker
|
||||
docs/user-guide/swarm-mode.md @containous/docker
|
||||
|
||||
docs/configuration/backends/docker.md @containous/docker
|
||||
docs/configuration/backends/kubernetes.md @containous/kubernetes
|
||||
docs/configuration/backends/marathon.md @containous/marathon
|
||||
docs/configuration/backends/rancher.md @containous/rancher
|
||||
|
||||
examples/k8s/ @containous/kubernetes
|
||||
examples/compose-k8s.yaml @containous/kubernetes
|
||||
examples/k8s.namespace.yaml @containous/kubernetes
|
||||
examples/compose-rancher.yml @containous/rancher
|
||||
examples/compose-marathon.yml @containous/marathon
|
||||
|
||||
vendor/github.com/gambol99/go-marathon @containous/marathon
|
||||
vendor/github.com/rancher @containous/rancher
|
||||
vendor/k8s.io/ @containous/kubernetes
|
32
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,29 +1,23 @@
|
||||
<!-- PLEASE FOLLOW THE ISSUE TEMPLATE TO HELP TRIAGE AND SUPPORT! -->
|
||||
|
||||
### Do you want to request a *feature* or report a *bug*?
|
||||
|
||||
<!--
|
||||
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
|
||||
- the Traefik community forum: https://community.traefik.io/
|
||||
|
||||
-->
|
||||
|
||||
|
||||
### Do you want to request a *feature* or report a *bug*?
|
||||
|
||||
<!--
|
||||
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
|
||||
Bug
|
||||
|
||||
<!--
|
||||
|
||||
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/.
|
||||
The configurations between 1.X and 2.X are NOT compatible.
|
||||
Please have a look here https://doc.traefik.io/traefik/getting-started/configuration-overview/.
|
||||
|
||||
-->
|
||||
|
||||
@@ -31,7 +25,7 @@ If you just checked the "Yes" box, be aware that this is probably not a bug. The
|
||||
|
||||
<!--
|
||||
|
||||
HOW TO WRITE A GOOD ISSUE?
|
||||
HOW TO WRITE A GOOD BUG REPORT?
|
||||
|
||||
- Respect the issue template as much as possible.
|
||||
- The title should be short and descriptive.
|
||||
@@ -53,13 +47,12 @@ HOW TO WRITE A GOOD ISSUE?
|
||||
### Output of `traefik version`: (_What version of Traefik are you using?_)
|
||||
|
||||
<!--
|
||||
`latest` is not considered as a valid version.
|
||||
|
||||
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
|
||||
-->
|
||||
|
||||
```
|
||||
@@ -71,12 +64,13 @@ For the alpine Traefik Docker image:
|
||||
```toml
|
||||
# (paste your configuration here)
|
||||
```
|
||||
|
||||
<!--
|
||||
Add more configuration information here.
|
||||
-->
|
||||
|
||||
|
||||
### If applicable, please paste the log output at DEBUG level (`--log.level=DEBUG` switch)
|
||||
### If applicable, please paste the log output in DEBUG level (`--log.level=DEBUG` switch)
|
||||
|
||||
```
|
||||
(paste your output here)
|
||||
|
88
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
@@ -1,88 +0,0 @@
|
||||
---
|
||||
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
@@ -1,36 +0,0 @@
|
||||
---
|
||||
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
|
||||
|
||||
-->
|
80
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
name: Bug Report (Traefik)
|
||||
description: Create a report to help us improve.
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
attributes:
|
||||
label: Welcome!
|
||||
description: |
|
||||
The issue tracker is for reporting bugs and feature requests only. For end-user related support questions, please refer to one of the following:
|
||||
- the Traefik community forum: https://community.traefik.io/
|
||||
|
||||
The configurations between 1.X and 2.X are NOT compatible. Please have a look [here](https://doc.traefik.io/traefik/getting-started/configuration-overview/).
|
||||
|
||||
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
|
||||
options:
|
||||
- label: Yes, I've searched similar issues on [GitHub](https://github.com/traefik/traefik/issues) and didn't find any.
|
||||
required: true
|
||||
- label: Yes, I've searched similar issues on the [Traefik community forum](https://community.traefik.io) and didn't find any.
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: What did you do?
|
||||
description: |
|
||||
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)
|
||||
placeholder: What did you do?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: What did you see instead?
|
||||
placeholder: What did you see instead?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: What version of Traefik are you using?
|
||||
description: |
|
||||
`latest` is not considered as a valid version.
|
||||
|
||||
Output of `traefik version`.
|
||||
|
||||
For the Traefik Docker image (`docker run [IMAGE] version`), example:
|
||||
```console
|
||||
$ docker run traefik version
|
||||
```
|
||||
placeholder: Paste your output here.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: What is your environment & configuration?
|
||||
description: arguments, toml, provider, platform, ...
|
||||
placeholder: Add information here.
|
||||
value: |
|
||||
```yaml
|
||||
# (paste your configuration here)
|
||||
```
|
||||
|
||||
Add more configuration information here.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: If applicable, please paste the log output in DEBUG level
|
||||
description: "`--log.level=DEBUG` switch."
|
||||
placeholder: Paste your output here.
|
||||
validations:
|
||||
required: false
|
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Traefik Community Support
|
||||
url: https://community.traefik.io/
|
||||
about: If you have a question, or are looking for advice, please post on our Discuss forum! The community loves to chime in to help. Happy Coding!
|
||||
- name: Traefik Helm Chart Issues
|
||||
url: https://github.com/traefik/traefik-helm-chart
|
||||
about: Are you submitting an issue or feature enhancement for the Traefik helm chart? Please post in the traefik-helm-chart GitHub Issues.
|
33
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: Feature Request (Traefik)
|
||||
description: Suggest an idea for this project.
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
attributes:
|
||||
label: Welcome!
|
||||
description: |
|
||||
The issue tracker is for reporting bugs and feature requests only. For end-user related support questions, please refer to one of the following:
|
||||
- the Traefik community forum: https://community.traefik.io/
|
||||
|
||||
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
|
||||
options:
|
||||
- label: Yes, I've searched similar issues on [GitHub](https://github.com/traefik/traefik/issues) and didn't find any.
|
||||
required: true
|
||||
- label: Yes, I've searched similar issues on the [Traefik community forum](https://community.traefik.io) and didn't find any.
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: What did you expect to see?
|
||||
description: |
|
||||
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)
|
||||
placeholder: What did you expect to see?
|
||||
validations:
|
||||
required: true
|
23
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,18 +1,19 @@
|
||||
<!--
|
||||
PLEASE READ THIS MESSAGE.
|
||||
|
||||
HOW TO WRITE A GOOD PULL REQUEST?
|
||||
Documentation fixes or enhancements:
|
||||
- for Traefik v1: use branch v1.7
|
||||
- for Traefik v2: use branch v2.6
|
||||
|
||||
- Make it small.
|
||||
- Do only one thing.
|
||||
- Avoid re-formatting.
|
||||
- Make sure the code builds.
|
||||
- Make sure all tests pass.
|
||||
- Add tests.
|
||||
- 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/CONTRIBUTING.md.
|
||||
Bug fixes:
|
||||
- for Traefik v1: use branch v1.7
|
||||
- for Traefik v2: use branch v2.6
|
||||
|
||||
Enhancements:
|
||||
- for Traefik v1: we only accept bug fixes
|
||||
- for Traefik v2: use branch master
|
||||
|
||||
HOW TO WRITE A GOOD PULL REQUEST? https://doc.traefik.io/traefik/contributing/submitting-pull-requests/
|
||||
|
||||
-->
|
||||
|
||||
|
79
.github/workflows/build.yaml
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
name: Build Binaries
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
env:
|
||||
GO_VERSION: 1.17
|
||||
CGO_ENABLED: 0
|
||||
IN_DOCKER: ""
|
||||
|
||||
jobs:
|
||||
|
||||
build-webui:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Build webui
|
||||
run: |
|
||||
make clean-webui generate-webui
|
||||
tar czvf webui.tar.gz ./webui/static/
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: webui.tar.gz
|
||||
path: webui.tar.gz
|
||||
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-20.04, macos-latest, windows-latest ]
|
||||
needs:
|
||||
- build-webui
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
|
||||
|
||||
steps:
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: go/src/github.com/traefik/traefik
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
~/.cache/go-build
|
||||
~/Library/Caches/go-build
|
||||
'%LocalAppData%\go-build'
|
||||
key: ${{ runner.os }}-build-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: ${{ runner.os }}-build-go-
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: webui.tar.gz
|
||||
path: ${{ github.workspace }}/go/src/github.com/traefik/traefik
|
||||
|
||||
- name: Untar webui
|
||||
run: tar xvf webui.tar.gz
|
||||
|
||||
- name: Build
|
||||
run: make binary
|
21
.github/workflows/check_doc.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
name: Check Documentation
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
|
||||
docs:
|
||||
name: Check, verify and build documentation
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check documentation
|
||||
run: make docs-pull-images docs
|
52
.github/workflows/documentation.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
name: Build and Publish Documentation
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- v*
|
||||
|
||||
env:
|
||||
STRUCTOR_VERSION: v1.11.2
|
||||
MIXTUS_VERSION: v0.4.1
|
||||
|
||||
jobs:
|
||||
|
||||
docs:
|
||||
name: Doc Process
|
||||
runs-on: ubuntu-20.04
|
||||
if: github.repository == 'traefik/traefik'
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Install Structor ${{ env.STRUCTOR_VERSION }}
|
||||
run: curl -sSfL https://raw.githubusercontent.com/traefik/structor/master/godownloader.sh | sh -s -- -b $HOME/bin ${STRUCTOR_VERSION}
|
||||
|
||||
- name: Install Seo-doc
|
||||
run: curl -sSfL https://raw.githubusercontent.com/traefik/seo-doc/master/godownloader.sh | sh -s -- -b "${HOME}/bin"
|
||||
|
||||
- name: Install Mixtus ${{ env.MIXTUS_VERSION }}
|
||||
run: curl -sSfL https://raw.githubusercontent.com/traefik/mixtus/master/godownloader.sh | sh -s -- -b $HOME/bin ${MIXTUS_VERSION}
|
||||
|
||||
- name: Build documentation
|
||||
run: $HOME/bin/structor -o traefik -r traefik --dockerfile-url="https://raw.githubusercontent.com/traefik/traefik/v1.7/docs.Dockerfile" --menu.js-url="https://raw.githubusercontent.com/traefik/structor/master/traefik-menu.js.gotmpl" --rqts-url="https://raw.githubusercontent.com/traefik/structor/master/requirements-override.txt" --force-edit-url --exp-branch=master --debug
|
||||
env:
|
||||
STRUCTOR_LATEST_TAG: ${{ secrets.STRUCTOR_LATEST_TAG }}
|
||||
|
||||
- name: Apply seo
|
||||
run: $HOME/bin/seo -path=./site -product=traefik
|
||||
|
||||
- name: Publish documentation
|
||||
run: $HOME/bin/mixtus --dst-doc-path="./traefik" --dst-owner=traefik --dst-repo-name=doc --git-user-email="30906710+traefiker@users.noreply.github.com" --git-user-name=traefiker --src-doc-path="./site" --src-owner=containous --src-repo-name=traefik
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_REPO }}
|
37
.github/workflows/experimental.yaml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: Build experimental image on branch
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- v*
|
||||
|
||||
jobs:
|
||||
|
||||
experimental:
|
||||
if: github.repository == 'traefik/traefik'
|
||||
name: Build experimental image on branch
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
|
||||
# https://github.com/marketplace/actions/checkout
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Branch name
|
||||
run: echo ${GITHUB_REF##*/}
|
||||
|
||||
- name: Build docker experimental image
|
||||
run: docker build -t traefik/traefik:experimental-${GITHUB_REF##*/} -f exp.Dockerfile .
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Push to Docker Hub
|
||||
run: docker push traefik/traefik:experimental-${GITHUB_REF##*/}
|
46
.github/workflows/test-unit.yaml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
name: Test Unit
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
env:
|
||||
GO_VERSION: 1.17
|
||||
IN_DOCKER: ""
|
||||
|
||||
jobs:
|
||||
|
||||
test-unit:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
|
||||
|
||||
steps:
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: go/src/github.com/traefik/traefik
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
~/.cache/go-build
|
||||
key: ${{ runner.os }}-test-unit-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: ${{ runner.os }}-test-unit-go-
|
||||
|
||||
- name: Avoid generating webui
|
||||
run: touch webui/static/index.html
|
||||
|
||||
- name: Tests
|
||||
run: make test-unit
|
97
.github/workflows/validate.yaml
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
name: Validate
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
env:
|
||||
GO_VERSION: 1.17
|
||||
GOLANGCI_LINT_VERSION: v1.46.2
|
||||
MISSSPELL_VERSION: v0.3.4
|
||||
IN_DOCKER: ""
|
||||
|
||||
jobs:
|
||||
|
||||
validate:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
|
||||
|
||||
steps:
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: go/src/github.com/traefik/traefik
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
~/.cache/go-build
|
||||
key: ${{ runner.os }}-validate-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: ${{ runner.os }}-validate-go-
|
||||
|
||||
- name: Install golangci-lint ${{ env.GOLANGCI_LINT_VERSION }}
|
||||
run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCI_LINT_VERSION}
|
||||
|
||||
- name: Install missspell ${{ env.MISSSPELL_VERSION }}
|
||||
run: curl -sfL https://raw.githubusercontent.com/client9/misspell/master/install-misspell.sh | sh -s -- -b $(go env GOPATH)/bin ${MISSSPELL_VERSION}
|
||||
|
||||
- name: Avoid generating webui
|
||||
run: touch webui/static/index.html
|
||||
|
||||
- name: Validate
|
||||
run: make validate
|
||||
|
||||
validate-generate:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
|
||||
|
||||
steps:
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: go/src/github.com/traefik/traefik
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
~/.cache/go-build
|
||||
key: ${{ runner.os }}-validate-generate-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: ${{ runner.os }}-validate-generate-go-
|
||||
|
||||
- name: go generate
|
||||
run: |
|
||||
go generate
|
||||
git diff --exit-code
|
||||
|
||||
- name: go mod tidy
|
||||
run: |
|
||||
go mod tidy
|
||||
git diff --exit-code
|
||||
|
||||
- name: make generate-crd
|
||||
run: |
|
||||
make generate-crd
|
||||
git diff --exit-code
|
5
.gitignore
vendored
@@ -7,7 +7,6 @@
|
||||
/webui/.tmp/
|
||||
/site/
|
||||
/docs/site/
|
||||
/static/
|
||||
/autogen/
|
||||
/traefik
|
||||
/traefik.toml
|
||||
@@ -15,3 +14,7 @@
|
||||
*.log
|
||||
*.exe
|
||||
cover.out
|
||||
vendor/
|
||||
plugins-storage/
|
||||
plugins-local/
|
||||
traefik_changelog.md
|
||||
|
182
.golangci.toml
@@ -1,6 +1,9 @@
|
||||
[run]
|
||||
deadline = "10m"
|
||||
timeout = "10m"
|
||||
skip-files = []
|
||||
skip-dirs = [
|
||||
"pkg/provider/kubernetes/crd/generated/",
|
||||
]
|
||||
|
||||
[linters-settings]
|
||||
|
||||
@@ -13,9 +16,6 @@
|
||||
[linters-settings.gocyclo]
|
||||
min-complexity = 14.0
|
||||
|
||||
[linters-settings.maligned]
|
||||
suggest-new = true
|
||||
|
||||
[linters-settings.goconst]
|
||||
min-len = 3.0
|
||||
min-occurrences = 4.0
|
||||
@@ -23,21 +23,142 @@
|
||||
[linters-settings.misspell]
|
||||
locale = "US"
|
||||
|
||||
[linters-settings.funlen]
|
||||
lines = 230 # default 60
|
||||
statements = 120 # default 40
|
||||
|
||||
[linters-settings.forbidigo]
|
||||
forbid = [
|
||||
'^print(ln)?$',
|
||||
'^spew\.Print(f|ln)?$',
|
||||
'^spew\.Dump$',
|
||||
]
|
||||
|
||||
[linters-settings.depguard]
|
||||
list-type = "blacklist"
|
||||
include-go-root = false
|
||||
packages = ["github.com/pkg/errors"]
|
||||
|
||||
[linters-settings.godox]
|
||||
keywords = ["FIXME"]
|
||||
|
||||
[linters-settings.importas]
|
||||
corev1 = "k8s.io/api/core/v1"
|
||||
networkingv1beta1 = "k8s.io/api/networking/v1beta1"
|
||||
extensionsv1beta1 = "k8s.io/api/extensions/v1beta1"
|
||||
metav1 = "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
kubeerror = "k8s.io/apimachinery/pkg/api/errors"
|
||||
composeapi = "github.com/docker/compose/v2/pkg/api"
|
||||
|
||||
[linters-settings.revive]
|
||||
[[linters-settings.revive.rules]]
|
||||
name = "struct-tag"
|
||||
[[linters-settings.rules]]
|
||||
name = "blank-imports"
|
||||
[[linters-settings.rules]]
|
||||
name = "context-as-argument"
|
||||
[[linters-settings.rules]]
|
||||
name = "context-keys-type"
|
||||
[[linters-settings.rules]]
|
||||
name = "dot-imports"
|
||||
[[linters-settings.rules]]
|
||||
name = "error-return"
|
||||
[[linters-settings.rules]]
|
||||
name = "error-strings"
|
||||
[[linters-settings.rules]]
|
||||
name = "error-naming"
|
||||
[[linters-settings.rules]]
|
||||
name = "exported"
|
||||
[[linters-settings.rules]]
|
||||
name = "if-return"
|
||||
[[linters-settings.rules]]
|
||||
name = "increment-decrement"
|
||||
[[linters-settings.rules]]
|
||||
name = "var-naming"
|
||||
[[linters-settings.rules]]
|
||||
name = "var-declaration"
|
||||
[[linters-settings.rules]]
|
||||
name = "package-comments"
|
||||
[[linters-settings.rules]]
|
||||
name = "range"
|
||||
[[linters-settings.rules]]
|
||||
name = "receiver-naming"
|
||||
[[linters-settings.rules]]
|
||||
name = "time-naming"
|
||||
[[linters-settings.rules]]
|
||||
name = "unexported-return"
|
||||
[[linters-settings.rules]]
|
||||
name = "indent-error-flow"
|
||||
[[linters-settings.rules]]
|
||||
name = "errorf"
|
||||
[[linters-settings.rules]]
|
||||
name = "empty-block"
|
||||
[[linters-settings.rules]]
|
||||
name = "superfluous-else"
|
||||
[[linters-settings.rules]]
|
||||
name = "unused-parameter"
|
||||
[[linters-settings.rules]]
|
||||
name = "unreachable-code"
|
||||
[[linters-settings.rules]]
|
||||
name = "redefines-builtin-id"
|
||||
|
||||
[linters-settings.gomoddirectives]
|
||||
replace-allow-list = [
|
||||
"github.com/abbot/go-http-auth",
|
||||
"github.com/go-check/check",
|
||||
"github.com/gorilla/mux",
|
||||
"github.com/mailgun/minheap",
|
||||
"github.com/mailgun/multibuf",
|
||||
"github.com/jaguilar/vt100",
|
||||
]
|
||||
|
||||
[linters]
|
||||
enable-all = true
|
||||
disable = [
|
||||
"scopelint", # Deprecated
|
||||
"interfacer", # Deprecated
|
||||
"maligned", # Deprecated
|
||||
"golint", # Deprecated
|
||||
"execinquery", # Not relevant (SQL)
|
||||
"sqlclosecheck", # Not relevant (SQL)
|
||||
"rowserrcheck", # Not relevant (SQL)
|
||||
"lll", # Not relevant
|
||||
"gocyclo", # FIXME must be fixed
|
||||
"gosec",
|
||||
"dupl",
|
||||
"maligned",
|
||||
"lll",
|
||||
"unparam",
|
||||
"prealloc",
|
||||
"scopelint",
|
||||
"cyclop", # Duplicate of gocyclo
|
||||
"gocognit", # Too strict
|
||||
"nestif", # Too many false-positive.
|
||||
"prealloc", # Too many false-positive.
|
||||
"makezero", # Not relevant
|
||||
"ifshort", # Not relevant
|
||||
"dupl", # Too strict
|
||||
"gosec", # Too strict
|
||||
"gochecknoinits",
|
||||
"gochecknoglobals",
|
||||
# uncomment when the CI will be updated
|
||||
# "bodyclose", # Too many false-positive and panics.
|
||||
"wsl", # Too strict
|
||||
"nlreturn", # Not relevant
|
||||
"gomnd", # Too strict
|
||||
"stylecheck", # skip because report issues related to some generated files.
|
||||
"testpackage", # Too strict
|
||||
"tparallel", # Not relevant
|
||||
"paralleltest", # Not relevant
|
||||
"exhaustive", # Not relevant
|
||||
"exhaustivestruct", # Not relevant
|
||||
"exhaustruct", # duplicate of exhaustivestruct
|
||||
"goerr113", # Too strict
|
||||
"wrapcheck", # Too strict
|
||||
"noctx", # Too strict
|
||||
"bodyclose", # Too many false-positive and panics.
|
||||
"unparam", # Too strict
|
||||
"godox", # Too strict
|
||||
"forcetypeassert", # Too strict
|
||||
"tagliatelle", # Not compatible with current tags.
|
||||
"varnamelen", # not relevant
|
||||
"nilnil", # not relevant
|
||||
"ireturn", # not relevant
|
||||
"contextcheck", # too many false-positive
|
||||
"containedctx", # too many false-positive
|
||||
"maintidx", # kind of duplicate of gocyclo
|
||||
"nonamedreturns", # not relevant
|
||||
]
|
||||
|
||||
[issues]
|
||||
@@ -45,13 +166,18 @@
|
||||
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",
|
||||
"SA1019: http.CloseNotifier has been deprecated", # FIXME must be fixed
|
||||
"SA1019: cfg.SSLRedirect is deprecated",
|
||||
"SA1019: cfg.SSLTemporaryRedirect is deprecated",
|
||||
"SA1019: cfg.SSLHost is deprecated",
|
||||
"SA1019: cfg.SSLForceHost is deprecated",
|
||||
"SA1019: cfg.FeaturePolicy is deprecated",
|
||||
]
|
||||
[[issues.exclude-rules]]
|
||||
path = ".+_test.go"
|
||||
linters = ["goconst"]
|
||||
path = "(.+)_test.go"
|
||||
linters = ["goconst", "funlen", "godot"]
|
||||
[[issues.exclude-rules]]
|
||||
path = "integration/.+_test.go"
|
||||
text = "Error return value of `cmd\\.Process\\.Kill` is not checked"
|
||||
@@ -64,24 +190,30 @@
|
||||
[[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"
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/server/middleware/middlewares.go"
|
||||
text = "Function 'buildConstructor' has too many statements"
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/tracing/haystack/logger.go"
|
||||
linters = ["goprintffuncname"]
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/tracing/tracing.go"
|
||||
text = "printf-like formatting function 'SetErrorWithEvent' should be named 'SetErrorWithEventf'"
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/log/deprecated.go"
|
||||
linters = ["godot"]
|
||||
[[issues.exclude-rules]]
|
||||
path = "(.+)\\.go"
|
||||
text = "struct-tag: unknown option 'inline' in JSON tag"
|
||||
|
@@ -7,12 +7,13 @@ before:
|
||||
builds:
|
||||
- binary: traefik
|
||||
|
||||
main: ./cmd/traefik/traefik.go
|
||||
main: ./cmd/traefik/
|
||||
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}}
|
||||
|
||||
- -s -w -X github.com/traefik/traefik/v2/pkg/version.Version={{.Version}} -X github.com/traefik/traefik/v2/pkg/version.Codename={{.Env.CODENAME}} -X github.com/traefik/traefik/v2/pkg/version.BuildDate={{.Date}}
|
||||
flags:
|
||||
- -trimpath
|
||||
goos:
|
||||
- linux
|
||||
- darwin
|
||||
@@ -25,6 +26,7 @@ builds:
|
||||
- arm
|
||||
- arm64
|
||||
- ppc64le
|
||||
- s390x
|
||||
goarm:
|
||||
- 7
|
||||
- 6
|
||||
@@ -34,22 +36,24 @@ builds:
|
||||
goarch: 386
|
||||
- goos: openbsd
|
||||
goarch: arm
|
||||
- goos: openbsd
|
||||
goarch: arm64
|
||||
- goos: freebsd
|
||||
goarch: arm
|
||||
goarch: arm64
|
||||
|
||||
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
|
||||
archives:
|
||||
- id: traefik
|
||||
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"
|
||||
|
83
.semaphore/semaphore.yml
Normal file
@@ -0,0 +1,83 @@
|
||||
version: v1.0
|
||||
name: Traefik
|
||||
agent:
|
||||
machine:
|
||||
type: e1-standard-4
|
||||
os_image: ubuntu1804
|
||||
|
||||
fail_fast:
|
||||
stop:
|
||||
when: "branch != 'master'"
|
||||
|
||||
auto_cancel:
|
||||
queued:
|
||||
when: "branch != 'master'"
|
||||
running:
|
||||
when: "branch != 'master'"
|
||||
|
||||
global_job_config:
|
||||
prologue:
|
||||
commands:
|
||||
- curl -sSfL https://raw.githubusercontent.com/ldez/semgo/master/godownloader.sh | sudo sh -s -- -b "/usr/local/bin"
|
||||
- sudo semgo go1.17
|
||||
- export "GOPATH=$(go env GOPATH)"
|
||||
- export "SEMAPHORE_GIT_DIR=${GOPATH}/src/github.com/traefik/${SEMAPHORE_PROJECT_NAME}"
|
||||
- export "PATH=${GOPATH}/bin:${PATH}"
|
||||
- mkdir -vp "${SEMAPHORE_GIT_DIR}" "${GOPATH}/bin"
|
||||
- export GOPROXY=https://proxy.golang.org,direct
|
||||
- curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b "${GOPATH}/bin" v1.46.2
|
||||
- curl -sSfL https://gist.githubusercontent.com/traefiker/6d7ac019c11d011e4f131bb2cca8900e/raw/goreleaser.sh | bash -s -- -b "${GOPATH}/bin"
|
||||
- checkout
|
||||
- cache restore traefik-$(checksum go.sum)
|
||||
|
||||
blocks:
|
||||
- name: Test Integration
|
||||
dependencies: []
|
||||
run:
|
||||
when: "branch =~ '.*' OR pull_request =~'.*'"
|
||||
task:
|
||||
jobs:
|
||||
- name: Test Integration
|
||||
commands:
|
||||
- make pull-images
|
||||
- touch webui/static/index.html # Avoid generating webui
|
||||
- IN_DOCKER="" make binary
|
||||
- make test-integration
|
||||
- df -h
|
||||
epilogue:
|
||||
always:
|
||||
commands:
|
||||
- cache store traefik-$(checksum go.sum) $HOME/go/pkg/mod
|
||||
|
||||
- name: Release
|
||||
dependencies: []
|
||||
run:
|
||||
when: "tag =~ '.*'"
|
||||
task:
|
||||
agent:
|
||||
machine:
|
||||
type: e1-standard-8
|
||||
os_image: ubuntu1804
|
||||
secrets:
|
||||
- name: traefik
|
||||
env_vars:
|
||||
- name: GH_VERSION
|
||||
value: 1.12.1
|
||||
- name: CODENAME
|
||||
value: "rocamadour"
|
||||
- name: IN_DOCKER
|
||||
value: ""
|
||||
prologue:
|
||||
commands:
|
||||
- export VERSION=${SEMAPHORE_GIT_TAG_NAME}
|
||||
- curl -sSL -o /tmp/gh_${GH_VERSION}_linux_amd64.tar.gz https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_amd64.tar.gz
|
||||
- tar -zxvf /tmp/gh_${GH_VERSION}_linux_amd64.tar.gz -C /tmp
|
||||
- sudo mv /tmp/gh_${GH_VERSION}_linux_amd64/bin/gh /usr/local/bin/gh
|
||||
- sudo rm -rf ~/.phpbrew ~/.kerl ~/.sbt ~/.nvm ~/.npm ~/.kiex /usr/lib/jvm /opt/az /opt/firefox # Remove unnecessary data.
|
||||
- sudo service docker stop && sudo umount /var/lib/docker && sudo service docker start # Unmounts the docker disk and the whole system disk is usable.
|
||||
jobs:
|
||||
- name: Release
|
||||
commands:
|
||||
- make release-packages
|
||||
- gh release create ${SEMAPHORE_GIT_TAG_NAME} ./dist/traefik*.* --repo traefik/traefik --title ${SEMAPHORE_GIT_TAG_NAME} --notes ${SEMAPHORE_GIT_TAG_NAME}
|
||||
- ./script/deploy.sh
|
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
sudo rm -rf static
|
@@ -1,20 +0,0 @@
|
||||
#!/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
|
@@ -1,6 +0,0 @@
|
||||
#!/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
|
@@ -1,8 +0,0 @@
|
||||
#!/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,17 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
export DOCKER_VERSION=17.03.1
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
source .semaphoreci/vars
|
||||
|
||||
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,36 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
export REPO='containous/traefik'
|
||||
|
||||
if VERSION=$(git describe --exact-match --abbrev=0 --tags);
|
||||
then
|
||||
export VERSION
|
||||
else
|
||||
export VERSION=''
|
||||
fi
|
||||
|
||||
export CODENAME=faisselle
|
||||
|
||||
export N_MAKE_JOBS=2
|
||||
|
||||
|
||||
function ci_retry {
|
||||
|
||||
local NRETRY=3
|
||||
local NSLEEP=5
|
||||
local n=0
|
||||
|
||||
until [ $n -ge $NRETRY ]
|
||||
do
|
||||
"$@" && break
|
||||
n=$((n+1))
|
||||
echo "${*} failed, attempt ${n}/${NRETRY}"
|
||||
sleep $NSLEEP
|
||||
done
|
||||
|
||||
[ $n -lt $NRETRY ]
|
||||
|
||||
}
|
||||
|
||||
export -f ci_retry
|
57
.travis.yml
@@ -1,57 +0,0 @@
|
||||
sudo: required
|
||||
dist: trusty
|
||||
|
||||
git:
|
||||
depth: false
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
env:
|
||||
global:
|
||||
- REPO: $TRAVIS_REPO_SLUG
|
||||
- VERSION: $TRAVIS_TAG
|
||||
- CODENAME: faisselle
|
||||
|
||||
script:
|
||||
- echo "Skipping tests... (Tests are executed on SemaphoreCI)"
|
||||
- if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then make docs; fi
|
||||
|
||||
before_deploy:
|
||||
- >
|
||||
if ! [ "$BEFORE_DEPLOY_RUN" ]; then
|
||||
export BEFORE_DEPLOY_RUN=1;
|
||||
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 build-image;
|
||||
if [ "$TRAVIS_TAG" ]; then
|
||||
make release-packages;
|
||||
fi;
|
||||
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}
|
||||
file: dist/traefik*
|
||||
skip_cleanup: true
|
||||
file_glob: true
|
||||
on:
|
||||
repo: containous/traefik
|
||||
tags: true
|
||||
- provider: script
|
||||
script: sh script/deploy.sh
|
||||
skip_cleanup: true
|
||||
on:
|
||||
repo: containous/traefik
|
||||
tags: true
|
||||
- provider: pages
|
||||
edge: false
|
||||
github_token: ${GITHUB_TOKEN}
|
||||
local_dir: site
|
||||
skip_cleanup: true
|
||||
on:
|
||||
repo: containous/traefik
|
||||
all_branches: true
|
8259
CHANGELOG.md
@@ -2,7 +2,7 @@
|
||||
|
||||
## 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
|
||||
|
||||
@@ -30,15 +30,19 @@ Project maintainers have the right and responsibility to remove, edit, or reject
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or our 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
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at contact@traefik.io
|
||||
|
||||
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.
|
||||
@@ -48,4 +52,4 @@ Project maintainers who do not follow or enforce the Code of Conduct in good fai
|
||||
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/
|
||||
|
@@ -1,3 +1,9 @@
|
||||
# Contributing
|
||||
|
||||
See <https://docs.traefik.io/v2.0/contributing/thank-you/>.
|
||||
Here are some guidelines that should help to start contributing to the project.
|
||||
|
||||
- [Submitting pull Requests](https://github.com/traefik/contributors-guide/blob/master/pr_guidelines.md)
|
||||
- [Submitting issues](https://doc.traefik.io/traefik/contributing/submitting-issues/)
|
||||
- [Submitting security issues](docs/content/contributing/submitting-security-issues.md)
|
||||
|
||||
If you are willing to become a maintainer of the project, please take a look at the [maintainers guidelines](docs/content/contributing/maintainers-guidelines.md).
|
||||
|
2351
Gopkg.lock
generated
288
Gopkg.toml
@@ -1,288 +0,0 @@
|
||||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
|
||||
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/NYTimes/gziphandler"
|
||||
|
||||
[[constraint]]
|
||||
branch = "containous-fork"
|
||||
name = "github.com/abbot/go-http-auth"
|
||||
source = "github.com/containous/go-http-auth"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/armon/go-proxyproto"
|
||||
|
||||
#[[constraint]]
|
||||
# name = "github.com/aws/aws-sdk-go"
|
||||
# version = "1.13.11"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/cenkalti/backoff"
|
||||
version = "2.1.1"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/containous/mux"
|
||||
|
||||
[[constraint]]
|
||||
branch = "containous-fork"
|
||||
name = "github.com/containous/alice"
|
||||
|
||||
#[[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]]
|
||||
name = "github.com/eapache/channels"
|
||||
version = "1.1.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/elazarl/go-bindata-assetfs"
|
||||
|
||||
[[constraint]]
|
||||
branch = "fork-containous"
|
||||
name = "github.com/go-check/check"
|
||||
source = "github.com/containous/check"
|
||||
|
||||
[[override]]
|
||||
branch = "fork-containous"
|
||||
name = "github.com/go-check/check"
|
||||
source = "github.com/containous/check"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/go-kit/kit"
|
||||
version = "0.7.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/gorilla/websocket"
|
||||
|
||||
#[[constraint]]
|
||||
# name = "github.com/hashicorp/consul"
|
||||
# version = "1.0.6"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/influxdata/influxdb"
|
||||
version = "1.3.7"
|
||||
|
||||
#[[constraint]]
|
||||
# branch = "master"
|
||||
# name = "github.com/jjcollinge/servicefabric"
|
||||
|
||||
#[[constraint]]
|
||||
# branch = "master"
|
||||
# name = "github.com/abronan/valkeyrie"
|
||||
|
||||
#[[constraint]]
|
||||
# name = "github.com/mesosphere/mesos-dns"
|
||||
# source = "https://github.com/containous/mesos-dns.git"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/opentracing/opentracing-go"
|
||||
version = "1.0.2"
|
||||
|
||||
[[constraint]]
|
||||
branch = "containous-fork"
|
||||
name = "github.com/rancher/go-rancher-metadata"
|
||||
source = "github.com/containous/go-rancher-metadata"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/ryanuber/go-glob"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/Masterminds/sprig"
|
||||
version = "2.19.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/stvp/go-udp-testing"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/stretchr/testify"
|
||||
version = "1.2.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/uber/jaeger-client-go"
|
||||
source = "github.com/jaegertracing/jaeger-client-go"
|
||||
version = "2.16.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/uber/jaeger-lib"
|
||||
source = "github.com/jaegertracing/jaeger-lib"
|
||||
version = "2.0.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "v1"
|
||||
name = "github.com/unrolled/secure"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/vdemeester/shakers"
|
||||
version = "0.1.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/vulcand/oxy"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/go-acme/lego"
|
||||
version = "2.6.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "google.golang.org/grpc"
|
||||
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"
|
||||
source = "github.com/fsnotify/fsnotify"
|
||||
version = "1.4.2"
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/client-go"
|
||||
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.11.7" # "kubernetes-1.11.7"
|
||||
|
||||
[[constraint]]
|
||||
name = "k8s.io/apimachinery"
|
||||
version = "kubernetes-1.11.7" # "kubernetes-1.11.7"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/json-iterator/go"
|
||||
version = "1.1.6"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/libkermit/docker"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/libkermit/docker-check"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "github.com/libkermit/compose"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/docker/docker"
|
||||
revision = "7848b8beb9d38a98a78b75f78e05f8d2255f9dfe"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/docker/docker"
|
||||
revision = "7848b8beb9d38a98a78b75f78e05f8d2255f9dfe"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/docker/cli"
|
||||
revision = "6b63d7b96a41055baddc3fa71f381c7f60bd5d8e"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/docker/distribution"
|
||||
revision = "edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c"
|
||||
|
||||
[[override]]
|
||||
branch = "master"
|
||||
name = "github.com/docker/libcompose"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/Nvveen/Gotty"
|
||||
revision = "a8b993ba6abdb0e0c12b0125c603323a71c7790c"
|
||||
source = "github.com/ijc25/Gotty"
|
||||
|
||||
[[override]]
|
||||
# ALWAYS keep this override
|
||||
name = "github.com/mailgun/timetools"
|
||||
revision = "7e6055773c5137efbeb3bd2410d705fe10ab6bfd"
|
||||
|
||||
[[override]]
|
||||
version = "v1.1.1"
|
||||
name = "github.com/miekg/dns"
|
||||
|
||||
[[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"
|
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2018 Containous SAS
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2022 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
180
Makefile
@@ -1,5 +1,3 @@
|
||||
.PHONY: all docs docs-serve
|
||||
|
||||
SRCS = $(shell git ls-files '*.go' | grep -v '^vendor/')
|
||||
|
||||
TAG_NAME := $(shell git tag -l --contains HEAD)
|
||||
@@ -7,15 +5,13 @@ 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")
|
||||
TRAEFIK_IMAGE := $(if $(REPONAME),$(REPONAME),"traefik/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")
|
||||
INTEGRATION_OPTS := $(if $(MAKE_DOCKER_HOST),-e "DOCKER_HOST=$(MAKE_DOCKER_HOST)",-v "/var/run/docker.sock:/var/run/docker.sock")
|
||||
DOCKER_BUILD_ARGS := $(if $(DOCKER_VERSION), "--build-arg=DOCKER_VERSION=$(DOCKER_VERSION)",)
|
||||
|
||||
TRAEFIK_ENVS := \
|
||||
@@ -29,132 +25,188 @@ TRAEFIK_ENVS := \
|
||||
-e CI \
|
||||
-e CONTAINER=DOCKER # Indicator for integration tests that we are running inside a container.
|
||||
|
||||
TRAEFIK_MOUNT := -v "$(CURDIR)/$(BIND_DIR):/go/src/github.com/containous/traefik/$(BIND_DIR)"
|
||||
TRAEFIK_MOUNT := -v "$(CURDIR)/dist:/go/src/github.com/traefik/traefik/dist"
|
||||
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_NON_INTERACTIVE ?= false
|
||||
DOCKER_RUN_TRAEFIK := docker run $(INTEGRATION_OPTS) $(if $(DOCKER_NON_INTERACTIVE), , -it) $(DOCKER_RUN_OPTS)
|
||||
DOCKER_RUN_TRAEFIK_TEST := docker run --add-host=host.docker.internal:127.0.0.1 --rm --name=traefik --network traefik-test-network -v $(PWD):$(PWD) -w $(PWD) $(INTEGRATION_OPTS) $(if $(DOCKER_NON_INTERACTIVE), , -it) $(DOCKER_RUN_OPTS)
|
||||
DOCKER_RUN_TRAEFIK_NOTTY := docker run $(INTEGRATION_OPTS) $(if $(DOCKER_NON_INTERACTIVE), , -i) $(DOCKER_RUN_OPTS)
|
||||
|
||||
PRE_TARGET ?= build-dev-image
|
||||
IN_DOCKER ?= true
|
||||
|
||||
PLATFORM_URL := $(if $(PLATFORM_URL),$(PLATFORM_URL),"https://pilot.traefik.io")
|
||||
|
||||
.PHONY: default
|
||||
default: binary
|
||||
|
||||
## Build Dev Docker image
|
||||
build-dev-image: dist
|
||||
docker build $(DOCKER_BUILD_ARGS) -t "$(TRAEFIK_DEV_IMAGE)" -f build.Dockerfile .
|
||||
|
||||
## Build Dev Docker image without cache
|
||||
build-dev-image-no-cache: dist
|
||||
docker build --no-cache -t "$(TRAEFIK_DEV_IMAGE)" -f build.Dockerfile .
|
||||
|
||||
## Create the "dist" directory
|
||||
dist:
|
||||
mkdir dist
|
||||
mkdir -p dist
|
||||
|
||||
## Build Dev Docker image
|
||||
.PHONY: build-dev-image
|
||||
build-dev-image: dist
|
||||
ifneq ("$(IN_DOCKER)", "")
|
||||
docker build $(DOCKER_BUILD_ARGS) -t "$(TRAEFIK_DEV_IMAGE)" --build-arg HOST_PWD="$(PWD)" -f build.Dockerfile .
|
||||
endif
|
||||
|
||||
## Build Dev Docker image without cache
|
||||
.PHONY: build-dev-image-no-cache
|
||||
build-dev-image-no-cache: dist
|
||||
ifneq ("$(IN_DOCKER)", "")
|
||||
docker build $(DOCKER_BUILD_ARGS) --no-cache -t "$(TRAEFIK_DEV_IMAGE)" --build-arg HOST_PWD="$(PWD)" -f build.Dockerfile .
|
||||
endif
|
||||
|
||||
## Build WebUI Docker image
|
||||
.PHONY: build-webui-image
|
||||
build-webui-image:
|
||||
docker build -t traefik-webui -f webui/Dockerfile webui
|
||||
docker build -t traefik-webui --build-arg ARG_PLATFORM_URL=$(PLATFORM_URL) -f webui/Dockerfile webui
|
||||
|
||||
## Clean WebUI static generated assets
|
||||
.PHONY: clean-webui
|
||||
clean-webui:
|
||||
rm -r webui/static
|
||||
mkdir -p webui/static
|
||||
printf 'For more information see `webui/readme.md`' > webui/static/DONT-EDIT-FILES-IN-THIS-DIRECTORY.md
|
||||
|
||||
## 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
|
||||
webui/static/index.html:
|
||||
$(MAKE) build-webui-image
|
||||
docker run --rm -v "$(PWD)/webui/static":'/src/webui/static' traefik-webui npm run build:nc
|
||||
docker run --rm -v "$(PWD)/webui/static":'/src/webui/static' traefik-webui chown -R $(shell id -u):$(shell id -g) ./static
|
||||
|
||||
## Build the linux binary
|
||||
binary: generate-webui $(PRE_TARGET)
|
||||
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate binary
|
||||
.PHONY: generate-webui
|
||||
generate-webui: webui/static/index.html
|
||||
|
||||
## Build the binary for the standard plaforms (linux, darwin, windows)
|
||||
## Build the binary
|
||||
.PHONY: binary
|
||||
binary: generate-webui build-dev-image
|
||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate binary
|
||||
|
||||
## Build the linux binary locally
|
||||
.PHONY: binary-debug
|
||||
binary-debug: generate-webui
|
||||
GOOS=linux ./script/make.sh binary
|
||||
|
||||
## Build the binary for the standard platforms (linux, darwin, windows)
|
||||
.PHONY: crossbinary-default
|
||||
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
|
||||
## Build the binary for the standard platforms (linux, darwin, windows) in parallel
|
||||
.PHONY: crossbinary-default-parallel
|
||||
crossbinary-default-parallel:
|
||||
$(MAKE) generate-webui
|
||||
$(MAKE) build-dev-image crossbinary-default
|
||||
|
||||
## Run the unit and integration tests
|
||||
.PHONY: test
|
||||
test: build-dev-image
|
||||
$(DOCKER_RUN_TRAEFIK) ./script/make.sh generate test-unit binary test-integration
|
||||
-docker network create traefik-test-network --driver bridge --subnet 172.31.42.0/24
|
||||
trap 'docker network rm traefik-test-network' EXIT; \
|
||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_TEST),) ./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
|
||||
.PHONY: test-unit
|
||||
test-unit: build-dev-image
|
||||
-docker network create traefik-test-network --driver bridge --subnet 172.31.42.0/24
|
||||
trap 'docker network rm traefik-test-network' EXIT; \
|
||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_TEST)) ./script/make.sh generate test-unit
|
||||
|
||||
## 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
|
||||
.PHONY: test-integration
|
||||
test-integration: build-dev-image
|
||||
-docker network create traefik-test-network --driver bridge --subnet 172.31.42.0/24
|
||||
trap 'docker network rm traefik-test-network' EXIT; \
|
||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_TEST),) ./script/make.sh generate binary test-integration
|
||||
|
||||
## Pull all images for integration tests
|
||||
.PHONY: pull-images
|
||||
pull-images:
|
||||
grep --no-filename -E '^\s+image:' ./integration/resources/compose/*.yml \
|
||||
| awk '{print $$2}' \
|
||||
| sort \
|
||||
| uniq \
|
||||
| xargs -P 6 -n 1 docker pull
|
||||
|
||||
## Validate code and docs
|
||||
validate-files: $(PRE_TARGET)
|
||||
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate validate-lint validate-misspell
|
||||
.PHONY: validate-files
|
||||
validate-files: build-dev-image
|
||||
$(if $(IN_DOCKER),$(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
|
||||
.PHONY: validate
|
||||
validate: build-dev-image
|
||||
$(if $(IN_DOCKER),$(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
|
||||
.PHONY: build-image
|
||||
build-image: clean-webui binary
|
||||
docker build -t $(TRAEFIK_IMAGE) .
|
||||
|
||||
## Build a Docker Traefik image
|
||||
## Build a Docker Traefik image without re-building the webui
|
||||
.PHONY: build-image-dirty
|
||||
build-image-dirty: binary
|
||||
docker build -t $(TRAEFIK_IMAGE) .
|
||||
|
||||
## Locally build traefik for linux, then shove it an alpine image, with basic tools.
|
||||
.PHONY: build-image-debug
|
||||
build-image-debug: binary-debug
|
||||
docker build -t $(TRAEFIK_IMAGE) -f debug.Dockerfile .
|
||||
|
||||
## Start a shell inside the build env
|
||||
.PHONY: shell
|
||||
shell: build-dev-image
|
||||
$(DOCKER_RUN_TRAEFIK) /bin/bash
|
||||
|
||||
## Build documentation site
|
||||
.PHONY: docs
|
||||
docs:
|
||||
make -C ./docs docs
|
||||
|
||||
## Serve the documentation site localy
|
||||
## Serve the documentation site locally
|
||||
.PHONY: docs-serve
|
||||
docs-serve:
|
||||
make -C ./docs docs-serve
|
||||
|
||||
## Generate CRD clientset
|
||||
## Pull image for doc building
|
||||
.PHONY: docs-pull-images
|
||||
docs-pull-images:
|
||||
make -C ./docs docs-pull-images
|
||||
|
||||
## Generate CRD clientset and CRD manifests
|
||||
.PHONY: generate-crd
|
||||
generate-crd:
|
||||
./script/update-generated-crd-code.sh
|
||||
@$(CURDIR)/script/code-gen.sh
|
||||
|
||||
## Download dependencies
|
||||
dep-ensure:
|
||||
dep ensure -v
|
||||
./script/prune-dep.sh
|
||||
|
||||
## Clean vendor directory
|
||||
dep-prune:
|
||||
./script/prune-dep.sh
|
||||
## Generate code from dynamic configuration https://github.com/traefik/genconf
|
||||
.PHONY: generate-genconf
|
||||
generate-genconf:
|
||||
go run ./cmd/internal/gen/
|
||||
|
||||
## Create packages for the release
|
||||
.PHONY: release-packages
|
||||
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 \
|
||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) goreleaser release --skip-publish --timeout="90m"
|
||||
$(if $(IN_DOCKER),$(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/
|
||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) chown -R $(shell id -u):$(shell id -g) dist/
|
||||
|
||||
## Format the Code
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
gofmt -s -l -w $(SRCS)
|
||||
|
||||
.PHONY: run-dev
|
||||
run-dev:
|
||||
go generate
|
||||
go build ./cmd/traefik
|
||||
GO111MODULE=on go build ./cmd/traefik
|
||||
./traefik
|
||||
|
90
README.md
@@ -4,11 +4,10 @@
|
||||
</p>
|
||||
|
||||
[](https://semaphoreci.com/containous/traefik)
|
||||
[](https://docs.traefik.io)
|
||||
[](http://goreportcard.com/report/containous/traefik)
|
||||
[](https://microbadger.com/images/traefik)
|
||||
[](https://github.com/containous/traefik/blob/master/LICENSE.md)
|
||||
[](https://slack.traefik.io)
|
||||
[](https://doc.traefik.io/traefik)
|
||||
[](https://goreportcard.com/report/traefik/traefik)
|
||||
[](https://github.com/traefik/traefik/blob/master/LICENSE.md)
|
||||
[](https://community.traefik.io/)
|
||||
[](https://twitter.com/intent/follow?screen_name=traefik)
|
||||
|
||||
|
||||
@@ -33,7 +32,7 @@ Pointing Traefik at your orchestrator should be the _only_ configuration step yo
|
||||
|
||||
---
|
||||
|
||||
: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/).
|
||||
: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 running v2, please ensure you are using a [v2 configuration](https://doc.traefik.io/traefik/).
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -64,59 +63,49 @@ _(But if you'd rather configure some of your routes manually, Traefik supports t
|
||||
- Keeps access logs (JSON, CLF)
|
||||
- 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
|
||||
- Packaged as a single binary file (made with :heart: with go) and available as an [official](https://hub.docker.com/r/_/traefik/) docker image
|
||||
|
||||
|
||||
## Supported Backends
|
||||
|
||||
- [Docker](https://docs.traefik.io/configuration/backends/docker) / [Swarm mode](https://docs.traefik.io/configuration/backends/docker#docker-swarm-mode)
|
||||
- [Kubernetes](https://docs.traefik.io/configuration/backends/kubernetes)
|
||||
- [Mesos](https://docs.traefik.io/configuration/backends/mesos) / [Marathon](https://docs.traefik.io/configuration/backends/marathon)
|
||||
- [Rancher](https://docs.traefik.io/configuration/backends/rancher) (API, Metadata)
|
||||
- [Azure Service Fabric](https://docs.traefik.io/configuration/backends/servicefabric)
|
||||
- [Consul Catalog](https://docs.traefik.io/configuration/backends/consulcatalog)
|
||||
- [Consul](https://docs.traefik.io/configuration/backends/consul) / [Etcd](https://docs.traefik.io/configuration/backends/etcd) / [Zookeeper](https://docs.traefik.io/configuration/backends/zookeeper) / [BoltDB](https://docs.traefik.io/configuration/backends/boltdb)
|
||||
- [Eureka](https://docs.traefik.io/configuration/backends/eureka)
|
||||
- [Amazon ECS](https://docs.traefik.io/configuration/backends/ecs)
|
||||
- [Amazon DynamoDB](https://docs.traefik.io/configuration/backends/dynamodb)
|
||||
- [File](https://docs.traefik.io/configuration/backends/file)
|
||||
- [Rest](https://docs.traefik.io/configuration/backends/rest)
|
||||
- [Docker](https://doc.traefik.io/traefik/providers/docker/) / [Swarm mode](https://doc.traefik.io/traefik/providers/docker/)
|
||||
- [Kubernetes](https://doc.traefik.io/traefik/providers/kubernetes-crd/)
|
||||
- [Marathon](https://doc.traefik.io/traefik/providers/marathon/)
|
||||
- [Rancher](https://doc.traefik.io/traefik/providers/rancher/) (Metadata)
|
||||
- [File](https://doc.traefik.io/traefik/providers/file/)
|
||||
|
||||
## Quickstart
|
||||
|
||||
To get your hands on Traefik, you can use the [5-Minute Quickstart](http://docs.traefik.io/#the-traefik-quickstart-using-docker) in our documentation (you will need Docker).
|
||||
To get your hands on Traefik, you can use the [5-Minute Quickstart](https://doc.traefik.io/traefik/getting-started/quick-start/) in our documentation (you will need Docker).
|
||||
|
||||
## Web UI
|
||||
|
||||
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 Traefik can be found at [https://awesome.traefik.io](https://awesome.traefik.io).
|
||||
You can find the complete documentation of Traefik v2 at [https://doc.traefik.io/traefik/](https://doc.traefik.io/traefik/).
|
||||
|
||||
:warning: If you're testing out v2, please ensure you are using the [v2 documentation](https://docs.traefik.io/v2.0/).
|
||||
A collection of contributions around Traefik can be found at [https://awesome.traefik.io](https://awesome.traefik.io).
|
||||
|
||||
## Support
|
||||
|
||||
To get community support, you can:
|
||||
- join the Traefik community Slack channel: [](https://slack.traefik.io)
|
||||
- use [Stack Overflow](https://stackoverflow.com/questions/tagged/traefik) (using the `traefik` tag)
|
||||
- join the Traefik community forum: [](https://community.traefik.io/)
|
||||
|
||||
If you need commercial support, please contact [Containo.us](https://containo.us) by mail: <mailto:support@containo.us>.
|
||||
If you need commercial support, please contact [Traefik.io](https://traefik.io) by mail: <mailto:support@traefik.io>.
|
||||
|
||||
## Download
|
||||
|
||||
- Grab the latest binary from the [releases](https://github.com/containous/traefik/releases) page and run it with the [sample configuration file](https://raw.githubusercontent.com/containous/traefik/master/traefik.sample.toml):
|
||||
- Grab the latest binary from the [releases](https://github.com/traefik/traefik/releases) page and run it with the [sample configuration file](https://raw.githubusercontent.com/traefik/traefik/master/traefik.sample.toml):
|
||||
|
||||
```shell
|
||||
./traefik --configFile=traefik.toml
|
||||
```
|
||||
|
||||
- Or use the official tiny Docker image and run it with the [sample configuration file](https://raw.githubusercontent.com/containous/traefik/master/traefik.sample.toml):
|
||||
- Or use the official tiny Docker image and run it with the [sample configuration file](https://raw.githubusercontent.com/traefik/traefik/master/traefik.sample.toml):
|
||||
|
||||
```shell
|
||||
docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/etc/traefik/traefik.toml traefik
|
||||
@@ -125,26 +114,19 @@ docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/etc/traefik/traefik.to
|
||||
- Or get the sources:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/containous/traefik
|
||||
git clone https://github.com/traefik/traefik
|
||||
```
|
||||
|
||||
## Introductory Videos
|
||||
|
||||
: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 Traefik features and see some demos with Kubernetes.
|
||||
|
||||
[](https://www.youtube.com/watch?v=aFtpIShV60I)
|
||||
You can find high level and deep dive videos on [videos.traefik.io](https://videos.traefik.io).
|
||||
|
||||
## Maintainers
|
||||
|
||||
[Information about process and maintainers](docs/content/contributing/maintainers.md)
|
||||
We are strongly promoting a philosophy of openness and sharing, and firmly standing against the elitist closed approach. Being part of the core team should be accessible to anyone who is motivated and want to be part of that journey!
|
||||
This [document](docs/content/contributing/maintainers-guidelines.md) describes how to be part of the core team as well as various responsibilities and guidelines for Traefik maintainers.
|
||||
You can also find more information on our process to review pull requests and manage issues [in this document](docs/content/contributing/maintainers.md).
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -155,24 +137,24 @@ By participating in this project, you agree to abide by its terms.
|
||||
|
||||
## Release Cycle
|
||||
|
||||
- We release a new version (e.g. 1.1.0, 1.2.0, 1.3.0) every other month.
|
||||
- Release Candidates are available before the release (e.g. 1.1.0-rc1, 1.1.0-rc2, 1.1.0-rc3, 1.1.0-rc4, before 1.1.0)
|
||||
- Bug-fixes (e.g. 1.1.1, 1.1.2, 1.2.1, 1.2.3) are released as needed (no additional features are delivered in those versions, bug-fixes only)
|
||||
- We usually release 3/4 new versions (e.g. 1.1.0, 1.2.0, 1.3.0) per year.
|
||||
- Release Candidates are available before the release (e.g. 1.1.0-rc1, 1.1.0-rc2, 1.1.0-rc3, 1.1.0-rc4, before 1.1.0).
|
||||
- Bug-fixes (e.g. 1.1.1, 1.1.2, 1.2.1, 1.2.3) are released as needed (no additional features are delivered in those versions, bug-fixes only).
|
||||
|
||||
Each version is supported until the next one is released (e.g. 1.1.x will be supported until 1.2.0 is out)
|
||||
Each version is supported until the next one is released (e.g. 1.1.x will be supported until 1.2.0 is out).
|
||||
|
||||
We use [Semantic Versioning](http://semver.org/)
|
||||
We use [Semantic Versioning](https://semver.org/).
|
||||
|
||||
## Mailing lists
|
||||
## Mailing Lists
|
||||
|
||||
- General announcements, new releases: mail at news+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/news)
|
||||
- General announcements, new releases: mail at news+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/news).
|
||||
- Security announcements: mail at security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
|
||||
|
||||
## Credits
|
||||
|
||||
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 gopher's logo!.
|
||||
|
||||
Traefik's logo is licensed under the Creative Commons 3.0 Attributions license.
|
||||
The gopher's logo of Traefik 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 gopher's logo of Traefik was inspired by the gopher stickers made by [Takuya Ueda](https://twitter.com/tenntenn).
|
||||
The original Go gopher was designed by [Renee French](https://reneefrench.blogspot.com/).
|
||||
|
29
SECURITY.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Security Policy
|
||||
|
||||
We strongly advise you to register your Traefik instances to [Pilot](https://pilot.traefik.io) to be notified of security advisories that apply to your Traefik version.
|
||||
You can also join our security mailing list to be aware of the latest announcements from our security team.
|
||||
You can subscribe sending a mail to security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
|
||||
|
||||
Reported vulnerabilities can be found on [cve.mitre.org](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=traefik).
|
||||
|
||||
## Supported Versions
|
||||
|
||||
- We usually release 3/4 new versions (e.g. 1.1.0, 1.2.0, 1.3.0) per year.
|
||||
- Release Candidates are available before the release (e.g. 1.1.0-rc1, 1.1.0-rc2, 1.1.0-rc3, 1.1.0-rc4, before 1.1.0).
|
||||
- Bug-fixes (e.g. 1.1.1, 1.1.2, 1.2.1, 1.2.3) are released as needed (no additional features are delivered in those versions, bug-fixes only).
|
||||
|
||||
Each version is supported until the next one is released (e.g. 1.1.x will be supported until 1.2.0 is out).
|
||||
|
||||
We use [Semantic Versioning](https://semver.org/).
|
||||
|
||||
| Version | Supported |
|
||||
| --------- | ------------------ |
|
||||
| `2.2.x` | :white_check_mark: |
|
||||
| `< 2.2.x` | :x: |
|
||||
| `1.7.x` | :white_check_mark: |
|
||||
| `< 1.7.x` | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
We want to keep Traefik safe for everyone.
|
||||
If you've discovered a security vulnerability in Traefik, we appreciate your help in disclosing it to us in a responsible manner, using [this form](https://security.traefik.io).
|
@@ -1,35 +1,37 @@
|
||||
FROM golang:1.12-alpine
|
||||
FROM golang:1.17-alpine
|
||||
|
||||
RUN apk --update upgrade \
|
||||
&& apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar ca-certificates tzdata \
|
||||
RUN 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/*
|
||||
|
||||
# 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.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 \
|
||||
&& curl -fsSL -o /usr/local/bin/dep https://github.com/golang/dep/releases/download/v${DEP_VERSION}/dep-linux-amd64 \
|
||||
&& chmod +x /usr/local/bin/dep
|
||||
ARG DOCKER_VERSION=18.09.7
|
||||
|
||||
# Download docker
|
||||
RUN mkdir -p /usr/local/bin \
|
||||
&& curl -fL https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}-ce.tgz \
|
||||
&& curl -fL https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}.tgz \
|
||||
| tar -xzC /usr/local/bin --transform 's#^.+/##x'
|
||||
|
||||
WORKDIR /go/src/github.com/containous/traefik
|
||||
COPY . /go/src/github.com/containous/traefik
|
||||
# Download golangci-lint binary to bin folder in $GOPATH
|
||||
RUN curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash -s -- -b $GOPATH/bin v1.46.2
|
||||
|
||||
# Download misspell binary to bin folder in $GOPATH
|
||||
RUN curl -sfL https://raw.githubusercontent.com/client9/misspell/master/install-misspell.sh | bash -s -- -b $GOPATH/bin v0.3.4
|
||||
|
||||
# Download goreleaser binary to bin folder in $GOPATH
|
||||
RUN curl -sfL https://gist.githubusercontent.com/traefiker/6d7ac019c11d011e4f131bb2cca8900e/raw/goreleaser.sh | sh
|
||||
|
||||
WORKDIR /go/src/github.com/traefik/traefik
|
||||
|
||||
# Because of CVE-2022-24765 (https://github.blog/2022-04-12-git-security-vulnerability-announced/),
|
||||
# we configure git to allow the Traefik codebase path on the Host for docker in docker usages.
|
||||
ARG HOST_PWD=""
|
||||
|
||||
RUN git config --global --add safe.directory "${HOST_PWD}"
|
||||
|
||||
# Download go modules
|
||||
COPY go.mod .
|
||||
COPY go.sum .
|
||||
RUN GO111MODULE=on GOPROXY=https://proxy.golang.org go mod download
|
||||
|
||||
COPY . /go/src/github.com/traefik/traefik
|
||||
|
@@ -3,8 +3,8 @@ package cmd
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/pkg/config/static"
|
||||
"github.com/containous/traefik/pkg/types"
|
||||
ptypes "github.com/traefik/paerser/types"
|
||||
"github.com/traefik/traefik/v2/pkg/config/static"
|
||||
)
|
||||
|
||||
// TraefikCmdConfiguration wraps the static configuration and extra parameters.
|
||||
@@ -23,7 +23,7 @@ func NewTraefikConfiguration() *TraefikCmdConfiguration {
|
||||
},
|
||||
EntryPoints: make(static.EntryPoints),
|
||||
Providers: &static.Providers{
|
||||
ProvidersThrottleDuration: types.Duration(2 * time.Second),
|
||||
ProvidersThrottleDuration: ptypes.Duration(2 * time.Second),
|
||||
},
|
||||
ServersTransport: &static.ServersTransport{
|
||||
MaxIdleConnsPerHost: 200,
|
||||
|
@@ -1,22 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// 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)
|
||||
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
|
||||
go func() {
|
||||
select {
|
||||
case <-signals:
|
||||
cancel()
|
||||
}
|
||||
}()
|
||||
return newCtx
|
||||
}
|
@@ -7,15 +7,15 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/pkg/cli"
|
||||
"github.com/containous/traefik/pkg/config/static"
|
||||
"github.com/traefik/paerser/cli"
|
||||
"github.com/traefik/traefik/v2/pkg/config/static"
|
||||
)
|
||||
|
||||
// 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).`,
|
||||
Description: `Calls Traefik /ping endpoint (disabled by default) to check the health of Traefik.`,
|
||||
Configuration: traefikConfiguration,
|
||||
Run: runCmd(traefikConfiguration),
|
||||
Resources: loaders,
|
||||
@@ -24,7 +24,7 @@ func NewCmd(traefikConfiguration *static.Configuration, loaders []cli.ResourceLo
|
||||
|
||||
func runCmd(traefikConfiguration *static.Configuration) func(_ []string) error {
|
||||
return func(_ []string) error {
|
||||
traefikConfiguration.SetEffectiveConfiguration("")
|
||||
traefikConfiguration.SetEffectiveConfiguration()
|
||||
|
||||
resp, errPing := Do(*traefikConfiguration)
|
||||
if resp != nil {
|
||||
@@ -45,15 +45,20 @@ func runCmd(traefikConfiguration *static.Configuration) func(_ []string) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Do try to do a healthcheck
|
||||
// Do try to do a healthcheck.
|
||||
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 := staticConfiguration.EntryPoints[staticConfiguration.Ping.EntryPoint]
|
||||
ep := staticConfiguration.Ping.EntryPoint
|
||||
if ep == "" {
|
||||
ep = "traefik"
|
||||
}
|
||||
|
||||
pingEntryPoint, ok := staticConfiguration.EntryPoints[ep]
|
||||
if !ok {
|
||||
return nil, errors.New("missing `ping` entrypoint")
|
||||
return nil, fmt.Errorf("ping: missing %s entry point", ep)
|
||||
}
|
||||
|
||||
client := &http.Client{Timeout: 5 * time.Second}
|
||||
@@ -70,5 +75,5 @@ func Do(staticConfiguration static.Configuration) (*http.Response, error) {
|
||||
|
||||
path := "/"
|
||||
|
||||
return client.Head(protocol + "://" + pingEntryPoint.Address + path + "ping")
|
||||
return client.Head(protocol + "://" + pingEntryPoint.GetAddress() + path + "ping")
|
||||
}
|
||||
|
347
cmd/internal/gen/centrifuge.go
Normal file
@@ -0,0 +1,347 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"go/importer"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/imports"
|
||||
)
|
||||
|
||||
// File a kind of AST element that represents a file.
|
||||
type File struct {
|
||||
Package string
|
||||
Imports []string
|
||||
Elements []Element
|
||||
}
|
||||
|
||||
// Element is a simplified version of a symbol.
|
||||
type Element struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
// Centrifuge a centrifuge.
|
||||
// Generate Go Structures from Go structures.
|
||||
type Centrifuge struct {
|
||||
IncludedImports []string
|
||||
ExcludedTypes []string
|
||||
ExcludedFiles []string
|
||||
|
||||
TypeCleaner func(types.Type, string) string
|
||||
PackageCleaner func(string) string
|
||||
|
||||
rootPkg string
|
||||
fileSet *token.FileSet
|
||||
pkg *types.Package
|
||||
}
|
||||
|
||||
// NewCentrifuge creates a new Centrifuge.
|
||||
func NewCentrifuge(rootPkg string) (*Centrifuge, error) {
|
||||
fileSet := token.NewFileSet()
|
||||
|
||||
pkg, err := importer.ForCompiler(fileSet, "source", nil).Import(rootPkg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Centrifuge{
|
||||
fileSet: fileSet,
|
||||
pkg: pkg,
|
||||
rootPkg: rootPkg,
|
||||
|
||||
TypeCleaner: func(typ types.Type, _ string) string {
|
||||
return typ.String()
|
||||
},
|
||||
PackageCleaner: func(s string) string {
|
||||
return s
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Run runs the code extraction and the code generation.
|
||||
func (c Centrifuge) Run(dest string, pkgName string) error {
|
||||
files, err := c.run(c.pkg.Scope(), c.rootPkg, pkgName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = fileWriter{baseDir: dest}.Write(files)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, p := range c.pkg.Imports() {
|
||||
if contains(c.IncludedImports, p.Path()) {
|
||||
fls, err := c.run(p.Scope(), p.Path(), p.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = fileWriter{baseDir: filepath.Join(dest, p.Name())}.Write(fls)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (c Centrifuge) run(sc *types.Scope, rootPkg string, pkgName string) (map[string]*File, error) {
|
||||
files := map[string]*File{}
|
||||
|
||||
for _, name := range sc.Names() {
|
||||
if contains(c.ExcludedTypes, name) {
|
||||
continue
|
||||
}
|
||||
|
||||
o := sc.Lookup(name)
|
||||
if !o.Exported() {
|
||||
continue
|
||||
}
|
||||
|
||||
filename := filepath.Base(c.fileSet.File(o.Pos()).Name())
|
||||
if contains(c.ExcludedFiles, path.Join(rootPkg, filename)) {
|
||||
continue
|
||||
}
|
||||
|
||||
fl, ok := files[filename]
|
||||
if !ok {
|
||||
files[filename] = &File{Package: pkgName}
|
||||
fl = files[filename]
|
||||
}
|
||||
|
||||
elt := Element{
|
||||
Name: name,
|
||||
}
|
||||
|
||||
switch ob := o.(type) {
|
||||
case *types.TypeName:
|
||||
|
||||
switch obj := ob.Type().(*types.Named).Underlying().(type) {
|
||||
case *types.Struct:
|
||||
elt.Value = c.writeStruct(name, obj, rootPkg, fl)
|
||||
|
||||
case *types.Map:
|
||||
elt.Value = fmt.Sprintf("type %s map[%s]%s\n", name, obj.Key().String(), c.TypeCleaner(obj.Elem(), rootPkg))
|
||||
|
||||
case *types.Slice:
|
||||
elt.Value = fmt.Sprintf("type %s []%v\n", name, c.TypeCleaner(obj.Elem(), rootPkg))
|
||||
|
||||
case *types.Basic:
|
||||
elt.Value = fmt.Sprintf("type %s %v\n", name, obj.Name())
|
||||
|
||||
default:
|
||||
log.Printf("OTHER TYPE::: %s %T\n", name, o.Type().(*types.Named).Underlying())
|
||||
continue
|
||||
}
|
||||
|
||||
default:
|
||||
log.Printf("OTHER::: %s %T\n", name, o)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(elt.Value) > 0 {
|
||||
fl.Elements = append(fl.Elements, elt)
|
||||
}
|
||||
}
|
||||
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func (c Centrifuge) writeStruct(name string, obj *types.Struct, rootPkg string, elt *File) string {
|
||||
b := strings.Builder{}
|
||||
b.WriteString(fmt.Sprintf("type %s struct {\n", name))
|
||||
|
||||
for i := 0; i < obj.NumFields(); i++ {
|
||||
field := obj.Field(i)
|
||||
|
||||
if !field.Exported() {
|
||||
continue
|
||||
}
|
||||
|
||||
fPkg := c.PackageCleaner(extractPackage(field.Type()))
|
||||
if fPkg != "" && fPkg != rootPkg {
|
||||
elt.Imports = append(elt.Imports, fPkg)
|
||||
}
|
||||
|
||||
fType := c.TypeCleaner(field.Type(), rootPkg)
|
||||
|
||||
if field.Embedded() {
|
||||
b.WriteString(fmt.Sprintf("\t%s\n", fType))
|
||||
continue
|
||||
}
|
||||
|
||||
values, ok := lookupTagValue(obj.Tag(i), "json")
|
||||
if len(values) > 0 && values[0] == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
b.WriteString(fmt.Sprintf("\t%s %s", field.Name(), fType))
|
||||
|
||||
if ok {
|
||||
b.WriteString(fmt.Sprintf(" `json:\"%s\"`", strings.Join(values, ",")))
|
||||
}
|
||||
|
||||
b.WriteString("\n")
|
||||
}
|
||||
|
||||
b.WriteString("}\n")
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func lookupTagValue(raw, key string) ([]string, bool) {
|
||||
value, ok := reflect.StructTag(raw).Lookup(key)
|
||||
if !ok {
|
||||
return nil, ok
|
||||
}
|
||||
|
||||
values := strings.Split(value, ",")
|
||||
|
||||
if len(values) < 1 {
|
||||
return nil, true
|
||||
}
|
||||
|
||||
return values, true
|
||||
}
|
||||
|
||||
func extractPackage(t types.Type) string {
|
||||
switch tu := t.(type) {
|
||||
case *types.Named:
|
||||
return tu.Obj().Pkg().Path()
|
||||
|
||||
case *types.Slice:
|
||||
if v, ok := tu.Elem().(*types.Named); ok {
|
||||
return v.Obj().Pkg().Path()
|
||||
}
|
||||
return ""
|
||||
|
||||
case *types.Map:
|
||||
if v, ok := tu.Elem().(*types.Named); ok {
|
||||
return v.Obj().Pkg().Path()
|
||||
}
|
||||
return ""
|
||||
|
||||
case *types.Pointer:
|
||||
return extractPackage(tu.Elem())
|
||||
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func contains(values []string, value string) bool {
|
||||
for _, val := range values {
|
||||
if val == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
type fileWriter struct {
|
||||
baseDir string
|
||||
}
|
||||
|
||||
func (f fileWriter) Write(files map[string]*File) error {
|
||||
err := os.MkdirAll(f.baseDir, 0o755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for name, file := range files {
|
||||
err = f.writeFile(name, file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f fileWriter) writeFile(name string, desc *File) error {
|
||||
if len(desc.Elements) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
filename := filepath.Join(f.baseDir, name)
|
||||
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create file: %w", err)
|
||||
}
|
||||
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
b := bytes.NewBufferString("package ")
|
||||
b.WriteString(desc.Package)
|
||||
b.WriteString("\n")
|
||||
b.WriteString("// Code generated by centrifuge. DO NOT EDIT.\n")
|
||||
|
||||
b.WriteString("\n")
|
||||
f.writeImports(b, desc.Imports)
|
||||
b.WriteString("\n")
|
||||
|
||||
for _, elt := range desc.Elements {
|
||||
b.WriteString(elt.Value)
|
||||
b.WriteString("\n")
|
||||
}
|
||||
|
||||
// gofmt
|
||||
source, err := format.Source(b.Bytes())
|
||||
if err != nil {
|
||||
log.Println(b.String())
|
||||
return fmt.Errorf("failed to format sources: %w", err)
|
||||
}
|
||||
|
||||
// goimports
|
||||
process, err := imports.Process(filename, source, nil)
|
||||
if err != nil {
|
||||
log.Println(string(source))
|
||||
return fmt.Errorf("failed to format imports: %w", err)
|
||||
}
|
||||
|
||||
_, err = file.Write(process)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f fileWriter) writeImports(b io.StringWriter, imports []string) {
|
||||
if len(imports) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
uniq := map[string]struct{}{}
|
||||
|
||||
sort.Strings(imports)
|
||||
|
||||
_, _ = b.WriteString("import (\n")
|
||||
for _, s := range imports {
|
||||
if _, exist := uniq[s]; exist {
|
||||
continue
|
||||
}
|
||||
|
||||
uniq[s] = struct{}{}
|
||||
|
||||
_, _ = b.WriteString(fmt.Sprintf(` "%s"`+"\n", s))
|
||||
}
|
||||
|
||||
_, _ = b.WriteString(")\n")
|
||||
}
|
124
cmd/internal/gen/main.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/build"
|
||||
"go/types"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const rootPkg = "github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
|
||||
const (
|
||||
destModuleName = "github.com/traefik/genconf"
|
||||
destPkg = "dynamic"
|
||||
)
|
||||
|
||||
const marsh = `package %s
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type JSONPayload struct {
|
||||
*Configuration
|
||||
}
|
||||
|
||||
func (c JSONPayload) MarshalJSON() ([]byte, error) {
|
||||
if c.Configuration == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return json.Marshal(c.Configuration)
|
||||
}
|
||||
`
|
||||
|
||||
// main generate Go Structures from Go structures.
|
||||
// Allows to create an external module (destModuleName) used by the plugin's providers
|
||||
// that contains Go structs of the dynamic configuration and nothing else.
|
||||
// These Go structs do not have any non-exported fields and do not rely on any external dependencies.
|
||||
func main() {
|
||||
dest := filepath.Join(path.Join(build.Default.GOPATH, "src"), destModuleName, destPkg)
|
||||
|
||||
log.Println("Output:", dest)
|
||||
|
||||
err := run(dest)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func run(dest string) error {
|
||||
centrifuge, err := NewCentrifuge(rootPkg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
centrifuge.IncludedImports = []string{
|
||||
"github.com/traefik/traefik/v2/pkg/tls",
|
||||
"github.com/traefik/traefik/v2/pkg/types",
|
||||
}
|
||||
|
||||
centrifuge.ExcludedTypes = []string{
|
||||
// tls
|
||||
"CertificateStore", "Manager",
|
||||
// dynamic
|
||||
"Message", "Configurations",
|
||||
// types
|
||||
"HTTPCodeRanges", "HostResolverConfig",
|
||||
}
|
||||
|
||||
centrifuge.ExcludedFiles = []string{
|
||||
"github.com/traefik/traefik/v2/pkg/types/logs.go",
|
||||
"github.com/traefik/traefik/v2/pkg/types/metrics.go",
|
||||
}
|
||||
|
||||
centrifuge.TypeCleaner = cleanType
|
||||
centrifuge.PackageCleaner = cleanPackage
|
||||
|
||||
err = centrifuge.Run(dest, destPkg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.WriteFile(filepath.Join(dest, "marshaler.go"), []byte(fmt.Sprintf(marsh, destPkg)), 0o666)
|
||||
}
|
||||
|
||||
func cleanType(typ types.Type, base string) string {
|
||||
if typ.String() == "github.com/traefik/traefik/v2/pkg/tls.FileOrContent" {
|
||||
return "string"
|
||||
}
|
||||
|
||||
if typ.String() == "[]github.com/traefik/traefik/v2/pkg/tls.FileOrContent" {
|
||||
return "[]string"
|
||||
}
|
||||
|
||||
if typ.String() == "github.com/traefik/paerser/types.Duration" {
|
||||
return "string"
|
||||
}
|
||||
|
||||
if strings.Contains(typ.String(), base) {
|
||||
return strings.ReplaceAll(typ.String(), base+".", "")
|
||||
}
|
||||
|
||||
if strings.Contains(typ.String(), "github.com/traefik/traefik/v2/pkg/") {
|
||||
return strings.ReplaceAll(typ.String(), "github.com/traefik/traefik/v2/pkg/", "")
|
||||
}
|
||||
|
||||
return typ.String()
|
||||
}
|
||||
|
||||
func cleanPackage(src string) string {
|
||||
switch src {
|
||||
case "github.com/traefik/paerser/types":
|
||||
return ""
|
||||
case "github.com/traefik/traefik/v2/pkg/tls":
|
||||
return path.Join(destModuleName, destPkg, "tls")
|
||||
case "github.com/traefik/traefik/v2/pkg/types":
|
||||
return path.Join(destModuleName, destPkg, "types")
|
||||
default:
|
||||
return src
|
||||
}
|
||||
}
|
88
cmd/traefik/plugins.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/traefik/traefik/v2/pkg/config/static"
|
||||
"github.com/traefik/traefik/v2/pkg/plugins"
|
||||
)
|
||||
|
||||
const outputDir = "./plugins-storage/"
|
||||
|
||||
func createPluginBuilder(staticConfiguration *static.Configuration) (*plugins.Builder, error) {
|
||||
client, plgs, localPlgs, err := initPlugins(staticConfiguration)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return plugins.NewBuilder(client, plgs, localPlgs)
|
||||
}
|
||||
|
||||
func initPlugins(staticCfg *static.Configuration) (*plugins.Client, map[string]plugins.Descriptor, map[string]plugins.LocalDescriptor, error) {
|
||||
err := checkUniquePluginNames(staticCfg.Experimental)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
var client *plugins.Client
|
||||
plgs := map[string]plugins.Descriptor{}
|
||||
|
||||
if isPilotEnabled(staticCfg) && hasPlugins(staticCfg) {
|
||||
opts := plugins.ClientOptions{
|
||||
Output: outputDir,
|
||||
Token: staticCfg.Pilot.Token,
|
||||
}
|
||||
|
||||
var err error
|
||||
client, err = plugins.NewClient(opts)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
err = plugins.SetupRemotePlugins(client, staticCfg.Experimental.Plugins)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
plgs = staticCfg.Experimental.Plugins
|
||||
}
|
||||
|
||||
localPlgs := map[string]plugins.LocalDescriptor{}
|
||||
|
||||
if hasLocalPlugins(staticCfg) {
|
||||
err := plugins.SetupLocalPlugins(staticCfg.Experimental.LocalPlugins)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
localPlgs = staticCfg.Experimental.LocalPlugins
|
||||
}
|
||||
|
||||
return client, plgs, localPlgs, nil
|
||||
}
|
||||
|
||||
func checkUniquePluginNames(e *static.Experimental) error {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for s := range e.LocalPlugins {
|
||||
if _, ok := e.Plugins[s]; ok {
|
||||
return fmt.Errorf("the plugin's name %q must be unique", s)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isPilotEnabled(staticCfg *static.Configuration) bool {
|
||||
return staticCfg.Pilot != nil && staticCfg.Pilot.Token != ""
|
||||
}
|
||||
|
||||
func hasPlugins(staticCfg *static.Configuration) bool {
|
||||
return staticCfg.Experimental != nil && len(staticCfg.Experimental.Plugins) > 0
|
||||
}
|
||||
|
||||
func hasLocalPlugins(staticCfg *static.Configuration) bool {
|
||||
return staticCfg.Experimental != nil && len(staticCfg.Experimental.LocalPlugins) > 0
|
||||
}
|
@@ -2,49 +2,54 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
stdlog "log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/autogen/genstatic"
|
||||
"github.com/containous/traefik/cmd"
|
||||
"github.com/containous/traefik/cmd/healthcheck"
|
||||
cmdVersion "github.com/containous/traefik/cmd/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"
|
||||
assetfs "github.com/elazarl/go-bindata-assetfs"
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
gokitmetrics "github.com/go-kit/kit/metrics"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/traefik/paerser/cli"
|
||||
"github.com/traefik/traefik/v2/cmd"
|
||||
"github.com/traefik/traefik/v2/cmd/healthcheck"
|
||||
cmdVersion "github.com/traefik/traefik/v2/cmd/version"
|
||||
tcli "github.com/traefik/traefik/v2/pkg/cli"
|
||||
"github.com/traefik/traefik/v2/pkg/collector"
|
||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v2/pkg/config/runtime"
|
||||
"github.com/traefik/traefik/v2/pkg/config/static"
|
||||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
"github.com/traefik/traefik/v2/pkg/metrics"
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares/accesslog"
|
||||
"github.com/traefik/traefik/v2/pkg/pilot"
|
||||
"github.com/traefik/traefik/v2/pkg/provider/acme"
|
||||
"github.com/traefik/traefik/v2/pkg/provider/aggregator"
|
||||
"github.com/traefik/traefik/v2/pkg/provider/traefik"
|
||||
"github.com/traefik/traefik/v2/pkg/safe"
|
||||
"github.com/traefik/traefik/v2/pkg/server"
|
||||
"github.com/traefik/traefik/v2/pkg/server/middleware"
|
||||
"github.com/traefik/traefik/v2/pkg/server/service"
|
||||
traefiktls "github.com/traefik/traefik/v2/pkg/tls"
|
||||
"github.com/traefik/traefik/v2/pkg/types"
|
||||
"github.com/traefik/traefik/v2/pkg/version"
|
||||
"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
|
||||
tConfig := cmd.NewTraefikConfiguration()
|
||||
|
||||
loaders := []cli.ResourceLoader{&cli.FileLoader{}, &cli.EnvLoader{}, &cli.FlagLoader{}}
|
||||
loaders := []cli.ResourceLoader{&tcli.FileLoader{}, &tcli.FlagLoader{}, &tcli.EnvLoader{}}
|
||||
|
||||
cmdTraefik := &cli.Command{
|
||||
Name: "traefik",
|
||||
@@ -53,7 +58,7 @@ Complete documentation is available at https://traefik.io`,
|
||||
Configuration: tConfig,
|
||||
Resources: loaders,
|
||||
Run: func(_ []string) error {
|
||||
return runCmd(&tConfig.Configuration, cli.GetConfigFile(loaders))
|
||||
return runCmd(&tConfig.Configuration)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -72,23 +77,25 @@ Complete documentation is available at https://traefik.io`,
|
||||
err = cli.Execute(cmdTraefik)
|
||||
if err != nil {
|
||||
stdlog.Println(err)
|
||||
os.Exit(1)
|
||||
logrus.Exit(1)
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
logrus.Exit(0)
|
||||
}
|
||||
|
||||
func runCmd(staticConfiguration *static.Configuration, configFile string) error {
|
||||
func runCmd(staticConfiguration *static.Configuration) error {
|
||||
configureLogging(staticConfiguration)
|
||||
|
||||
http.DefaultTransport.(*http.Transport).Proxy = http.ProxyFromEnvironment
|
||||
|
||||
if err := roundrobin.SetDefaultWeight(0); err != nil {
|
||||
log.WithoutContext().Errorf("Could not set roundrobin default weight: %v", err)
|
||||
log.WithoutContext().Errorf("Could not set round robin default weight: %v", err)
|
||||
}
|
||||
|
||||
staticConfiguration.SetEffectiveConfiguration(configFile)
|
||||
staticConfiguration.ValidateConfiguration()
|
||||
staticConfiguration.SetEffectiveConfiguration()
|
||||
if err := staticConfiguration.ValidateConfiguration(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.WithoutContext().Infof("Traefik version %s built on %s", version.Version, version.BuildDate)
|
||||
|
||||
@@ -100,57 +107,18 @@ func runCmd(staticConfiguration *static.Configuration, configFile string) error
|
||||
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(staticConfiguration)
|
||||
|
||||
providerAggregator := aggregator.NewProviderAggregator(*staticConfiguration.Providers)
|
||||
|
||||
acmeProvider, err := staticConfiguration.InitACMEProvider()
|
||||
svr, err := setupServer(staticConfiguration)
|
||||
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
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
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())
|
||||
ctx, _ := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
if staticConfiguration.Ping != nil {
|
||||
staticConfiguration.Ping.WithContext(ctx)
|
||||
@@ -176,7 +144,7 @@ func runCmd(staticConfiguration *static.Configuration, configFile string) error
|
||||
for range tick {
|
||||
resp, errHealthCheck := healthcheck.Do(*staticConfiguration)
|
||||
if resp != nil {
|
||||
resp.Body.Close()
|
||||
_ = resp.Body.Close()
|
||||
}
|
||||
|
||||
if staticConfiguration.Ping == nil || errHealthCheck == nil {
|
||||
@@ -192,10 +160,327 @@ func runCmd(staticConfiguration *static.Configuration, configFile string) error
|
||||
|
||||
svr.Wait()
|
||||
log.WithoutContext().Info("Shutting down")
|
||||
logrus.Exit(0)
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupServer(staticConfiguration *static.Configuration) (*server.Server, error) {
|
||||
providerAggregator := aggregator.NewProviderAggregator(*staticConfiguration.Providers)
|
||||
|
||||
ctx := context.Background()
|
||||
routinesPool := safe.NewPool(ctx)
|
||||
|
||||
// adds internal provider
|
||||
err := providerAggregator.AddProvider(traefik.New(*staticConfiguration))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ACME
|
||||
|
||||
tlsManager := traefiktls.NewManager()
|
||||
httpChallengeProvider := acme.NewChallengeHTTP()
|
||||
|
||||
// we need to wait at least 2 times the ProvidersThrottleDuration to be sure to handle the challenge.
|
||||
tlsChallengeProvider := acme.NewChallengeTLSALPN(time.Duration(staticConfiguration.Providers.ProvidersThrottleDuration) * 2)
|
||||
err = providerAggregator.AddProvider(tlsChallengeProvider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
acmeProviders := initACMEProvider(staticConfiguration, &providerAggregator, tlsManager, httpChallengeProvider, tlsChallengeProvider)
|
||||
|
||||
// Entrypoints
|
||||
|
||||
serverEntryPointsTCP, err := server.NewTCPEntryPoints(staticConfiguration.EntryPoints, staticConfiguration.HostResolver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serverEntryPointsUDP, err := server.NewUDPEntryPoints(staticConfiguration.EntryPoints)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Pilot
|
||||
|
||||
var aviator *pilot.Pilot
|
||||
var pilotRegistry *metrics.PilotRegistry
|
||||
if isPilotEnabled(staticConfiguration) {
|
||||
pilotRegistry = metrics.RegisterPilot()
|
||||
|
||||
aviator = pilot.New(staticConfiguration.Pilot.Token, pilotRegistry, routinesPool)
|
||||
|
||||
routinesPool.GoCtx(func(ctx context.Context) {
|
||||
aviator.Tick(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
if staticConfiguration.Pilot != nil {
|
||||
version.PilotEnabled = staticConfiguration.Pilot.Dashboard
|
||||
}
|
||||
|
||||
// Plugins
|
||||
|
||||
pluginBuilder, err := createPluginBuilder(staticConfiguration)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Providers plugins
|
||||
|
||||
for name, conf := range staticConfiguration.Providers.Plugin {
|
||||
p, err := pluginBuilder.BuildProvider(name, conf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("plugin: failed to build provider: %w", err)
|
||||
}
|
||||
|
||||
err = providerAggregator.AddProvider(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("plugin: failed to add provider: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Metrics
|
||||
|
||||
metricRegistries := registerMetricClients(staticConfiguration.Metrics)
|
||||
if pilotRegistry != nil {
|
||||
metricRegistries = append(metricRegistries, pilotRegistry)
|
||||
}
|
||||
metricsRegistry := metrics.NewMultiRegistry(metricRegistries)
|
||||
|
||||
// Service manager factory
|
||||
|
||||
roundTripperManager := service.NewRoundTripperManager()
|
||||
acmeHTTPHandler := getHTTPChallengeHandler(acmeProviders, httpChallengeProvider)
|
||||
managerFactory := service.NewManagerFactory(*staticConfiguration, routinesPool, metricsRegistry, roundTripperManager, acmeHTTPHandler)
|
||||
|
||||
// Router factory
|
||||
|
||||
accessLog := setupAccessLog(staticConfiguration.AccessLog)
|
||||
chainBuilder := middleware.NewChainBuilder(*staticConfiguration, metricsRegistry, accessLog)
|
||||
routerFactory := server.NewRouterFactory(*staticConfiguration, managerFactory, tlsManager, chainBuilder, pluginBuilder, metricsRegistry)
|
||||
|
||||
// Watcher
|
||||
|
||||
watcher := server.NewConfigurationWatcher(
|
||||
routinesPool,
|
||||
providerAggregator,
|
||||
time.Duration(staticConfiguration.Providers.ProvidersThrottleDuration),
|
||||
getDefaultsEntrypoints(staticConfiguration),
|
||||
"internal",
|
||||
)
|
||||
|
||||
// TLS
|
||||
watcher.AddListener(func(conf dynamic.Configuration) {
|
||||
ctx := context.Background()
|
||||
tlsManager.UpdateConfigs(ctx, conf.TLS.Stores, conf.TLS.Options, conf.TLS.Certificates)
|
||||
|
||||
gauge := metricsRegistry.TLSCertsNotAfterTimestampGauge()
|
||||
for _, certificate := range tlsManager.GetCertificates() {
|
||||
appendCertMetric(gauge, certificate)
|
||||
}
|
||||
})
|
||||
|
||||
// Metrics
|
||||
watcher.AddListener(func(_ dynamic.Configuration) {
|
||||
metricsRegistry.ConfigReloadsCounter().Add(1)
|
||||
metricsRegistry.LastConfigReloadSuccessGauge().Set(float64(time.Now().Unix()))
|
||||
})
|
||||
|
||||
// Server Transports
|
||||
watcher.AddListener(func(conf dynamic.Configuration) {
|
||||
roundTripperManager.Update(conf.HTTP.ServersTransports)
|
||||
})
|
||||
|
||||
// Switch router
|
||||
watcher.AddListener(switchRouter(routerFactory, serverEntryPointsTCP, serverEntryPointsUDP, aviator))
|
||||
|
||||
// Metrics
|
||||
if metricsRegistry.IsEpEnabled() || metricsRegistry.IsSvcEnabled() {
|
||||
var eps []string
|
||||
for key := range serverEntryPointsTCP {
|
||||
eps = append(eps, key)
|
||||
}
|
||||
watcher.AddListener(func(conf dynamic.Configuration) {
|
||||
metrics.OnConfigurationUpdate(conf, eps)
|
||||
})
|
||||
}
|
||||
|
||||
// TLS challenge
|
||||
watcher.AddListener(tlsChallengeProvider.ListenConfiguration)
|
||||
|
||||
// ACME
|
||||
resolverNames := map[string]struct{}{}
|
||||
for _, p := range acmeProviders {
|
||||
resolverNames[p.ResolverName] = struct{}{}
|
||||
watcher.AddListener(p.ListenConfiguration)
|
||||
}
|
||||
|
||||
// Certificate resolver logs
|
||||
watcher.AddListener(func(config dynamic.Configuration) {
|
||||
for rtName, rt := range config.HTTP.Routers {
|
||||
if rt.TLS == nil || rt.TLS.CertResolver == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := resolverNames[rt.TLS.CertResolver]; !ok {
|
||||
log.WithoutContext().Errorf("the router %s uses a non-existent resolver: %s", rtName, rt.TLS.CertResolver)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return server.NewServer(routinesPool, serverEntryPointsTCP, serverEntryPointsUDP, watcher, chainBuilder, accessLog), nil
|
||||
}
|
||||
|
||||
func getHTTPChallengeHandler(acmeProviders []*acme.Provider, httpChallengeProvider http.Handler) http.Handler {
|
||||
var acmeHTTPHandler http.Handler
|
||||
for _, p := range acmeProviders {
|
||||
if p != nil && p.HTTPChallenge != nil {
|
||||
acmeHTTPHandler = httpChallengeProvider
|
||||
break
|
||||
}
|
||||
}
|
||||
return acmeHTTPHandler
|
||||
}
|
||||
|
||||
func getDefaultsEntrypoints(staticConfiguration *static.Configuration) []string {
|
||||
var defaultEntryPoints []string
|
||||
for name, cfg := range staticConfiguration.EntryPoints {
|
||||
protocol, err := cfg.GetProtocol()
|
||||
if err != nil {
|
||||
// Should never happen because Traefik should not start if protocol is invalid.
|
||||
log.WithoutContext().Errorf("Invalid protocol: %v", err)
|
||||
}
|
||||
|
||||
if protocol != "udp" && name != static.DefaultInternalEntryPointName {
|
||||
defaultEntryPoints = append(defaultEntryPoints, name)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(defaultEntryPoints)
|
||||
return defaultEntryPoints
|
||||
}
|
||||
|
||||
func switchRouter(routerFactory *server.RouterFactory, serverEntryPointsTCP server.TCPEntryPoints, serverEntryPointsUDP server.UDPEntryPoints, aviator *pilot.Pilot) func(conf dynamic.Configuration) {
|
||||
return func(conf dynamic.Configuration) {
|
||||
rtConf := runtime.NewConfig(conf)
|
||||
|
||||
routers, udpRouters := routerFactory.CreateRouters(rtConf)
|
||||
|
||||
if aviator != nil {
|
||||
aviator.SetDynamicConfiguration(conf)
|
||||
}
|
||||
|
||||
serverEntryPointsTCP.Switch(routers)
|
||||
serverEntryPointsUDP.Switch(udpRouters)
|
||||
}
|
||||
}
|
||||
|
||||
// initACMEProvider creates an acme provider from the ACME part of globalConfiguration.
|
||||
func initACMEProvider(c *static.Configuration, providerAggregator *aggregator.ProviderAggregator, tlsManager *traefiktls.Manager, httpChallengeProvider, tlsChallengeProvider challenge.Provider) []*acme.Provider {
|
||||
localStores := map[string]*acme.LocalStore{}
|
||||
|
||||
var resolvers []*acme.Provider
|
||||
for name, resolver := range c.CertificatesResolvers {
|
||||
if resolver.ACME == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if localStores[resolver.ACME.Storage] == nil {
|
||||
localStores[resolver.ACME.Storage] = acme.NewLocalStore(resolver.ACME.Storage)
|
||||
}
|
||||
|
||||
p := &acme.Provider{
|
||||
Configuration: resolver.ACME,
|
||||
Store: localStores[resolver.ACME.Storage],
|
||||
ResolverName: name,
|
||||
HTTPChallengeProvider: httpChallengeProvider,
|
||||
TLSChallengeProvider: tlsChallengeProvider,
|
||||
}
|
||||
|
||||
if err := providerAggregator.AddProvider(p); err != nil {
|
||||
log.WithoutContext().Errorf("The ACME resolver %q is skipped from the resolvers list because: %v", name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
p.SetTLSManager(tlsManager)
|
||||
|
||||
p.SetConfigListenerChan(make(chan dynamic.Configuration))
|
||||
|
||||
resolvers = append(resolvers, p)
|
||||
}
|
||||
|
||||
return resolvers
|
||||
}
|
||||
|
||||
func registerMetricClients(metricsConfig *types.Metrics) []metrics.Registry {
|
||||
if metricsConfig == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var registries []metrics.Registry
|
||||
|
||||
if metricsConfig.Prometheus != nil {
|
||||
ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "prometheus"))
|
||||
prometheusRegister := metrics.RegisterPrometheus(ctx, metricsConfig.Prometheus)
|
||||
if prometheusRegister != nil {
|
||||
registries = append(registries, prometheusRegister)
|
||||
log.FromContext(ctx).Debug("Configured Prometheus metrics")
|
||||
}
|
||||
}
|
||||
|
||||
if metricsConfig.Datadog != nil {
|
||||
ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "datadog"))
|
||||
registries = append(registries, metrics.RegisterDatadog(ctx, metricsConfig.Datadog))
|
||||
log.FromContext(ctx).Debugf("Configured Datadog metrics: pushing to %s once every %s",
|
||||
metricsConfig.Datadog.Address, metricsConfig.Datadog.PushInterval)
|
||||
}
|
||||
|
||||
if metricsConfig.StatsD != nil {
|
||||
ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "statsd"))
|
||||
registries = append(registries, metrics.RegisterStatsd(ctx, metricsConfig.StatsD))
|
||||
log.FromContext(ctx).Debugf("Configured StatsD metrics: pushing to %s once every %s",
|
||||
metricsConfig.StatsD.Address, metricsConfig.StatsD.PushInterval)
|
||||
}
|
||||
|
||||
if metricsConfig.InfluxDB != nil {
|
||||
ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "influxdb"))
|
||||
registries = append(registries, metrics.RegisterInfluxDB(ctx, metricsConfig.InfluxDB))
|
||||
log.FromContext(ctx).Debugf("Configured InfluxDB metrics: pushing to %s once every %s",
|
||||
metricsConfig.InfluxDB.Address, metricsConfig.InfluxDB.PushInterval)
|
||||
}
|
||||
|
||||
return registries
|
||||
}
|
||||
|
||||
func appendCertMetric(gauge gokitmetrics.Gauge, certificate *x509.Certificate) {
|
||||
sort.Strings(certificate.DNSNames)
|
||||
|
||||
labels := []string{
|
||||
"cn", certificate.Subject.CommonName,
|
||||
"serial", certificate.SerialNumber.String(),
|
||||
"sans", strings.Join(certificate.DNSNames, ","),
|
||||
}
|
||||
|
||||
notAfter := float64(certificate.NotAfter.Unix())
|
||||
|
||||
gauge.With(labels...).Set(notAfter)
|
||||
}
|
||||
|
||||
func setupAccessLog(conf *types.AccessLog) *accesslog.Handler {
|
||||
if conf == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
accessLoggerMiddleware, err := accesslog.NewHandler(conf)
|
||||
if err != nil {
|
||||
log.WithoutContext().Warnf("Unable to create access logger : %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return accessLoggerMiddleware
|
||||
}
|
||||
|
||||
func configureLogging(staticConfiguration *static.Configuration) {
|
||||
// configure default log flags
|
||||
stdlog.SetFlags(stdlog.Lshortfile | stdlog.LstdFlags)
|
||||
@@ -204,16 +489,11 @@ func configureLogging(staticConfiguration *static.Configuration) {
|
||||
// an explicitly defined log level always has precedence. if none is
|
||||
// given and debug mode is disabled, the default is ERROR, and DEBUG
|
||||
// otherwise.
|
||||
var levelStr string
|
||||
if staticConfiguration.Log != nil {
|
||||
levelStr := "error"
|
||||
if staticConfiguration.Log != nil && staticConfiguration.Log.Level != "" {
|
||||
levelStr = strings.ToLower(staticConfiguration.Log.Level)
|
||||
}
|
||||
if levelStr == "" {
|
||||
levelStr = "error"
|
||||
if staticConfiguration.Global.Debug {
|
||||
levelStr = "debug"
|
||||
}
|
||||
}
|
||||
|
||||
level, err := logrus.ParseLevel(levelStr)
|
||||
if err != nil {
|
||||
log.WithoutContext().Errorf("Error getting level: %v", err)
|
||||
@@ -238,7 +518,7 @@ func configureLogging(staticConfiguration *static.Configuration) {
|
||||
if len(logFile) > 0 {
|
||||
dir := filepath.Dir(logFile)
|
||||
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
log.WithoutContext().Errorf("Failed to create log path %s: %s", dir, err)
|
||||
}
|
||||
|
||||
@@ -264,27 +544,19 @@ func checkNewVersion() {
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
logger := log.WithoutContext()
|
||||
|
||||
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
|
||||
`)
|
||||
if staticConfiguration.Global.SendAnonymousUsage {
|
||||
logger.Info(`Stats collection is enabled.`)
|
||||
logger.Info(`Many thanks for contributing to Traefik's improvement by allowing us to receive anonymous information from your configuration.`)
|
||||
logger.Info(`Help us improve Traefik by leaving this feature on :)`)
|
||||
logger.Info(`More details on: https://doc.traefik.io/traefik/contributing/data-collection/`)
|
||||
collect(staticConfiguration)
|
||||
} else {
|
||||
log.WithoutContext().Info(`
|
||||
logger.Info(`
|
||||
Stats collection is disabled.
|
||||
Help us improve Traefik by turning this feature on :)
|
||||
More details on: https://docs.traefik.io/basics/#collected-data
|
||||
More details on: https://doc.traefik.io/traefik/contributing/data-collection/
|
||||
`)
|
||||
}
|
||||
}
|
||||
|
116
cmd/traefik/traefik_test.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// FooCert is a PEM-encoded TLS cert.
|
||||
// generated from src/crypto/tls:
|
||||
// go run generate_cert.go --rsa-bits 1024 --host foo.org,foo.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
|
||||
const fooCert = `-----BEGIN CERTIFICATE-----
|
||||
MIICHzCCAYigAwIBAgIQXQFLeYRwc5X21t457t2xADANBgkqhkiG9w0BAQsFADAS
|
||||
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
|
||||
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
|
||||
iQKBgQDCjn67GSs/khuGC4GNN+tVo1S+/eSHwr/hWzhfMqO7nYiXkFzmxi+u14CU
|
||||
Pda6WOeps7T2/oQEFMxKKg7zYOqkLSbjbE0ZfosopaTvEsZm/AZHAAvoOrAsIJOn
|
||||
SEiwy8h0tLA4z1SNR6rmIVQWyqBZEPAhBTQM1z7tFp48FakCFwIDAQABo3QwcjAO
|
||||
BgNVHQ8BAf8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUw
|
||||
AwEB/zAdBgNVHQ4EFgQUDHG3ASzeUezElup9zbPpBn/vjogwGwYDVR0RBBQwEoIH
|
||||
Zm9vLm9yZ4IHZm9vLmNvbTANBgkqhkiG9w0BAQsFAAOBgQBT+VLMbB9u27tBX8Aw
|
||||
ZrGY3rbNdBGhXVTksrjiF+6ZtDpD3iI56GH9zLxnqvXkgn3u0+Ard5TqF/xmdwVw
|
||||
NY0V/aWYfcL2G2auBCQrPvM03ozRnVUwVfP23eUzX2ORNHCYhd2ObQx4krrhs7cJ
|
||||
SWxtKwFlstoXY3K2g9oRD9UxdQ==
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
// BarCert is a PEM-encoded TLS cert.
|
||||
// generated from src/crypto/tls:
|
||||
// go run generate_cert.go --rsa-bits 1024 --host bar.org,bar.com --ca --start-date "Jan 1 00:00:00 1970" --duration=10000h
|
||||
const barCert = `-----BEGIN CERTIFICATE-----
|
||||
MIICHTCCAYagAwIBAgIQcuIcNEXzBHPoxna5S6wG4jANBgkqhkiG9w0BAQsFADAS
|
||||
MRAwDgYDVQQKEwdBY21lIENvMB4XDTcwMDEwMTAwMDAwMFoXDTcxMDIyMTE2MDAw
|
||||
MFowEjEQMA4GA1UEChMHQWNtZSBDbzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
|
||||
gYEAqtcrP+KA7D6NjyztGNIPMup9KiBMJ8QL+preog/YHR7SQLO3kGFhpS3WKMab
|
||||
SzMypC3ZX1PZjBP5ZzwaV3PFbuwlCkPlyxR2lOWmullgI7mjY0TBeYLDIclIzGRp
|
||||
mpSDDSpkW1ay2iJDSpXjlhmwZr84hrCU7BRTQJo91fdsRTsCAwEAAaN0MHIwDgYD
|
||||
VR0PAQH/BAQDAgKkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMB
|
||||
Af8wHQYDVR0OBBYEFK8jnzFQvBAgWtfzOyXY4VSkwrTXMBsGA1UdEQQUMBKCB2Jh
|
||||
ci5vcmeCB2Jhci5jb20wDQYJKoZIhvcNAQELBQADgYEAJz0ifAExisC/ZSRhWuHz
|
||||
7qs1i6Nd4+YgEVR8dR71MChP+AMxucY1/ajVjb9xlLys3GPE90TWSdVppabEVjZY
|
||||
Oq11nPKc50ItTt8dMku6t0JHBmzoGdkN0V4zJCBqdQJxhop8JpYJ0S9CW0eT93h3
|
||||
ipYQSsmIINGtMXJ8VkP/MlM=
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
type gaugeMock struct {
|
||||
metrics map[string]float64
|
||||
labels string
|
||||
}
|
||||
|
||||
func (g gaugeMock) With(labelValues ...string) metrics.Gauge {
|
||||
g.labels = strings.Join(labelValues, ",")
|
||||
return g
|
||||
}
|
||||
|
||||
func (g gaugeMock) Set(value float64) {
|
||||
g.metrics[g.labels] = value
|
||||
}
|
||||
|
||||
func (g gaugeMock) Add(delta float64) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func TestAppendCertMetric(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
certs []string
|
||||
expected map[string]float64
|
||||
}{
|
||||
{
|
||||
desc: "No certs",
|
||||
certs: []string{},
|
||||
expected: map[string]float64{},
|
||||
},
|
||||
{
|
||||
desc: "One cert",
|
||||
certs: []string{fooCert},
|
||||
expected: map[string]float64{
|
||||
"cn,,serial,123624926713171615935660664614975025408,sans,foo.com,foo.org": 3.6e+09,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Two certs",
|
||||
certs: []string{fooCert, barCert},
|
||||
expected: map[string]float64{
|
||||
"cn,,serial,123624926713171615935660664614975025408,sans,foo.com,foo.org": 3.6e+09,
|
||||
"cn,,serial,152706022658490889223053211416725817058,sans,bar.com,bar.org": 3.6e+07,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
gauge := &gaugeMock{
|
||||
metrics: map[string]float64{},
|
||||
}
|
||||
|
||||
for _, cert := range test.certs {
|
||||
block, _ := pem.Decode([]byte(cert))
|
||||
parsedCert, err := x509.ParseCertificate(block.Bytes)
|
||||
require.NoError(t, err)
|
||||
|
||||
appendCertMetric(gauge, parsedCert)
|
||||
}
|
||||
|
||||
assert.Equal(t, test.expected, gauge.metrics)
|
||||
})
|
||||
}
|
||||
}
|
@@ -7,8 +7,8 @@ import (
|
||||
"runtime"
|
||||
"text/template"
|
||||
|
||||
"github.com/containous/traefik/pkg/cli"
|
||||
"github.com/containous/traefik/pkg/version"
|
||||
"github.com/traefik/paerser/cli"
|
||||
"github.com/traefik/traefik/v2/pkg/version"
|
||||
)
|
||||
|
||||
var versionTemplate = `Version: {{.Version}}
|
||||
@@ -17,7 +17,7 @@ Go version: {{.GoVersion}}
|
||||
Built: {{.BuildTime}}
|
||||
OS/Arch: {{.Os}}/{{.Arch}}`
|
||||
|
||||
// NewCmd builds a new Version command
|
||||
// NewCmd builds a new Version command.
|
||||
func NewCmd() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "version",
|
||||
@@ -33,7 +33,7 @@ func NewCmd() *cli.Command {
|
||||
}
|
||||
}
|
||||
|
||||
// GetPrint write Printable version
|
||||
// GetPrint write Printable version.
|
||||
func GetPrint(wr io.Writer) error {
|
||||
tmpl, err := template.New("").Parse(versionTemplate)
|
||||
if err != nil {
|
||||
|
@@ -130,7 +130,7 @@
|
||||
"tableColumn": "",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "count(kube_pod_status_ready{namespace=\"$namespace\",condition=\"true\",pod=~\"traefik.*\"})",
|
||||
"expr": "count(kube_pod_status_ready{condition=\"true\",pod=~\"traefik.*\"})",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"refId": "A"
|
||||
@@ -150,10 +150,7 @@
|
||||
"valueName": "current"
|
||||
},
|
||||
{
|
||||
"aliasColors": {
|
||||
"Latency over 1 min": "rgb(9, 116, 190)",
|
||||
"Latency over 5 min": "#bf1b00"
|
||||
},
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
@@ -183,22 +180,17 @@
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [
|
||||
{
|
||||
"alias": "Latency over 5 min",
|
||||
"yaxis": 1
|
||||
}
|
||||
],
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "histogram_quantile(0.$percentiles, sum(rate(traefik_entrypoint_request_duration_seconds_bucket{namespace=\"$namespace\", code=\"200\",method=\"GET\"}[5m])) by (le))",
|
||||
"expr": "histogram_quantile(0.$percentiles, sum(rate(traefik_entrypoint_request_duration_seconds_bucket{code=~\"2..\"}[5m])) by (le))",
|
||||
"format": "time_series",
|
||||
"hide": false,
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "Latency over 1 min",
|
||||
"legendFormat": "Latency over 5 min",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
@@ -281,7 +273,7 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "histogram_quantile(0.$percentiles, rate(traefik_entrypoint_request_duration_seconds_bucket{namespace=\"$namespace\",code=\"200\",method=\"GET\"}[5m]))",
|
||||
"expr": "histogram_quantile(0.$percentiles, sum(rate(traefik_entrypoint_request_duration_seconds_bucket{code=~\"2..\"}[5m])) by (instance, le))",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "{{ instance }}",
|
||||
@@ -343,7 +335,7 @@
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": null,
|
||||
"datasource": "${DS_PROMETHEUS}",
|
||||
"fill": 7,
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
@@ -379,7 +371,7 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(traefik_entrypoint_open_connections{namespace=\"$namespace\"}) by (method)",
|
||||
"expr": "sum(traefik_entrypoint_open_connections) by (method)",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "{{ method }}",
|
||||
@@ -431,7 +423,7 @@
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": null,
|
||||
"datasource": "${DS_PROMETHEUS}",
|
||||
"fill": 1,
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
@@ -465,7 +457,7 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(traefik_entrypoint_request_duration_seconds_bucket{namespace=\"$namespace\",le=\"0.1\",code=\"200\"}[5m])) by (job) / sum(rate(traefik_entrypoint_request_duration_seconds_count{namespace=\"$namespace\",code=\"200\"}[5m])) by (job)",
|
||||
"expr": "(sum(rate(traefik_entrypoint_request_duration_seconds_bucket{le=\"0.1\",code=\"200\"}[5m])) by (job) + sum(rate(traefik_entrypoint_request_duration_seconds_bucket{le=\"0.3\",code=\"200\"}[5m])) by (job)) / 2 / sum(rate(traefik_entrypoint_request_duration_seconds_count{code=\"200\"}[5m])) by (job)",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "Code 200",
|
||||
@@ -511,9 +503,97 @@
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": true,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_PROMETHEUS}",
|
||||
"fill": 1,
|
||||
"gridPos": {
|
||||
"h": 10,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 23
|
||||
},
|
||||
"id": 3,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": true,
|
||||
"current": true,
|
||||
"max": true,
|
||||
"min": false,
|
||||
"rightSide": false,
|
||||
"show": true,
|
||||
"sort": "avg",
|
||||
"sortDesc": true,
|
||||
"total": false,
|
||||
"values": true
|
||||
},
|
||||
"lines": false,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "null",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(traefik_entrypoint_requests_total[1m])) by (entrypoint)",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{ entrypoint }}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Service total requests over 1min per entrypoint",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"title": "Frontends (entrypoints)",
|
||||
"title": "Entrypoints",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
@@ -522,7 +602,7 @@
|
||||
"h": 1,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 16
|
||||
"y": 33
|
||||
},
|
||||
"id": 24,
|
||||
"panels": [
|
||||
@@ -531,13 +611,13 @@
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": null,
|
||||
"datasource": "${DS_PROMETHEUS}",
|
||||
"fill": 7,
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 17
|
||||
"y": 34
|
||||
},
|
||||
"id": 25,
|
||||
"legend": {
|
||||
@@ -567,7 +647,7 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(traefik_backend_open_connections{namespace=\"$namespace\"}) by (method)",
|
||||
"expr": "sum(traefik_service_open_connections) by (method)",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "{{ method }}",
|
||||
@@ -619,13 +699,13 @@
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": null,
|
||||
"datasource": "${DS_PROMETHEUS}",
|
||||
"fill": 1,
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 17
|
||||
"y": 34
|
||||
},
|
||||
"id": 26,
|
||||
"legend": {
|
||||
@@ -653,7 +733,7 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(traefik_backend_request_duration_seconds_bucket{namespace=\"$namespace\",le=\"0.1\",code=\"200\"}[5m])) by (job) / sum(rate(traefik_backend_request_duration_seconds_count{namespace=\"$namespace\",code=\"200\"}[5m])) by (job)",
|
||||
"expr": "(sum(rate(traefik_service_request_duration_seconds_bucket{le=\"0.1\",code=\"200\"}[5m])) by (job) + sum(rate(traefik_service_request_duration_seconds_bucket{le=\"0.3\",code=\"200\"}[5m])) by (job)) / 2 / sum(rate(traefik_service_request_duration_seconds_count{code=\"200\"}[5m])) by (job)",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "Code 200",
|
||||
@@ -699,9 +779,97 @@
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": true,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_PROMETHEUS}",
|
||||
"fill": 1,
|
||||
"gridPos": {
|
||||
"h": 10,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 41
|
||||
},
|
||||
"id": 4,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": true,
|
||||
"current": true,
|
||||
"max": true,
|
||||
"min": false,
|
||||
"rightSide": false,
|
||||
"show": true,
|
||||
"sort": "avg",
|
||||
"sortDesc": true,
|
||||
"total": false,
|
||||
"values": true
|
||||
},
|
||||
"lines": false,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "null",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(traefik_service_requests_total[1m])) by (service)",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{ service }}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Service total requests over 1min per service",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"title": "Backends",
|
||||
"title": "Services",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
@@ -710,7 +878,7 @@
|
||||
"h": 1,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 17
|
||||
"y": 51
|
||||
},
|
||||
"id": 15,
|
||||
"panels": [
|
||||
@@ -725,7 +893,7 @@
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 18
|
||||
"y": 52
|
||||
},
|
||||
"id": 5,
|
||||
"legend": {
|
||||
@@ -755,7 +923,7 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(traefik_backend_requests_total{namespace=\"$namespace\",code=~\"2..\"}[5m])) by (method, code)",
|
||||
"expr": "sum(rate(traefik_service_requests_total{code=~\"2..\"}[5m])) by (method, code)",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{method}} : {{code}}",
|
||||
@@ -813,7 +981,7 @@
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 18
|
||||
"y": 52
|
||||
},
|
||||
"id": 27,
|
||||
"legend": {
|
||||
@@ -841,7 +1009,7 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(traefik_backend_requests_total{namespace=\"$namespace\",code=~\"5..\"}[5m])) by (method, code)",
|
||||
"expr": "sum(rate(traefik_service_requests_total{code=~\"5..\"}[5m])) by (method, code)",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{method}} : {{code}}",
|
||||
@@ -899,95 +1067,7 @@
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 27
|
||||
},
|
||||
"id": 3,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": true,
|
||||
"current": true,
|
||||
"max": true,
|
||||
"min": false,
|
||||
"rightSide": true,
|
||||
"show": true,
|
||||
"sort": "avg",
|
||||
"sortDesc": true,
|
||||
"total": false,
|
||||
"values": true
|
||||
},
|
||||
"lines": false,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "null",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(traefik_backend_requests_total{namespace=\"$namespace\"}[1m])) by (backend)",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{ backend }}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Backend total requests over 1min per backend",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": true,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_PROMETHEUS}",
|
||||
"fill": 1,
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 27
|
||||
"y": 61
|
||||
},
|
||||
"id": 6,
|
||||
"legend": {
|
||||
@@ -1016,7 +1096,7 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(traefik_backend_requests_total{namespace=\"$namespace\",code!~\"2..|5..\"}[5m])) by (method, code)",
|
||||
"expr": "sum(rate(traefik_service_requests_total{code!~\"2..|5..\"}[5m])) by (method, code)",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{ method }} : {{code}}",
|
||||
@@ -1026,7 +1106,7 @@
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Others status code over 5min",
|
||||
"title": "Others statuses code over 5min",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
@@ -1064,7 +1144,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"title": "HTTP Codes stats",
|
||||
"title": "HTTP Codes stats",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
@@ -1073,7 +1153,7 @@
|
||||
"h": 1,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 18
|
||||
"y": 70
|
||||
},
|
||||
"id": 35,
|
||||
"panels": [
|
||||
@@ -1082,13 +1162,13 @@
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": null,
|
||||
"datasource": "${DS_PROMETHEUS}",
|
||||
"fill": 1,
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 19
|
||||
"y": 71
|
||||
},
|
||||
"id": 31,
|
||||
"legend": {
|
||||
@@ -1116,21 +1196,21 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "max(container_memory_usage_bytes{namespace=\"$namespace\", container_name=\"traefik\"})",
|
||||
"expr": "sum(container_memory_usage_bytes{container=\"traefik\"})",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "Max memory used",
|
||||
"legendFormat": "Memory used",
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"expr": "avg(kube_pod_container_resource_requests_memory_bytes{namespace=\"$namespace\", container=\"traefik\"})",
|
||||
"expr": "sum(kube_pod_container_resource_requests_memory_bytes{container=\"traefik\"})",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "Requested memory usage",
|
||||
"legendFormat": "Requested memory",
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"expr": "avg(kube_pod_container_resource_limits_memory_bytes{namespace=\"$namespace\", container=\"traefik\"})",
|
||||
"expr": "sum(kube_pod_container_resource_limits_memory_bytes{container=\"traefik\"})",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "Limit memory usage",
|
||||
@@ -1140,7 +1220,7 @@
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Traefik max memory usage",
|
||||
"title": "Traefik memory usage",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
@@ -1182,13 +1262,13 @@
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": null,
|
||||
"datasource": "${DS_PROMETHEUS}",
|
||||
"fill": 1,
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 19
|
||||
"y": 71
|
||||
},
|
||||
"id": 33,
|
||||
"legend": {
|
||||
@@ -1215,21 +1295,21 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "max(rate(container_cpu_usage_seconds_total{namespace=\"$namespace\", container_name=\"traefik\"}[1m]))",
|
||||
"expr": "sum(rate(container_cpu_usage_seconds_total{container=\"traefik\"}[2m]))",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "Max cpu used",
|
||||
"legendFormat": "Cpu used",
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"expr": "avg(kube_pod_container_resource_requests_cpu_cores{namespace=\"$namespace\", container=\"traefik\"})",
|
||||
"expr": "sum(kube_pod_container_resource_requests_cpu_cores{container=\"traefik\"})",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "Requested cpu usage",
|
||||
"legendFormat": "Requested cpu",
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"expr": "avg(kube_pod_container_resource_limits_cpu_cores{namespace=\"$namespace\", container=\"traefik\"})",
|
||||
"expr": "sum(kube_pod_container_resource_limits_cpu_cores{container=\"traefik\"})",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "Limit cpu usage",
|
||||
@@ -1239,7 +1319,7 @@
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Traefik max CPU usage",
|
||||
"title": "Traefik CPU usage",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
@@ -1277,7 +1357,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"title": "Pods ressources",
|
||||
"title": "Pods resources",
|
||||
"type": "row"
|
||||
}
|
||||
],
|
||||
@@ -1288,26 +1368,6 @@
|
||||
],
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"allValue": null,
|
||||
"current": {},
|
||||
"datasource": "${DS_PROMETHEUS}",
|
||||
"hide": 0,
|
||||
"includeAll": false,
|
||||
"label": null,
|
||||
"multi": false,
|
||||
"name": "namespace",
|
||||
"options": [],
|
||||
"query": "label_values(traefik_config_reloads_total, namespace)",
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"sort": 0,
|
||||
"tagValuesQuery": "",
|
||||
"tags": [],
|
||||
"tagsQuery": "",
|
||||
"type": "query",
|
||||
"useTags": false
|
||||
},
|
||||
{
|
||||
"allValue": null,
|
||||
"current": {
|
||||
@@ -1370,5 +1430,5 @@
|
||||
"timezone": "",
|
||||
"title": "Traefik",
|
||||
"uid": "traefik-kubernetes",
|
||||
"version": 1
|
||||
"version": 2
|
||||
}
|
||||
|
@@ -64,10 +64,7 @@
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
"aliasColors": {
|
||||
"Latency over 1 min": "rgb(9, 116, 190)",
|
||||
"Latency over 5 min": "#bf1b00"
|
||||
},
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
@@ -97,22 +94,17 @@
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [
|
||||
{
|
||||
"alias": "Latency over 5 min",
|
||||
"yaxis": 1
|
||||
}
|
||||
],
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "histogram_quantile(0.$percentiles, sum(rate(traefik_entrypoint_request_duration_seconds_bucket{code=\"200\",method=\"GET\"}[5m])) by (le))",
|
||||
"expr": "histogram_quantile(0.$percentiles, sum(rate(traefik_entrypoint_request_duration_seconds_bucket{code=~\"2..\"}[5m])) by (le))",
|
||||
"format": "time_series",
|
||||
"hide": false,
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "Latency over 1 min",
|
||||
"legendFormat": "Latency over 5 min",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
@@ -195,7 +187,7 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "histogram_quantile(0.$percentiles, rate(traefik_entrypoint_request_duration_seconds_bucket{code=\"200\",method=\"GET\"}[5m]))",
|
||||
"expr": "histogram_quantile(0.$percentiles, sum(rate(traefik_entrypoint_request_duration_seconds_bucket{code=~\"2..\"}[5m])) by (instance, le))",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "{{ instance }}",
|
||||
@@ -257,13 +249,13 @@
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": null,
|
||||
"datasource": "${DS_PROMETHEUS}",
|
||||
"fill": 7,
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 2
|
||||
"y": 16
|
||||
},
|
||||
"id": 19,
|
||||
"legend": {
|
||||
@@ -345,13 +337,13 @@
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": null,
|
||||
"datasource": "${DS_PROMETHEUS}",
|
||||
"fill": 1,
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 2
|
||||
"y": 16
|
||||
},
|
||||
"id": 22,
|
||||
"legend": {
|
||||
@@ -379,7 +371,7 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(traefik_entrypoint_request_duration_seconds_bucket{le=\"0.1\",code=\"200\"}[5m])) by (job) / sum(rate(traefik_entrypoint_request_duration_seconds_count{code=\"200\"}[5m])) by (job)",
|
||||
"expr": "(sum(rate(traefik_entrypoint_request_duration_seconds_bucket{le=\"0.1\",code=\"200\"}[5m])) by (job) + sum(rate(traefik_entrypoint_request_duration_seconds_bucket{le=\"0.3\",code=\"200\"}[5m])) by (job)) / 2 / sum(rate(traefik_entrypoint_request_duration_seconds_count{code=\"200\"}[5m])) by (job)",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "Code 200",
|
||||
@@ -425,9 +417,97 @@
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": true,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_PROMETHEUS}",
|
||||
"fill": 1,
|
||||
"gridPos": {
|
||||
"h": 10,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 23
|
||||
},
|
||||
"id": 3,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": true,
|
||||
"current": true,
|
||||
"max": true,
|
||||
"min": false,
|
||||
"rightSide": false,
|
||||
"show": true,
|
||||
"sort": "avg",
|
||||
"sortDesc": true,
|
||||
"total": false,
|
||||
"values": true
|
||||
},
|
||||
"lines": false,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "null",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(traefik_entrypoint_requests_total[1m])) by (entrypoint)",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{ entrypoint }}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Service total requests over 1min per entrypoint",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"title": "Frontends (entrypoints)",
|
||||
"title": "Entrypoints",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
@@ -436,7 +516,7 @@
|
||||
"h": 1,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 16
|
||||
"y": 33
|
||||
},
|
||||
"id": 24,
|
||||
"panels": [
|
||||
@@ -445,13 +525,13 @@
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": null,
|
||||
"datasource": "${DS_PROMETHEUS}",
|
||||
"fill": 7,
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 3
|
||||
"y": 34
|
||||
},
|
||||
"id": 25,
|
||||
"legend": {
|
||||
@@ -481,7 +561,7 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(traefik_backend_open_connections) by (method)",
|
||||
"expr": "sum(traefik_service_open_connections) by (method)",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "{{ method }}",
|
||||
@@ -533,13 +613,13 @@
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": null,
|
||||
"datasource": "${DS_PROMETHEUS}",
|
||||
"fill": 1,
|
||||
"gridPos": {
|
||||
"h": 7,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 3
|
||||
"y": 34
|
||||
},
|
||||
"id": 26,
|
||||
"legend": {
|
||||
@@ -567,7 +647,7 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(traefik_backend_request_duration_seconds_bucket{le=\"0.1\",code=\"200\"}[5m])) by (job) / sum(rate(traefik_backend_request_duration_seconds_count{code=\"200\"}[5m])) by (job)",
|
||||
"expr": "(sum(rate(traefik_service_request_duration_seconds_bucket{le=\"0.1\",code=\"200\"}[5m])) by (job) + sum(rate(traefik_service_request_duration_seconds_bucket{le=\"0.3\",code=\"200\"}[5m])) by (job)) / 2 / sum(rate(traefik_service_request_duration_seconds_count{code=\"200\"}[5m])) by (job)",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 1,
|
||||
"legendFormat": "Code 200",
|
||||
@@ -613,9 +693,97 @@
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": true,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_PROMETHEUS}",
|
||||
"fill": 1,
|
||||
"gridPos": {
|
||||
"h": 10,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 41
|
||||
},
|
||||
"id": 4,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": true,
|
||||
"current": true,
|
||||
"max": true,
|
||||
"min": false,
|
||||
"rightSide": false,
|
||||
"show": true,
|
||||
"sort": "avg",
|
||||
"sortDesc": true,
|
||||
"total": false,
|
||||
"values": true
|
||||
},
|
||||
"lines": false,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "null",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(traefik_service_requests_total[1m])) by (service)",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{ service }}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Service total requests over 1min per service",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"title": "Backends",
|
||||
"title": "Services",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
@@ -624,7 +792,7 @@
|
||||
"h": 1,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 17
|
||||
"y": 51
|
||||
},
|
||||
"id": 15,
|
||||
"panels": [
|
||||
@@ -639,7 +807,7 @@
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 4
|
||||
"y": 52
|
||||
},
|
||||
"id": 5,
|
||||
"legend": {
|
||||
@@ -669,7 +837,7 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(traefik_backend_requests_total{code=~\"2..\"}[5m])) by (method, code)",
|
||||
"expr": "sum(rate(traefik_service_requests_total{code=~\"2..\"}[5m])) by (method, code)",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{method}} : {{code}}",
|
||||
@@ -727,7 +895,7 @@
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 4
|
||||
"y": 52
|
||||
},
|
||||
"id": 27,
|
||||
"legend": {
|
||||
@@ -755,7 +923,7 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(traefik_backend_requests_total{code=~\"5..\"}[5m])) by (method, code)",
|
||||
"expr": "sum(rate(traefik_service_requests_total{code=~\"5..\"}[5m])) by (method, code)",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{method}} : {{code}}",
|
||||
@@ -813,95 +981,7 @@
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 13
|
||||
},
|
||||
"id": 3,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": true,
|
||||
"current": true,
|
||||
"max": true,
|
||||
"min": false,
|
||||
"rightSide": true,
|
||||
"show": true,
|
||||
"sort": "avg",
|
||||
"sortDesc": true,
|
||||
"total": false,
|
||||
"values": true
|
||||
},
|
||||
"lines": false,
|
||||
"linewidth": 1,
|
||||
"links": [],
|
||||
"nullPointMode": "null",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(traefik_backend_requests_total[1m])) by (backend)",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{ backend }}",
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Backend total requests over 1min per backend",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false,
|
||||
"alignLevel": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": true,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_PROMETHEUS}",
|
||||
"fill": 1,
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 12,
|
||||
"x": 12,
|
||||
"y": 13
|
||||
"y": 61
|
||||
},
|
||||
"id": 6,
|
||||
"legend": {
|
||||
@@ -930,7 +1010,7 @@
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(traefik_backend_requests_total{code!~\"2..|5..\"}[5m])) by (method, code)",
|
||||
"expr": "sum(rate(traefik_service_requests_total{code!~\"2..|5..\"}[5m])) by (method, code)",
|
||||
"format": "time_series",
|
||||
"intervalFactor": 2,
|
||||
"legendFormat": "{{ method }} : {{code}}",
|
||||
@@ -940,7 +1020,7 @@
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Others status code over 5min",
|
||||
"title": "Others statuses code over 5min",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
@@ -978,7 +1058,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"title": "HTTP Codes stats",
|
||||
"title": "HTTP Codes stats",
|
||||
"type": "row"
|
||||
}
|
||||
],
|
||||
@@ -1051,5 +1131,5 @@
|
||||
"timezone": "",
|
||||
"title": "Traefik",
|
||||
"uid": "traefik",
|
||||
"version": 1
|
||||
"version": 2
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
[Unit]
|
||||
Description=Traefik
|
||||
Documentation=https://docs.traefik.io
|
||||
Documentation=https://doc.traefik.io/traefik/
|
||||
#After=network-online.target
|
||||
#AssertFileIsExecutable=/usr/bin/traefik
|
||||
#AssertPathExists=/etc/traefik/traefik.toml
|
||||
|
10
debug.Dockerfile
Normal file
@@ -0,0 +1,10 @@
|
||||
FROM alpine:3.14
|
||||
# Feel free to add below any helpful dependency for debugging.
|
||||
# iproute2 is for ss.
|
||||
RUN apk --no-cache --no-progress add bash curl ca-certificates tzdata lsof iproute2 \
|
||||
&& update-ca-certificates \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
COPY dist/traefik /
|
||||
EXPOSE 80
|
||||
VOLUME ["/tmp"]
|
||||
ENTRYPOINT ["/traefik"]
|
@@ -3,8 +3,11 @@
|
||||
"MD007": { "indent": 4 },
|
||||
"MD009": false,
|
||||
"MD013": false,
|
||||
"MD024": false,
|
||||
"MD025": false,
|
||||
"MD026": false,
|
||||
"MD033": false,
|
||||
"MD034": false,
|
||||
"MD036": false
|
||||
"MD036": false,
|
||||
"MD046": false
|
||||
}
|
||||
|
@@ -1 +0,0 @@
|
||||
docs.traefik.io
|
@@ -1,4 +1,3 @@
|
||||
|
||||
#######
|
||||
# This Makefile contains all targets related to the documentation
|
||||
#######
|
||||
@@ -12,41 +11,55 @@ 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_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)
|
||||
.PHONY: docs
|
||||
docs: docs-clean docs-image docs-lint docs-build docs-verify
|
||||
|
||||
# Writer Mode: build and serve docs on http://localhost:8000 with livereload
|
||||
.PHONY: docs-serve
|
||||
docs-serve: docs-image
|
||||
docker run $(DOCKER_RUN_DOC_OPTS) $(TRAEFIK_DOCS_BUILD_IMAGE) mkdocs serve
|
||||
|
||||
## Pull image for doc building
|
||||
.PHONY: docs-pull-images
|
||||
docs-pull-images:
|
||||
grep --no-filename -E '^FROM' ./*.Dockerfile \
|
||||
| awk '{print $$2}' \
|
||||
| sort \
|
||||
| uniq \
|
||||
| xargs -P 6 -n 1 docker pull
|
||||
|
||||
# Utilities Targets for each step
|
||||
.PHONY: docs-image
|
||||
docs-image:
|
||||
docker build -t $(TRAEFIK_DOCS_BUILD_IMAGE) -f docs.Dockerfile ./
|
||||
|
||||
.PHONY: docs-build
|
||||
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"
|
||||
|
||||
.PHONY: docs-verify
|
||||
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
|
||||
ifneq ("$(DOCS_VERIFY_SKIP)", "true")
|
||||
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."
|
||||
endif
|
||||
|
||||
.PHONY: docs-lint
|
||||
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
|
||||
ifneq ("$(DOCS_LINT_SKIP)", "true")
|
||||
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."
|
||||
endif
|
||||
|
||||
.PHONY: docs-clean
|
||||
docs-clean:
|
||||
rm -rf $(SITE_DIR)
|
||||
|
||||
.PHONY: all docs-verify docs docs-clean docs-build docs-lint
|
||||
|
@@ -1,31 +1,33 @@
|
||||
FROM alpine:3.14 as alpine
|
||||
|
||||
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 \
|
||||
ruby-bigdecimal \
|
||||
ruby-dev \
|
||||
&& gem install --no-document html-proofer -v 3.10.2 \
|
||||
&& apk del build-dependencies
|
||||
ruby-etc \
|
||||
ruby-ffi \
|
||||
ruby-json \
|
||||
zlib-dev
|
||||
|
||||
RUN gem install nokogiri --version 1.13.3 --no-document -- --use-system-libraries
|
||||
RUN gem install html-proofer --version 3.19.3 --no-document -- --use-system-libraries
|
||||
|
||||
# After Ruby, some NodeJS YAY!
|
||||
RUN apk --no-cache --no-progress add \
|
||||
git \
|
||||
nodejs \
|
||||
npm \
|
||||
&& npm install markdownlint@0.12.0 markdownlint-cli@0.13.0 --global
|
||||
npm
|
||||
|
||||
# To handle 'not get uid/gid'
|
||||
RUN npm config set unsafe-perm true
|
||||
|
||||
RUN npm install --global \
|
||||
markdownlint@0.22.0 \
|
||||
markdownlint-cli@0.26.0
|
||||
|
||||
# Finally the shell tools we need for later
|
||||
# tini helps to terminate properly all the parallelized tasks when sending CTRL-C
|
||||
|
Before Width: | Height: | Size: 208 KiB |
Before Width: | Height: | Size: 274 KiB |
BIN
docs/content/assets/img/maintainers-guidelines.png
Normal file
After Width: | Height: | Size: 966 KiB |
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
Before Width: | Height: | Size: 68 KiB |
BIN
docs/content/assets/img/providers/consul.png
Normal file
After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 289 KiB After Width: | Height: | Size: 284 KiB |
Before Width: | Height: | Size: 2.0 KiB |
BIN
docs/content/assets/img/traefikproxy-icon-color.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
After Width: | Height: | Size: 20 KiB |
4
docs/content/assets/img/user-guides/grpc.svg
Normal file
After Width: | Height: | Size: 186 KiB |
BIN
docs/content/assets/img/webui-dashboard.png
Normal file
After Width: | Height: | Size: 125 KiB |
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
|
||||
Atom One Light by Daniel Gamage
|
||||
Original One Light Syntax theme from https://github.com/atom/one-light-syntax
|
||||
|
||||
base: #fafafa
|
||||
mono-1: #383a42
|
||||
mono-2: #686b77
|
||||
mono-3: #a0a1a7
|
||||
hue-1: #0184bb
|
||||
hue-2: #4078f2
|
||||
hue-3: #a626a4
|
||||
hue-4: #50a14f
|
||||
hue-5: #e45649
|
||||
hue-5-2: #c91243
|
||||
hue-6: #986801
|
||||
hue-6-2: #c18401
|
||||
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
color: #383a42;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #a0a1a7;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-doctag,
|
||||
.hljs-keyword,
|
||||
.hljs-formula {
|
||||
color: #a626a4;
|
||||
}
|
||||
|
||||
.hljs-section,
|
||||
.hljs-name,
|
||||
.hljs-selector-tag,
|
||||
.hljs-deletion,
|
||||
.hljs-subst {
|
||||
color: #e45649;
|
||||
}
|
||||
|
||||
.hljs-literal {
|
||||
color: #0184bb;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-regexp,
|
||||
.hljs-addition,
|
||||
.hljs-attribute,
|
||||
.hljs-meta-string {
|
||||
color: #50a14f;
|
||||
}
|
||||
|
||||
.hljs-built_in,
|
||||
.hljs-class .hljs-title {
|
||||
color: #c18401;
|
||||
}
|
||||
|
||||
.hljs-attr,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-type,
|
||||
.hljs-selector-class,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo,
|
||||
.hljs-number {
|
||||
color: #986801;
|
||||
}
|
||||
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-link,
|
||||
.hljs-meta,
|
||||
.hljs-selector-id,
|
||||
.hljs-title {
|
||||
color: #4078f2;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-link {
|
||||
text-decoration: underline;
|
||||
}
|
@@ -1,59 +0,0 @@
|
||||
@import url('https://fonts.googleapis.com/css?family=Noto+Sans|Noto+Serif');
|
||||
|
||||
.md-logo img {
|
||||
background-color: white;
|
||||
border-radius: 50%;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
/* Fix for Chrome */
|
||||
.md-typeset__table td code {
|
||||
word-break: unset;
|
||||
}
|
||||
|
||||
.md-typeset__table tr :nth-child(1) {
|
||||
word-wrap: break-word;
|
||||
max-width: 30em;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Noto Sans', sans-serif;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: bold !important;
|
||||
color: rgba(0,0,0,.9) !important;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
figcaption {
|
||||
text-align: center;
|
||||
font-size: 0.8em;
|
||||
font-style: italic;
|
||||
color: #8D909F;
|
||||
}
|
||||
|
||||
p.subtitle {
|
||||
color: rgba(0,0,0,.54);
|
||||
padding-top: 0;
|
||||
margin-top: -2em;
|
||||
font-weight: bold;
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
.markdown-body .task-list-item {
|
||||
list-style-type: none !important;
|
||||
}
|
||||
|
||||
.markdown-body .task-list-item input[type="checkbox"] {
|
||||
margin: 0 4px 0.25em -20px;
|
||||
vertical-align: middle;
|
||||
}
|
@@ -1,3 +1,8 @@
|
||||
---
|
||||
title: "Traefik Advocation Documentation"
|
||||
description: "There are many ways to contribute to Traefik Proxy. If you're talking about Traefik, let us know and we'll promote your enthusiasm!"
|
||||
---
|
||||
|
||||
# Advocating
|
||||
|
||||
Spread the Love & Tell Us about It
|
||||
@@ -5,6 +10,6 @@ Spread the Love & Tell Us about It
|
||||
|
||||
There are many ways to contribute to the project, and there is one that always spark joy: when we see/read about users talking about how Traefik helps them solve their problems.
|
||||
|
||||
If you're talking about Traefik, [let us know](https://blog.containo.us/spread-the-love-ba5a40aa72e7) and we'll promote your enthusiasm!
|
||||
If you're talking about Traefik, [let us know](https://blog.traefik.io/spread-the-love-ba5a40aa72e7) and we'll promote your enthusiasm!
|
||||
|
||||
Also, if you've written about Traefik or shared useful information you'd like to promote, feel free to add links in the [dedicated wiki page on Github](https://github.com/containous/traefik/wiki/Awesome-Traefik).
|
||||
Also, if you've written about Traefik or shared useful information you'd like to promote, feel free to add links in the [dedicated wiki page on Github](https://github.com/traefik/traefik/wiki/Awesome-Traefik).
|
||||
|
@@ -1,3 +1,8 @@
|
||||
---
|
||||
title: "Traefik Building & Testing Documentation"
|
||||
description: "Compile and test your own Traefik Proxy! Learn how to build your own Traefik binary from the sources, and read the technical documentation."
|
||||
---
|
||||
|
||||
# Building and Testing
|
||||
|
||||
Compile and Test Your Own Traefik!
|
||||
@@ -16,6 +21,8 @@ For changes to its dependencies, the `dep` dependency management tool is require
|
||||
Run make with the `binary` target.
|
||||
This will create binaries for the Linux platform in the `dist` folder.
|
||||
|
||||
In case when you run build on CI, you may probably want to run docker in non-interactive mode. To achieve that define `DOCKER_NON_INTERACTIVE=true` environment variable.
|
||||
|
||||
```bash
|
||||
$ make binary
|
||||
docker build -t traefik-webui -f webui/Dockerfile webui
|
||||
@@ -28,12 +35,12 @@ Successfully tagged traefik-webui:latest
|
||||
[...]
|
||||
docker build -t "traefik-dev:4475--feature-documentation" -f build.Dockerfile .
|
||||
Sending build context to Docker daemon 279MB
|
||||
Step 1/10 : FROM golang:1.12-alpine
|
||||
Step 1/10 : FROM golang:1.16-alpine
|
||||
---> f4bfb3d22bda
|
||||
[...]
|
||||
Successfully built 5c3c1a911277
|
||||
Successfully tagged traefik-dev:4475--feature-documentation
|
||||
docker run -e "TEST_CONTAINER=1" -v "/var/run/docker.sock:/var/run/docker.sock" -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -e VERBOSE -e VERSION -e CODENAME -e TESTDIRS -e CI -e CONTAINER=DOCKER -v "/home/ldez/sources/go/src/github.com/containous/traefik/"dist":/go/src/github.com/containous/traefik/"dist"" "traefik-dev:4475--feature-documentation" ./script/make.sh generate binary
|
||||
docker run -e "TEST_CONTAINER=1" -v "/var/run/docker.sock:/var/run/docker.sock" -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -e VERBOSE -e VERSION -e CODENAME -e TESTDIRS -e CI -e CONTAINER=DOCKER -v "/home/ldez/sources/go/src/github.com/traefik/traefik/"dist":/go/src/github.com/traefik/traefik/"dist"" "traefik-dev:4475--feature-documentation" ./script/make.sh generate binary
|
||||
---> Making bundle: generate (in .)
|
||||
removed 'autogen/genstatic/gen.go'
|
||||
|
||||
@@ -43,7 +50,7 @@ $ ls dist/
|
||||
traefik*
|
||||
```
|
||||
|
||||
The following targets can be executed outside Docker by setting the variable `PRE_TARGET` to an empty string (we don't recommend that):
|
||||
The following targets can be executed outside Docker by setting the variable `IN_DOCKER` to an empty string (although be aware that some of the tests might fail in that context):
|
||||
|
||||
- `test-unit`
|
||||
- `test-integration`
|
||||
@@ -53,16 +60,19 @@ The following targets can be executed outside Docker by setting the variable `PR
|
||||
ex:
|
||||
|
||||
```bash
|
||||
PRE_TARGET= make test-unit
|
||||
IN_DOCKER= make test-unit
|
||||
```
|
||||
|
||||
### Method 2: Using `go`
|
||||
|
||||
You need `go` v1.12+.
|
||||
Requirements:
|
||||
|
||||
- `go` v1.16+
|
||||
- environment variable `GO111MODULE=on`
|
||||
|
||||
!!! tip "Source Directory"
|
||||
|
||||
It is recommended that you clone Traefik into the `~/go/src/github.com/containous/traefik` directory.
|
||||
It is recommended that you clone Traefik into the `~/go/src/github.com/traefik/traefik` directory.
|
||||
This is the official golang workspace hierarchy that will allow dependencies to be properly resolved.
|
||||
|
||||
!!! note "Environment"
|
||||
@@ -94,52 +104,22 @@ You need `go` v1.12+.
|
||||
#### Build Traefik
|
||||
|
||||
Once you've set up your go environment and cloned the source repository, you can build Traefik.
|
||||
Beforehand, you need to get `go-bindata` (the first time) in order to be able to use the `go generate` command (which is part of the build process).
|
||||
|
||||
```bash
|
||||
cd ~/go/src/github.com/containous/traefik
|
||||
# Generate UI static files
|
||||
make clean-webui generate-webui
|
||||
|
||||
# Get go-bindata. (Important: the ellipses are required.)
|
||||
go get github.com/containous/go-bindata/...
|
||||
|
||||
# Let's build
|
||||
|
||||
# generate
|
||||
# (required to merge non-code components into the final binary, such as the web dashboard and the provider's templates)
|
||||
# required to merge non-code components into the final binary,
|
||||
# such as the web dashboard/UI
|
||||
go generate
|
||||
```
|
||||
|
||||
```bash
|
||||
# Standard go build
|
||||
go build ./cmd/traefik
|
||||
```
|
||||
|
||||
You will find the Traefik executable (`traefik`) in the `~/go/src/github.com/containous/traefik` directory.
|
||||
|
||||
### Updating the templates
|
||||
|
||||
If you happen to update the provider's templates (located in `/templates`), you must run `go generate` to update the `autogen` package.
|
||||
|
||||
### Setting up dependency management
|
||||
|
||||
The [dep](https://github.com/golang/dep) command is not required for building;
|
||||
however, it is necessary if you need to update the dependencies (i.e., add, update, or remove third-party packages).
|
||||
|
||||
You need [dep](https://github.com/golang/dep) >= 0.5.0.
|
||||
|
||||
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
|
||||
```
|
||||
You will find the Traefik executable (`traefik`) in the `~/go/src/github.com/traefik/traefik` directory.
|
||||
|
||||
## Testing
|
||||
|
||||
@@ -153,13 +133,13 @@ Run all tests (unit and integration) using the `test` target.
|
||||
$ 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
|
||||
docker run --rm -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -v "/home/user/go/src/github/traefik/traefik/dist:/go/src/github.com/traefik/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
|
||||
ok github.com/traefik/traefik 0.005s coverage: 4.1% of statements
|
||||
|
||||
Test success
|
||||
```
|
||||
@@ -187,7 +167,7 @@ More: https://labix.org/gocheck
|
||||
Unit tests can be run from the cloned directory using `$ go test ./...` which should return `ok`, similar to:
|
||||
|
||||
```test
|
||||
ok _/home/user/go/src/github/containous/traefik 0.004s
|
||||
ok _/home/user/go/src/github/traefik/traefik 0.004s
|
||||
```
|
||||
|
||||
Integration tests must be run from the `integration/` directory and require the `-integration` switch: `$ cd integration && go test -integration ./...`.
|
||||
|
@@ -1,3 +1,8 @@
|
||||
---
|
||||
title: "Traefik Data Collection Documentation"
|
||||
description: "To learn more about how Traefik is being used and improve it, we collect anonymous usage statistics from running instances. Read the technical documentation."
|
||||
---
|
||||
|
||||
# Data Collection
|
||||
|
||||
Understanding How Traefik is Being Used
|
||||
@@ -8,26 +13,30 @@ Understanding How Traefik is Being Used
|
||||
Understanding how you use Traefik is very important to us: it helps us improve the solution in many different ways.
|
||||
For this very reason, the sendAnonymousUsage option is mandatory: we want you to take time to consider whether or not you wish to share anonymous data with us so we can benefit from your experience and use cases.
|
||||
|
||||
!!! warning
|
||||
During the alpha stage only, leaving this option unset will not prevent Traefik from running but will generate an error log indicating that it enables data collection by default.
|
||||
!!! example "Enabling Data Collection"
|
||||
|
||||
??? example "Enabling Data Collection with TOML"
|
||||
|
||||
```toml
|
||||
[Global]
|
||||
# Send anonymous usage data
|
||||
sendAnonymousUsage = true
|
||||
```yaml tab="File (YAML)"
|
||||
global:
|
||||
# Send anonymous usage data
|
||||
sendAnonymousUsage: true
|
||||
```
|
||||
|
||||
??? example "Enabling Data Collection with the CLI"
|
||||
|
||||
```bash
|
||||
./traefik --sendAnonymousUsage=true
|
||||
```toml tab="File (TOML)"
|
||||
[global]
|
||||
# Send anonymous usage data
|
||||
sendAnonymousUsage = true
|
||||
```
|
||||
|
||||
|
||||
```bash tab="CLI"
|
||||
# Send anonymous usage data
|
||||
--global.sendAnonymousUsage
|
||||
```
|
||||
|
||||
## Collected Data
|
||||
|
||||
This feature comes from the public proposal [here](https://github.com/containous/traefik/issues/2369).
|
||||
This feature comes from the public proposal [here](https://github.com/traefik/traefik/issues/2369).
|
||||
|
||||
This feature is activated when using Traefik Pilot to better understand the community's need, and also to get information about plug-ins popularity.
|
||||
|
||||
In order to help us learn more about how Traefik 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 important for our users (for example, which provider is popular, and which is not).
|
||||
@@ -40,75 +49,56 @@ Once a day (the first call begins 10 minutes after the start of Traefik), we col
|
||||
- a hash of the configuration
|
||||
- an **anonymized version** of the static configuration (token, user name, password, URL, IP, domain, email, etc, are removed).
|
||||
|
||||
!!! note
|
||||
We do not collect the dynamic configuration information (routers & services).
|
||||
We do not collect these data to run advertising programs.
|
||||
We do not sell these data to third-parties.
|
||||
!!! info
|
||||
|
||||
- We do not collect the dynamic configuration information (routers & services).
|
||||
- We do not collect this data to run advertising programs.
|
||||
- We do not sell this data to third-parties.
|
||||
|
||||
### Example of Collected Data
|
||||
|
||||
??? example "Original configuration"
|
||||
```yaml tab="Original configuration"
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
|
||||
```toml
|
||||
[entryPoints]
|
||||
[entryPoints.web]
|
||||
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"
|
||||
```
|
||||
api: {}
|
||||
|
||||
??? example "Resulting Obfuscated Configuration"
|
||||
providers:
|
||||
docker:
|
||||
endpoint: "tcp://10.10.10.10:2375"
|
||||
exposedByDefault: true
|
||||
swarmMode: true
|
||||
|
||||
```toml
|
||||
[entryPoints]
|
||||
[entryPoints.web]
|
||||
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"
|
||||
```
|
||||
tls:
|
||||
ca: dockerCA
|
||||
cert: dockerCert
|
||||
key: dockerKey
|
||||
insecureSkipVerify: true
|
||||
```
|
||||
|
||||
```yaml tab="Resulting Obfuscated Configuration"
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
|
||||
api: {}
|
||||
|
||||
providers:
|
||||
docker:
|
||||
endpoint: "xxxx"
|
||||
exposedByDefault: true
|
||||
swarmMode: true
|
||||
|
||||
tls:
|
||||
ca: xxxx
|
||||
cert: xxxx
|
||||
key: xxxx
|
||||
insecureSkipVerify: true
|
||||
```
|
||||
|
||||
## The Code for Data Collection
|
||||
|
||||
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/pkg/collector/collector.go)
|
||||
If you want to dig into more details, here is the source code of the collecting system: [collector.go](https://github.com/traefik/traefik/blob/master/pkg/collector/collector.go)
|
||||
|
||||
By default we anonymize all configuration fields, except fields tagged with `export=true`.
|
||||
|
@@ -1,3 +1,8 @@
|
||||
---
|
||||
title: "Traefik Contribution Documentation"
|
||||
description: "Found something unclear in the Traefik Proxy documentation and want to give a try at explaining it better? Read the guide to building documentation."
|
||||
---
|
||||
|
||||
# Documentation
|
||||
|
||||
Features Are Better When You Know How to Use Them
|
||||
@@ -10,17 +15,17 @@ Let's see how.
|
||||
|
||||
### General
|
||||
|
||||
This [documentation](https://docs.traefik.io/) is built with [mkdocs](https://mkdocs.org/).
|
||||
This [documentation](https://doc.traefik.io/traefik/) is built with [mkdocs](https://mkdocs.org/).
|
||||
|
||||
### Method 1: `Docker` and `make`
|
||||
|
||||
You can build the documentation and test it locally (with live reloading), using the `docs` target:
|
||||
You can build the documentation and test it locally (with live reloading), using the `docs-serve` target:
|
||||
|
||||
```bash
|
||||
$ make docs
|
||||
$ make docs-serve
|
||||
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
|
||||
docker run --rm -v /home/user/go/github/traefik/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
|
||||
@@ -29,7 +34,7 @@ docker run --rm -v /home/user/go/github/containous/traefik:/mkdocs -p 8000:8000
|
||||
|
||||
!!! tip "Default URL"
|
||||
|
||||
Your local documentation server will run by default on [http://127.0.0.1:8000](http://127.0.0.1:8000).
|
||||
Your local documentation server will run by default on <http://127.0.0.1:8000>.
|
||||
|
||||
If you only want to build the documentation without serving it locally, you can use the following command:
|
||||
|
||||
@@ -75,24 +80,26 @@ To check that the documentation meets standard expectations (no dead links, html
|
||||
$ make docs-verify
|
||||
docker build -t traefik-docs-verify ./script/docs-verify-docker-image ## Build Validator image
|
||||
...
|
||||
docker run --rm -v /home/travis/build/containous/traefik:/app traefik-docs-verify ## Check for dead links and w3c compliance
|
||||
docker run --rm -v /home/travis/build/traefik/traefik:/app traefik-docs-verify ## Check for dead links and w3c compliance
|
||||
=== Checking HTML content...
|
||||
Running ["HtmlCheck", "ImageCheck", "ScriptCheck", "LinkCheck"] on /app/site/basics/index.html on *.html...
|
||||
```
|
||||
|
||||
!!! note "Clean & Verify"
|
||||
|
||||
If you've made changes to the documentation, it's safter to clean it before verifying it.
|
||||
If you've made changes to the documentation, it's safter to clean it before verifying it.
|
||||
|
||||
```bash
|
||||
$ make docs-clean docs-verify
|
||||
$ make docs
|
||||
...
|
||||
```
|
||||
|
||||
Will perform all necessary steps for you.
|
||||
|
||||
!!! note "Disabling Documentation Verification"
|
||||
|
||||
Verification can be disabled by setting the environment variable `DOCS_VERIFY_SKIP` to `true`:
|
||||
|
||||
|
||||
```shell
|
||||
DOCS_VERIFY_SKIP=true make docs-verify
|
||||
...
|
||||
|
134
docs/content/contributing/maintainers-guidelines.md
Normal file
@@ -0,0 +1,134 @@
|
||||
---
|
||||
title: "Traefik Maintainer's Guidelines Documentation"
|
||||
description: "Interested in contributing more to the community and becoming a Traefik Proxy maintainer? Read the guide to becoming a part of the core team."
|
||||
---
|
||||
|
||||
# Maintainer's Guidelines
|
||||
|
||||

|
||||
|
||||
Note: the document is a work in progress.
|
||||
|
||||
Welcome to the Traefik Community.
|
||||
This document describes how to be part of the core team
|
||||
as well as various responsibilities
|
||||
and guidelines for Traefik maintainers.
|
||||
We are strongly promoting a philosophy of openness and sharing,
|
||||
and firmly standing against the elitist closed approach.
|
||||
Being part of the core team should be accessible to anyone motivated
|
||||
and wants to be part of that journey!
|
||||
|
||||
## Onboarding Process
|
||||
|
||||
If you consider joining our community please drop us a line using Twitter or leave a note in the issue.
|
||||
We will schedule a quick call to meet you and learn more about your motivation.
|
||||
During the call, the team will discuss the process of becoming a maintainer.
|
||||
We will be happy to answer any questions and explain all your doubts.
|
||||
|
||||
## Maintainer's Requirements
|
||||
|
||||
Note: you do not have to meet all the listed requirements,
|
||||
but must have achieved several.
|
||||
|
||||
- Enabled [2FA](https://docs.github.com/en/authentication/securing-your-account-with-two-factor-authentication-2fa/configuring-two-factor-authentication) on your GitHub account
|
||||
- The contributor has opened and successfully run medium to large PR’s in the past 6 months.
|
||||
- The contributor has participated in multiple code reviews of other PR’s,
|
||||
including those of other maintainers and contributors.
|
||||
- The contributor showed a consistent pattern of helpful, non-threatening, and friendly behavior towards other community members in the past.
|
||||
- The contributor is active on Traefik Community forums
|
||||
or other technical forums/boards such as K8S slack, Reddit, StackOverflow, hacker news.
|
||||
- Have read and accepted the contributor guidelines.
|
||||
|
||||
## Maintainer's Responsibilities and Privileges
|
||||
|
||||
There are lots of areas where you can contribute to the project,
|
||||
but we can suggest you start with activities such as:
|
||||
|
||||
- PR reviewing.
|
||||
- According to our guidelines we require you have at least 3 reviewers,
|
||||
thus you can review a PR and leave the relevant comment if it is necessary.
|
||||
- Participating in a daily [issue triage](https://github.com/traefik/contributors-guide/blob/master/issue_triage.md).
|
||||
- The process helps to understand and prioritize the reported issue according to its importance and severity.
|
||||
This is crucial to learn how our users implement Traefik.
|
||||
Each of the issues that are labeled as bug/possible bug/confirmed requires a reproducible use case.
|
||||
You can help in creating a reproducible use case if it has not been added to the issue
|
||||
or use the sample code provided by the reporter.
|
||||
Typically, a simple docker compose should be enough to reproduce the issue.
|
||||
- Code contribution.
|
||||
- Documentation contribution.
|
||||
- Technical documentation is one of the most important components of the product.
|
||||
The ability to set up a testing environment in a few minutes,
|
||||
using the official documentation,
|
||||
is a game changer.
|
||||
- You will be listed on our Maintainers GitHub page
|
||||
as well as on our website in the section [maintainers](maintainers.md).
|
||||
- We will be promoting you on social channels (mostly on Twitter).
|
||||
|
||||
## Governance
|
||||
|
||||
- Roadmap meetings on a regular basis where all maintainers are welcome.
|
||||
|
||||
## Communicating
|
||||
|
||||
- All of our maintainers are added to Slack #traefik-maintainers channel that belongs to Traefik labs workspace.
|
||||
Having the team in one place helps us to communicate effectively.
|
||||
You can reach Traefik core developers directly,
|
||||
which offers the possibility to discuss issues, pull requests, enhancements more efficiently
|
||||
and get the feedback almost immediately.
|
||||
Fewer blockers mean more fun and engaging work.
|
||||
|
||||
- On a daily basis, we publish a report that includes all the activities performed during the day.
|
||||
You are updated in regard to the workload that has been processed including:
|
||||
working on the new features and enhancements,
|
||||
activities related to the reported issues and PR’s,
|
||||
other important project-related announcements.
|
||||
|
||||
- At 5:00 PM CET every day we review all the created issues that have been reported,
|
||||
assign them the appropriate *[labels](maintainers.md#labels)*
|
||||
and prioritize them based on the severity of the problem.
|
||||
The process is called *[issue triaging](https://github.com/traefik/contributors-guide/blob/master/issue_triage.md)*.
|
||||
Each of the maintainers is welcome to join the meeting.
|
||||
For that purpose, we use a dedicated Discord server
|
||||
where you are invited once you have become the official maintainer.
|
||||
|
||||
## Maintainers Activity
|
||||
|
||||
In order to keep the core team efficient and dynamic,
|
||||
maintainers' activity and involvement will be reviewed on a regular basis.
|
||||
|
||||
- Has the maintainer engaged with the team and the community by meeting two or more of these benchmarks in the past six months?
|
||||
- Has the maintainer participated in at least two or three maintainer meetings?
|
||||
- Substantial review of at least one or two PRs from either contributors or maintainers.
|
||||
- Opened at least one or two bug fixes or feature request PRs
|
||||
that were eventually merged (or on a trajectory for merge).
|
||||
- Substantial participation in the Help Wanted program (answered questions, helped identify issues, applied guidelines from the Help Wanted guide to open issues).
|
||||
- Substantial participation with the community in general.
|
||||
|
||||
- Has the maintainer shown a consistent pattern of helpful,
|
||||
non-threatening,
|
||||
and friendly behavior towards other people on the maintainer team and with our community?
|
||||
|
||||
## Additional Comments for (not only) Maintainers
|
||||
|
||||
- Be able to put yourself in users’ shoes.
|
||||
- Be open-minded and respectful with other maintainers and other community members.
|
||||
- Keep the communication public -
|
||||
if anyone tries to communicate with you directly,
|
||||
ask him politely to move the conversation to a public communication channel.
|
||||
- Stay away from defensive comments.
|
||||
- Please try to express your thoughts clearly enough
|
||||
and note that some of us are not native English speakers.
|
||||
Try to rephrase your sentences, avoiding mental shortcuts;
|
||||
none of us is able to predict your thoughts.
|
||||
- There are a lot of use cases of using Traefik
|
||||
and even more issues that are difficult to reproduce.
|
||||
If the issue can’t be replicated due to a lack of reproducible case (a simple docker compose should be enough) -
|
||||
set your time limits while working on the issue
|
||||
and express clearly that you were not able to replicate it.
|
||||
You can come back later to that case.
|
||||
- Be proactive.
|
||||
- Emoji are fine,
|
||||
but if you express yourself clearly enough they are not necessary.
|
||||
They will not replace good communication.
|
||||
- Embrace mentorship.
|
||||
- Keep in mind that we all have the same intent to improve the project.
|
@@ -1,6 +1,11 @@
|
||||
---
|
||||
title: "Traefik Maintainers Documentation"
|
||||
description: "Traefik Proxy is an open source software with a thriving community of contributors and maintainers. Read the list of maintainers on this page."
|
||||
---
|
||||
|
||||
# Maintainers
|
||||
|
||||
## The team
|
||||
## The Team
|
||||
|
||||
* Emile Vauge [@emilevauge](https://github.com/emilevauge)
|
||||
* Vincent Demeester [@vdemeester](https://github.com/vdemeester)
|
||||
@@ -11,75 +16,27 @@
|
||||
* 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)
|
||||
* Marco Jantke [@mjantke](https://github.com/mjeri)
|
||||
* Michaël Matur [@mmatur](https://github.com/mmatur)
|
||||
* Gérald Croës [@geraldcroes](https://github.com/geraldcroes)
|
||||
* Jean-Baptiste Doumenjou [@jbdoumenjou](https://github.com/jbdoumenjou)
|
||||
* Damien Duportal [@dduportal](https://github.com/dduportal)
|
||||
* Mathieu Lonjaret [@mpl](https://github.com/mpl)
|
||||
* Romain Tribotté [@rtribotte](https://github.com/rtribotte)
|
||||
* Kevin Pollet [@kevinpollet](https://github.com/kevinpollet)
|
||||
* Harold Ozouf [@jspdown](https://github.com/jspdown)
|
||||
* Tom Moulard [@tommoulard](https://github.com/tommoulard)
|
||||
|
||||
## Contributions Daily Meeting
|
||||
## Maintainer's Guidelines
|
||||
|
||||
* 3 Maintainers should attend to a Contributions Daily Meeting where we sort and label new issues ([is:issue label:status/0-needs-triage](https://github.com/containous/traefik/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3Astatus%2F0-needs-triage+)), and review every Pull Requests
|
||||
* Every pull request should be checked during the Contributions Daily Meeting
|
||||
* Even if it’s already assigned
|
||||
* Even PR labelled with `contributor/waiting-for-corrections` or `contributor/waiting-for-feedback`
|
||||
* Issues labeled with `priority/P0` and `priority/P1` should be assigned.
|
||||
* Modifying an issue or a pull request (labels, assignees, milestone) is only possible:
|
||||
* During the Contributions Daily Meeting
|
||||
* By an assigned maintainer
|
||||
* In case of emergency, if a change proposal is approved by 2 other maintainers (on Slack, Discord, etc)
|
||||
Please read the [maintainer's guidelines](maintainers-guidelines.md)
|
||||
|
||||
## PR review process:
|
||||
## Issue Triage
|
||||
|
||||
* The status `needs-design-review` is only used in complex/heavy/tricky PRs.
|
||||
* From `1` to `2`: 1 comment that says “design LGTM” (by a senior maintainer).
|
||||
* From `2` to `3`: 3 LGTM approvals by any maintainer.
|
||||
* If needed, a specific maintainer familiar with a particular domain can be requested for the review.
|
||||
* If a PR has been implemented in pair programming, one peer's LGTM goes into the review for free
|
||||
* Amending someone else's pull request is authorized only in emergency, if a rebase is needed, or if the initial contributor is silent
|
||||
Issues and PRs are triaged daily and the process for triaging may be found under [triaging issues](https://github.com/traefik/contributors-guide/blob/master/issue_triage.md) in our [contributors guide repository](https://github.com/traefik/contributors-guide).
|
||||
|
||||
We use [PRM](https://github.com/ldez/prm) to manage locally pull requests.
|
||||
## PR Review Process
|
||||
|
||||
## 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.
|
||||
To preserve commits, add `bot/merge-method-rebase` before `status/3-needs-merge`.
|
||||
|
||||
The status `status/4-merge-in-progress` is only used by the bot.
|
||||
|
||||
If the bot is not able to perform the merge, the label `bot/need-human-merge` is added.
|
||||
In such a situation, solve the conflicts/CI/... and then remove the label `bot/need-human-merge`.
|
||||
|
||||
To prevent the bot from automatically merging a PR, add the label `bot/no-merge`.
|
||||
|
||||
The label `bot/light-review` decreases the number of required LGTM from 3 to 1.
|
||||
|
||||
This label is used when:
|
||||
|
||||
* Updating the vendors from previously reviewed PRs
|
||||
* Merging branches into the master
|
||||
* Preparing the 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]
|
||||
The process for reviewing PRs may be found under [review guidelines](https://github.com/traefik/contributors-guide/blob/master/review_guidelines.md) in our contributors guide repository.
|
||||
|
||||
## Labels
|
||||
|
||||
@@ -167,7 +124,7 @@ The `status/*` labels represent the desired state in the workflow.
|
||||
* `priority/P2`: need to be fixed in the future.
|
||||
* `priority/P3`: maybe.
|
||||
|
||||
### PR size
|
||||
### PR Size
|
||||
|
||||
Automatically set by a bot.
|
||||
|
||||
|
@@ -1,9 +1,14 @@
|
||||
---
|
||||
title: "Traefik Submitting Issues Documentation"
|
||||
description: "Help us help you! Learn how to submit an issue, following the guidelines, so the Traefik Proxy team can help. Read the technical documentation."
|
||||
---
|
||||
|
||||
# Submitting Issues
|
||||
|
||||
Help Us Help You!
|
||||
{: .subtitle }
|
||||
|
||||
We use the [GitHub issue tracker](https://github.com/containous/traefik/issues) to keep track of issues in Traefik.
|
||||
We use the [GitHub issue tracker](https://github.com/traefik/traefik/issues) to keep track of issues in Traefik.
|
||||
|
||||
The process of sorting and checking the issues is a daunting task, and requires a lot of work (more than an hour a day ... just for sorting).
|
||||
To save us some time and get quicker feedback, be sure to follow the guide lines below.
|
||||
@@ -11,11 +16,10 @@ To save us some time and get quicker feedback, be sure to follow the guide lines
|
||||
!!! important "Getting Help Vs Reporting an Issue"
|
||||
|
||||
The issue tracker is not a general support forum, but a place to report bugs and asks for new features.
|
||||
|
||||
|
||||
For end-user related support questions, try using first:
|
||||
|
||||
- the Traefik community Slack channel: [](https://slack.traefik.io)
|
||||
- [Stack Overflow](https://stackoverflow.com/questions/tagged/traefik) (using the `traefik` tag)
|
||||
|
||||
- the Traefik community forum: [](https://community.traefik.io/)
|
||||
|
||||
## Issue Title
|
||||
|
||||
@@ -23,14 +27,14 @@ The title must be short and descriptive. (~60 characters)
|
||||
|
||||
## Description
|
||||
|
||||
Follow the [issue template](https://github.com/containous/traefik/blob/master/.github/ISSUE_TEMPLATE.md) as much as possible.
|
||||
Follow the [issue template](https://github.com/traefik/traefik/blob/master/.github/ISSUE_TEMPLATE.md) as much as possible.
|
||||
|
||||
Explain us in which conditions you encountered the issue, what is your context.
|
||||
|
||||
Remain as clear and concise as possible
|
||||
|
||||
Take time to polish the format of your message so we'll enjoy reading it and working on it.
|
||||
Help the readers focus on what matters, and help them understand the structure of your message (see the [Github Markdown Syntax](https://help.github.com/articles/github-flavored-markdown)).
|
||||
Help the readers focus on what matters, and help them understand the structure of your message (see the [GitHub Markdown Syntax](https://docs.github.com/en/get-started/writing-on-github)).
|
||||
|
||||
## Feature Request
|
||||
|
||||
|
@@ -1,45 +1,14 @@
|
||||
---
|
||||
title: "Traefik Pull Requests Documentation"
|
||||
description: "Looking to contribute to Traefik Proxy? This guide will show you the guidelines for submitting a PR in our contributors guide repository."
|
||||
---
|
||||
|
||||
# Submitting Pull Requests
|
||||
|
||||
A Quick Guide for Efficient Contributions
|
||||
{: .subtitle }
|
||||
|
||||
So you've decide to improve Traefik?
|
||||
So you've decided to improve Traefik?
|
||||
Thank You!
|
||||
Now the last step is to submit your Pull Request in a way that makes sure it gets the attention it deserves.
|
||||
|
||||
Let's go though the classic pitfalls to make sure everything is right.
|
||||
|
||||
## Title
|
||||
|
||||
The title must be short and descriptive. (~60 characters)
|
||||
|
||||
## Description
|
||||
|
||||
Follow the [pull request template](https://github.com/containous/traefik/blob/master/.github/PULL_REQUEST_TEMPLATE.md) as much as possible.
|
||||
|
||||
Explain the conditions which led you to write this PR: give us context.
|
||||
The context should lead to something, an idea or a problem that you’re facing.
|
||||
|
||||
Remain clear and concise.
|
||||
|
||||
Take time to polish the format of your message so we'll enjoy reading it and working on it.
|
||||
Help the readers focus on what matters, and help them understand the structure of your message (see the [Github Markdown Syntax](https://help.github.com/articles/github-flavored-markdown)).
|
||||
|
||||
## PR Content
|
||||
|
||||
- Make it small.
|
||||
- One feature per Pull Request.
|
||||
- Write useful descriptions and titles.
|
||||
- Avoid re-formatting code that is not on the path of your PR.
|
||||
- Make sure the [code builds](building-testing.md).
|
||||
- Make sure [all tests pass](building-testing.md).
|
||||
- Add tests.
|
||||
- Address review comments in terms of additional commits (and don't amend/squash existing ones unless the PR is trivial).
|
||||
|
||||
!!! note "third-party dependencies"
|
||||
|
||||
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.
|
||||
|
||||
!!! tip "10 Tips for Better Pull Requests"
|
||||
|
||||
We enjoyed this article, maybe you will too! [10 tips for better pull requests](https://blog.ploeh.dk/2015/01/15/10-tips-for-better-pull-requests/).
|
||||
Please review the [guidelines on creating PRs](https://github.com/traefik/contributors-guide/blob/master/pr_guidelines.md) for Traefik in our [contributors guide repository](https://github.com/traefik/contributors-guide).
|
||||
|
21
docs/content/contributing/submitting-security-issues.md
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
title: "Traefik Security Documentation"
|
||||
description: "Security is a key part of Traefik Proxy. Read the technical documentation to learn about security advisories, CVE, and how to report a vulnerability."
|
||||
---
|
||||
|
||||
# Security
|
||||
|
||||
## Security Advisories
|
||||
|
||||
We strongly advise you to join our mailing list to be aware of the latest announcements from our security team.
|
||||
You can subscribe sending a mail to security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
|
||||
|
||||
## CVE
|
||||
|
||||
Reported vulnerabilities can be found on
|
||||
[cve.mitre.org](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=traefik).
|
||||
|
||||
## Report a Vulnerability
|
||||
|
||||
We want to keep Traefik safe for everyone.
|
||||
If you've discovered a security vulnerability in Traefik, we appreciate your help in disclosing it to us in a responsible manner, using [this form](https://security.traefik.io).
|
@@ -1,10 +1,15 @@
|
||||
---
|
||||
title: "Traefik Contribution Documentation"
|
||||
description: "Thank you to all those who have contributed! Traefik Proxy is an open-source project that thrives with the support of our passionate community."
|
||||
---
|
||||
|
||||
# Thank You!
|
||||
|
||||
_You_ Made It
|
||||
{: .subtitle}
|
||||
|
||||
Traefik truly is an [open-source project](https://github.com/containous/traefik/),
|
||||
and wouldn't have become what it is today without the help of our [many contributors](https://github.com/containous/traefik/graphs/contributors) (at the time of writing this),
|
||||
Traefik truly is an [open-source project](https://github.com/traefik/traefik/),
|
||||
and wouldn't have become what it is today without the help of our [many contributors](https://github.com/traefik/traefik/graphs/contributors) (at the time of writing this),
|
||||
not accounting for people having helped with issues, tests, comments, articles, ... or just enjoying it and letting others know.
|
||||
|
||||
So once again, thank you for your invaluable help on making Traefik such a good product.
|
||||
|
37
docs/content/deprecation/releases.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# Releases
|
||||
|
||||
## Versions
|
||||
|
||||
Below is a non-exhaustive list of versions and their maintenance status:
|
||||
|
||||
| Version | Release Date | Active Support | Security Support |
|
||||
|---------|--------------|--------------------|------------------|
|
||||
| 2.6 | Jan 24, 2022 | Yes | Yes |
|
||||
| 2.5 | Aug 17, 2021 | Ended Jan 24, 2022 | No |
|
||||
| 2.4 | Jan 19, 2021 | Ended Aug 17, 2021 | No |
|
||||
| 2.3 | Sep 23, 2020 | Ended Jan 19, 2021 | No |
|
||||
| 2.2 | Mar 25, 2020 | Ended Sep 23, 2020 | No |
|
||||
| 2.1 | Dec 11, 2019 | Ended Mar 25, 2020 | No |
|
||||
| 2.0 | Sep 16, 2019 | Ended Dec 11, 2019 | No |
|
||||
| 1.7 | Sep 24, 2018 | Ended Dec 31, 2021 | Contact Support |
|
||||
|
||||
??? example "Active Support / Security Support"
|
||||
|
||||
**Active support**: receives any bug fixes.
|
||||
**Security support**: receives only critical bug and security fixes.
|
||||
|
||||
This page is maintained and updated periodically to reflect our roadmap and any decisions affecting the end of support for Traefik Proxy.
|
||||
|
||||
Please refer to our migration guides for specific instructions on upgrading between versions, an example is the [v1 to v2 migration guide](../migration/v1-to-v2.md).
|
||||
|
||||
!!! important "All target dates for end of support or feature removal announcements may be subject to change."
|
||||
|
||||
## Versioning Scheme
|
||||
|
||||
The Traefik Proxy project follows the [semantic versioning](https://semver.org/) scheme and maintains a separate branch for each minor version. The main branch always represents the next upcoming minor or major version.
|
||||
|
||||
And these are our guiding rules for version support:
|
||||
|
||||
- **Only the latest `minor`** will be on active support at any given time
|
||||
- **The last `minor` after releasing a new `major`** will be supported for 1 year following the `major` release
|
||||
- **Previous rules are subject to change** and in such cases an announcement will be made publicly, [here](https://traefik.io/blog/traefik-2-1-in-the-wild/) is an example extending v1.x branch support.
|
@@ -1,3 +1,8 @@
|
||||
---
|
||||
title: "Traefik Concepts Documentation"
|
||||
description: "Get started with Traefik Proxy. Read the technical documentation for an introduction into the key concepts behind our open source edge router."
|
||||
---
|
||||
|
||||
# Concepts
|
||||
|
||||
Everything You Need to Know
|
||||
@@ -19,15 +24,15 @@ Deploying your services, you attach information that tells Traefik the character
|
||||

|
||||
|
||||
It means that when a service is deployed, Traefik detects it immediately and updates the routing rules in real time.
|
||||
The opposite is true: when you remove a service from your infrastructure, the route will disappear accordingly.
|
||||
Similarly, when a service is removed from the infrastructure, the corresponding route is deleted accordingly.
|
||||
|
||||
You no longer need to create and synchronize configuration files cluttered with IP addresses or other rules.
|
||||
|
||||
!!! note "Many different rules"
|
||||
!!! info "Many different rules"
|
||||
|
||||
In the example above, we used the request [path](../routing/routers/index.md#rule) to determine which service was in charge, but of course you can use many other different [rules](../routing/routers/index.md#rule).
|
||||
|
||||
!!! note "Updating the requests"
|
||||
!!! info "Updating the requests"
|
||||
|
||||
In the [middleware](../middlewares/overview.md) section, you can learn about how to update the requests before forwarding them to the services.
|
||||
|
||||
|
@@ -1,3 +1,8 @@
|
||||
---
|
||||
title: "Traefik Configuration Documentation"
|
||||
description: "Get started with Traefik Proxy. This page will introduce you to the dynamic routing and startup configurations. Read the technical documentation."
|
||||
---
|
||||
|
||||
# Configuration Introduction
|
||||
|
||||
How the Magic Happens
|
||||
@@ -6,55 +11,62 @@ How the Magic Happens
|
||||

|
||||
|
||||
Configuration in Traefik can refer to two different things:
|
||||
|
||||
|
||||
- The fully dynamic routing configuration (referred to as the _dynamic configuration_)
|
||||
- The startup configuration (referred to as the _static configuration_)
|
||||
|
||||
Elements in the _static configuration_ set up connections to [providers](../../providers/overview/) and define the [entrypoints](../../routing/entrypoints/) Traefik will listen to (these elements don't change often).
|
||||
Elements in the _static configuration_ set up connections to [providers](../providers/overview.md) and define the [entrypoints](../routing/entrypoints.md) Traefik will listen to (these elements don't change often).
|
||||
|
||||
The _dynamic configuration_ contains everything that defines how the requests are handled by your system.
|
||||
This configuration can change and is seamlessly hot-reloaded, without any request interuption or connection loss.
|
||||
This configuration can change and is seamlessly hot-reloaded, without any request interruption or connection loss.
|
||||
|
||||
!!! warning "Incompatible Configuration"
|
||||
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.
|
||||
Please be aware that the old configurations for Traefik v1.x are NOT compatible with the v2.x config as of now.
|
||||
If you are running v2, please ensure you are using a v2 configuration.
|
||||
|
||||
## The Dynamic Configuration
|
||||
## The Dynamic Configuration
|
||||
|
||||
Traefik gets its _dynamic configuration_ from [providers](../providers/overview.md): whether an orchestrator, a service registry, or a plain old configuration file. Since this configuration is specific to your infrastructure choices, we invite you to refer to the [dedicated section of this documentation](../providers/overview.md).
|
||||
Traefik gets its _dynamic configuration_ from [providers](../providers/overview.md): whether an orchestrator, a service registry, or a plain old configuration file.
|
||||
|
||||
Since this configuration is specific to your infrastructure choices, we invite you to refer to the [dedicated section of this documentation](../routing/overview.md).
|
||||
|
||||
!!! info ""
|
||||
|
||||
!!! Note
|
||||
|
||||
In the [Quick Start example](../getting-started/quick-start.md), the dynamic configuration comes from docker in the form of labels attached to your containers.
|
||||
|
||||
!!! Note
|
||||
|
||||
HTTPS Certificates also belong to the dynamic configuration. You can add / update / remove them without restarting your Traefik instance.
|
||||
|
||||
|
||||
!!! info "HTTPS Certificates also belong to the dynamic configuration."
|
||||
|
||||
You can add / update / remove them without restarting your Traefik instance.
|
||||
|
||||
## The Static Configuration
|
||||
|
||||
There are three different, mutually exclusive, ways to define static configuration options in Traefik:
|
||||
There are three different, **mutually exclusive** (i.e. you can use only one at the same time), ways to define static configuration options in Traefik:
|
||||
|
||||
- In a configuration file
|
||||
- As environment variables
|
||||
- In the command-line arguments
|
||||
1. In a configuration file
|
||||
1. In the command-line arguments
|
||||
1. As environment variables
|
||||
|
||||
These ways are evaluated in the order listed above.
|
||||
|
||||
If no value was provided for a given option, a default value applies.
|
||||
Moreover, if an option has sub-options, and any of these sub-options is not specified, a default value will apply as well.
|
||||
|
||||
|
||||
For example, the `--providers.docker` option is enough by itself to enable the docker provider, even though sub-options like `--providers.docker.endpoint` exist.
|
||||
Once positioned, this option sets (and resets) all the default values of the sub-options of `--providers.docker`.
|
||||
|
||||
|
||||
### Configuration File
|
||||
|
||||
At startup, Traefik searches for a file named `traefik.toml` in `/etc/traefik/`, `$XDG_CONFIG_HOME/`, `$HOME/.config/`, and `.` (_the working directory_).
|
||||
At startup, Traefik searches for static configuration in a file named `traefik.yml` (or `traefik.yaml` or `traefik.toml`) in:
|
||||
|
||||
- `/etc/traefik/`
|
||||
- `$XDG_CONFIG_HOME/`
|
||||
- `$HOME/.config/`
|
||||
- `.` (_the working directory_).
|
||||
|
||||
You can override this using the `configFile` argument.
|
||||
|
||||
```bash
|
||||
traefik --configFile=foo/bar/myconfigfile.toml
|
||||
traefik --configFile=foo/bar/myconfigfile.yml
|
||||
```
|
||||
|
||||
### Arguments
|
||||
@@ -67,9 +79,15 @@ traefik --help
|
||||
# or
|
||||
|
||||
docker run traefik[:version] --help
|
||||
# ex: docker run traefik:2.0 --help
|
||||
# ex: docker run traefik:v2.6 --help
|
||||
```
|
||||
|
||||
All available arguments can also be found [here](../reference/static-configuration/cli.md).
|
||||
|
||||
### Environment Variables
|
||||
|
||||
All available environment variables can be found [here](../reference/static-configuration/env.md)
|
||||
|
||||
## Available Configuration Options
|
||||
|
||||
All the configuration options are documented in their related section.
|
||||
|
159
docs/content/getting-started/faq.md
Normal file
@@ -0,0 +1,159 @@
|
||||
---
|
||||
title: "Traefik Getting Started FAQ"
|
||||
description: "Check out our FAQ page for answers to commonly asked questions on getting started with Traefik Proxy. Read the technical documentation."
|
||||
---
|
||||
|
||||
# FAQ
|
||||
|
||||
## Why is Traefik Answering `XXX` HTTP Response Status Code?
|
||||
|
||||
Traefik is a dynamic reverse proxy,
|
||||
and while the documentation often demonstrates configuration options through file examples,
|
||||
the core feature of Traefik is its dynamic configurability,
|
||||
directly reacting to changes from providers over time.
|
||||
|
||||
Notably, a part of the configuration is [static](../configuration-overview/#the-static-configuration),
|
||||
and can be provided by a file on startup, whereas various providers,
|
||||
such as the file provider,
|
||||
contribute dynamically all along the traefik instance lifetime to its [dynamic configuration](../configuration-overview/#the-dynamic-configuration) changes.
|
||||
|
||||
In addition, the configuration englobes concepts such as the EntryPoint which can be seen as a listener on the Transport Layer (TCP),
|
||||
as apposed to the Router which is more about the Presentation (TLS) and Application layers (HTTP).
|
||||
And there can be as many routers as one wishes for a given EntryPoint.
|
||||
|
||||
In other words, for a given Entrypoint,
|
||||
at any given time the traffic seen is not bound to be just about one protocol.
|
||||
It could be HTTP, or otherwise. Over TLS, or not.
|
||||
Not to mention that dynamic configuration changes potentially make that kind of traffic vary over time.
|
||||
|
||||
Therefore, in this dynamic context,
|
||||
the static configuration of an `entryPoint` does not give any hint whatsoever about how the traffic going through that `entryPoint` is going to be routed.
|
||||
Or whether it's even going to be routed at all,
|
||||
i.e. whether there is a Router matching the kind of traffic going through it.
|
||||
|
||||
### `404 Not found`
|
||||
|
||||
Traefik returns a `404` response code in the following situations:
|
||||
|
||||
- A request reaching an EntryPoint that has no Routers
|
||||
- An HTTP request reaching an EntryPoint that has no HTTP Router
|
||||
- An HTTPS request reaching an EntryPoint that has no HTTPS Router
|
||||
- A request reaching an EntryPoint that has HTTP/HTTPS Routers that cannot be matched
|
||||
|
||||
From Traefik's point of view,
|
||||
every time a request cannot be matched with a router the correct response code is a `404 Not found`.
|
||||
|
||||
In this situation, the response code is not a `503 Service Unavailable`
|
||||
because Traefik is not able to confirm that the lack of a matching router for a request is only temporary.
|
||||
Traefik's routing configuration is dynamic and aggregated from different providers,
|
||||
hence it's not possible to assume at any moment that a specific route should be handled or not.
|
||||
|
||||
??? info "This behavior is consistent with rfc7231"
|
||||
|
||||
```txt
|
||||
The server is currently unable to handle the request due to a
|
||||
temporary overloading or maintenance of the server. The implication
|
||||
is that this is a temporary condition which will be alleviated after
|
||||
some delay. If known, the length of the delay MAY be indicated in a
|
||||
Retry-After header. If no Retry-After is given, the client SHOULD
|
||||
handle the response as it would for a 500 response.
|
||||
|
||||
Note: The existence of the 503 status code does not imply that a
|
||||
server must use it when becoming overloaded. Some servers may wish
|
||||
to simply refuse the connection.
|
||||
```
|
||||
|
||||
Extract from [rfc7231#section-6.6.4](https://datatracker.ietf.org/doc/html/rfc7231#section-6.6.4).
|
||||
|
||||
### `502 Bad Gateway`
|
||||
|
||||
Traefik returns a `502` response code when an error happens while contacting the upstream service.
|
||||
|
||||
### `503 Service Unavailable`
|
||||
|
||||
Traefik returns a `503` response code when a Router has been matched
|
||||
but there are no servers ready to handle the request.
|
||||
|
||||
This situation is encountered when a service has been explicitly configured without servers,
|
||||
or when a service has healthcheck enabled and all servers are unhealthy.
|
||||
|
||||
### `XXX` Instead of `404`
|
||||
|
||||
Sometimes, the `404` response code doesn't play well with other parties or services (such as CDNs).
|
||||
|
||||
In these situations, you may want Traefik to always reply with a `503` response code,
|
||||
instead of a `404` response code.
|
||||
|
||||
To achieve this behavior, a simple catchall router,
|
||||
with the lowest possible priority and routing to a service without servers,
|
||||
can handle all the requests when no other router has been matched.
|
||||
|
||||
The example below is a file provider only version (`yaml`) of what this configuration could look like:
|
||||
|
||||
```yaml tab="Static configuration"
|
||||
# traefik.yml
|
||||
|
||||
entrypoints:
|
||||
web:
|
||||
address: :80
|
||||
|
||||
providers:
|
||||
file:
|
||||
filename: dynamic.yaml
|
||||
```
|
||||
|
||||
```yaml tab="Dynamic configuration"
|
||||
# dynamic.yaml
|
||||
|
||||
http:
|
||||
routers:
|
||||
catchall:
|
||||
# attached only to web entryPoint
|
||||
entryPoints:
|
||||
- "web"
|
||||
# catchall rule
|
||||
rule: "PathPrefix(`/`)"
|
||||
service: unavailable
|
||||
# lowest possible priority
|
||||
# evaluated when no other router is matched
|
||||
priority: 1
|
||||
|
||||
services:
|
||||
# Service that will always answer a 503 Service Unavailable response
|
||||
unavailable:
|
||||
loadBalancer:
|
||||
servers: {}
|
||||
```
|
||||
|
||||
!!! info "Dedicated service"
|
||||
If there is a need for a response code other than a `503` and/or a custom message,
|
||||
the principle of the above example above (a catchall router) still stands,
|
||||
but the `unavailable` service should be adapted to fit such a need.
|
||||
|
||||
## Why Is My TLS Certificate Not Reloaded When Its Contents Change?
|
||||
|
||||
With the file provider,
|
||||
a configuration update is only triggered when one of the [watched](../providers/file.md#provider-configuration) configuration files is modified.
|
||||
|
||||
Which is why, when a certificate is defined by path,
|
||||
and the actual contents of this certificate change,
|
||||
a configuration update is _not_ triggered.
|
||||
|
||||
To take into account the new certificate contents, the update of the dynamic configuration must be forced.
|
||||
One way to achieve that, is to trigger a file notification,
|
||||
for example, by using the `touch` command on the configuration file.
|
||||
|
||||
## What Are the Forwarded Headers When Proxying HTTP Requests?
|
||||
|
||||
By default, the following headers are automatically added when proxying requests:
|
||||
|
||||
| Property | HTTP Header |
|
||||
|---------------------------|----------------------------|
|
||||
| Client's IP | X-Forwarded-For, X-Real-Ip |
|
||||
| Host | X-Forwarded-Host |
|
||||
| Port | X-Forwarded-Port |
|
||||
| Protocol | X-Forwarded-Proto |
|
||||
| Proxy Server's Hostname | X-Forwarded-Server |
|
||||
|
||||
For more details,
|
||||
please check out the [forwarded header](../routing/entrypoints.md#forwarded-headers) documentation.
|
180
docs/content/getting-started/install-traefik.md
Normal file
@@ -0,0 +1,180 @@
|
||||
---
|
||||
title: "Traefik Installation Documentation"
|
||||
description: "There are several flavors to choose from when installing Traefik Proxy. Get started with Traefik Proxy, and read the technical documentation."
|
||||
---
|
||||
|
||||
# Install Traefik
|
||||
|
||||
You can install Traefik with the following flavors:
|
||||
|
||||
* [Use the official Docker image](./#use-the-official-docker-image)
|
||||
* [Use the Helm Chart](./#use-the-helm-chart)
|
||||
* [Use the binary distribution](./#use-the-binary-distribution)
|
||||
* [Compile your binary from the sources](./#compile-your-binary-from-the-sources)
|
||||
|
||||
## Use the Official Docker Image
|
||||
|
||||
Choose one of the [official Docker images](https://hub.docker.com/_/traefik) and run it with one sample configuration file:
|
||||
|
||||
* [YAML](https://raw.githubusercontent.com/traefik/traefik/v2.6/traefik.sample.yml)
|
||||
* [TOML](https://raw.githubusercontent.com/traefik/traefik/v2.6/traefik.sample.toml)
|
||||
|
||||
```bash
|
||||
docker run -d -p 8080:8080 -p 80:80 \
|
||||
-v $PWD/traefik.yml:/etc/traefik/traefik.yml traefik:v2.6
|
||||
```
|
||||
|
||||
For more details, go to the [Docker provider documentation](../providers/docker.md)
|
||||
|
||||
!!! tip
|
||||
|
||||
* Prefer a fixed version than the latest that could be an unexpected version.
|
||||
ex: `traefik:v2.6`
|
||||
* Docker images are based from the [Alpine Linux Official image](https://hub.docker.com/_/alpine).
|
||||
* Any orchestrator using docker images can fetch the official Traefik docker image.
|
||||
|
||||
## Use the Helm Chart
|
||||
|
||||
!!! warning
|
||||
|
||||
The Traefik Chart from
|
||||
[Helm's default charts repository](https://github.com/helm/charts/tree/master/stable/traefik) is still using [Traefik v1.7](https://doc.traefik.io/traefik/v1.7).
|
||||
|
||||
Traefik can be installed in Kubernetes using the Helm chart from <https://github.com/traefik/traefik-helm-chart>.
|
||||
|
||||
Ensure that the following requirements are met:
|
||||
|
||||
* Kubernetes 1.14+
|
||||
* Helm version 3.x is [installed](https://helm.sh/docs/intro/install/)
|
||||
|
||||
Add Traefik's chart repository to Helm:
|
||||
|
||||
```bash
|
||||
helm repo add traefik https://helm.traefik.io/traefik
|
||||
```
|
||||
|
||||
You can update the chart repository by running:
|
||||
|
||||
```bash
|
||||
helm repo update
|
||||
```
|
||||
|
||||
And install it with the `helm` command line:
|
||||
|
||||
```bash
|
||||
helm install traefik traefik/traefik
|
||||
```
|
||||
|
||||
!!! tip "Helm Features"
|
||||
|
||||
All [Helm features](https://helm.sh/docs/intro/using_helm/) are supported.
|
||||
For instance, installing the chart in a dedicated namespace:
|
||||
|
||||
```bash tab="Install in a Dedicated Namespace"
|
||||
kubectl create ns traefik-v2
|
||||
# Install in the namespace "traefik-v2"
|
||||
helm install --namespace=traefik-v2 \
|
||||
traefik traefik/traefik
|
||||
```
|
||||
|
||||
??? example "Installing with Custom Values"
|
||||
|
||||
You can customize the installation by specifying custom values,
|
||||
as with [any helm chart](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing).
|
||||
{: #helm-custom-values }
|
||||
|
||||
The values are not (yet) documented, but are self-explanatory:
|
||||
you can look at the [default `values.yaml`](https://github.com/traefik/traefik-helm-chart/blob/master/traefik/values.yaml) file to explore possibilities.
|
||||
|
||||
You can also set Traefik command line flags using `additionalArguments`.
|
||||
Example of installation with logging set to `DEBUG`:
|
||||
|
||||
```bash tab="Using Helm CLI"
|
||||
helm install --namespace=traefik-v2 \
|
||||
--set="additionalArguments={--log.level=DEBUG}" \
|
||||
traefik traefik/traefik
|
||||
```
|
||||
|
||||
```yml tab="With a custom values file"
|
||||
# File custom-values.yml
|
||||
## Install with "helm install --values=./custom-values.yml traefik traefik/traefik
|
||||
additionalArguments:
|
||||
- "--log.level=DEBUG"
|
||||
```
|
||||
|
||||
### Exposing the Traefik dashboard
|
||||
|
||||
This HelmChart does not expose the Traefik dashboard by default, for security concerns.
|
||||
Thus, there are multiple ways to expose the dashboard.
|
||||
For instance, the dashboard access could be achieved through a port-forward:
|
||||
|
||||
```shell
|
||||
kubectl port-forward $(kubectl get pods --selector "app.kubernetes.io/name=traefik" --output=name) 9000:9000
|
||||
```
|
||||
|
||||
It can then be reached at: `http://127.0.0.1:9000/dashboard/`
|
||||
|
||||
Another way would be to apply your own configuration, for instance,
|
||||
by defining and applying an IngressRoute CRD (`kubectl apply -f dashboard.yaml`):
|
||||
|
||||
```yaml
|
||||
# dashboard.yaml
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: dashboard
|
||||
spec:
|
||||
entryPoints:
|
||||
- web
|
||||
routes:
|
||||
- match: Host(`traefik.localhost`) && (PathPrefix(`/dashboard`) || PathPrefix(`/api`))
|
||||
kind: Rule
|
||||
services:
|
||||
- name: api@internal
|
||||
kind: TraefikService
|
||||
```
|
||||
|
||||
## Use the Binary Distribution
|
||||
|
||||
Grab the latest binary from the [releases](https://github.com/traefik/traefik/releases) page.
|
||||
|
||||
??? info "Check the integrity of the downloaded file"
|
||||
|
||||
```bash tab="Linux"
|
||||
# Compare this value to the one found in traefik-${traefik_version}_checksums.txt
|
||||
sha256sum ./traefik_${traefik_version}_linux_${arch}.tar.gz
|
||||
```
|
||||
|
||||
```bash tab="macOS"
|
||||
# Compare this value to the one found in traefik-${traefik_version}_checksums.txt
|
||||
shasum -a256 ./traefik_${traefik_version}_darwin_amd64.tar.gz
|
||||
```
|
||||
|
||||
```powershell tab="Windows PowerShell"
|
||||
# Compare this value to the one found in traefik-${traefik_version}_checksums.txt
|
||||
Get-FileHash ./traefik_${traefik_version}_windows_${arch}.zip -Algorithm SHA256
|
||||
```
|
||||
|
||||
??? info "Extract the downloaded archive"
|
||||
|
||||
```bash tab="Linux"
|
||||
tar -zxvf traefik_${traefik_version}_linux_${arch}.tar.gz
|
||||
```
|
||||
|
||||
```bash tab="macOS"
|
||||
tar -zxvf ./traefik_${traefik_version}_darwin_amd64.tar.gz
|
||||
```
|
||||
|
||||
```powershell tab="Windows PowerShell"
|
||||
Expand-Archive traefik_${traefik_version}_windows_${arch}.zip
|
||||
```
|
||||
|
||||
And run it:
|
||||
|
||||
```bash
|
||||
./traefik --help
|
||||
```
|
||||
|
||||
## Compile your Binary from the Sources
|
||||
|
||||
All the details are available in the [Contributing Guide](../contributing/building-testing.md)
|
@@ -1,3 +1,8 @@
|
||||
---
|
||||
title: "Traefik Getting Started Quickly"
|
||||
description: "Looking to get started with Traefik Proxy quickly? Read the technical documentation to learn a simple use case that leverages Docker."
|
||||
---
|
||||
|
||||
# Quick Start
|
||||
|
||||
A Simple Use Case Using Docker
|
||||
@@ -14,13 +19,18 @@ version: '3'
|
||||
|
||||
services:
|
||||
reverse-proxy:
|
||||
image: traefik:v2.0 # The official v2.0 Traefik docker image
|
||||
command: --api --providers.docker # Enables the web UI and tells Traefik to listen to docker
|
||||
# The official v2 Traefik docker image
|
||||
image: traefik:v2.6
|
||||
# Enables the web UI and tells Traefik to listen to docker
|
||||
command: --api.insecure=true --providers.docker
|
||||
ports:
|
||||
- "80:80" # The HTTP port
|
||||
- "8080:8080" # The Web UI (enabled by --api)
|
||||
# The HTTP port
|
||||
- "80:80"
|
||||
# The Web UI (enabled by --api.insecure=true)
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock # So that Traefik can listen to the Docker events
|
||||
# So that Traefik can listen to the Docker events
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
```
|
||||
|
||||
**That's it. Now you can launch Traefik!**
|
||||
@@ -31,7 +41,7 @@ Start your `reverse-proxy` with the following command:
|
||||
docker-compose up -d reverse-proxy
|
||||
```
|
||||
|
||||
You can open a browser and go to [http://localhost:8080/api/rawdata](http://localhost:8080/api/rawdata) to see Traefik's API rawdata (we'll go back there once we have launched a service in step 2).
|
||||
You can open a browser and go to `http://localhost:8080/api/rawdata` to see Traefik's API rawdata (we'll go back there once we have launched a service in step 2).
|
||||
|
||||
## Traefik Detects New Services and Creates the Route for You
|
||||
|
||||
@@ -42,7 +52,8 @@ Edit your `docker-compose.yml` file and add the following at the end of your fil
|
||||
```yaml
|
||||
# ...
|
||||
whoami:
|
||||
image: containous/whoami # A container that exposes an API to show its IP address
|
||||
# A container that exposes an API to show its IP address
|
||||
image: traefik/whoami
|
||||
labels:
|
||||
- "traefik.http.routers.whoami.rule=Host(`whoami.docker.localhost`)"
|
||||
```
|
||||
@@ -55,7 +66,7 @@ Start the `whoami` service with the following command:
|
||||
docker-compose up -d whoami
|
||||
```
|
||||
|
||||
Go back to your browser ([http://localhost:8080/api/rawdata](http://localhost:8080/api/rawdata)) and see that Traefik has automatically detected the new container and updated its own configuration.
|
||||
Go back to your browser (`http://localhost:8080/api/rawdata`) and see that Traefik has automatically detected the new container and updated its own configuration.
|
||||
|
||||
When Traefik detects new services, it creates the corresponding routes so you can call them ... _let's see!_ (Here, we're using curl)
|
||||
|
||||
@@ -79,7 +90,7 @@ Run more instances of your `whoami` service with the following command:
|
||||
docker-compose up -d --scale whoami=2
|
||||
```
|
||||
|
||||
Go back to your browser ([http://localhost:8080/api/rawdata](http://localhost:8080/api/rawdata)) and see that Traefik has automatically detected the new instance of the container.
|
||||
Go back to your browser (`http://localhost:8080/api/rawdata`) and see that Traefik has automatically detected the new instance of the container.
|
||||
|
||||
Finally, see that Traefik load-balances between the two instances of your service by running the following command twice:
|
||||
|
||||
|
@@ -1,22 +0,0 @@
|
||||
# TODO -- Glossary
|
||||
|
||||
Where Every Technical Word finds its Definition`
|
||||
{: .subtitle}
|
||||
|
||||
- [ ] Provider
|
||||
- [ ] Types of providers (KV, annotation based, label based, configuration based)
|
||||
- [ ] Entrypoint
|
||||
- [ ] Routers
|
||||
- [ ] Middleware
|
||||
- [ ] Service
|
||||
- [ ] [Static configuration](getting-started/configuration-overview.md#the-static-configuration)
|
||||
- [ ] [Dynamic configuration](getting-started/configuration-overview.md#the-dynamic-configuration)
|
||||
- [ ] ACME
|
||||
- [ ] TraefikEE
|
||||
- [ ] Tracing
|
||||
- [ ] Metrics
|
||||
- [ ] Orchestrator
|
||||
- [ ] Key Value Store
|
||||
- [ ] Logs
|
||||
- [ ] Traefiker
|
||||
- [ ] Traefik (How to pronounce)
|
@@ -1,348 +0,0 @@
|
||||
# ACME
|
||||
|
||||
Automatic HTTPS
|
||||
{: .subtitle }
|
||||
|
||||
You can configure Traefik to use an ACME provider (like Let's Encrypt) for automatic certificate generation.
|
||||
|
||||
!!! warning "Let's Encrypt and Rate Limiting"
|
||||
Note that Let's Encrypt API has [rate limiting](https://letsencrypt.org/docs/rate-limits).
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
??? example "Enabling ACME"
|
||||
|
||||
```toml
|
||||
[entryPoints]
|
||||
[entryPoints.web]
|
||||
address = ":80"
|
||||
|
||||
[entryPoints.http-tls]
|
||||
address = ":443"
|
||||
|
||||
[acme] # every router with TLS enabled will now be able to use ACME for its certificates
|
||||
email = "your-email@your-domain.org"
|
||||
storage = "acme.json"
|
||||
onHostRule = true # dynamic generation based on the Host() & HostSNI() matchers
|
||||
[acme.httpChallenge]
|
||||
entryPoint = "web" # used during the challenge
|
||||
```
|
||||
|
||||
??? example "Configuring Wildcard Certificates"
|
||||
|
||||
```toml
|
||||
[entryPoints]
|
||||
[entryPoints.web]
|
||||
address = ":80"
|
||||
|
||||
[entryPoints.http-tls]
|
||||
address = ":443"
|
||||
|
||||
[acme]
|
||||
email = "your-email@your-domain.org"
|
||||
storage = "acme.json"
|
||||
[acme.dnsChallenge]
|
||||
provider = "xxx"
|
||||
|
||||
[[acme.domains]]
|
||||
main = "*.mydomain.com"
|
||||
sans = ["mydomain.com"]
|
||||
```
|
||||
|
||||
??? note "Configuration Reference"
|
||||
|
||||
There are many available options for ACME. For a quick glance at what's possible, browse the configuration reference:
|
||||
|
||||
```toml
|
||||
--8<-- "content/https-tls/ref-acme.toml"
|
||||
```
|
||||
|
||||
## Automatic Renewals
|
||||
|
||||
Traefik automatically tracks the expiry date of ACME certificates it generates.
|
||||
|
||||
If there are less than 30 days remaining before the certificate expires, Traefik will attempt to rewnew it automatically.
|
||||
|
||||
!!! note
|
||||
Certificates that are no longer used may still be renewed, as Traefik does not currently check if the certificate is being used before renewing.
|
||||
|
||||
## The Different ACME Challenges
|
||||
|
||||
### `tlsChallenge`
|
||||
|
||||
Use the `TLS-ALPN-01` challenge to generate and renew ACME certificates by provisioning a TLS certificate.
|
||||
|
||||
As described on the Let's Encrypt [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72),
|
||||
when using the `TLS-ALPN-01` challenge, Traefik must be reachable by Let's Encrypt through port 443.
|
||||
|
||||
??? example "Configuring the `tlsChallenge`"
|
||||
|
||||
```toml
|
||||
[acme]
|
||||
[acme.tlsChallenge]
|
||||
```
|
||||
|
||||
### `httpChallenge`
|
||||
|
||||
Use the `HTTP-01` challenge to generate and renew ACME certificates by provisioning an HTTP resource under a well-known URI.
|
||||
|
||||
As described on the Let's Encrypt [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72),
|
||||
when using the `HTTP-01` challenge, `acme.httpChallenge.entryPoint` must be reachable by Let's Encrypt through port 80.
|
||||
|
||||
??? example "Using an EntryPoint Called http for the `httpChallenge`"
|
||||
|
||||
```toml
|
||||
[acme]
|
||||
# ...
|
||||
[acme.httpChallenge]
|
||||
entryPoint = "http"
|
||||
```
|
||||
|
||||
!!! note
|
||||
Redirection is fully compatible with the `HTTP-01` challenge.
|
||||
|
||||
### `dnsChallenge`
|
||||
|
||||
Use the `DNS-01` challenge to generate and renew ACME certificates by provisioning a DNS record.
|
||||
|
||||
??? example "Configuring a `dnsChallenge` with the DigitalOcean Provider"
|
||||
|
||||
```toml
|
||||
[acme]
|
||||
# ...
|
||||
[acme.dnsChallenge]
|
||||
provider = "digitalocean"
|
||||
delayBeforeCheck = 0
|
||||
# ...
|
||||
```
|
||||
|
||||
!!! important
|
||||
A `provider` is mandatory.
|
||||
|
||||
#### `providers`
|
||||
|
||||
Here is a list of supported `providers`, that can automate the DNS verification,
|
||||
along with the required environment variables and their [wildcard & root domain support](#wildcard-domains).
|
||||
Do not hesitate to complete it.
|
||||
|
||||
Every lego environment variable can be overridden by their respective `_FILE` counterpart, which should have a filepath to a file that contains the secret as its value.
|
||||
For example, `CF_API_EMAIL_FILE=/run/secrets/traefik_cf-api-email` could be used to provide a Cloudflare API email address as a Docker secret named `traefik_cf-api-email`.
|
||||
|
||||
| Provider Name | Provider Code | Environment Variables | |
|
||||
|-------------------------------------------------------------|----------------|---------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------|
|
||||
| [ACME DNS](https://github.com/joohoi/acme-dns) | `acme-dns` | `ACME_DNS_API_BASE`, `ACME_DNS_STORAGE_PATH` | [Additional configuration](https://go-acme.github.io/lego/dns/acme-dns) |
|
||||
| [Alibaba Cloud](https://www.vultr.com) | `alidns` | `ALICLOUD_ACCESS_KEY`, `ALICLOUD_SECRET_KEY`, `ALICLOUD_REGION_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/alidns) |
|
||||
| [Auroradns](https://www.pcextreme.com/aurora/dns) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` | [Additional configuration](https://go-acme.github.io/lego/dns/auroradns) |
|
||||
| [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP`, `[AZURE_METADATA_ENDPOINT]` | [Additional configuration](https://go-acme.github.io/lego/dns/azure) |
|
||||
| [Blue Cat](https://www.bluecatnetworks.com/) | `bluecat` | `BLUECAT_SERVER_URL`, `BLUECAT_USER_NAME`, `BLUECAT_PASSWORD`, `BLUECAT_CONFIG_NAME`, `BLUECAT_DNS_VIEW` | [Additional configuration](https://go-acme.github.io/lego/dns/bluecat) |
|
||||
| [ClouDNS](https://www.cloudns.net/) | `cloudns` | `CLOUDNS_AUTH_ID`, `CLOUDNS_AUTH_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudns) |
|
||||
| [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CF_API_EMAIL`, `CF_API_KEY` - The `Global API Key` needs to be used, not the `Origin CA Key` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudflare) |
|
||||
| [CloudXNS](https://www.cloudxns.net) | `cloudxns` | `CLOUDXNS_API_KEY`, `CLOUDXNS_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudxns) |
|
||||
| [ConoHa](https://www.conoha.jp) | `conoha` | `CONOHA_TENANT_ID`, `CONOHA_API_USERNAME`, `CONOHA_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/conoha) |
|
||||
| [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/digitalocean) |
|
||||
| [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/dnsimple) |
|
||||
| [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` | [Additional configuration](https://go-acme.github.io/lego/dns/dnsmadeeasy) |
|
||||
| [DNSPod](https://www.dnspod.com/) | `dnspod` | `DNSPOD_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/dnspod) |
|
||||
| [Domain Offensive (do.de)](https://www.do.de/) | `dode` | `DODE_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/dode) |
|
||||
| [DreamHost](https://www.dreamhost.com/) | `dreamhost` | `DREAMHOST_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/dreamhost) |
|
||||
| [Duck DNS](https://www.duckdns.org/) | `duckdns` | `DUCKDNS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/duckdns) |
|
||||
| [Dyn](https://dyn.com) | `dyn` | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/dyn) |
|
||||
| External Program | `exec` | `EXEC_PATH` | [Additional configuration](https://go-acme.github.io/lego/dns/exec) |
|
||||
| [Exoscale](https://www.exoscale.com) | `exoscale` | `EXOSCALE_API_KEY`, `EXOSCALE_API_SECRET`, `EXOSCALE_ENDPOINT` | [Additional configuration](https://go-acme.github.io/lego/dns/exoscale) |
|
||||
| [Fast DNS](https://www.akamai.com/) | `fastdns` | `AKAMAI_CLIENT_TOKEN`, `AKAMAI_CLIENT_SECRET`, `AKAMAI_ACCESS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/fastdns) |
|
||||
| [Gandi](https://www.gandi.net) | `gandi` | `GANDI_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/gandi) |
|
||||
| [Gandi v5](http://doc.livedns.gandi.net) | `gandiv5` | `GANDIV5_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/gandiv5) |
|
||||
| [Glesys](https://glesys.com/) | `glesys` | `GLESYS_API_USER`, `GLESYS_API_KEY`, `GLESYS_DOMAIN` | [Additional configuration](https://go-acme.github.io/lego/dns/glesys) |
|
||||
| [GoDaddy](https://godaddy.com/) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/godaddy) |
|
||||
| [Google Cloud DNS](https://cloud.google.com/dns/docs/) | `gcloud` | `GCE_PROJECT`, Application Default Credentials [^2] [^3], [`GCE_SERVICE_ACCOUNT_FILE`] | [Additional configuration](https://go-acme.github.io/lego/dns/gcloud) |
|
||||
| [hosting.de](https://www.hosting.de) | `hostingde` | `HOSTINGDE_API_KEY`, `HOSTINGDE_ZONE_NAME` | [Additional configuration](https://go-acme.github.io/lego/dns/hostingde) |
|
||||
| HTTP request | `httpreq` | `HTTPREQ_ENDPOINT`, `HTTPREQ_MODE`, `HTTPREQ_USERNAME`, `HTTPREQ_PASSWORD` [^1] | [Additional configuration](https://go-acme.github.io/lego/dns/httpreq) |
|
||||
| [IIJ](https://www.iij.ad.jp/) | `iij` | `IIJ_API_ACCESS_KEY`, `IIJ_API_SECRET_KEY`, `IIJ_DO_SERVICE_CODE` | [Additional configuration](https://go-acme.github.io/lego/dns/iij) |
|
||||
| [INWX](https://www.inwx.de/en) | `inwx` | `INWX_USERNAME`, `INWX_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/inwx) |
|
||||
| [Lightsail](https://aws.amazon.com/lightsail/) | `lightsail` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `DNS_ZONE` | [Additional configuration](https://go-acme.github.io/lego/dns/lightsail) |
|
||||
| [Linode](https://www.linode.com) | `linode` | `LINODE_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/linode) |
|
||||
| [Linode v4](https://www.linode.com) | `linodev4` | `LINODE_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/linodev4) |
|
||||
| manual | - | none, but you need to run Traefik interactively [^4], turn on `acmeLogging` to see instructions and press <kbd>Enter</kbd>. | |
|
||||
| [MyDNS.jp](https://www.mydns.jp/) | `mydnsjp` | `MYDNSJP_MASTER_ID`, `MYDNSJP_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/mydnsjp) |
|
||||
| [Namecheap](https://www.namecheap.com) | `namecheap` | `NAMECHEAP_API_USER`, `NAMECHEAP_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/namecheap) |
|
||||
| [name.com](https://www.name.com/) | `namedotcom` | `NAMECOM_USERNAME`, `NAMECOM_API_TOKEN`, `NAMECOM_SERVER` | [Additional configuration](https://go-acme.github.io/lego/dns/namedotcom) |
|
||||
| [Netcup](https://www.netcup.eu/) | `netcup` | `NETCUP_CUSTOMER_NUMBER`, `NETCUP_API_KEY`, `NETCUP_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/netcup) |
|
||||
| [NIFCloud](https://cloud.nifty.com/service/dns.htm) | `nifcloud` | `NIFCLOUD_ACCESS_KEY_ID`, `NIFCLOUD_SECRET_ACCESS_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/nifcloud) |
|
||||
| [Ns1](https://ns1.com/) | `ns1` | `NS1_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/ns1) |
|
||||
| [Open Telekom Cloud](https://cloud.telekom.de) | `otc` | `OTC_DOMAIN_NAME`, `OTC_USER_NAME`, `OTC_PASSWORD`, `OTC_PROJECT_NAME`, `OTC_IDENTITY_ENDPOINT` | [Additional configuration](https://go-acme.github.io/lego/dns/otc) |
|
||||
| [OVH](https://www.ovh.com) | `ovh` | `OVH_ENDPOINT`, `OVH_APPLICATION_KEY`, `OVH_APPLICATION_SECRET`, `OVH_CONSUMER_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/ovh) |
|
||||
| [Openstack Designate](https://docs.openstack.org/designate) | `designate` | `OS_AUTH_URL`, `OS_USERNAME`, `OS_PASSWORD`, `OS_TENANT_NAME`, `OS_REGION_NAME` | [Additional configuration](https://go-acme.github.io/lego/dns/designate) |
|
||||
| [Oracle Cloud](https://cloud.oracle.com/home) | `oraclecloud` | `OCI_COMPARTMENT_OCID`, `OCI_PRIVKEY_FILE`, `OCI_PRIVKEY_PASS`, `OCI_PUBKEY_FINGERPRINT`, `OCI_REGION`, `OCI_TENANCY_OCID`, `OCI_USER_OCID` | [Additional configuration](https://go-acme.github.io/lego/dns/oraclecloud) |
|
||||
| [PowerDNS](https://www.powerdns.com) | `pdns` | `PDNS_API_KEY`, `PDNS_API_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/pdns) |
|
||||
| [Rackspace](https://www.rackspace.com/cloud/dns) | `rackspace` | `RACKSPACE_USER`, `RACKSPACE_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/rackspace) |
|
||||
| [RFC2136](https://tools.ietf.org/html/rfc2136) | `rfc2136` | `RFC2136_TSIG_KEY`, `RFC2136_TSIG_SECRET`, `RFC2136_TSIG_ALGORITHM`, `RFC2136_NAMESERVER` | [Additional configuration](https://go-acme.github.io/lego/dns/rfc2136) |
|
||||
| [Route 53](https://aws.amazon.com/route53/) | `route53` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `[AWS_REGION]`, `[AWS_HOSTED_ZONE_ID]` or a configured user/instance IAM profile. | [Additional configuration](https://go-acme.github.io/lego/dns/route53) |
|
||||
| [Sakura Cloud](https://cloud.sakura.ad.jp/) | `sakuracloud` | `SAKURACLOUD_ACCESS_TOKEN`, `SAKURACLOUD_ACCESS_TOKEN_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/sakuracloud) |
|
||||
| [Selectel](https://selectel.ru/en/) | `selectel` | `SELECTEL_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/selectel) |
|
||||
| [Stackpath](https://www.stackpath.com/) | `stackpath` | `STACKPATH_CLIENT_ID`, `STACKPATH_CLIENT_SECRET`, `STACKPATH_STACK_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/stackpath) |
|
||||
| [TransIP](https://www.transip.nl/) | `transip` | `TRANSIP_ACCOUNT_NAME`, `TRANSIP_PRIVATE_KEY_PATH` | [Additional configuration](https://go-acme.github.io/lego/dns/transip) |
|
||||
| [VegaDNS](https://github.com/shupp/VegaDNS-API) | `vegadns` | `SECRET_VEGADNS_KEY`, `SECRET_VEGADNS_SECRET`, `VEGADNS_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/vegadns) |
|
||||
| [Vscale](https://vscale.io/) | `vscale` | `VSCALE_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/vscale) |
|
||||
| [VULTR](https://www.vultr.com) | `vultr` | `VULTR_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/vultr) |
|
||||
| [Zone.ee](https://www.zone.ee) | `zoneee` | `ZONEEE_API_USER`, `ZONEEE_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/zoneee) |
|
||||
|
||||
[^1]: more information about the HTTP message format can be found [here](https://go-acme.github.io/lego/dns/httpreq/)
|
||||
[^2]: [providing_credentials_to_your_application](https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application)
|
||||
[^3]: [google/default.go](https://github.com/golang/oauth2/blob/36a7019397c4c86cf59eeab3bc0d188bac444277/google/default.go#L61-L76)
|
||||
[^4]: `docker stack` remark: there is no way to support terminal attached to container when deploying with `docker stack`, so you might need to run container with `docker run -it` to generate certificates using `manual` provider.
|
||||
|
||||
!!! note "`delayBeforeCheck`"
|
||||
By default, the `provider` verifies the TXT record _before_ letting ACME verify.
|
||||
You can delay this operation by specifying a delay (in seconds) with `delayBeforeCheck` (value must be greater than zero).
|
||||
This option is useful when internal networks block external DNS queries.
|
||||
|
||||
#### `resolvers`
|
||||
|
||||
Use custom DNS servers to resolve the FQDN authority.
|
||||
|
||||
```toml
|
||||
[acme]
|
||||
# ...
|
||||
[acme.dnsChallenge]
|
||||
# ...
|
||||
resolvers = ["1.1.1.1:53", "8.8.8.8:53"]
|
||||
```
|
||||
|
||||
#### Wildcard Domains
|
||||
|
||||
[ACME V2](https://community.letsencrypt.org/t/acme-v2-and-wildcard-certificate-support-is-live/55579) supports wildcard certificates.
|
||||
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](#dnschallenge).
|
||||
|
||||
```toml
|
||||
[acme]
|
||||
# ...
|
||||
[[acme.domains]]
|
||||
main = "*.local1.com"
|
||||
sans = ["local1.com"]
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
!!! note "Double Wildcard Certificates"
|
||||
It is not possible to request a double wildcard certificate for a domain (for example `*.*.local.com`).
|
||||
|
||||
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.
|
||||
Even though this behavior 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 given time (TTL) and this TTL can be greater than the challenge timeout making the `DNS-01` challenge fail.
|
||||
|
||||
The Traefik ACME client library [LEGO](https://github.com/go-acme/lego) supports some but not all DNS providers to work around this issue.
|
||||
The [Supported `provider` table](#providers) indicates if they allow generating certificates for a wildcard domain and its root domain.
|
||||
|
||||
## Known Domains, SANs
|
||||
|
||||
You can set SANs (alternative domains) for each main domain.
|
||||
Every domain must have A/AAAA records pointing to Traefik.
|
||||
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"]
|
||||
# ...
|
||||
```
|
||||
|
||||
!!! important
|
||||
The certificates for the domains listed in `acme.domains` are negotiated at Traefik startup only.
|
||||
|
||||
!!! note
|
||||
Wildcard certificates can only be verified through a `DNS-01` challenge.
|
||||
|
||||
## `caServer`
|
||||
|
||||
??? example "Using the Let's Encrypt staging server"
|
||||
|
||||
```toml
|
||||
[acme]
|
||||
# ...
|
||||
caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
# ...
|
||||
```
|
||||
|
||||
## `onHostRule`
|
||||
|
||||
Enable certificate generation on [routers](../routing/routers/index.md) `Host` & `HostSNI` rules.
|
||||
|
||||
This will request a certificate from Let's Encrypt for each router with a Host rule.
|
||||
|
||||
```toml
|
||||
[acme]
|
||||
# ...
|
||||
onHostRule = true
|
||||
# ...
|
||||
```
|
||||
|
||||
!!! note "Multiple Hosts in a Rule"
|
||||
The rule `Host(test1.traefik.io,test2.traefik.io)` will request a certificate with the 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](#wildcard-domains) 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
|
||||
|
||||
### In a File
|
||||
|
||||
ACME certificates can be stored in a JSON file that needs to have a `600` file mode .
|
||||
|
||||
In Docker you can mount either the JSON file, or the folder containing it:
|
||||
|
||||
```bash
|
||||
docker run -v "/my/host/acme.json:acme.json" traefik
|
||||
```
|
||||
|
||||
```bash
|
||||
docker run -v "/my/host/acme:/etc/traefik/acme" traefik
|
||||
```
|
||||
|
||||
!!! warning
|
||||
For concurrency reason, this file cannot be shared across multiple instances of Traefik. Use a key value store entry instead.
|
||||
|
||||
### In a a Key Value Store Entry
|
||||
|
||||
ACME certificates can be stored in a key-value store entry.
|
||||
|
||||
```toml
|
||||
storage = "traefik/acme/account"
|
||||
```
|
||||
|
||||
!!! note "Storage Size"
|
||||
|
||||
Because key-value stores have limited entry size, the certificates list is compressed _before_ it is saved.
|
||||
For example, it is possible to store up to _approximately_ 100 ACME certificates in Consul.
|
||||
|
||||
## Fallback
|
||||
|
||||
If Let's Encrypt is not reachable, the following certificates will apply:
|
||||
|
||||
1. Previously generated ACME certificates (before downtime)
|
||||
1. Expired ACME certificates
|
||||
1. Provided certificates
|
||||
|
||||
!!! note
|
||||
For new (sub)domains which need Let's Encrypt authentication, the default Traefik certificate will be used until Traefik is restarted.
|
@@ -1,145 +0,0 @@
|
||||
# HTTPS & TLS
|
||||
|
||||
Traefik supports HTTPS & TLS, and is able to accept new certificates / updates over time (without being restarted).
|
||||
TLS is enabled at the [router](../routing/routers/index.md) level, but some options are configured in dedicated sections (`tlsOptions` & `tlsStores`) described in this section.
|
||||
|
||||
## Configuration Example
|
||||
|
||||
??? example "Configuring a Default Certificate"
|
||||
|
||||
```toml
|
||||
[tlsStores]
|
||||
[tlsStores.default]
|
||||
[tlsStores.default.defaultCertificate]
|
||||
certFile = "path/to/cert.crt"
|
||||
keyFile = "path/to/cert.key"
|
||||
```
|
||||
|
||||
??? example "Configuring a Minimum TLS Version"
|
||||
|
||||
```toml
|
||||
[tlsOptions]
|
||||
[tlsOptions.default]
|
||||
minVersion = "VersionTLS12"
|
||||
```
|
||||
|
||||
??? example "Defining Certificates"
|
||||
|
||||
```toml
|
||||
[[tls]]
|
||||
[tls.certificate]
|
||||
certFile = "/path/to/domain.cert"
|
||||
keyFile = "/path/to/domain.key"
|
||||
|
||||
[[tls]]
|
||||
[tls.certificate]
|
||||
certFile = "/path/to/other-domain.cert"
|
||||
keyFile = "/path/to/other-domain.key"
|
||||
```
|
||||
|
||||
!!! important "File Provider Only"
|
||||
|
||||
In the above example, we've used the [file provider](../providers/file.md) to handle the TLS configuration (tlsStores, tlsOptions, and TLS certificates).
|
||||
In its current alpha version, it is the only available method to configure these elements.
|
||||
Of course, these options are hot reloaded and can be updated at runtime (they belong to the [dynamic configuration](../getting-started/configuration-overview.md)).
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### Dynamic Certificates
|
||||
|
||||
To add / remove TLS certificates while Traefik is running, the [file provider](../providers/file.md) supports Dynamic TLS certificates in its `[[tls]]` section.
|
||||
|
||||
!!! example "Defining Certificates"
|
||||
|
||||
```toml
|
||||
[[tls]]
|
||||
stores = ["default"]
|
||||
[tls.certificate]
|
||||
certFile = "/path/to/domain.cert"
|
||||
keyFile = "/path/to/domain.key"
|
||||
|
||||
[[tls]]
|
||||
stores = ["default"]
|
||||
[tls.certificate]
|
||||
certFile = "/path/to/other-domain.cert"
|
||||
keyFile = "/path/to/other-domain.key"
|
||||
```
|
||||
|
||||
??? note "Stores"
|
||||
|
||||
During the alpha version, the stores option will be ignored and be automatically set to ["default"].
|
||||
|
||||
### Mutual Authentication
|
||||
|
||||
Traefik supports both optional and non optional (defaut value) mutual authentication.
|
||||
|
||||
- When `optional = false`, Traefik accepts connections only from client presenting a certificate signed by a CA listed in `ClientCA.files`.
|
||||
- When `optional = true`, Traefik authorizes connections from client presenting a certificate signed by an unknown CA.
|
||||
|
||||
!!! example "Non Optional Mutual Authentication"
|
||||
|
||||
In the following example, both `snitest.com` and `snitest.org` will require client certificates.
|
||||
|
||||
```toml
|
||||
[tlsOptions]
|
||||
[tlsOptions.default]
|
||||
[tlsOptions.default.ClientCA]
|
||||
files = ["tests/clientca1.crt", "tests/clientca2.crt"]
|
||||
optional = false
|
||||
```
|
||||
|
||||
??? note "ClientCA.files"
|
||||
|
||||
You can use a file per `CA:s`, or a single file containing multiple `CA:s` (in `PEM` format).
|
||||
|
||||
`ClientCA.files` is not optional: every client will have to present a valid certificate. (This requirement will apply to every server certificate declared in the entrypoint.)
|
||||
|
||||
### Minimum TLS Version
|
||||
|
||||
!!! example "Min TLS version & [cipherSuites](https://godoc.org/crypto/tls#pkg-constants)"
|
||||
|
||||
```toml
|
||||
[tlsOptions]
|
||||
[tlsOptions.default]
|
||||
minVersion = "VersionTLS12"
|
||||
cipherSuites = [
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||
"TLS_RSA_WITH_AES_256_GCM_SHA384"
|
||||
]
|
||||
```
|
||||
|
||||
### Strict SNI Checking
|
||||
|
||||
With strict SNI checking, Traefik won't allow connections without a matching certificate.
|
||||
|
||||
!!! example "Strict SNI"
|
||||
|
||||
```toml
|
||||
[tlsOptions]
|
||||
[tlsOptions.default]
|
||||
sniStrict = true
|
||||
```
|
||||
|
||||
### Default Certificate
|
||||
|
||||
Traefik can use a default certificate for connections without a SNI, or without a matching domain.
|
||||
|
||||
If no default certificate is provided, Traefik generates and uses a self-signed certificate.
|
||||
|
||||
!!! example "Setting a Default Certificate"
|
||||
|
||||
```toml
|
||||
[tlsStores]
|
||||
[tlsStores.default]
|
||||
[tlsStores.default.defaultCertificate]
|
||||
certFile = "path/to/cert.crt"
|
||||
keyFile = "path/to/cert.key"
|
||||
```
|
||||
|
||||
??? note "Only One Default Certificate"
|
||||
|
||||
There can only be one `defaultCertificate` per tlsOptions.
|
||||
|
||||
??? note "Default TLS Store"
|
||||
|
||||
During the alpha version, there is only one globally available TLS Store (`default`).
|
@@ -1,150 +0,0 @@
|
||||
# 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.
|
||||
|
||||
# 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
|
||||
|
||||
# If true, override certificates in key-value store when using storeconfig.
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
# overrideCertificates = 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"
|
||||
|
||||
# KeyType to use.
|
||||
#
|
||||
# Optional
|
||||
# Default: "RSA4096"
|
||||
#
|
||||
# Available values : "EC256", "EC384", "RSA2048", "RSA4096", "RSA8192"
|
||||
#
|
||||
# KeyType = "RSA4096"
|
||||
|
||||
# Use a TLS-ALPN-01 ACME challenge.
|
||||
#
|
||||
# Optional (but recommended)
|
||||
#
|
||||
[acme.tlsChallenge]
|
||||
|
||||
# Use a HTTP-01 ACME challenge.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# [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
|
||||
|
||||
# Use following DNS servers to resolve the FQDN authority.
|
||||
#
|
||||
# Optional
|
||||
# Default: empty
|
||||
#
|
||||
# resolvers = ["1.1.1.1:53", "8.8.8.8:53"]
|
||||
|
||||
# Disable the DNS propagation checks before notifying ACME that the DNS challenge is ready.
|
||||
#
|
||||
# NOT RECOMMENDED:
|
||||
# Increase the risk of reaching Let's Encrypt's rate limits.
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
# disablePropagationCheck = true
|
||||
|
||||
# Domains list.
|
||||
# Only domains defined here can generate wildcard certificates.
|
||||
# The certificates for these domains are negotiated at traefik startup only.
|
||||
#
|
||||
# [[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"]
|
665
docs/content/https/acme.md
Normal file
@@ -0,0 +1,665 @@
|
||||
---
|
||||
title: "Traefik Let's Encrypt Documentation"
|
||||
description: "Learn how to configure Traefik Proxy to use an ACME provider like Let's Encrypt for automatic certificate generation. Read the technical documentation."
|
||||
---
|
||||
|
||||
# Let's Encrypt
|
||||
|
||||
Automatic HTTPS
|
||||
{: .subtitle }
|
||||
|
||||
You can configure Traefik to use an ACME provider (like Let's Encrypt) for automatic certificate generation.
|
||||
|
||||
!!! warning "Let's Encrypt and Rate Limiting"
|
||||
Note that Let's Encrypt API has [rate limiting](https://letsencrypt.org/docs/rate-limits).
|
||||
|
||||
Use Let's Encrypt staging server with the [`caServer`](#caserver) configuration option
|
||||
when experimenting to avoid hitting this limit too fast.
|
||||
|
||||
## Certificate Resolvers
|
||||
|
||||
Traefik requires you to define "Certificate Resolvers" in the [static configuration](../getting-started/configuration-overview.md#the-static-configuration),
|
||||
which are responsible for retrieving certificates from an ACME server.
|
||||
|
||||
Then, each ["router"](../routing/routers/index.md) is configured to enable TLS,
|
||||
and is associated to a certificate resolver through the [`tls.certresolver` configuration option](../routing/routers/index.md#certresolver).
|
||||
|
||||
Certificates are requested for domain names retrieved from the router's [dynamic configuration](../getting-started/configuration-overview.md#the-dynamic-configuration).
|
||||
|
||||
You can read more about this retrieval mechanism in the following section: [ACME Domain Definition](#domain-definition).
|
||||
|
||||
!!! warning "Defining an [ACME challenge type](#the-different-acme-challenges) is a requirement for a certificate resolver to be functional."
|
||||
|
||||
!!! important "Defining a certificate resolver does not result in all routers automatically using it. Each router that is supposed to use the resolver must [reference](../routing/routers/index.md#certresolver) it."
|
||||
|
||||
??? note "Configuration Reference"
|
||||
|
||||
There are many available options for ACME.
|
||||
For a quick glance at what's possible, browse the configuration reference:
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
--8<-- "content/https/ref-acme.yaml"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
--8<-- "content/https/ref-acme.toml"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--8<-- "content/https/ref-acme.txt"
|
||||
```
|
||||
|
||||
## Domain Definition
|
||||
|
||||
Certificate resolvers request certificates for a set of the domain names
|
||||
inferred from routers, with the following logic:
|
||||
|
||||
- If the router has a [`tls.domains`](../routing/routers/index.md#domains) option set,
|
||||
then the certificate resolver uses the `main` (and optionally `sans`) option of `tls.domains` to know the domain names for this router.
|
||||
|
||||
- If no [`tls.domains`](../routing/routers/index.md#domains) option is set,
|
||||
then the certificate resolver uses the [router's rule](../routing/routers/index.md#rule),
|
||||
by checking the `Host()` matchers.
|
||||
Please note that [multiple `Host()` matchers can be used](../routing/routers/index.md#certresolver)) for specifying multiple domain names for this router.
|
||||
|
||||
Please note that:
|
||||
|
||||
- When multiple domain names are inferred from a given router,
|
||||
only **one** certificate is requested with the first domain name as the main domain,
|
||||
and the other domains as ["SANs" (Subject Alternative Name)](https://en.wikipedia.org/wiki/Subject_Alternative_Name).
|
||||
|
||||
- As [ACME V2 supports "wildcard domains"](#wildcard-domains),
|
||||
any router can provide a [wildcard domain](https://en.wikipedia.org/wiki/Wildcard_certificate) name, as "main" domain or as "SAN" domain.
|
||||
|
||||
Please check the [configuration examples below](#configuration-examples) for more details.
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
??? example "Enabling ACME"
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
|
||||
websecure:
|
||||
address: ":443"
|
||||
|
||||
certificatesResolvers:
|
||||
myresolver:
|
||||
acme:
|
||||
email: your-email@example.com
|
||||
storage: acme.json
|
||||
httpChallenge:
|
||||
# used during the challenge
|
||||
entryPoint: web
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[entryPoints]
|
||||
[entryPoints.web]
|
||||
address = ":80"
|
||||
|
||||
[entryPoints.websecure]
|
||||
address = ":443"
|
||||
|
||||
[certificatesResolvers.myresolver.acme]
|
||||
email = "your-email@example.com"
|
||||
storage = "acme.json"
|
||||
[certificatesResolvers.myresolver.acme.httpChallenge]
|
||||
# used during the challenge
|
||||
entryPoint = "web"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--entrypoints.web.address=:80
|
||||
--entrypoints.websecure.address=:443
|
||||
# ...
|
||||
--certificatesresolvers.myresolver.acme.email=your-email@example.com
|
||||
--certificatesresolvers.myresolver.acme.storage=acme.json
|
||||
# used during the challenge
|
||||
--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web
|
||||
```
|
||||
|
||||
!!! important "Defining a certificate resolver does not result in all routers automatically using it. Each router that is supposed to use the resolver must [reference](../routing/routers/index.md#certresolver) it."
|
||||
|
||||
??? example "Single Domain from Router's Rule Example"
|
||||
|
||||
* A certificate for the domain `example.com` is requested:
|
||||
|
||||
--8<-- "content/https/include-acme-single-domain-example.md"
|
||||
|
||||
??? example "Multiple Domains from Router's Rule Example"
|
||||
|
||||
* A certificate for the domains `example.com` (main) and `blog.example.org`
|
||||
is requested:
|
||||
|
||||
--8<-- "content/https/include-acme-multiple-domains-from-rule-example.md"
|
||||
|
||||
??? example "Multiple Domains from Router's `tls.domain` Example"
|
||||
|
||||
* A certificate for the domains `example.com` (main) and `*.example.org` (SAN)
|
||||
is requested:
|
||||
|
||||
--8<-- "content/https/include-acme-multiple-domains-example.md"
|
||||
|
||||
## Automatic Renewals
|
||||
|
||||
Traefik automatically tracks the expiry date of ACME certificates it generates.
|
||||
|
||||
By default, Traefik manages 90 days certificates,
|
||||
and starts to renew certificates 30 days before their expiry.
|
||||
|
||||
When using a certificate resolver that issues certificates with custom durations,
|
||||
one can configure the certificates' duration with the [`certificatesDuration`](#certificatesduration) option.
|
||||
|
||||
!!! info ""
|
||||
Certificates that are no longer used may still be renewed, as Traefik does not currently check if the certificate is being used before renewing.
|
||||
|
||||
## Using LetsEncrypt with Kubernetes
|
||||
|
||||
When using LetsEncrypt with kubernetes, there are some known caveats with both the [ingress](../providers/kubernetes-ingress.md) and [crd](../providers/kubernetes-crd.md) providers.
|
||||
|
||||
!!! info ""
|
||||
If you intend to run multiple instances of Traefik with LetsEncrypt, please ensure you read the sections on those provider pages.
|
||||
|
||||
## The Different ACME Challenges
|
||||
|
||||
!!! warning "Defining one ACME challenge is a requirement for a certificate resolver to be functional."
|
||||
|
||||
!!! important "Defining a certificate resolver does not result in all routers automatically using it. Each router that is supposed to use the resolver must [reference](../routing/routers/index.md#certresolver) it."
|
||||
|
||||
### `tlsChallenge`
|
||||
|
||||
Use the `TLS-ALPN-01` challenge to generate and renew ACME certificates by provisioning a TLS certificate.
|
||||
|
||||
As described on the Let's Encrypt [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72),
|
||||
when using the `TLS-ALPN-01` challenge, Traefik must be reachable by Let's Encrypt through port 443.
|
||||
|
||||
??? example "Configuring the `tlsChallenge`"
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
certificatesResolvers:
|
||||
myresolver:
|
||||
acme:
|
||||
# ...
|
||||
tlsChallenge: {}
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[certificatesResolvers.myresolver.acme]
|
||||
# ...
|
||||
[certificatesResolvers.myresolver.acme.tlsChallenge]
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
# ...
|
||||
--certificatesresolvers.myresolver.acme.tlschallenge=true
|
||||
```
|
||||
|
||||
### `httpChallenge`
|
||||
|
||||
Use the `HTTP-01` challenge to generate and renew ACME certificates by provisioning an HTTP resource under a well-known URI.
|
||||
|
||||
As described on the Let's Encrypt [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72),
|
||||
when using the `HTTP-01` challenge, `certificatesresolvers.myresolver.acme.httpchallenge.entrypoint` must be reachable by Let's Encrypt through port 80.
|
||||
|
||||
??? example "Using an EntryPoint Called web for the `httpChallenge`"
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
|
||||
websecure:
|
||||
address: ":443"
|
||||
|
||||
certificatesResolvers:
|
||||
myresolver:
|
||||
acme:
|
||||
# ...
|
||||
httpChallenge:
|
||||
entryPoint: web
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[entryPoints]
|
||||
[entryPoints.web]
|
||||
address = ":80"
|
||||
|
||||
[entryPoints.websecure]
|
||||
address = ":443"
|
||||
|
||||
[certificatesResolvers.myresolver.acme]
|
||||
# ...
|
||||
[certificatesResolvers.myresolver.acme.httpChallenge]
|
||||
entryPoint = "web"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--entrypoints.web.address=:80
|
||||
--entrypoints.websecure.address=:443
|
||||
# ...
|
||||
--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web
|
||||
```
|
||||
|
||||
!!! info ""
|
||||
Redirection is fully compatible with the `HTTP-01` challenge.
|
||||
|
||||
### `dnsChallenge`
|
||||
|
||||
Use the `DNS-01` challenge to generate and renew ACME certificates by provisioning a DNS record.
|
||||
|
||||
??? example "Configuring a `dnsChallenge` with the DigitalOcean Provider"
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
certificatesResolvers:
|
||||
myresolver:
|
||||
acme:
|
||||
# ...
|
||||
dnsChallenge:
|
||||
provider: digitalocean
|
||||
delayBeforeCheck: 0
|
||||
# ...
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[certificatesResolvers.myresolver.acme]
|
||||
# ...
|
||||
[certificatesResolvers.myresolver.acme.dnsChallenge]
|
||||
provider = "digitalocean"
|
||||
delayBeforeCheck = 0
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
# ...
|
||||
--certificatesresolvers.myresolver.acme.dnschallenge.provider=digitalocean
|
||||
--certificatesresolvers.myresolver.acme.dnschallenge.delaybeforecheck=0
|
||||
# ...
|
||||
```
|
||||
|
||||
!!! important
|
||||
A `provider` is mandatory.
|
||||
|
||||
#### `providers`
|
||||
|
||||
Here is a list of supported `providers`, that can automate the DNS verification,
|
||||
along with the required environment variables and their [wildcard & root domain support](#wildcard-domains).
|
||||
Do not hesitate to complete it.
|
||||
|
||||
Many lego environment variables can be overridden by their respective `_FILE` counterpart, which should have a filepath to a file that contains the secret as its value.
|
||||
For example, `CF_API_EMAIL_FILE=/run/secrets/traefik_cf-api-email` could be used to provide a Cloudflare API email address as a Docker secret named `traefik_cf-api-email`.
|
||||
|
||||
For complete details, refer to your provider's _Additional configuration_ link.
|
||||
|
||||
| Provider Name | Provider Code | Environment Variables | |
|
||||
|-------------------------------------------------------------|----------------|---------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------|
|
||||
| [ACME DNS](https://github.com/joohoi/acme-dns) | `acme-dns` | `ACME_DNS_API_BASE`, `ACME_DNS_STORAGE_PATH` | [Additional configuration](https://go-acme.github.io/lego/dns/acme-dns) |
|
||||
| [Alibaba Cloud](https://www.alibabacloud.com) | `alidns` | `ALICLOUD_ACCESS_KEY`, `ALICLOUD_SECRET_KEY`, `ALICLOUD_REGION_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/alidns) |
|
||||
| [all-inkl](https://all-inkl.com) | `allinkl` | `ALL_INKL_LOGIN`, `ALL_INKL_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/allinkl) |
|
||||
| [ArvanCloud](https://www.arvancloud.com/en) | `arvancloud` | `ARVANCLOUD_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/arvancloud) |
|
||||
| [Auroradns](https://www.pcextreme.com/dns-health-checks) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` | [Additional configuration](https://go-acme.github.io/lego/dns/auroradns) |
|
||||
| [Autodns](https://www.internetx.com/domains/autodns/) | `autodns` | `AUTODNS_API_USER`, `AUTODNS_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/autodns) |
|
||||
| [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP`, `[AZURE_METADATA_ENDPOINT]` | [Additional configuration](https://go-acme.github.io/lego/dns/azure) |
|
||||
| [Bindman](https://github.com/labbsr0x/bindman-dns-webhook) | `bindman` | `BINDMAN_MANAGER_ADDRESS` | [Additional configuration](https://go-acme.github.io/lego/dns/bindman) |
|
||||
| [Blue Cat](https://www.bluecatnetworks.com/) | `bluecat` | `BLUECAT_SERVER_URL`, `BLUECAT_USER_NAME`, `BLUECAT_PASSWORD`, `BLUECAT_CONFIG_NAME`, `BLUECAT_DNS_VIEW` | [Additional configuration](https://go-acme.github.io/lego/dns/bluecat) |
|
||||
| [Checkdomain](https://www.checkdomain.de/) | `checkdomain` | `CHECKDOMAIN_TOKEN`, | [Additional configuration](https://go-acme.github.io/lego/dns/checkdomain/) |
|
||||
| [CloudDNS](https://vshosting.eu/) | `clouddns` | `CLOUDDNS_CLIENT_ID`, `CLOUDDNS_EMAIL`, `CLOUDDNS_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/clouddns) |
|
||||
| [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CF_API_EMAIL`, `CF_API_KEY` [^5] or `CF_DNS_API_TOKEN`, `[CF_ZONE_API_TOKEN]` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudflare) |
|
||||
| [ClouDNS](https://www.cloudns.net/) | `cloudns` | `CLOUDNS_AUTH_ID`, `CLOUDNS_AUTH_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudns) |
|
||||
| [CloudXNS](https://www.cloudxns.net) | `cloudxns` | `CLOUDXNS_API_KEY`, `CLOUDXNS_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudxns) |
|
||||
| [ConoHa](https://www.conoha.jp) | `conoha` | `CONOHA_TENANT_ID`, `CONOHA_API_USERNAME`, `CONOHA_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/conoha) |
|
||||
| [Constellix](https://constellix.com) | `constellix` | `CONSTELLIX_API_KEY`, `CONSTELLIX_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/constellix) |
|
||||
| [deSEC](https://desec.io) | `desec` | `DESEC_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/desec) |
|
||||
| [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/digitalocean) |
|
||||
| [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` | [Additional configuration](https://go-acme.github.io/lego/dns/dnsmadeeasy) |
|
||||
| [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/dnsimple) |
|
||||
| [DNSPod](https://www.dnspod.com/) | `dnspod` | `DNSPOD_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/dnspod) |
|
||||
| [Domain Offensive (do.de)](https://www.do.de/) | `dode` | `DODE_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/dode) |
|
||||
| [Domeneshop](https://domene.shop) | `domeneshop` | `DOMENESHOP_API_TOKEN`, `DOMENESHOP_API_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/domeneshop) |
|
||||
| [DreamHost](https://www.dreamhost.com/) | `dreamhost` | `DREAMHOST_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/dreamhost) |
|
||||
| [Duck DNS](https://www.duckdns.org/) | `duckdns` | `DUCKDNS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/duckdns) |
|
||||
| [Dyn](https://dyn.com) | `dyn` | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/dyn) |
|
||||
| [Dynu](https://www.dynu.com) | `dynu` | `DYNU_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/dynu) |
|
||||
| [EasyDNS](https://easydns.com/) | `easydns` | `EASYDNS_TOKEN`, `EASYDNS_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/easydns) |
|
||||
| [EdgeDNS](https://www.akamai.com/) | `edgedns` | `AKAMAI_CLIENT_TOKEN`, `AKAMAI_CLIENT_SECRET`, `AKAMAI_ACCESS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/edgedns) |
|
||||
| [Epik](https://www.epik.com) | `epik` | `EPIK_SIGNATURE` | [Additional configuration](https://go-acme.github.io/lego/dns/epik) |
|
||||
| [Exoscale](https://www.exoscale.com) | `exoscale` | `EXOSCALE_API_KEY`, `EXOSCALE_API_SECRET`, `EXOSCALE_ENDPOINT` | [Additional configuration](https://go-acme.github.io/lego/dns/exoscale) |
|
||||
| [Fast DNS](https://www.akamai.com/) | `fastdns` | `AKAMAI_CLIENT_TOKEN`, `AKAMAI_CLIENT_SECRET`, `AKAMAI_ACCESS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/edgedns) |
|
||||
| [Freemyip.com](https://freemyip.com) | `freemyip` | `FREEMYIP_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/freemyip) |
|
||||
| [G-Core Lab](https://gcorelabs.com/dns/) | `gcore` | `GCORE_PERMANENT_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/gcore) |
|
||||
| [Gandi v5](https://doc.livedns.gandi.net) | `gandiv5` | `GANDIV5_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/gandiv5) |
|
||||
| [Gandi](https://www.gandi.net) | `gandi` | `GANDI_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/gandi) |
|
||||
| [Glesys](https://glesys.com/) | `glesys` | `GLESYS_API_USER`, `GLESYS_API_KEY`, `GLESYS_DOMAIN` | [Additional configuration](https://go-acme.github.io/lego/dns/glesys) |
|
||||
| [GoDaddy](https://godaddy.com/) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/godaddy) |
|
||||
| [Google Cloud DNS](https://cloud.google.com/dns/docs/) | `gcloud` | `GCE_PROJECT`, Application Default Credentials [^2] [^3], [`GCE_SERVICE_ACCOUNT_FILE`] | [Additional configuration](https://go-acme.github.io/lego/dns/gcloud) |
|
||||
| [Hetzner](https://hetzner.com) | `hetzner` | `HETZNER_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/hetzner) |
|
||||
| [hosting.de](https://www.hosting.de) | `hostingde` | `HOSTINGDE_API_KEY`, `HOSTINGDE_ZONE_NAME` | [Additional configuration](https://go-acme.github.io/lego/dns/hostingde) |
|
||||
| [Hosttech](https://www.hosttech.eu) | `hosttech` | `HOSTTECH_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/hosttech) |
|
||||
| [HyperOne](https://www.hyperone.com) | `hyperone` | `HYPERONE_PASSPORT_LOCATION`, `HYPERONE_LOCATION_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/hyperone) |
|
||||
| [Hurricane Electric](https://dns.he.net) | `hurricane` | `HURRICANE_TOKENS` [^6] | [Additional configuration](https://go-acme.github.io/lego/dns/hurricane) |
|
||||
| [IBM Cloud (SoftLayer)](https://www.ibm.com/cloud/) | `ibmcloud` | `SOFTLAYER_USERNAME`, `SOFTLAYER_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/ibmcloud) |
|
||||
| [IIJ](https://www.iij.ad.jp/) | `iij` | `IIJ_API_ACCESS_KEY`, `IIJ_API_SECRET_KEY`, `IIJ_DO_SERVICE_CODE` | [Additional configuration](https://go-acme.github.io/lego/dns/iij) |
|
||||
| [Infoblox](https://www.infoblox.com/) | `infoblox` | `INFOBLOX_USER`, `INFOBLOX_PASSWORD`, `INFOBLOX_HOST` | [Additional configuration](https://go-acme.github.io/lego/dns/infoblox) |
|
||||
| [Infomaniak](https://www.infomaniak.com) | `infomaniak` | `INFOMANIAK_ACCESS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/infomaniak) |
|
||||
| [Internet.bs](https://internetbs.net) | `internetbs` | `INTERNET_BS_API_KEY`, `INTERNET_BS_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/internetbs) |
|
||||
| [INWX](https://www.inwx.de/en) | `inwx` | `INWX_USERNAME`, `INWX_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/inwx) |
|
||||
| [ionos](https://ionos.com/) | `ionos` | `IONOS_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/ionos) |
|
||||
| [Joker.com](https://joker.com) | `joker` | `JOKER_API_MODE` with `JOKER_API_KEY` or `JOKER_USERNAME`, `JOKER_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/joker) |
|
||||
| [Lightsail](https://aws.amazon.com/lightsail/) | `lightsail` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `DNS_ZONE` | [Additional configuration](https://go-acme.github.io/lego/dns/lightsail) |
|
||||
| [Linode v4](https://www.linode.com) | `linode` | `LINODE_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/linode) |
|
||||
| [Liquid Web](https://www.liquidweb.com/) | `liquidweb` | `LIQUID_WEB_PASSWORD`, `LIQUID_WEB_USERNAME`, `LIQUID_WEB_ZONE` | [Additional configuration](https://go-acme.github.io/lego/dns/liquidweb) |
|
||||
| [Loopia](https://loopia.com/) | `loopia` | `LOOPIA_API_PASSWORD`, `LOOPIA_API_USER` | [Additional configuration](https://go-acme.github.io/lego/dns/loopia) |
|
||||
| [LuaDNS](https://luadns.com) | `luadns` | `LUADNS_API_USERNAME`, `LUADNS_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/luadns) |
|
||||
| [MyDNS.jp](https://www.mydns.jp/) | `mydnsjp` | `MYDNSJP_MASTER_ID`, `MYDNSJP_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/mydnsjp) |
|
||||
| [Mythic Beasts](https://www.mythic-beasts.com) | `mythicbeasts` | `MYTHICBEASTS_USER_NAME`, `MYTHICBEASTS_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/mythicbeasts) |
|
||||
| [name.com](https://www.name.com/) | `namedotcom` | `NAMECOM_USERNAME`, `NAMECOM_API_TOKEN`, `NAMECOM_SERVER` | [Additional configuration](https://go-acme.github.io/lego/dns/namedotcom) |
|
||||
| [Namecheap](https://www.namecheap.com) | `namecheap` | `NAMECHEAP_API_USER`, `NAMECHEAP_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/namecheap) |
|
||||
| [Namesilo](https://www.namesilo.com/) | `namesilo` | `NAMESILO_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/namesilo) |
|
||||
| [Netcup](https://www.netcup.eu/) | `netcup` | `NETCUP_CUSTOMER_NUMBER`, `NETCUP_API_KEY`, `NETCUP_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/netcup) |
|
||||
| [Netlify](https://www.netlify.com) | `netlify` | `NETLIFY_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/netlify) |
|
||||
| [Nicmanager](https://www.nicmanager.com) | `nicmanager` | `NICMANAGER_API_EMAIL`, `NICMANAGER_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/nicmanager) |
|
||||
| [NIFCloud](https://cloud.nifty.com/service/dns.htm) | `nifcloud` | `NIFCLOUD_ACCESS_KEY_ID`, `NIFCLOUD_SECRET_ACCESS_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/nifcloud) |
|
||||
| [Njalla](https://njal.la) | `njalla` | `NJALLA_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/njalla) |
|
||||
| [NS1](https://ns1.com/) | `ns1` | `NS1_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/ns1) |
|
||||
| [Open Telekom Cloud](https://cloud.telekom.de) | `otc` | `OTC_DOMAIN_NAME`, `OTC_USER_NAME`, `OTC_PASSWORD`, `OTC_PROJECT_NAME`, `OTC_IDENTITY_ENDPOINT` | [Additional configuration](https://go-acme.github.io/lego/dns/otc) |
|
||||
| [Openstack Designate](https://docs.openstack.org/designate) | `designate` | `OS_AUTH_URL`, `OS_USERNAME`, `OS_PASSWORD`, `OS_TENANT_NAME`, `OS_REGION_NAME` | [Additional configuration](https://go-acme.github.io/lego/dns/designate) |
|
||||
| [Oracle Cloud](https://cloud.oracle.com/home) | `oraclecloud` | `OCI_COMPARTMENT_OCID`, `OCI_PRIVKEY_FILE`, `OCI_PRIVKEY_PASS`, `OCI_PUBKEY_FINGERPRINT`, `OCI_REGION`, `OCI_TENANCY_OCID`, `OCI_USER_OCID` | [Additional configuration](https://go-acme.github.io/lego/dns/oraclecloud) |
|
||||
| [OVH](https://www.ovh.com) | `ovh` | `OVH_ENDPOINT`, `OVH_APPLICATION_KEY`, `OVH_APPLICATION_SECRET`, `OVH_CONSUMER_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/ovh) |
|
||||
| [Porkbun](https://porkbun.com/) | `porkbun` | `PORKBUN_SECRET_API_KEY`, `PORKBUN_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/porkbun) |
|
||||
| [PowerDNS](https://www.powerdns.com) | `pdns` | `PDNS_API_KEY`, `PDNS_API_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/pdns) |
|
||||
| [Rackspace](https://www.rackspace.com/cloud/dns) | `rackspace` | `RACKSPACE_USER`, `RACKSPACE_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/rackspace) |
|
||||
| [reg.ru](https://www.reg.ru) | `regru` | `REGRU_USERNAME`, `REGRU_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/regru) |
|
||||
| [RFC2136](https://tools.ietf.org/html/rfc2136) | `rfc2136` | `RFC2136_TSIG_KEY`, `RFC2136_TSIG_SECRET`, `RFC2136_TSIG_ALGORITHM`, `RFC2136_NAMESERVER` | [Additional configuration](https://go-acme.github.io/lego/dns/rfc2136) |
|
||||
| [RimuHosting](https://rimuhosting.com) | `rimuhosting` | `RIMUHOSTING_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/rimuhosting) |
|
||||
| [Route 53](https://aws.amazon.com/route53/) | `route53` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `[AWS_REGION]`, `[AWS_HOSTED_ZONE_ID]` or a configured user/instance IAM profile. | [Additional configuration](https://go-acme.github.io/lego/dns/route53) |
|
||||
| [Sakura Cloud](https://cloud.sakura.ad.jp/) | `sakuracloud` | `SAKURACLOUD_ACCESS_TOKEN`, `SAKURACLOUD_ACCESS_TOKEN_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/sakuracloud) |
|
||||
| [Scaleway](https://www.scaleway.com) | `scaleway` | `SCALEWAY_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/scaleway) |
|
||||
| [Selectel](https://selectel.ru/en/) | `selectel` | `SELECTEL_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/selectel) |
|
||||
| [Servercow](https://servercow.de) | `servercow` | `SERVERCOW_USERNAME`, `SERVERCOW_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/servercow) |
|
||||
| [Simply.com](https://www.simply.com/en/domains/) | `simply` | `SIMPLY_ACCOUNT_NAME`, `SIMPLY_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/simply) |
|
||||
| [Sonic](https://www.sonic.com/) | `sonic` | `SONIC_USER_ID`, `SONIC_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/sonic) |
|
||||
| [Stackpath](https://www.stackpath.com/) | `stackpath` | `STACKPATH_CLIENT_ID`, `STACKPATH_CLIENT_SECRET`, `STACKPATH_STACK_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/stackpath) |
|
||||
| [Tencent Cloud DNS](https://cloud.tencent.com/product/cns) | `tencentcloud` | `TENCENTCLOUD_SECRET_ID`, `TENCENTCLOUD_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/tencentcloud) |
|
||||
| [TransIP](https://www.transip.nl/) | `transip` | `TRANSIP_ACCOUNT_NAME`, `TRANSIP_PRIVATE_KEY_PATH` | [Additional configuration](https://go-acme.github.io/lego/dns/transip) |
|
||||
| [UKFast SafeDNS](https://www.ukfast.co.uk/dns-hosting.html) | `safedns` | `SAFEDNS_AUTH_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/safedns) |
|
||||
| [VegaDNS](https://github.com/shupp/VegaDNS-API) | `vegadns` | `SECRET_VEGADNS_KEY`, `SECRET_VEGADNS_SECRET`, `VEGADNS_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/vegadns) |
|
||||
| [Versio](https://www.versio.nl/domeinnamen) | `versio` | `VERSIO_USERNAME`, `VERSIO_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/versio) |
|
||||
| [VinylDNS](https://www.vinyldns.io) | `vinyldns` | `VINYLDNS_ACCESS_KEY`, `VINYLDNS_SECRET_KEY`, `VINYLDNS_HOST` | [Additional configuration](https://go-acme.github.io/lego/dns/vinyldns) |
|
||||
| [Vscale](https://vscale.io/) | `vscale` | `VSCALE_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/vscale) |
|
||||
| [VULTR](https://www.vultr.com) | `vultr` | `VULTR_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/vultr) |
|
||||
| [WEDOS](https://www.wedos.com) | `wedos` | `WEDOS_USERNAME`, `WEDOS_WAPI_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/wedos) |
|
||||
| [Yandex](https://yandex.com) | `yandex` | `YANDEX_PDD_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/yandex) |
|
||||
| [Zone.ee](https://www.zone.ee) | `zoneee` | `ZONEEE_API_USER`, `ZONEEE_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/zoneee) |
|
||||
| [Zonomi](https://zonomi.com) | `zonomi` | `ZONOMI_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/zonomi) |
|
||||
| External Program | `exec` | `EXEC_PATH` | [Additional configuration](https://go-acme.github.io/lego/dns/exec) |
|
||||
| HTTP request | `httpreq` | `HTTPREQ_ENDPOINT`, `HTTPREQ_MODE`, `HTTPREQ_USERNAME`, `HTTPREQ_PASSWORD` [^1] | [Additional configuration](https://go-acme.github.io/lego/dns/httpreq) |
|
||||
| manual | `manual` | none, but you need to run Traefik interactively [^4], turn on debug log to see instructions and press <kbd>Enter</kbd>. | |
|
||||
|
||||
[^1]: More information about the HTTP message format can be found [here](https://go-acme.github.io/lego/dns/httpreq/).
|
||||
[^2]: [Providing credentials to your application](https://cloud.google.com/docs/authentication/production).
|
||||
[^3]: [google/default.go](https://github.com/golang/oauth2/blob/36a7019397c4c86cf59eeab3bc0d188bac444277/google/default.go#L61-L76)
|
||||
[^4]: `docker stack` remark: there is no way to support terminal attached to container when deploying with `docker stack`, so you might need to run container with `docker run -it` to generate certificates using `manual` provider.
|
||||
[^5]: The `Global API Key` needs to be used, not the `Origin CA Key`.
|
||||
[^6]: As explained in the [LEGO hurricane configuration](https://go-acme.github.io/lego/dns/hurricane/#credentials), each domain or wildcard (record name) needs a token. So each update of record name must be followed by an update of the `HURRICANE_TOKENS` variable, and a restart of Traefik.
|
||||
|
||||
!!! info "`delayBeforeCheck`"
|
||||
By default, the `provider` verifies the TXT record _before_ letting ACME verify.
|
||||
You can delay this operation by specifying a delay (in seconds) with `delayBeforeCheck` (value must be greater than zero).
|
||||
This option is useful when internal networks block external DNS queries.
|
||||
|
||||
#### `resolvers`
|
||||
|
||||
Use custom DNS servers to resolve the FQDN authority.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
certificatesResolvers:
|
||||
myresolver:
|
||||
acme:
|
||||
# ...
|
||||
dnsChallenge:
|
||||
# ...
|
||||
resolvers:
|
||||
- "1.1.1.1:53"
|
||||
- "8.8.8.8:53"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[certificatesResolvers.myresolver.acme]
|
||||
# ...
|
||||
[certificatesResolvers.myresolver.acme.dnsChallenge]
|
||||
# ...
|
||||
resolvers = ["1.1.1.1:53", "8.8.8.8:53"]
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
# ...
|
||||
--certificatesresolvers.myresolver.acme.dnschallenge.resolvers=1.1.1.1:53,8.8.8.8:53
|
||||
```
|
||||
|
||||
#### Wildcard Domains
|
||||
|
||||
[ACME V2](https://community.letsencrypt.org/t/acme-v2-and-wildcard-certificate-support-is-live/55579) supports wildcard certificates.
|
||||
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](#dnschallenge).
|
||||
|
||||
## External Account Binding
|
||||
|
||||
- `kid`: Key identifier from External CA
|
||||
- `hmacEncoded`: HMAC key from External CA, should be in Base64 URL Encoding without padding format
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
certificatesResolvers:
|
||||
myresolver:
|
||||
acme:
|
||||
# ...
|
||||
eab:
|
||||
kid: abc-keyID-xyz
|
||||
hmacEncoded: abc-hmac-xyz
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[certificatesResolvers.myresolver.acme]
|
||||
# ...
|
||||
[certificatesResolvers.myresolver.acme.eab]
|
||||
kid = "abc-keyID-xyz"
|
||||
hmacEncoded = "abc-hmac-xyz"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
# ...
|
||||
--certificatesresolvers.myresolver.acme.eab.kid=abc-keyID-xyz
|
||||
--certificatesresolvers.myresolver.acme.eab.hmacencoded=abc-hmac-xyz
|
||||
```
|
||||
|
||||
## More Configuration
|
||||
|
||||
### `caServer`
|
||||
|
||||
_Required, Default="https://acme-v02.api.letsencrypt.org/directory"_
|
||||
|
||||
The CA server to use:
|
||||
|
||||
- Let's Encrypt production server: https://acme-v02.api.letsencrypt.org/directory
|
||||
- Let's Encrypt staging server: https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
|
||||
??? example "Using the Let's Encrypt staging server"
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
certificatesResolvers:
|
||||
myresolver:
|
||||
acme:
|
||||
# ...
|
||||
caServer: https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
# ...
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[certificatesResolvers.myresolver.acme]
|
||||
# ...
|
||||
caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
# ...
|
||||
--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
# ...
|
||||
```
|
||||
|
||||
### `storage`
|
||||
|
||||
_Required, Default="acme.json"_
|
||||
|
||||
The `storage` option sets the location where your ACME certificates are saved to.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
certificatesResolvers:
|
||||
myresolver:
|
||||
acme:
|
||||
# ...
|
||||
storage: acme.json
|
||||
# ...
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[certificatesResolvers.myresolver.acme]
|
||||
# ...
|
||||
storage = "acme.json"
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
# ...
|
||||
--certificatesresolvers.myresolver.acme.storage=acme.json
|
||||
# ...
|
||||
```
|
||||
|
||||
ACME certificates are stored in a JSON file that needs to have a `600` file mode.
|
||||
|
||||
In Docker you can mount either the JSON file, or the folder containing it:
|
||||
|
||||
```bash
|
||||
docker run -v "/my/host/acme.json:/acme.json" traefik
|
||||
```
|
||||
|
||||
```bash
|
||||
docker run -v "/my/host/acme:/etc/traefik/acme" traefik
|
||||
```
|
||||
|
||||
!!! warning
|
||||
For concurrency reasons, this file cannot be shared across multiple instances of Traefik.
|
||||
|
||||
### `certificatesDuration`
|
||||
|
||||
_Optional, Default=2160_
|
||||
|
||||
The `certificatesDuration` option defines the certificates' duration in hours.
|
||||
It defaults to `2160` (90 days) to follow Let's Encrypt certificates' duration.
|
||||
|
||||
!!! warning "Traefik cannot manage certificates with a duration lower than 1 hour."
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
certificatesResolvers:
|
||||
myresolver:
|
||||
acme:
|
||||
# ...
|
||||
certificatesDuration: 72
|
||||
# ...
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[certificatesResolvers.myresolver.acme]
|
||||
# ...
|
||||
certificatesDuration=72
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
# ...
|
||||
--certificatesresolvers.myresolver.acme.certificatesduration=72
|
||||
# ...
|
||||
```
|
||||
|
||||
`certificatesDuration` is used to calculate two durations:
|
||||
|
||||
- `Renew Period`: the period before the end of the certificate duration, during which the certificate should be renewed.
|
||||
- `Renew Interval`: the interval between renew attempts.
|
||||
|
||||
| Certificate Duration | Renew Period | Renew Interval |
|
||||
|----------------------|-------------------|-------------------------|
|
||||
| >= 1 year | 4 months | 1 week |
|
||||
| >= 90 days | 30 days | 1 day |
|
||||
| >= 7 days | 1 day | 1 hour |
|
||||
| >= 24 hours | 6 hours | 10 min |
|
||||
| < 24 hours | 20 min | 1 min |
|
||||
|
||||
### `preferredChain`
|
||||
|
||||
_Optional, Default=""_
|
||||
|
||||
Preferred chain to use.
|
||||
|
||||
If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name.
|
||||
If no match, the default offered chain will be used.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
certificatesResolvers:
|
||||
myresolver:
|
||||
acme:
|
||||
# ...
|
||||
preferredChain: 'ISRG Root X1'
|
||||
# ...
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[certificatesResolvers.myresolver.acme]
|
||||
# ...
|
||||
preferredChain = "ISRG Root X1"
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
# ...
|
||||
--certificatesresolvers.myresolver.acme.preferredChain=ISRG Root X1
|
||||
# ...
|
||||
```
|
||||
|
||||
### `keyType`
|
||||
|
||||
_Optional, Default="RSA4096"_
|
||||
|
||||
KeyType used for generating certificate private key. Allow value 'EC256', 'EC384', 'RSA2048', 'RSA4096', 'RSA8192'.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
certificatesResolvers:
|
||||
myresolver:
|
||||
acme:
|
||||
# ...
|
||||
keyType: 'RSA4096'
|
||||
# ...
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[certificatesResolvers.myresolver.acme]
|
||||
# ...
|
||||
keyType = "RSA4096"
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
# ...
|
||||
--certificatesresolvers.myresolver.acme.keyType=RSA4096
|
||||
# ...
|
||||
```
|
||||
|
||||
## Fallback
|
||||
|
||||
If Let's Encrypt is not reachable, the following certificates will apply:
|
||||
|
||||
1. Previously generated ACME certificates (before downtime)
|
||||
1. Expired ACME certificates
|
||||
1. Provided certificates
|
||||
|
||||
!!! important
|
||||
For new (sub)domains which need Let's Encrypt authentication, the default Traefik certificate will be used until Traefik is restarted.
|
91
docs/content/https/include-acme-multiple-domains-example.md
Normal file
@@ -0,0 +1,91 @@
|
||||
|
||||
```yaml tab="Docker"
|
||||
## Dynamic configuration
|
||||
labels:
|
||||
- traefik.http.routers.blog.rule=Host(`example.com`) && Path(`/blog`)
|
||||
- traefik.http.routers.blog.tls=true
|
||||
- traefik.http.routers.blog.tls.certresolver=myresolver
|
||||
- traefik.http.routers.blog.tls.domains[0].main=example.org
|
||||
- traefik.http.routers.blog.tls.domains[0].sans=*.example.org
|
||||
```
|
||||
|
||||
```yaml tab="Docker (Swarm)"
|
||||
## Dynamic configuration
|
||||
deploy:
|
||||
labels:
|
||||
- traefik.http.routers.blog.rule=Host(`example.com`) && Path(`/blog`)
|
||||
- traefik.http.services.blog-svc.loadbalancer.server.port=8080"
|
||||
- traefik.http.routers.blog.tls=true
|
||||
- traefik.http.routers.blog.tls.certresolver=myresolver
|
||||
- traefik.http.routers.blog.tls.domains[0].main=example.org
|
||||
- traefik.http.routers.blog.tls.domains[0].sans=*.example.org
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: blogtls
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`example.com`) && Path(`/blog`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: blog
|
||||
port: 8080
|
||||
tls:
|
||||
certResolver: myresolver
|
||||
domains:
|
||||
- main: example.org
|
||||
sans:
|
||||
- '*.example.org'
|
||||
```
|
||||
|
||||
```json tab="Marathon"
|
||||
labels: {
|
||||
"traefik.http.routers.blog.rule": "Host(`example.com`) && Path(`/blog`)",
|
||||
"traefik.http.routers.blog.tls": "true",
|
||||
"traefik.http.routers.blog.tls.certresolver": "myresolver",
|
||||
"traefik.http.routers.blog.tls.domains[0].main": "example.com",
|
||||
"traefik.http.routers.blog.tls.domains[0].sans": "*.example.com",
|
||||
"traefik.http.services.blog-svc.loadbalancer.server.port": "8080"
|
||||
}
|
||||
```
|
||||
|
||||
```yaml tab="Rancher"
|
||||
## Dynamic configuration
|
||||
labels:
|
||||
- traefik.http.routers.blog.rule=Host(`example.com`) && Path(`/blog`)
|
||||
- traefik.http.routers.blog.tls=true
|
||||
- traefik.http.routers.blog.tls.certresolver=myresolver
|
||||
- traefik.http.routers.blog.tls.domains[0].main=example.org
|
||||
- traefik.http.routers.blog.tls.domains[0].sans=*.example.org
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
## Dynamic configuration
|
||||
http:
|
||||
routers:
|
||||
blog:
|
||||
rule: "Host(`example.com`) && Path(`/blog`)"
|
||||
tls:
|
||||
certResolver: myresolver
|
||||
domains:
|
||||
- main: "example.org"
|
||||
sans:
|
||||
- "*.example.org"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
## Dynamic configuration
|
||||
[http.routers]
|
||||
[http.routers.blog]
|
||||
rule = "Host(`example.com`) && Path(`/blog`)"
|
||||
[http.routers.blog.tls]
|
||||
certResolver = "myresolver" # From static configuration
|
||||
[[http.routers.blog.tls.domains]]
|
||||
main = "example.org"
|
||||
sans = ["*.example.org"]
|
||||
```
|
@@ -0,0 +1,72 @@
|
||||
|
||||
```yaml tab="Docker"
|
||||
## Dynamic configuration
|
||||
labels:
|
||||
- traefik.http.routers.blog.rule=(Host(`example.com`) && Path(`/blog`)) || Host(`blog.example.org`)
|
||||
- traefik.http.routers.blog.tls=true
|
||||
- traefik.http.routers.blog.tls.certresolver=myresolver
|
||||
```
|
||||
|
||||
```yaml tab="Docker (Swarm)"
|
||||
## Dynamic configuration
|
||||
deploy:
|
||||
labels:
|
||||
- traefik.http.routers.blog.rule=(Host(`example.com`) && Path(`/blog`)) || Host(`blog.example.org`)
|
||||
- traefik.http.routers.blog.tls=true
|
||||
- traefik.http.routers.blog.tls.certresolver=myresolver
|
||||
- traefik.http.services.blog-svc.loadbalancer.server.port=8080"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: blogtls
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: (Host(`example.com`) && Path(`/blog`)) || Host(`blog.example.org`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: blog
|
||||
port: 8080
|
||||
tls:
|
||||
certResolver: myresolver
|
||||
```
|
||||
|
||||
```json tab="Marathon"
|
||||
labels: {
|
||||
"traefik.http.routers.blog.rule": "(Host(`example.com`) && Path(`/blog`)) || Host(`blog.example.org`)",
|
||||
"traefik.http.routers.blog.tls": "true",
|
||||
"traefik.http.routers.blog.tls.certresolver": "myresolver",
|
||||
"traefik.http.services.blog-svc.loadbalancer.server.port": "8080"
|
||||
}
|
||||
```
|
||||
|
||||
```yaml tab="Rancher"
|
||||
## Dynamic configuration
|
||||
labels:
|
||||
- traefik.http.routers.blog.rule=(Host(`example.com`) && Path(`/blog`)) || Host(`blog.example.org`)
|
||||
- traefik.http.routers.blog.tls=true
|
||||
- traefik.http.routers.blog.tls.certresolver=myresolver
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
## Dynamic configuration
|
||||
http:
|
||||
routers:
|
||||
blog:
|
||||
rule: "(Host(`example.com`) && Path(`/blog`)) || Host(`blog.example.org`)"
|
||||
tls:
|
||||
certResolver: myresolver
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
## Dynamic configuration
|
||||
[http.routers]
|
||||
[http.routers.blog]
|
||||
rule = "(Host(`example.com`) && Path(`/blog`)) || Host(`blog.example.org`)"
|
||||
[http.routers.blog.tls]
|
||||
certResolver = "myresolver"
|
||||
```
|
72
docs/content/https/include-acme-single-domain-example.md
Normal file
@@ -0,0 +1,72 @@
|
||||
|
||||
```yaml tab="Docker"
|
||||
## Dynamic configuration
|
||||
labels:
|
||||
- traefik.http.routers.blog.rule=Host(`example.com`) && Path(`/blog`)
|
||||
- traefik.http.routers.blog.tls=true
|
||||
- traefik.http.routers.blog.tls.certresolver=myresolver
|
||||
```
|
||||
|
||||
```yaml tab="Docker (Swarm)"
|
||||
## Dynamic configuration
|
||||
deploy:
|
||||
labels:
|
||||
- traefik.http.routers.blog.rule=Host(`example.com`) && Path(`/blog`)
|
||||
- traefik.http.routers.blog.tls=true
|
||||
- traefik.http.routers.blog.tls.certresolver=myresolver
|
||||
- traefik.http.services.blog-svc.loadbalancer.server.port=8080"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: blogtls
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`example.com`) && Path(`/blog`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: blog
|
||||
port: 8080
|
||||
tls:
|
||||
certResolver: myresolver
|
||||
```
|
||||
|
||||
```json tab="Marathon"
|
||||
labels: {
|
||||
"traefik.http.routers.blog.rule": "Host(`example.com`) && Path(`/blog`)",
|
||||
"traefik.http.routers.blog.tls": "true",
|
||||
"traefik.http.routers.blog.tls.certresolver": "myresolver",
|
||||
"traefik.http.services.blog-svc.loadbalancer.server.port": "8080"
|
||||
}
|
||||
```
|
||||
|
||||
```yaml tab="Rancher"
|
||||
## Dynamic configuration
|
||||
labels:
|
||||
- traefik.http.routers.blog.rule=Host(`example.com`) && Path(`/blog`)
|
||||
- traefik.http.routers.blog.tls=true
|
||||
- traefik.http.routers.blog.tls.certresolver=myresolver
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
## Dynamic configuration
|
||||
http:
|
||||
routers:
|
||||
blog:
|
||||
rule: "Host(`example.com`) && Path(`/blog`)"
|
||||
tls:
|
||||
certResolver: myresolver
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
## Dynamic configuration
|
||||
[http.routers]
|
||||
[http.routers.blog]
|
||||
rule = "Host(`example.com`) && Path(`/blog`)"
|
||||
[http.routers.blog.tls]
|
||||
certResolver = "myresolver"
|
||||
```
|
21
docs/content/https/overview.md
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
title: "Traefik Proxy HTTPS & TLS Overview |Traefik Docs"
|
||||
description: "Traefik supports HTTPS & TLS, which concerns roughly two parts of the configuration: routers, and the TLS connection. Read the documentation to learn more."
|
||||
---
|
||||
|
||||
# HTTPS & TLS
|
||||
|
||||
Overview
|
||||
{: .subtitle }
|
||||
|
||||
Traefik supports HTTPS & TLS, which concerns roughly two parts of the configuration:
|
||||
routers, and the TLS connection (and its underlying certificates).
|
||||
|
||||
When a router has to handle HTTPS traffic,
|
||||
it should be specified with a `tls` field of the router definition.
|
||||
See the TLS section of the [routers documentation](../routing/routers/index.md#tls).
|
||||
|
||||
The next sections of this documentation explain how to configure the TLS connection itself.
|
||||
That is to say, how to obtain [TLS certificates](./tls.md#certificates-definition):
|
||||
either through a definition in the dynamic configuration, or through [Let's Encrypt](./acme.md) (ACME).
|
||||
And how to configure [TLS options](./tls.md#tls-options), and [certificates stores](./tls.md#certificates-stores).
|
107
docs/content/https/ref-acme.toml
Normal file
@@ -0,0 +1,107 @@
|
||||
# Enable ACME (Let's Encrypt): automatic SSL.
|
||||
[certificatesResolvers.myresolver.acme]
|
||||
|
||||
# Email address used for registration.
|
||||
#
|
||||
# Required
|
||||
#
|
||||
email = "test@example.com"
|
||||
|
||||
# File or key used for certificates storage.
|
||||
#
|
||||
# Required
|
||||
#
|
||||
storage = "acme.json"
|
||||
|
||||
# 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"
|
||||
|
||||
# The certificates' duration in hours.
|
||||
# It defaults to 2160 (90 days) to follow Let's Encrypt certificates' duration.
|
||||
#
|
||||
# Optional
|
||||
# Default: 2160
|
||||
#
|
||||
# certificatesDuration=2160
|
||||
|
||||
# Preferred chain to use.
|
||||
#
|
||||
# If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name.
|
||||
# If no match, the default offered chain will be used.
|
||||
#
|
||||
# Optional
|
||||
# Default: ""
|
||||
#
|
||||
# preferredChain = "ISRG Root X1"
|
||||
|
||||
# KeyType to use.
|
||||
#
|
||||
# Optional
|
||||
# Default: "RSA4096"
|
||||
#
|
||||
# Available values : "EC256", "EC384", "RSA2048", "RSA4096", "RSA8192"
|
||||
#
|
||||
# keyType = "RSA4096"
|
||||
|
||||
# Use a TLS-ALPN-01 ACME challenge.
|
||||
#
|
||||
# Optional (but recommended)
|
||||
#
|
||||
[certificatesResolvers.myresolver.acme.tlsChallenge]
|
||||
|
||||
# Use a HTTP-01 ACME challenge.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# [certificatesResolvers.myresolver.acme.httpChallenge]
|
||||
|
||||
# EntryPoint to use for the HTTP-01 challenges.
|
||||
#
|
||||
# Required
|
||||
#
|
||||
# entryPoint = "web"
|
||||
|
||||
# Use a DNS-01 ACME challenge rather than HTTP-01 challenge.
|
||||
# Note: mandatory for wildcard certificate generation.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# [certificatesResolvers.myresolver.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
|
||||
|
||||
# Use following DNS servers to resolve the FQDN authority.
|
||||
#
|
||||
# Optional
|
||||
# Default: empty
|
||||
#
|
||||
# resolvers = ["1.1.1.1:53", "8.8.8.8:53"]
|
||||
|
||||
# Disable the DNS propagation checks before notifying ACME that the DNS challenge is ready.
|
||||
#
|
||||
# NOT RECOMMENDED:
|
||||
# Increase the risk of reaching Let's Encrypt's rate limits.
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
# disablePropagationCheck = true
|
106
docs/content/https/ref-acme.txt
Normal file
@@ -0,0 +1,106 @@
|
||||
# Enable ACME (Let's Encrypt): automatic SSL.
|
||||
|
||||
# Email address used for registration.
|
||||
#
|
||||
# Required
|
||||
#
|
||||
--certificatesresolvers.myresolver.acme.email=test@example.com
|
||||
|
||||
# File or key used for certificates storage.
|
||||
#
|
||||
# Required
|
||||
#
|
||||
--certificatesresolvers.myresolver.acme.storage=acme.json
|
||||
|
||||
# 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"
|
||||
#
|
||||
--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
|
||||
# The certificates' duration in hours.
|
||||
# It defaults to 2160 (90 days) to follow Let's Encrypt certificates' duration.
|
||||
#
|
||||
# Optional
|
||||
# Default: 2160
|
||||
#
|
||||
--certificatesresolvers.myresolver.acme.certificatesDuration=2160
|
||||
|
||||
# Preferred chain to use.
|
||||
#
|
||||
# If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name.
|
||||
# If no match, the default offered chain will be used.
|
||||
#
|
||||
# Optional
|
||||
# Default: ""
|
||||
#
|
||||
--certificatesresolvers.myresolver.acme.preferredchain="ISRG Root X1"
|
||||
|
||||
# KeyType to use.
|
||||
#
|
||||
# Optional
|
||||
# Default: "RSA4096"
|
||||
#
|
||||
# Available values : "EC256", "EC384", "RSA2048", "RSA4096", "RSA8192"
|
||||
#
|
||||
--certificatesresolvers.myresolver.acme.keytype=RSA4096
|
||||
|
||||
# Use a TLS-ALPN-01 ACME challenge.
|
||||
#
|
||||
# Optional (but recommended)
|
||||
#
|
||||
--certificatesresolvers.myresolver.acme.tlschallenge=true
|
||||
|
||||
# Use a HTTP-01 ACME challenge.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
--certificatesresolvers.myresolver.acme.httpchallenge=true
|
||||
|
||||
# EntryPoint to use for the HTTP-01 challenges.
|
||||
#
|
||||
# Required
|
||||
#
|
||||
--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web
|
||||
|
||||
# Use a DNS-01 ACME challenge rather than HTTP-01 challenge.
|
||||
# Note: mandatory for wildcard certificate generation.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
--certificatesresolvers.myresolver.acme.dnschallenge=true
|
||||
|
||||
# DNS provider used.
|
||||
#
|
||||
# Required
|
||||
#
|
||||
--certificatesresolvers.myresolver.acme.dnschallenge.provider=digitalocean
|
||||
|
||||
# By default, the provider will verify the TXT DNS challenge record before letting ACME verify.
|
||||
# If delayBeforeCheck is greater than zero, this check is delayed for the configured duration in seconds.
|
||||
# Useful if internal networks block external DNS queries.
|
||||
#
|
||||
# Optional
|
||||
# Default: 0
|
||||
#
|
||||
--certificatesresolvers.myresolver.acme.dnschallenge.delaybeforecheck=0
|
||||
|
||||
# Use following DNS servers to resolve the FQDN authority.
|
||||
#
|
||||
# Optional
|
||||
# Default: empty
|
||||
#
|
||||
--certificatesresolvers.myresolver.acme.dnschallenge.resolvers=1.1.1.1:53,8.8.8.8:53
|
||||
|
||||
# Disable the DNS propagation checks before notifying ACME that the DNS challenge is ready.
|
||||
#
|
||||
# NOT RECOMMENDED:
|
||||
# Increase the risk of reaching Let's Encrypt's rate limits.
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
--certificatesresolvers.myresolver.acme.dnschallenge.disablepropagationcheck=true
|
111
docs/content/https/ref-acme.yaml
Normal file
@@ -0,0 +1,111 @@
|
||||
certificatesResolvers:
|
||||
myresolver:
|
||||
# Enable ACME (Let's Encrypt): automatic SSL.
|
||||
acme:
|
||||
|
||||
# Email address used for registration.
|
||||
#
|
||||
# Required
|
||||
#
|
||||
email: "test@example.com"
|
||||
|
||||
# File or key used for certificates storage.
|
||||
#
|
||||
# Required
|
||||
#
|
||||
storage: "acme.json"
|
||||
|
||||
# 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"
|
||||
|
||||
# The certificates' duration in hours.
|
||||
# It defaults to 2160 (90 days) to follow Let's Encrypt certificates' duration.
|
||||
#
|
||||
# Optional
|
||||
# Default: 2160
|
||||
#
|
||||
# certificatesDuration: 2160
|
||||
|
||||
# Preferred chain to use.
|
||||
#
|
||||
# If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name.
|
||||
# If no match, the default offered chain will be used.
|
||||
#
|
||||
# Optional
|
||||
# Default: ""
|
||||
#
|
||||
# preferredChain: 'ISRG Root X1'
|
||||
|
||||
# KeyType to use.
|
||||
#
|
||||
# Optional
|
||||
# Default: "RSA4096"
|
||||
#
|
||||
# Available values : "EC256", "EC384", "RSA2048", "RSA4096", "RSA8192"
|
||||
#
|
||||
# keyType: RSA4096
|
||||
|
||||
# Use a TLS-ALPN-01 ACME challenge.
|
||||
#
|
||||
# Optional (but recommended)
|
||||
#
|
||||
tlsChallenge:
|
||||
|
||||
# Use a HTTP-01 ACME challenge.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# httpChallenge:
|
||||
|
||||
# EntryPoint to use for the HTTP-01 challenges.
|
||||
#
|
||||
# Required
|
||||
#
|
||||
# entryPoint: web
|
||||
|
||||
# Use a DNS-01 ACME challenge rather than HTTP-01 challenge.
|
||||
# Note: mandatory for wildcard certificate generation.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# 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
|
||||
|
||||
# Use following DNS servers to resolve the FQDN authority.
|
||||
#
|
||||
# Optional
|
||||
# Default: empty
|
||||
#
|
||||
# resolvers
|
||||
# - "1.1.1.1:53"
|
||||
# - "8.8.8.8:53"
|
||||
|
||||
# Disable the DNS propagation checks before notifying ACME that the DNS challenge is ready.
|
||||
#
|
||||
# NOT RECOMMENDED:
|
||||
# Increase the risk of reaching Let's Encrypt's rate limits.
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
#
|
||||
# disablePropagationCheck: true
|
524
docs/content/https/tls.md
Normal file
@@ -0,0 +1,524 @@
|
||||
---
|
||||
title: "Traefik TLS Documentation"
|
||||
description: "Learn how to configure the transport layer security (TLS) connection in Traefik Proxy. Read the technical documentation."
|
||||
---
|
||||
|
||||
# TLS
|
||||
|
||||
Transport Layer Security
|
||||
{: .subtitle }
|
||||
|
||||
## Certificates Definition
|
||||
|
||||
### Automated
|
||||
|
||||
See the [Let's Encrypt](./acme.md) page.
|
||||
|
||||
### User defined
|
||||
|
||||
To add / remove TLS certificates, even when Traefik is already running, their definition can be added to the [dynamic configuration](../getting-started/configuration-overview.md), in the `[[tls.certificates]]` section:
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic configuration
|
||||
|
||||
tls:
|
||||
certificates:
|
||||
- certFile: /path/to/domain.cert
|
||||
keyFile: /path/to/domain.key
|
||||
- certFile: /path/to/other-domain.cert
|
||||
keyFile: /path/to/other-domain.key
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic configuration
|
||||
|
||||
[[tls.certificates]]
|
||||
certFile = "/path/to/domain.cert"
|
||||
keyFile = "/path/to/domain.key"
|
||||
|
||||
[[tls.certificates]]
|
||||
certFile = "/path/to/other-domain.cert"
|
||||
keyFile = "/path/to/other-domain.key"
|
||||
```
|
||||
|
||||
!!! important "Restriction"
|
||||
|
||||
In the above example, we've used the [file provider](../providers/file.md) to handle these definitions.
|
||||
It is the only available method to configure the certificates (as well as the options and the stores).
|
||||
However, in [Kubernetes](../providers/kubernetes-crd.md), the certificates can and must be provided by [secrets](https://kubernetes.io/docs/concepts/configuration/secret/).
|
||||
|
||||
## Certificates Stores
|
||||
|
||||
In Traefik, certificates are grouped together in certificates stores, which are defined as such:
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic configuration
|
||||
|
||||
tls:
|
||||
stores:
|
||||
default: {}
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic configuration
|
||||
|
||||
[tls.stores]
|
||||
[tls.stores.default]
|
||||
```
|
||||
|
||||
!!! important "Restriction"
|
||||
|
||||
Any store definition other than the default one (named `default`) will be ignored,
|
||||
and there is therefore only one globally available TLS store.
|
||||
|
||||
In the `tls.certificates` section, a list of stores can then be specified to indicate where the certificates should be stored:
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic configuration
|
||||
|
||||
tls:
|
||||
certificates:
|
||||
- certFile: /path/to/domain.cert
|
||||
keyFile: /path/to/domain.key
|
||||
stores:
|
||||
- default
|
||||
# Note that since no store is defined,
|
||||
# the certificate below will be stored in the `default` store.
|
||||
- certFile: /path/to/other-domain.cert
|
||||
keyFile: /path/to/other-domain.key
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic configuration
|
||||
|
||||
[[tls.certificates]]
|
||||
certFile = "/path/to/domain.cert"
|
||||
keyFile = "/path/to/domain.key"
|
||||
stores = ["default"]
|
||||
|
||||
[[tls.certificates]]
|
||||
# Note that since no store is defined,
|
||||
# the certificate below will be stored in the `default` store.
|
||||
certFile = "/path/to/other-domain.cert"
|
||||
keyFile = "/path/to/other-domain.key"
|
||||
```
|
||||
|
||||
!!! important "Restriction"
|
||||
|
||||
The `stores` list will actually be ignored and automatically set to `["default"]`.
|
||||
|
||||
### Default Certificate
|
||||
|
||||
Traefik can use a default certificate for connections without a SNI, or without a matching domain.
|
||||
This default certificate should be defined in a TLS store:
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic configuration
|
||||
|
||||
tls:
|
||||
stores:
|
||||
default:
|
||||
defaultCertificate:
|
||||
certFile: path/to/cert.crt
|
||||
keyFile: path/to/cert.key
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic configuration
|
||||
|
||||
[tls.stores]
|
||||
[tls.stores.default]
|
||||
[tls.stores.default.defaultCertificate]
|
||||
certFile = "path/to/cert.crt"
|
||||
keyFile = "path/to/cert.key"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: TLSStore
|
||||
metadata:
|
||||
name: default
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
defaultCertificate:
|
||||
secretName: default-certificate
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: default-certificate
|
||||
namespace: default
|
||||
|
||||
type: Opaque
|
||||
data:
|
||||
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
|
||||
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0=
|
||||
```
|
||||
|
||||
If no default certificate is provided, Traefik generates and uses a self-signed certificate.
|
||||
|
||||
## TLS Options
|
||||
|
||||
The TLS options allow one to configure some parameters of the TLS connection.
|
||||
|
||||
!!! important "'default' TLS Option"
|
||||
|
||||
The `default` option is special.
|
||||
When no tls options are specified in a tls router, the `default` option is used.
|
||||
When specifying the `default` option explicitly, make sure not to specify provider namespace as the `default` option does not have one.
|
||||
Conversely, for cross-provider references, for example, when referencing the file provider from a docker label,
|
||||
you must specify the provider namespace, for example:
|
||||
`traefik.http.routers.myrouter.tls.options=myoptions@file`
|
||||
|
||||
!!! important "TLSOption in Kubernetes"
|
||||
|
||||
When using the [TLSOption resource](../../routing/providers/kubernetes-crd#kind-tlsoption) in Kubernetes, one might setup a default set of options that,
|
||||
if not explicitly overwritten, should apply to all ingresses.
|
||||
To achieve that, you'll have to create a TLSOption resource with the name `default`.
|
||||
There may exist only one TLSOption with the name `default` (across all namespaces) - otherwise they will be dropped.
|
||||
To explicitly use a different TLSOption (and using the Kubernetes Ingress resources)
|
||||
you'll have to add an annotation to the Ingress in the following form:
|
||||
`traefik.ingress.kubernetes.io/router.tls.options: <resource-namespace>-<resource-name>@kubernetescrd`
|
||||
|
||||
### Minimum TLS Version
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic configuration
|
||||
|
||||
tls:
|
||||
options:
|
||||
default:
|
||||
minVersion: VersionTLS12
|
||||
|
||||
mintls13:
|
||||
minVersion: VersionTLS13
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic configuration
|
||||
|
||||
[tls.options]
|
||||
|
||||
[tls.options.default]
|
||||
minVersion = "VersionTLS12"
|
||||
|
||||
[tls.options.mintls13]
|
||||
minVersion = "VersionTLS13"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: TLSOption
|
||||
metadata:
|
||||
name: default
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
minVersion: VersionTLS12
|
||||
|
||||
---
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: TLSOption
|
||||
metadata:
|
||||
name: mintls13
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
minVersion: VersionTLS13
|
||||
```
|
||||
|
||||
### Maximum TLS Version
|
||||
|
||||
We discourage the use of this setting to disable TLS1.3.
|
||||
|
||||
The recommended approach is to update the clients to support TLS1.3.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic configuration
|
||||
|
||||
tls:
|
||||
options:
|
||||
default:
|
||||
maxVersion: VersionTLS13
|
||||
|
||||
maxtls12:
|
||||
maxVersion: VersionTLS12
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic configuration
|
||||
|
||||
[tls.options]
|
||||
|
||||
[tls.options.default]
|
||||
maxVersion = "VersionTLS13"
|
||||
|
||||
[tls.options.maxtls12]
|
||||
maxVersion = "VersionTLS12"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: TLSOption
|
||||
metadata:
|
||||
name: default
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
maxVersion: VersionTLS13
|
||||
|
||||
---
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: TLSOption
|
||||
metadata:
|
||||
name: maxtls12
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
maxVersion: VersionTLS12
|
||||
```
|
||||
|
||||
### Cipher Suites
|
||||
|
||||
See [cipherSuites](https://godoc.org/crypto/tls#pkg-constants) for more information.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic configuration
|
||||
|
||||
tls:
|
||||
options:
|
||||
default:
|
||||
cipherSuites:
|
||||
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic configuration
|
||||
|
||||
[tls.options]
|
||||
[tls.options.default]
|
||||
cipherSuites = [
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
|
||||
]
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: TLSOption
|
||||
metadata:
|
||||
name: default
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
cipherSuites:
|
||||
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|
||||
```
|
||||
|
||||
!!! important "TLS 1.3"
|
||||
|
||||
Cipher suites defined for TLS 1.2 and below cannot be used in TLS 1.3, and vice versa. (<https://tools.ietf.org/html/rfc8446>)
|
||||
With TLS 1.3, the cipher suites are not configurable (all supported cipher suites are safe in this case).
|
||||
<https://golang.org/doc/go1.12#tls_1_3>
|
||||
|
||||
### Curve Preferences
|
||||
|
||||
This option allows to set the preferred elliptic curves in a specific order.
|
||||
|
||||
The names of the curves defined by [`crypto`](https://godoc.org/crypto/tls#CurveID) (e.g. `CurveP521`) and the [RFC defined names](https://tools.ietf.org/html/rfc8446#section-4.2.7) (e. g. `secp521r1`) can be used.
|
||||
|
||||
See [CurveID](https://godoc.org/crypto/tls#CurveID) for more information.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic configuration
|
||||
|
||||
tls:
|
||||
options:
|
||||
default:
|
||||
curvePreferences:
|
||||
- CurveP521
|
||||
- CurveP384
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic configuration
|
||||
|
||||
[tls.options]
|
||||
[tls.options.default]
|
||||
curvePreferences = ["CurveP521", "CurveP384"]
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: TLSOption
|
||||
metadata:
|
||||
name: default
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
curvePreferences:
|
||||
- CurveP521
|
||||
- CurveP384
|
||||
```
|
||||
|
||||
### Strict SNI Checking
|
||||
|
||||
With strict SNI checking enabled, Traefik won't allow connections from clients
|
||||
that do not specify a server_name extension or don't match any certificate configured on the tlsOption.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic configuration
|
||||
|
||||
tls:
|
||||
options:
|
||||
default:
|
||||
sniStrict: true
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic configuration
|
||||
|
||||
[tls.options]
|
||||
[tls.options.default]
|
||||
sniStrict = true
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: TLSOption
|
||||
metadata:
|
||||
name: default
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
sniStrict: true
|
||||
```
|
||||
|
||||
### Prefer Server Cipher Suites
|
||||
|
||||
This option allows the server to choose its most preferred cipher suite instead of the client's.
|
||||
Please note that this is enabled automatically when `minVersion` or `maxVersion` are set.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic configuration
|
||||
|
||||
tls:
|
||||
options:
|
||||
default:
|
||||
preferServerCipherSuites: true
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic configuration
|
||||
|
||||
[tls.options]
|
||||
[tls.options.default]
|
||||
preferServerCipherSuites = true
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: TLSOption
|
||||
metadata:
|
||||
name: default
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
preferServerCipherSuites: true
|
||||
```
|
||||
|
||||
### ALPN Protocols
|
||||
|
||||
_Optional, Default="h2, http/1.1, acme-tls/1"_
|
||||
|
||||
This option allows to specify the list of supported application level protocols for the TLS handshake,
|
||||
in order of preference.
|
||||
If the client supports ALPN, the selected protocol will be one from this list,
|
||||
and the connection will fail if there is no mutually supported protocol.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic configuration
|
||||
|
||||
tls:
|
||||
options:
|
||||
default:
|
||||
alpnProtocols:
|
||||
- http/1.1
|
||||
- h2
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic configuration
|
||||
|
||||
[tls.options]
|
||||
[tls.options.default]
|
||||
alpnProtocols = ["http/1.1", "h2"]
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: TLSOption
|
||||
metadata:
|
||||
name: default
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
alpnProtocols:
|
||||
- http/1.1
|
||||
- h2
|
||||
```
|
||||
|
||||
### Client Authentication (mTLS)
|
||||
|
||||
Traefik supports mutual authentication, through the `clientAuth` section.
|
||||
|
||||
For authentication policies that require verification of the client certificate, the certificate authority for the certificate should be set in `clientAuth.caFiles`.
|
||||
|
||||
The `clientAuth.clientAuthType` option governs the behaviour as follows:
|
||||
|
||||
- `NoClientCert`: disregards any client certificate.
|
||||
- `RequestClientCert`: asks for a certificate but proceeds anyway if none is provided.
|
||||
- `RequireAnyClientCert`: requires a certificate but does not verify if it is signed by a CA listed in `clientAuth.caFiles`.
|
||||
- `VerifyClientCertIfGiven`: if a certificate is provided, verifies if it is signed by a CA listed in `clientAuth.caFiles`. Otherwise proceeds without any certificate.
|
||||
- `RequireAndVerifyClientCert`: requires a certificate, which must be signed by a CA listed in `clientAuth.caFiles`.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic configuration
|
||||
|
||||
tls:
|
||||
options:
|
||||
default:
|
||||
clientAuth:
|
||||
# in PEM format. each file can contain multiple CAs.
|
||||
caFiles:
|
||||
- tests/clientca1.crt
|
||||
- tests/clientca2.crt
|
||||
clientAuthType: RequireAndVerifyClientCert
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic configuration
|
||||
|
||||
[tls.options]
|
||||
[tls.options.default]
|
||||
[tls.options.default.clientAuth]
|
||||
# in PEM format. each file can contain multiple CAs.
|
||||
caFiles = ["tests/clientca1.crt", "tests/clientca2.crt"]
|
||||
clientAuthType = "RequireAndVerifyClientCert"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: TLSOption
|
||||
metadata:
|
||||
name: default
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
clientAuth:
|
||||
# the CA certificate is extracted from key `tls.ca` or `ca.crt` of the given secrets.
|
||||
secretNames:
|
||||
- secretCA
|
||||
clientAuthType: RequireAndVerifyClientCert
|
||||
```
|
@@ -1 +0,0 @@
|
||||
To learn more about configuration options in the command line, refer to the [configuration overview](../getting-started/configuration-overview.md)
|