mirror of
https://github.com/containous/traefik.git
synced 2025-11-06 20:24:01 +03:00
Compare commits
624 Commits
v2.4.11
...
v3.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e54ee89330 | ||
|
|
fdd3f2abef | ||
|
|
517917cd7c | ||
|
|
d97d3a6726 | ||
|
|
6c75052a13 | ||
|
|
a8df674dcf | ||
|
|
abd569701f | ||
|
|
7e3fe48b80 | ||
|
|
8cf9385938 | ||
|
|
519ed8bde5 | ||
|
|
46a61ce9c8 | ||
|
|
778188ed34 | ||
|
|
88603810a8 | ||
|
|
c7647b4938 | ||
|
|
af71443b61 | ||
|
|
c57876c116 | ||
|
|
0d81fac3fc | ||
|
|
db287c4d31 | ||
|
|
4d86668af3 | ||
|
|
b93141992e | ||
|
|
18d66d7432 | ||
|
|
a3e4c85ec0 | ||
|
|
bee86b5ac7 | ||
|
|
0ba51d62fa | ||
|
|
268d1edc8f | ||
|
|
580e7fa774 | ||
|
|
7c72780820 | ||
|
|
46c266661c | ||
|
|
61325d7b91 | ||
|
|
68e8eb2435 | ||
|
|
3f8aa13e68 | ||
|
|
08279047ae | ||
|
|
3dd4968c41 | ||
|
|
ba1ca68977 | ||
|
|
81a5b1b4c8 | ||
|
|
52e6ce95cf | ||
|
|
d547718fdd | ||
|
|
56f7515ecd | ||
|
|
af4e74c39d | ||
|
|
27c02b5a56 | ||
|
|
f6b7940b76 | ||
|
|
f1b91a119d | ||
|
|
630de7481e | ||
|
|
fadee5e87b | ||
|
|
35d8281f4d | ||
|
|
67d9c8da0b | ||
|
|
00de5c711a | ||
|
|
b935c80dbd | ||
|
|
22c6630412 | ||
|
|
1a1cfd1adc | ||
|
|
240fb871b6 | ||
|
|
b2c4221429 | ||
|
|
d131ef57da | ||
|
|
97de552e06 | ||
|
|
281fa25844 | ||
|
|
454f552691 | ||
|
|
7258048403 | ||
|
|
bd3eaf4f5e | ||
|
|
15f7472091 | ||
|
|
a041a6b198 | ||
|
|
7582da9650 | ||
|
|
7a6bfd3336 | ||
|
|
1b9873cae9 | ||
|
|
e86f21ae7b | ||
|
|
ccbbd0d766 | ||
|
|
93212125e3 | ||
|
|
be3b798dd6 | ||
|
|
8128d6ca26 | ||
|
|
194247caae | ||
|
|
cd0654026a | ||
|
|
14ab1514dc | ||
|
|
40242294d8 | ||
|
|
996eccf5b7 | ||
|
|
b39ce8cc58 | ||
|
|
e9de061b84 | ||
|
|
33f0aed5ea | ||
|
|
0ca1c8aac3 | ||
|
|
2c550c284d | ||
|
|
87815586be | ||
|
|
09d6383621 | ||
|
|
188ef84c4f | ||
|
|
a5c520664a | ||
|
|
39b0077725 | ||
|
|
e2a9caf760 | ||
|
|
bc79796c38 | ||
|
|
b1db81d8ac | ||
|
|
38d7011487 | ||
|
|
ae7db879d9 | ||
|
|
dd34905ea9 | ||
|
|
3812e6f3cb | ||
|
|
627175694d | ||
|
|
82cf6c9577 | ||
|
|
63a1186d3e | ||
|
|
f75f636e27 | ||
|
|
615dc7fd35 | ||
|
|
52b6b057f0 | ||
|
|
7b3faef4b3 | ||
|
|
7758880f3f | ||
|
|
d04903edb2 | ||
|
|
a63d5c95a8 | ||
|
|
bb66950197 | ||
|
|
c4cc30ccc6 | ||
|
|
9cd54baca4 | ||
|
|
7ac687a0a9 | ||
|
|
83ae1021f6 | ||
|
|
033fccccc7 | ||
|
|
df99a9fb57 | ||
|
|
67e3bc6380 | ||
|
|
d6b69e1347 | ||
|
|
4bd055cf97 | ||
|
|
4b291b2cf8 | ||
|
|
89870ad539 | ||
|
|
5bc03af75f | ||
|
|
30ec5c58fe | ||
|
|
a4b447256b | ||
|
|
1c9a7b8c61 | ||
|
|
d06573de6c | ||
|
|
6c2c561d8f | ||
|
|
e5309a4601 | ||
|
|
e9f98fb6eb | ||
|
|
b351266b2d | ||
|
|
fd95560c66 | ||
|
|
788f8fa951 | ||
|
|
89dc466b23 | ||
|
|
ab8d7d2e78 | ||
|
|
a002ccfce3 | ||
|
|
693d5da1b9 | ||
|
|
8ddc37d528 | ||
|
|
0cb2652f51 | ||
|
|
fe8e7ab5b8 | ||
|
|
d531963f95 | ||
|
|
d578ed7327 | ||
|
|
10528c973a | ||
|
|
56a1ed4220 | ||
|
|
37b6edb28c | ||
|
|
44a2b85dba | ||
|
|
77c8d60092 | ||
|
|
b33c8cec0b | ||
|
|
52df1d63fe | ||
|
|
c84378d649 | ||
|
|
12dccc4fdd | ||
|
|
32e44816c9 | ||
|
|
23c74c9f2e | ||
|
|
9a82d96e68 | ||
|
|
d9589878fb | ||
|
|
703de5331b | ||
|
|
d3e4d56a0d | ||
|
|
adf82d72ae | ||
|
|
25027d6df8 | ||
|
|
e56dfeb7d5 | ||
|
|
5ca7fff7f6 | ||
|
|
dfa1f3fc00 | ||
|
|
b26c45af2b | ||
|
|
626da4c0ae | ||
|
|
9c02612f65 | ||
|
|
b3f4f6bb21 | ||
|
|
2cac58d9c0 | ||
|
|
a553085689 | ||
|
|
6dd63e1702 | ||
|
|
868ab7a5c8 | ||
|
|
23c26d64ee | ||
|
|
63f9ec9c38 | ||
|
|
40db06204b | ||
|
|
4755bb2f33 | ||
|
|
45453b20fa | ||
|
|
40d2421db9 | ||
|
|
af749f1864 | ||
|
|
1576ad85b8 | ||
|
|
2a2ea759d1 | ||
|
|
b4ee7bdcbe | ||
|
|
146991efda | ||
|
|
ab94bbaece | ||
|
|
5a706296f2 | ||
|
|
5b3354b8ce | ||
|
|
7751fb24eb | ||
|
|
f85f3b68aa | ||
|
|
b361608693 | ||
|
|
cdda9a18ab | ||
|
|
3686f95832 | ||
|
|
2cb011f595 | ||
|
|
b7199a7a9b | ||
|
|
14eb56cf30 | ||
|
|
ff2911d070 | ||
|
|
f07fcd3d54 | ||
|
|
0e4b4c1a31 | ||
|
|
154d8470ab | ||
|
|
c9520480c2 | ||
|
|
05c3486347 | ||
|
|
0231db05b4 | ||
|
|
4dc379c601 | ||
|
|
8f6463ba7a | ||
|
|
aff334ffb4 | ||
|
|
28da781194 | ||
|
|
51a02caea3 | ||
|
|
839bc7b3a8 | ||
|
|
9c79fafeeb | ||
|
|
c51e590591 | ||
|
|
9c4b336f3b | ||
|
|
aa8fda5eae | ||
|
|
8b22101236 | ||
|
|
3c1d5e0393 | ||
|
|
03598d395b | ||
|
|
9d61cb64a2 | ||
|
|
ba3f5b318c | ||
|
|
62e17c659e | ||
|
|
41748c3ae4 | ||
|
|
65a317010b | ||
|
|
a887794313 | ||
|
|
77e1ce2877 | ||
|
|
470a4f6e5f | ||
|
|
94141233f0 | ||
|
|
467c8b31c3 | ||
|
|
ff17ac53df | ||
|
|
55ba4356f2 | ||
|
|
804b0ff2f2 | ||
|
|
818541d4d7 | ||
|
|
1b199730d2 | ||
|
|
f8f685193d | ||
|
|
6e535f8cef | ||
|
|
23340c46e6 | ||
|
|
5c15f5fe04 | ||
|
|
ba7e9ed788 | ||
|
|
9ccc8cfb25 | ||
|
|
9810bde68b | ||
|
|
251798a778 | ||
|
|
91f4ccf087 | ||
|
|
73306a1533 | ||
|
|
b3eb629785 | ||
|
|
aa0b5466a9 | ||
|
|
becee5e393 | ||
|
|
59e66dfce5 | ||
|
|
9c59df5e9c | ||
|
|
2a88b25712 | ||
|
|
b952f814c1 | ||
|
|
f90e3817e8 | ||
|
|
6d6f8b28d7 | ||
|
|
118d56fc40 | ||
|
|
f352c34136 | ||
|
|
fbf90e6981 | ||
|
|
607faace07 | ||
|
|
521109d3f2 | ||
|
|
ec25bdb9f9 | ||
|
|
685962545a | ||
|
|
34d29e7a10 | ||
|
|
05f3e60366 | ||
|
|
5aa1220e5a | ||
|
|
c1919c6b24 | ||
|
|
6349e2e28c | ||
|
|
e642365613 | ||
|
|
ac4086d0ac | ||
|
|
d5ff301d90 | ||
|
|
575d4ab431 | ||
|
|
ede2be1f66 | ||
|
|
d134a993d0 | ||
|
|
86cc6df374 | ||
|
|
32920ca65c | ||
|
|
3ac708ddcb | ||
|
|
0dac0c3a5b | ||
|
|
9810120aff | ||
|
|
ae6e844143 | ||
|
|
a34e1c0747 | ||
|
|
c29ed24a06 | ||
|
|
619621f239 | ||
|
|
ff5cd9b592 | ||
|
|
af855ef7b4 | ||
|
|
6559d63d3c | ||
|
|
4758cc0c8e | ||
|
|
e4ed829661 | ||
|
|
2968e5b61b | ||
|
|
7d274e8088 | ||
|
|
6c2eb6eef3 | ||
|
|
95257d2ee1 | ||
|
|
707d355d4a | ||
|
|
73ba7ed2d2 | ||
|
|
55addfefc8 | ||
|
|
0ecd85cc66 | ||
|
|
a9fe3f98c5 | ||
|
|
77b2a88819 | ||
|
|
44621ad28c | ||
|
|
232e2c1e7d | ||
|
|
ad3625bef3 | ||
|
|
7c4bf602f0 | ||
|
|
ffdd693ff6 | ||
|
|
85b0a47fe8 | ||
|
|
78822a8015 | ||
|
|
55cef21fbe | ||
|
|
2691ac1307 | ||
|
|
a51851247e | ||
|
|
0e532a3634 | ||
|
|
883422dc21 | ||
|
|
c9daf16388 | ||
|
|
b22945e185 | ||
|
|
71150bcaaf | ||
|
|
8c56d1a338 | ||
|
|
a49b537d9c | ||
|
|
45328ab719 | ||
|
|
4b755dc58d | ||
|
|
0f29e893f4 | ||
|
|
e3adf93a74 | ||
|
|
0d7d5a0318 | ||
|
|
81f88dd998 | ||
|
|
b6bfa905db | ||
|
|
c0b0f3f0f7 | ||
|
|
16d7b89cb1 | ||
|
|
a4560fa20d | ||
|
|
fbdb6e6e78 | ||
|
|
8d58f33a28 | ||
|
|
9398222db7 | ||
|
|
d2a2362be5 | ||
|
|
4c0a3721d0 | ||
|
|
ba2d09f6fb | ||
|
|
7243e65b51 | ||
|
|
3bf4a8fbe2 | ||
|
|
23a6602cbf | ||
|
|
822b94c45d | ||
|
|
0a776c3fd5 | ||
|
|
d7378a96ad | ||
|
|
db4c6111fd | ||
|
|
2da7fa0397 | ||
|
|
0d58e8d1ad | ||
|
|
dad76e0478 | ||
|
|
79aab5aab8 | ||
|
|
b02c651961 | ||
|
|
0617a1b0e0 | ||
|
|
06749e71f2 | ||
|
|
6622027c7c | ||
|
|
401c171bbd | ||
|
|
a1e766e180 | ||
|
|
63bb770b9c | ||
|
|
b3de9a040b | ||
|
|
a59dbc4c79 | ||
|
|
40deefa868 | ||
|
|
491de0cf64 | ||
|
|
c7b24f4e9c | ||
|
|
27a7563e33 | ||
|
|
25725e9b2f | ||
|
|
819de02101 | ||
|
|
ce851a5929 | ||
|
|
7e390ef516 | ||
|
|
fb23bd5d26 | ||
|
|
6974f54bfd | ||
|
|
aaf5aa4506 | ||
|
|
371b6e3c86 | ||
|
|
9297055ad8 | ||
|
|
9e96089da6 | ||
|
|
a79868fadc | ||
|
|
84a0810546 | ||
|
|
d9fbb5e25c | ||
|
|
e97aa6515b | ||
|
|
6bcfba43c8 | ||
|
|
0c83ee736c | ||
|
|
ca55dfe1c6 | ||
|
|
4da33c2bc2 | ||
|
|
2d56be0ebb | ||
|
|
5780dc2b15 | ||
|
|
764bf59d4d | ||
|
|
6742dd8454 | ||
|
|
3ac755bd2f | ||
|
|
7543709ecf | ||
|
|
3ed72c4e46 | ||
|
|
477fa15859 | ||
|
|
1048348ae6 | ||
|
|
390eb9cb61 | ||
|
|
5a1c936ede | ||
|
|
47ad6538f1 | ||
|
|
9be44d8330 | ||
|
|
a4b354b33f | ||
|
|
a70b864c55 | ||
|
|
3bd5fc0f90 | ||
|
|
aabfb792af | ||
|
|
e5e48d1cc1 | ||
|
|
42a110dd69 | ||
|
|
64af364b02 | ||
|
|
cf14b8fa92 | ||
|
|
e7dc6ec025 | ||
|
|
f29e311b73 | ||
|
|
a914ce2bd2 | ||
|
|
b42a7c89e7 | ||
|
|
67483c1b17 | ||
|
|
4071f1e7f2 | ||
|
|
577709fff3 | ||
|
|
8cd45476ac | ||
|
|
cf14504fd5 | ||
|
|
b84829336d | ||
|
|
ba822acb23 | ||
|
|
d969e59911 | ||
|
|
936b6148ff | ||
|
|
a9776ceafc | ||
|
|
e471239955 | ||
|
|
2e8156bfaa | ||
|
|
f5dd233a3b | ||
|
|
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 | ||
|
|
6ae50389e6 | ||
|
|
87fd51d7ec | ||
|
|
7e43e5615e | ||
|
|
3a180e2afc | ||
|
|
ca2ff214c4 | ||
|
|
f8db285d5d | ||
|
|
1f880662d6 | ||
|
|
febab86682 | ||
|
|
8070dfef45 | ||
|
|
fc69f882c5 | ||
|
|
838a8e18d3 | ||
|
|
5e3e47b484 | ||
|
|
a243ac4dde | ||
|
|
ce2e02b690 | ||
|
|
dca348359b | ||
|
|
cf0759a48f | ||
|
|
2ccdc419d0 | ||
|
|
9af0e705a5 | ||
|
|
0a3e40332a | ||
|
|
a758d18e51 | ||
|
|
f15d05b22f | ||
|
|
fc9f41b955 | ||
|
|
679def0151 | ||
|
|
e5024d5d0a | ||
|
|
dd04c432e9 | ||
|
|
b1fd3b8fc7 | ||
|
|
456df0fc19 | ||
|
|
526f493e12 | ||
|
|
5632ee6378 | ||
|
|
56f845c71a | ||
|
|
e1e1fd640c | ||
|
|
63ef0f1cee | ||
|
|
de2437cfec | ||
|
|
32e08f3510 | ||
|
|
0b48d5d0d2 | ||
|
|
080cf98e51 | ||
|
|
dc8d5ef744 | ||
|
|
70a02158e5 | ||
|
|
ab71dad51a | ||
|
|
0624cefc10 | ||
|
|
ac486d3d1d | ||
|
|
e28b33b53b | ||
|
|
d3a3aeb0fc | ||
|
|
702e0a461a | ||
|
|
cb4fb973b2 | ||
|
|
d13d078351 | ||
|
|
31a5f3591f | ||
|
|
8947f85ddd | ||
|
|
29908098e4 | ||
|
|
702e301990 | ||
|
|
606b43dc51 | ||
|
|
992d4c1b94 | ||
|
|
e658712d53 | ||
|
|
438eec720a | ||
|
|
1b21f0723f | ||
|
|
d211437d6c | ||
|
|
7996a42f76 | ||
|
|
2461e36ed4 | ||
|
|
2bbb6fc427 | ||
|
|
2747e240c1 | ||
|
|
4b370930b5 | ||
|
|
b05a5c818d | ||
|
|
41d22ef17e | ||
|
|
bbee63fcf3 | ||
|
|
b1ddd0e038 | ||
|
|
f742671bbe | ||
|
|
ed5321999c | ||
|
|
fc7ec17905 | ||
|
|
e5a01c7cc8 | ||
|
|
0509b6fdb9 | ||
|
|
759d17547a | ||
|
|
a3327c4430 |
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
@@ -8,7 +8,7 @@ DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
|
|||||||
The issue tracker is for reporting bugs and feature requests only.
|
The issue tracker is for reporting bugs and feature requests only.
|
||||||
For end-user related support questions, please refer to one of the following:
|
For end-user related support questions, please refer to one of the following:
|
||||||
|
|
||||||
- the Traefik community forum: https://community.containo.us/
|
- the Traefik community forum: https://community.traefik.io/
|
||||||
|
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
|||||||
10
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
10
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -6,16 +6,18 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: Welcome!
|
label: Welcome!
|
||||||
description: |
|
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 issue tracker is for reporting bugs and feature requests only.
|
||||||
- the Traefik community forum: https://community.containo.us/
|
For end-user related support questions, please use 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/).
|
All new/updated issues are triaged regularly by the maintainers.
|
||||||
|
All issues closed by a bot are subsequently double-checked by the maintainers.
|
||||||
|
|
||||||
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
|
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
|
||||||
|
|
||||||
options:
|
options:
|
||||||
- label: Yes, I've searched similar issues on [GitHub](https://github.com/traefik/traefik/issues) and didn't find any.
|
- label: Yes, I've searched similar issues on [GitHub](https://github.com/traefik/traefik/issues) and didn't find any.
|
||||||
required: true
|
required: true
|
||||||
- label: Yes, I've searched similar issues on the [Traefik community forum](https://community.containo.us) and didn't find any.
|
- label: Yes, I've searched similar issues on the [Traefik community forum](https://community.traefik.io) and didn't find any.
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
|||||||
4
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
4
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
@@ -7,13 +7,13 @@ body:
|
|||||||
label: Welcome!
|
label: Welcome!
|
||||||
description: |
|
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 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.containo.us/
|
- the Traefik community forum: https://community.traefik.io/
|
||||||
|
|
||||||
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
|
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
|
||||||
options:
|
options:
|
||||||
- label: Yes, I've searched similar issues on [GitHub](https://github.com/traefik/traefik/issues) and didn't find any.
|
- label: Yes, I've searched similar issues on [GitHub](https://github.com/traefik/traefik/issues) and didn't find any.
|
||||||
required: true
|
required: true
|
||||||
- label: Yes, I've searched similar issues on the [Traefik community forum](https://community.containo.us) and didn't find any.
|
- label: Yes, I've searched similar issues on the [Traefik community forum](https://community.traefik.io) and didn't find any.
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
|
|||||||
12
.github/PULL_REQUEST_TEMPLATE.md
vendored
12
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -2,16 +2,16 @@
|
|||||||
PLEASE READ THIS MESSAGE.
|
PLEASE READ THIS MESSAGE.
|
||||||
|
|
||||||
Documentation fixes or enhancements:
|
Documentation fixes or enhancements:
|
||||||
- for Traefik v1: use branch v1.7
|
- for Traefik v2: use branch v2.9
|
||||||
- for Traefik v2: use branch v2.4
|
- for Traefik v3: use branch master
|
||||||
|
|
||||||
Bug fixes:
|
Bug fixes:
|
||||||
- for Traefik v1: use branch v1.7
|
- for Traefik v2: use branch v2.9
|
||||||
- for Traefik v2: use branch v2.4
|
- for Traefik v3: use branch master
|
||||||
|
|
||||||
Enhancements:
|
Enhancements:
|
||||||
- for Traefik v1: we only accept bug fixes
|
- for Traefik v2: we only accept bug fixes
|
||||||
- for Traefik v2: use branch master
|
- for Traefik v3: use branch master
|
||||||
|
|
||||||
HOW TO WRITE A GOOD PULL REQUEST? https://doc.traefik.io/traefik/contributing/submitting-pull-requests/
|
HOW TO WRITE A GOOD PULL REQUEST? https://doc.traefik.io/traefik/contributing/submitting-pull-requests/
|
||||||
|
|
||||||
|
|||||||
79
.github/workflows/build.yaml
vendored
Normal file
79
.github/workflows/build.yaml
vendored
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
name: Build Binaries
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
env:
|
||||||
|
GO_VERSION: 1.19
|
||||||
|
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@v3
|
||||||
|
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
|
||||||
5
.github/workflows/check_doc.yml
vendored
5
.github/workflows/check_doc.yml
vendored
@@ -2,15 +2,16 @@ name: Check Documentation
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
docs:
|
docs:
|
||||||
name: Check, verify and build documentation
|
name: Check, verify and build documentation
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-20.04
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
|
|||||||
12
.github/workflows/documentation.yml
vendored
12
.github/workflows/documentation.yml
vendored
@@ -6,18 +6,18 @@ on:
|
|||||||
- master
|
- master
|
||||||
- v*
|
- v*
|
||||||
|
|
||||||
|
env:
|
||||||
|
STRUCTOR_VERSION: v1.11.2
|
||||||
|
MIXTUS_VERSION: v0.4.1
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
docs:
|
docs:
|
||||||
name: Doc Process
|
name: Doc Process
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-20.04
|
||||||
if: github.repository == 'traefik/traefik'
|
if: github.repository == 'traefik/traefik'
|
||||||
env:
|
|
||||||
STRUCTOR_VERSION: v1.11.2
|
|
||||||
MIXTUS_VERSION: v0.4.1
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
@@ -44,7 +44,7 @@ jobs:
|
|||||||
STRUCTOR_LATEST_TAG: ${{ secrets.STRUCTOR_LATEST_TAG }}
|
STRUCTOR_LATEST_TAG: ${{ secrets.STRUCTOR_LATEST_TAG }}
|
||||||
|
|
||||||
- name: Apply seo
|
- name: Apply seo
|
||||||
run: $HOME/bin/seo -path=./site
|
run: $HOME/bin/seo -path=./site -product=traefik
|
||||||
|
|
||||||
- name: Publish documentation
|
- 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
|
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
|
||||||
|
|||||||
37
.github/workflows/experimental.yaml
vendored
Normal file
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
46
.github/workflows/test-unit.yaml
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
name: Test Unit
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
env:
|
||||||
|
GO_VERSION: 1.19
|
||||||
|
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@v3
|
||||||
|
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
97
.github/workflows/validate.yaml
vendored
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
name: Validate
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
env:
|
||||||
|
GO_VERSION: 1.19
|
||||||
|
GOLANGCI_LINT_VERSION: v1.50.0
|
||||||
|
MISSSPELL_VERSION: v0.4.0
|
||||||
|
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@v3
|
||||||
|
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/golangci/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@v3
|
||||||
|
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
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -7,7 +7,6 @@
|
|||||||
/webui/.tmp/
|
/webui/.tmp/
|
||||||
/site/
|
/site/
|
||||||
/docs/site/
|
/docs/site/
|
||||||
/static/
|
|
||||||
/autogen/
|
/autogen/
|
||||||
/traefik
|
/traefik
|
||||||
/traefik.toml
|
/traefik.toml
|
||||||
@@ -17,4 +16,6 @@
|
|||||||
cover.out
|
cover.out
|
||||||
vendor/
|
vendor/
|
||||||
plugins-storage/
|
plugins-storage/
|
||||||
|
plugins-local/
|
||||||
traefik_changelog.md
|
traefik_changelog.md
|
||||||
|
integration/tailscale.secret
|
||||||
|
|||||||
151
.golangci.toml
151
.golangci.toml
@@ -1,151 +0,0 @@
|
|||||||
[run]
|
|
||||||
timeout = "10m"
|
|
||||||
skip-files = []
|
|
||||||
skip-dirs = [
|
|
||||||
"pkg/provider/kubernetes/crd/generated/",
|
|
||||||
]
|
|
||||||
|
|
||||||
[linters-settings]
|
|
||||||
|
|
||||||
[linters-settings.govet]
|
|
||||||
check-shadowing = false
|
|
||||||
|
|
||||||
[linters-settings.golint]
|
|
||||||
min-confidence = 0.0
|
|
||||||
|
|
||||||
[linters-settings.gocyclo]
|
|
||||||
min-complexity = 14.0
|
|
||||||
|
|
||||||
[linters-settings.maligned]
|
|
||||||
suggest-new = true
|
|
||||||
|
|
||||||
[linters-settings.goconst]
|
|
||||||
min-len = 3.0
|
|
||||||
min-occurrences = 4.0
|
|
||||||
|
|
||||||
[linters-settings.misspell]
|
|
||||||
locale = "US"
|
|
||||||
|
|
||||||
[linters-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"
|
|
||||||
|
|
||||||
[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",
|
|
||||||
]
|
|
||||||
|
|
||||||
[linters]
|
|
||||||
enable-all = true
|
|
||||||
disable = [
|
|
||||||
"scopelint", # Deprecated
|
|
||||||
"interfacer", # Deprecated
|
|
||||||
"maligned", # Deprecated
|
|
||||||
"golint", # Deprecated
|
|
||||||
"sqlclosecheck", # Not relevant (SQL)
|
|
||||||
"rowserrcheck", # Not relevant (SQL)
|
|
||||||
"lll", # Not relevant
|
|
||||||
"gocyclo", # FIXME must be fixed
|
|
||||||
"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",
|
|
||||||
"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
|
|
||||||
"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.
|
|
||||||
]
|
|
||||||
|
|
||||||
[issues]
|
|
||||||
exclude-use-default = false
|
|
||||||
max-per-linter = 0
|
|
||||||
max-same-issues = 0
|
|
||||||
exclude = [
|
|
||||||
"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
|
|
||||||
]
|
|
||||||
[[issues.exclude-rules]]
|
|
||||||
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"
|
|
||||||
[[issues.exclude-rules]]
|
|
||||||
path = "integration/(consul_catalog_test|constraint_test).go"
|
|
||||||
text = "Error return value of `(s.deregisterService|s.deregisterAgentService)` is not checked"
|
|
||||||
[[issues.exclude-rules]]
|
|
||||||
path = "integration/grpc_test.go"
|
|
||||||
text = "Error return value of `closer` is not checked"
|
|
||||||
[[issues.exclude-rules]]
|
|
||||||
path = "pkg/h2c/h2c.go"
|
|
||||||
text = "Error return value of `rw.Write` is not checked"
|
|
||||||
[[issues.exclude-rules]]
|
|
||||||
path = "pkg/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/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]]
|
|
||||||
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"]
|
|
||||||
181
.golangci.yml
Normal file
181
.golangci.yml
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
run:
|
||||||
|
timeout: 10m
|
||||||
|
skip-files: []
|
||||||
|
skip-dirs:
|
||||||
|
- pkg/provider/kubernetes/crd/generated/
|
||||||
|
|
||||||
|
linters-settings:
|
||||||
|
govet:
|
||||||
|
check-shadowing: false
|
||||||
|
golint:
|
||||||
|
min-confidence: 0
|
||||||
|
gocyclo:
|
||||||
|
min-complexity: 14
|
||||||
|
goconst:
|
||||||
|
min-len: 3
|
||||||
|
min-occurrences: 4
|
||||||
|
misspell:
|
||||||
|
locale: US
|
||||||
|
funlen:
|
||||||
|
lines: -1
|
||||||
|
statements: 120
|
||||||
|
forbidigo:
|
||||||
|
forbid:
|
||||||
|
- ^print(ln)?$
|
||||||
|
- ^spew\.Print(f|ln)?$
|
||||||
|
- ^spew\.Dump$
|
||||||
|
depguard:
|
||||||
|
list-type: denylist
|
||||||
|
include-go-root: false
|
||||||
|
packages:
|
||||||
|
- github.com/pkg/errors
|
||||||
|
godox:
|
||||||
|
keywords:
|
||||||
|
- FIXME
|
||||||
|
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
|
||||||
|
revive:
|
||||||
|
rules:
|
||||||
|
- name: struct-tag
|
||||||
|
rules:
|
||||||
|
- name: blank-imports
|
||||||
|
- name: context-as-argument
|
||||||
|
- name: context-keys-type
|
||||||
|
- name: dot-imports
|
||||||
|
- name: error-return
|
||||||
|
- name: error-strings
|
||||||
|
- name: error-naming
|
||||||
|
- name: exported
|
||||||
|
- name: if-return
|
||||||
|
- name: increment-decrement
|
||||||
|
- name: var-naming
|
||||||
|
- name: var-declaration
|
||||||
|
- name: package-comments
|
||||||
|
- name: range
|
||||||
|
- name: receiver-naming
|
||||||
|
- name: time-naming
|
||||||
|
- name: unexported-return
|
||||||
|
- name: indent-error-flow
|
||||||
|
- name: errorf
|
||||||
|
- name: empty-block
|
||||||
|
- name: superfluous-else
|
||||||
|
- name: unused-parameter
|
||||||
|
- name: unreachable-code
|
||||||
|
- name: redefines-builtin-id
|
||||||
|
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:
|
||||||
|
- deadcode # deprecated
|
||||||
|
- exhaustivestruct # deprecated
|
||||||
|
- golint # deprecated
|
||||||
|
- ifshort # deprecated
|
||||||
|
- interfacer # deprecated
|
||||||
|
- maligned # deprecated
|
||||||
|
- nosnakecase # deprecated
|
||||||
|
- scopelint # deprecated
|
||||||
|
- scopelint # deprecated
|
||||||
|
- structcheck # deprecated
|
||||||
|
- varcheck # deprecated
|
||||||
|
- sqlclosecheck # not relevant (SQL)
|
||||||
|
- rowserrcheck # not relevant (SQL)
|
||||||
|
- execinquery # not relevant (SQL)
|
||||||
|
- cyclop # duplicate of gocyclo
|
||||||
|
- lll # Not relevant
|
||||||
|
- gocyclo # FIXME must be fixed
|
||||||
|
- gocognit # Too strict
|
||||||
|
- nestif # Too many false-positive.
|
||||||
|
- prealloc # Too many false-positive.
|
||||||
|
- makezero # Not relevant
|
||||||
|
- dupl # Too strict
|
||||||
|
- gosec # Too strict
|
||||||
|
- gochecknoinits
|
||||||
|
- gochecknoglobals
|
||||||
|
- 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
|
||||||
|
- exhaustruct # Not relevant
|
||||||
|
- goerr113 # Too strict
|
||||||
|
- wrapcheck # Too strict
|
||||||
|
- noctx # Too strict
|
||||||
|
- bodyclose # too many false-positive
|
||||||
|
- forcetypeassert # Too strict
|
||||||
|
- tagliatelle # Too strict
|
||||||
|
- 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 # Too strict
|
||||||
|
|
||||||
|
issues:
|
||||||
|
exclude-use-default: false
|
||||||
|
max-per-linter: 0
|
||||||
|
max-same-issues: 0
|
||||||
|
exclude:
|
||||||
|
- '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"
|
||||||
|
exclude-rules:
|
||||||
|
- path: '(.+)_test.go'
|
||||||
|
linters:
|
||||||
|
- goconst
|
||||||
|
- funlen
|
||||||
|
- godot
|
||||||
|
- path: '(.+)_test.go'
|
||||||
|
text: ' always receives '
|
||||||
|
linters:
|
||||||
|
- unparam
|
||||||
|
- path: '(.+)\.go'
|
||||||
|
text: 'struct-tag: unknown option ''inline'' in JSON tag'
|
||||||
|
linters:
|
||||||
|
- revive
|
||||||
|
- path: pkg/server/service/bufferpool.go
|
||||||
|
text: 'SA6002: argument should be pointer-like to avoid allocations'
|
||||||
|
- path: pkg/server/middleware/middlewares.go
|
||||||
|
text: "Function 'buildConstructor' has too many statements"
|
||||||
|
linters:
|
||||||
|
- funlen
|
||||||
|
- path: pkg/logs/haystack.go
|
||||||
|
linters:
|
||||||
|
- goprintffuncname
|
||||||
|
- path: pkg/tracing/tracing.go
|
||||||
|
text: "printf-like formatting function 'SetErrorWithEvent' should be named 'SetErrorWithEventf'"
|
||||||
|
linters:
|
||||||
|
- goprintffuncname
|
||||||
|
- path: pkg/tls/tlsmanager_test.go
|
||||||
|
text: 'SA1019: config.ClientCAs.Subjects has been deprecated since Go 1.18'
|
||||||
|
- path: pkg/types/tls_test.go
|
||||||
|
text: 'SA1019: tlsConfig.RootCAs.Subjects has been deprecated since Go 1.18'
|
||||||
|
- path: pkg/provider/kubernetes/(crd|gateway)/client.go
|
||||||
|
linters:
|
||||||
|
- interfacebloat
|
||||||
|
- path: pkg/metrics/metrics.go
|
||||||
|
linters:
|
||||||
|
- interfacebloat
|
||||||
|
- path: integration/healthcheck_test.go
|
||||||
|
text: 'Duplicate words \(wsp2,\) found'
|
||||||
|
linters:
|
||||||
|
- dupword
|
||||||
|
- path: pkg/types/domain_test.go
|
||||||
|
text: 'Duplicate words \(sub\) found'
|
||||||
|
linters:
|
||||||
|
- dupword
|
||||||
@@ -12,7 +12,8 @@ builds:
|
|||||||
- CGO_ENABLED=0
|
- CGO_ENABLED=0
|
||||||
ldflags:
|
ldflags:
|
||||||
- -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}}
|
- -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:
|
goos:
|
||||||
- linux
|
- linux
|
||||||
- darwin
|
- darwin
|
||||||
@@ -21,23 +22,27 @@ builds:
|
|||||||
- openbsd
|
- openbsd
|
||||||
goarch:
|
goarch:
|
||||||
- amd64
|
- amd64
|
||||||
- 386
|
- '386'
|
||||||
- arm
|
- arm
|
||||||
- arm64
|
- arm64
|
||||||
- ppc64le
|
- ppc64le
|
||||||
|
- s390x
|
||||||
goarm:
|
goarm:
|
||||||
- 7
|
- '7'
|
||||||
- 6
|
- '6'
|
||||||
- 5
|
|
||||||
ignore:
|
ignore:
|
||||||
- goos: darwin
|
- goos: darwin
|
||||||
goarch: 386
|
goarch: '386'
|
||||||
- goos: openbsd
|
- goos: openbsd
|
||||||
goarch: arm
|
goarch: arm
|
||||||
- goos: openbsd
|
- goos: openbsd
|
||||||
goarch: arm64
|
goarch: arm64
|
||||||
|
- goos: freebsd
|
||||||
|
goarch: arm
|
||||||
- goos: freebsd
|
- goos: freebsd
|
||||||
goarch: arm64
|
goarch: arm64
|
||||||
|
- goos: windows
|
||||||
|
goarch: arm
|
||||||
|
|
||||||
changelog:
|
changelog:
|
||||||
skip: true
|
skip: true
|
||||||
|
|||||||
83
.semaphore/semaphore.yml
Normal file
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.19
|
||||||
|
- 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.50.0
|
||||||
|
- 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: "beaufort"
|
||||||
|
- 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/go"${GO_VERSION}".linux-amd64.tar.gz
|
|
||||||
|
|
||||||
tar -xvf go"${GO_VERSION}".linux-amd64.tar.gz
|
|
||||||
rm -rf go"${GO_VERSION}".linux-amd64.tar.gz
|
|
||||||
|
|
||||||
sudo mkdir -p /usr/local/golang/"${GO_VERSION}"/go
|
|
||||||
sudo mv go /usr/local/golang/"${GO_VERSION}"/
|
|
||||||
|
|
||||||
sudo rm /usr/local/bin/go
|
|
||||||
sudo chmod +x /usr/local/golang/"${GO_VERSION}"/go/bin/go
|
|
||||||
sudo ln -s /usr/local/golang/"${GO_VERSION}"/go/bin/go /usr/local/bin/go
|
|
||||||
|
|
||||||
export GOROOT="/usr/local/golang/${GO_VERSION}/go"
|
|
||||||
export GOTOOLDIR="/usr/local/golang/${GO_VERSION}/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,35 +0,0 @@
|
|||||||
# For personnal CI
|
|
||||||
# mv /home/runner/workspace/src/github.com/<username>/ /home/runner/workspace/src/github.com/traefik/
|
|
||||||
# cd /home/runner/workspace/src/github.com/traefik/traefik/
|
|
||||||
for s in apache2 cassandra elasticsearch memcached mysql mongod postgresql sphinxsearch rethinkdb rabbitmq-server redis-server; do sudo service $s stop; done
|
|
||||||
sudo swapoff -a
|
|
||||||
sudo dd if=/dev/zero of=/swapfile bs=1M count=3072
|
|
||||||
sudo mkswap /swapfile
|
|
||||||
sudo swapon /swapfile
|
|
||||||
sudo rm -rf /home/runner/.rbenv
|
|
||||||
sudo rm -rf /usr/local/golang/{1.4.3,1.5.4,1.6.4,1.7.6,1.8.6,1.9.7,1.10.3,1.11}
|
|
||||||
#export DOCKER_VERSION=18.06.3
|
|
||||||
source .semaphoreci/vars
|
|
||||||
if [ -z "${PULL_REQUEST_NUMBER}" ]; then SHOULD_TEST="-*-"; else TEMP_STORAGE=$(curl --silent https://patch-diff.githubusercontent.com/raw/traefik/traefik/pull/${PULL_REQUEST_NUMBER}.diff | patch --dry-run -p1 -R || true); fi
|
|
||||||
echo ${SHOULD_TEST}
|
|
||||||
if [ -n "$TEMP_STORAGE" ]; then SHOULD_TEST=$(echo "$TEMP_STORAGE" | grep -Ev '(.md|.yaml|.yml)' || :); fi
|
|
||||||
echo ${TEMP_STORAGE}
|
|
||||||
echo ${SHOULD_TEST}
|
|
||||||
#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
|
|
||||||
export GO_VERSION=1.13
|
|
||||||
if [ -f "./go.mod" ]; then GO_VERSION="$(grep '^go .*' go.mod | awk '{print $2}')"; export GO_VERSION; fi
|
|
||||||
#if [ "${GO_VERSION}" == '1.15' ]; then export GO_VERSION=1.15rc2; fi
|
|
||||||
echo "Selected Go version: ${GO_VERSION}"
|
|
||||||
|
|
||||||
if [ -f "./.semaphoreci/golang.sh" ]; then ./.semaphoreci/golang.sh; fi
|
|
||||||
if [ -f "./.semaphoreci/golang.sh" ]; then export GOROOT="/usr/local/golang/${GO_VERSION}/go"; fi
|
|
||||||
if [ -f "./.semaphoreci/golang.sh" ]; then export GOTOOLDIR="/usr/local/golang/${GO_VERSION}/go/pkg/tool/linux_amd64"; fi
|
|
||||||
go version
|
|
||||||
|
|
||||||
if [ -f "./go.mod" ]; then export GO111MODULE=on; fi
|
|
||||||
if [ -f "./go.mod" ]; then export GOPROXY=https://proxy.golang.org; fi
|
|
||||||
if [ -f "./go.mod" ]; then go mod download; fi
|
|
||||||
|
|
||||||
df
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
export REPO='traefik/traefik'
|
|
||||||
|
|
||||||
if VERSION=$(git describe --exact-match --abbrev=0 --tags);
|
|
||||||
then
|
|
||||||
export VERSION
|
|
||||||
else
|
|
||||||
export VERSION=''
|
|
||||||
fi
|
|
||||||
|
|
||||||
export CODENAME=livarot
|
|
||||||
|
|
||||||
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
|
|
||||||
49
.travis.yml
49
.travis.yml
@@ -1,49 +0,0 @@
|
|||||||
sudo: required
|
|
||||||
dist: trusty
|
|
||||||
|
|
||||||
git:
|
|
||||||
depth: false
|
|
||||||
|
|
||||||
services:
|
|
||||||
- docker
|
|
||||||
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
- REPO=$TRAVIS_REPO_SLUG
|
|
||||||
- VERSION=$TRAVIS_TAG
|
|
||||||
- CODENAME=livarot
|
|
||||||
- GO111MODULE=on
|
|
||||||
|
|
||||||
script:
|
|
||||||
- echo "Skipping tests... (Tests are executed on SemaphoreCI)"
|
|
||||||
|
|
||||||
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;
|
|
||||||
echo "${DOCKERHUB_PASSWORD}" | docker login -u "${DOCKERHUB_USERNAME}" --password-stdin;
|
|
||||||
make build-image;
|
|
||||||
if [ "$TRAVIS_TAG" ]; then
|
|
||||||
make release-packages;
|
|
||||||
fi;
|
|
||||||
fi
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
- provider: releases
|
|
||||||
api_key: ${GITHUB_TOKEN}
|
|
||||||
file: dist/traefik*
|
|
||||||
skip_cleanup: true
|
|
||||||
file_glob: true
|
|
||||||
on:
|
|
||||||
repo: traefik/traefik
|
|
||||||
tags: true
|
|
||||||
- provider: script
|
|
||||||
script: sh script/deploy.sh
|
|
||||||
skip_cleanup: true
|
|
||||||
on:
|
|
||||||
repo: traefik/traefik
|
|
||||||
tags: true
|
|
||||||
|
|
||||||
Binary file not shown.
1132
CHANGELOG.md
1132
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Our Pledge
|
## 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
|
## Our Standards
|
||||||
|
|
||||||
@@ -30,15 +30,19 @@ Project maintainers have the right and responsibility to remove, edit, or reject
|
|||||||
|
|
||||||
## Scope
|
## 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.
|
|
||||||
|
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.
|
Representation of a project may be further defined and clarified by project maintainers.
|
||||||
|
|
||||||
## Enforcement
|
## Enforcement
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at contact@traefik.io
|
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.
|
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.
|
Further details of specific enforcement policies may be posted separately.
|
||||||
|
|
||||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
Here are some guidelines that should help to start contributing to the project.
|
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 pull Requests](https://doc.traefik.io/traefik/contributing/submitting-pull-requests/)
|
||||||
- [Submitting issues](https://doc.traefik.io/traefik/contributing/submitting-issues/)
|
- [Submitting issues](https://doc.traefik.io/traefik/contributing/submitting-issues/)
|
||||||
- [Submitting security issues](docs/content/contributing/submitting-security-issues.md)
|
- [Submitting security issues](https://doc.traefik.io/traefik/contributing/submitting-security-issues/)
|
||||||
|
- [Advocating for Traefik](https://doc.traefik.io/traefik/contributing/advocating/)
|
||||||
|
- [Triage Process](https://github.com/traefik/contributors-guide/blob/master/issue_triage.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).
|
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).
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2016-2020 Containous SAS; 2020-2021 Traefik Labs
|
Copyright (c) 2016-2020 Containous SAS; 2020-2022 Traefik Labs
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
166
Makefile
166
Makefile
@@ -1,5 +1,3 @@
|
|||||||
.PHONY: all docs docs-serve
|
|
||||||
|
|
||||||
SRCS = $(shell git ls-files '*.go' | grep -v '^vendor/')
|
SRCS = $(shell git ls-files '*.go' | grep -v '^vendor/')
|
||||||
|
|
||||||
TAG_NAME := $(shell git tag -l --contains HEAD)
|
TAG_NAME := $(shell git tag -l --contains HEAD)
|
||||||
@@ -7,17 +5,16 @@ SHA := $(shell git rev-parse HEAD)
|
|||||||
VERSION_GIT := $(if $(TAG_NAME),$(TAG_NAME),$(SHA))
|
VERSION_GIT := $(if $(TAG_NAME),$(TAG_NAME),$(SHA))
|
||||||
VERSION := $(if $(VERSION),$(VERSION),$(VERSION_GIT))
|
VERSION := $(if $(VERSION),$(VERSION),$(VERSION_GIT))
|
||||||
|
|
||||||
BIND_DIR := "dist"
|
|
||||||
|
|
||||||
GIT_BRANCH := $(subst heads/,,$(shell git rev-parse --abbrev-ref HEAD 2>/dev/null))
|
GIT_BRANCH := $(subst heads/,,$(shell git rev-parse --abbrev-ref HEAD 2>/dev/null))
|
||||||
TRAEFIK_DEV_IMAGE := traefik-dev$(if $(GIT_BRANCH),:$(subst /,-,$(GIT_BRANCH)))
|
TRAEFIK_DEV_IMAGE := traefik-dev$(if $(GIT_BRANCH),:$(subst /,-,$(GIT_BRANCH)))
|
||||||
|
|
||||||
REPONAME := $(shell echo $(REPO) | tr '[:upper:]' '[:lower:]')
|
REPONAME := $(shell echo $(REPO) | tr '[:upper:]' '[:lower:]')
|
||||||
TRAEFIK_IMAGE := $(if $(REPONAME),$(REPONAME),"traefik/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)",)
|
DOCKER_BUILD_ARGS := $(if $(DOCKER_VERSION), "--build-arg=DOCKER_VERSION=$(DOCKER_VERSION)",)
|
||||||
|
|
||||||
|
# only used when running in docker
|
||||||
TRAEFIK_ENVS := \
|
TRAEFIK_ENVS := \
|
||||||
-e OS_ARCH_ARG \
|
-e OS_ARCH_ARG \
|
||||||
-e OS_PLATFORM_ARG \
|
-e OS_PLATFORM_ARG \
|
||||||
@@ -27,140 +24,187 @@ TRAEFIK_ENVS := \
|
|||||||
-e CODENAME \
|
-e CODENAME \
|
||||||
-e TESTDIRS \
|
-e TESTDIRS \
|
||||||
-e CI \
|
-e CI \
|
||||||
-e CONTAINER=DOCKER # Indicator for integration tests that we are running inside a container.
|
-e IN_DOCKER=true # Indicator for integration tests that we are running inside a container.
|
||||||
|
|
||||||
TRAEFIK_MOUNT := -v "$(CURDIR)/$(BIND_DIR):/go/src/github.com/traefik/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_OPTS := $(TRAEFIK_ENVS) $(TRAEFIK_MOUNT) "$(TRAEFIK_DEV_IMAGE)"
|
||||||
DOCKER_NON_INTERACTIVE ?= false
|
DOCKER_NON_INTERACTIVE ?= false
|
||||||
DOCKER_RUN_TRAEFIK := docker run --add-host=host.docker.internal:127.0.0.1 $(INTEGRATION_OPTS) $(if $(DOCKER_NON_INTERACTIVE), , -it) $(DOCKER_RUN_OPTS)
|
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)
|
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
|
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
|
## Create the "dist" directory
|
||||||
dist:
|
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
|
## Build WebUI Docker image
|
||||||
|
.PHONY: build-webui-image
|
||||||
build-webui-image:
|
build-webui-image:
|
||||||
docker build -t traefik-webui --build-arg ARG_PLATFORM_URL=$(PLATFORM_URL) -f webui/Dockerfile webui
|
docker build -t traefik-webui -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
|
||||||
generate-webui: build-webui-image
|
webui/static/index.html:
|
||||||
if [ ! -d "static" ]; then \
|
$(MAKE) build-webui-image
|
||||||
mkdir -p static; \
|
docker run --rm -v "$(PWD)/webui/static":'/src/webui/static' traefik-webui npm run build:nc
|
||||||
docker run --rm -v "$$PWD/static":'/src/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
|
||||||
docker run --rm -v "$$PWD/static":'/src/static' traefik-webui chown -R $(shell id -u):$(shell id -g) ../static; \
|
|
||||||
echo 'For more information show `webui/readme.md`' > $$PWD/static/DONT-EDIT-FILES-IN-THIS-DIRECTORY.md; \
|
|
||||||
fi
|
|
||||||
|
|
||||||
## Build the linux binary
|
.PHONY: generate-webui
|
||||||
binary: generate-webui $(PRE_TARGET)
|
generate-webui: webui/static/index.html
|
||||||
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate binary
|
|
||||||
|
## 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)
|
## Build the binary for the standard platforms (linux, darwin, windows)
|
||||||
|
.PHONY: crossbinary-default
|
||||||
crossbinary-default: generate-webui build-dev-image
|
crossbinary-default: generate-webui build-dev-image
|
||||||
$(DOCKER_RUN_TRAEFIK_NOTTY) ./script/make.sh generate crossbinary-default
|
$(DOCKER_RUN_TRAEFIK_NOTTY) ./script/make.sh generate crossbinary-default
|
||||||
|
|
||||||
## Build the binary for the standard platforms (linux, darwin, windows) in parallel
|
## Build the binary for the standard platforms (linux, darwin, windows) in parallel
|
||||||
|
.PHONY: crossbinary-default-parallel
|
||||||
crossbinary-default-parallel:
|
crossbinary-default-parallel:
|
||||||
$(MAKE) generate-webui
|
$(MAKE) generate-webui
|
||||||
$(MAKE) build-dev-image crossbinary-default
|
$(MAKE) build-dev-image crossbinary-default
|
||||||
|
|
||||||
## Run the unit and integration tests
|
## Run the unit and integration tests
|
||||||
|
.PHONY: test
|
||||||
test: build-dev-image
|
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
|
## Run the unit tests
|
||||||
test-unit: $(PRE_TARGET)
|
.PHONY: test-unit
|
||||||
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate test-unit
|
test-unit: build-dev-image
|
||||||
|
-docker network create traefik-test-network --driver bridge --subnet 172.31.42.0/24
|
||||||
## Pull all images for integration tests
|
trap 'docker network rm traefik-test-network' EXIT; \
|
||||||
pull-images:
|
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_TEST)) ./script/make.sh generate test-unit
|
||||||
grep --no-filename -E '^\s+image:' ./integration/resources/compose/*.yml | awk '{print $$2}' | sort | uniq | xargs -P 6 -n 1 docker pull
|
|
||||||
|
|
||||||
## Run the integration tests
|
## Run the integration tests
|
||||||
test-integration: $(PRE_TARGET)
|
.PHONY: test-integration
|
||||||
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK),TEST_CONTAINER=1) ./script/make.sh generate binary test-integration
|
test-integration: build-dev-image
|
||||||
TEST_HOST=1 ./script/make.sh 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 binary test-integration
|
||||||
|
|
||||||
## Run the container integration tests
|
## Pull all images for integration tests
|
||||||
test-integration-container: $(PRE_TARGET)
|
.PHONY: pull-images
|
||||||
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK),TEST_CONTAINER=1) ./script/make.sh generate binary test-integration
|
pull-images:
|
||||||
|
grep --no-filename -E '^\s+image:' ./integration/resources/compose/*.yml \
|
||||||
## Run the host integration tests
|
| awk '{print $$2}' \
|
||||||
test-integration-host: $(PRE_TARGET)
|
| sort \
|
||||||
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK),TEST_CONTAINER=1) ./script/make.sh generate binary
|
| uniq \
|
||||||
TEST_HOST=1 ./script/make.sh test-integration
|
| xargs -P 6 -n 1 docker pull
|
||||||
|
|
||||||
## Validate code and docs
|
## Validate code and docs
|
||||||
validate-files: $(PRE_TARGET)
|
.PHONY: validate-files
|
||||||
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate validate-lint validate-misspell
|
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
|
bash $(CURDIR)/script/validate-shell-script.sh
|
||||||
|
|
||||||
## Validate code, docs, and vendor
|
## Validate code, docs, and vendor
|
||||||
validate: $(PRE_TARGET)
|
.PHONY: validate
|
||||||
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate validate-lint validate-misspell validate-vendor
|
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
|
bash $(CURDIR)/script/validate-shell-script.sh
|
||||||
|
|
||||||
## Clean up static directory and build a Docker Traefik image
|
## Clean up static directory and build a Docker Traefik image
|
||||||
build-image: binary
|
.PHONY: build-image
|
||||||
rm -rf static
|
build-image: clean-webui binary
|
||||||
docker build -t $(TRAEFIK_IMAGE) .
|
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
|
build-image-dirty: binary
|
||||||
docker build -t $(TRAEFIK_IMAGE) .
|
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
|
## Start a shell inside the build env
|
||||||
|
.PHONY: shell
|
||||||
shell: build-dev-image
|
shell: build-dev-image
|
||||||
$(DOCKER_RUN_TRAEFIK) /bin/bash
|
$(DOCKER_RUN_TRAEFIK) /bin/bash
|
||||||
|
|
||||||
## Build documentation site
|
## Build documentation site
|
||||||
|
.PHONY: docs
|
||||||
docs:
|
docs:
|
||||||
make -C ./docs docs
|
make -C ./docs docs
|
||||||
|
|
||||||
## Serve the documentation site locally
|
## Serve the documentation site locally
|
||||||
|
.PHONY: docs-serve
|
||||||
docs-serve:
|
docs-serve:
|
||||||
make -C ./docs docs-serve
|
make -C ./docs docs-serve
|
||||||
|
|
||||||
## Pull image for doc building
|
## Pull image for doc building
|
||||||
|
.PHONY: docs-pull-images
|
||||||
docs-pull-images:
|
docs-pull-images:
|
||||||
make -C ./docs docs-pull-images
|
make -C ./docs docs-pull-images
|
||||||
|
|
||||||
## Generate CRD clientset
|
## Generate CRD clientset and CRD manifests
|
||||||
|
.PHONY: generate-crd
|
||||||
generate-crd:
|
generate-crd:
|
||||||
./script/update-generated-crd-code.sh
|
@$(CURDIR)/script/code-gen.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
|
## Create packages for the release
|
||||||
|
.PHONY: release-packages
|
||||||
release-packages: generate-webui build-dev-image
|
release-packages: generate-webui build-dev-image
|
||||||
rm -rf dist
|
rm -rf dist
|
||||||
$(DOCKER_RUN_TRAEFIK_NOTTY) goreleaser release --skip-publish --timeout="60m"
|
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) goreleaser release --skip-publish -p 4 --timeout="90m"
|
||||||
$(DOCKER_RUN_TRAEFIK_NOTTY) tar cfz dist/traefik-${VERSION}.src.tar.gz \
|
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) tar cfz dist/traefik-${VERSION}.src.tar.gz \
|
||||||
--exclude-vcs \
|
--exclude-vcs \
|
||||||
--exclude .idea \
|
--exclude .idea \
|
||||||
--exclude .travis \
|
--exclude .travis \
|
||||||
--exclude .semaphoreci \
|
--exclude .semaphoreci \
|
||||||
--exclude .github \
|
--exclude .github \
|
||||||
--exclude dist .
|
--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
|
## Format the Code
|
||||||
|
.PHONY: fmt
|
||||||
fmt:
|
fmt:
|
||||||
gofmt -s -l -w $(SRCS)
|
gofmt -s -l -w $(SRCS)
|
||||||
|
|
||||||
|
.PHONY: run-dev
|
||||||
run-dev:
|
run-dev:
|
||||||
go generate
|
go generate
|
||||||
GO111MODULE=on go build ./cmd/traefik
|
GO111MODULE=on go build ./cmd/traefik
|
||||||
|
|||||||
10
README.md
10
README.md
@@ -10,7 +10,6 @@
|
|||||||
[](https://community.traefik.io/)
|
[](https://community.traefik.io/)
|
||||||
[](https://twitter.com/intent/follow?screen_name=traefik)
|
[](https://twitter.com/intent/follow?screen_name=traefik)
|
||||||
|
|
||||||
|
|
||||||
Traefik (pronounced _traffic_) is a modern HTTP reverse proxy and load balancer that makes deploying microservices easy.
|
Traefik (pronounced _traffic_) is a modern HTTP reverse proxy and load balancer that makes deploying microservices easy.
|
||||||
Traefik integrates with your existing infrastructure components ([Docker](https://www.docker.com/), [Swarm mode](https://docs.docker.com/engine/swarm/), [Kubernetes](https://kubernetes.io), [Marathon](https://mesosphere.github.io/marathon/), [Consul](https://www.consul.io/), [Etcd](https://coreos.com/etcd/), [Rancher](https://rancher.com), [Amazon ECS](https://aws.amazon.com/ecs), ...) and configures itself automatically and dynamically.
|
Traefik integrates with your existing infrastructure components ([Docker](https://www.docker.com/), [Swarm mode](https://docs.docker.com/engine/swarm/), [Kubernetes](https://kubernetes.io), [Marathon](https://mesosphere.github.io/marathon/), [Consul](https://www.consul.io/), [Etcd](https://coreos.com/etcd/), [Rancher](https://rancher.com), [Amazon ECS](https://aws.amazon.com/ecs), ...) and configures itself automatically and dynamically.
|
||||||
Pointing Traefik at your orchestrator should be the _only_ configuration step you need.
|
Pointing Traefik at your orchestrator should be the _only_ configuration step you need.
|
||||||
@@ -58,13 +57,12 @@ _(But if you'd rather configure some of your routes manually, Traefik supports t
|
|||||||
- Provides HTTPS to your microservices by leveraging [Let's Encrypt](https://letsencrypt.org) (wildcard certificates support)
|
- Provides HTTPS to your microservices by leveraging [Let's Encrypt](https://letsencrypt.org) (wildcard certificates support)
|
||||||
- Circuit breakers, retry
|
- Circuit breakers, retry
|
||||||
- See the magic through its clean web UI
|
- See the magic through its clean web UI
|
||||||
- Websocket, HTTP/2, GRPC ready
|
- Websocket, HTTP/2, gRPC ready
|
||||||
- Provides metrics (Rest, Prometheus, Datadog, Statsd, InfluxDB)
|
- Provides metrics (Rest, Prometheus, Datadog, Statsd, InfluxDB)
|
||||||
- Keeps access logs (JSON, CLF)
|
- Keeps access logs (JSON, CLF)
|
||||||
- Fast
|
- Fast
|
||||||
- Exposes a Rest API
|
- 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
|
## Supported Backends
|
||||||
|
|
||||||
@@ -88,13 +86,12 @@ You can access the simple HTML frontend of Traefik.
|
|||||||
|
|
||||||
You can find the complete documentation of Traefik v2 at [https://doc.traefik.io/traefik/](https://doc.traefik.io/traefik/).
|
You can find the complete documentation of Traefik v2 at [https://doc.traefik.io/traefik/](https://doc.traefik.io/traefik/).
|
||||||
|
|
||||||
If you are using Traefik v1, you can find the complete documentation at [https://doc.traefik.io/traefik/v1.7/](https://doc.traefik.io/traefik/v1.7/).
|
|
||||||
|
|
||||||
A collection of contributions around Traefik can be found at [https://awesome.traefik.io](https://awesome.traefik.io).
|
A collection of contributions around Traefik can be found at [https://awesome.traefik.io](https://awesome.traefik.io).
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
To get community support, you can:
|
To get community support, you can:
|
||||||
|
|
||||||
- join the Traefik community forum: [](https://community.traefik.io/)
|
- join the Traefik community forum: [](https://community.traefik.io/)
|
||||||
|
|
||||||
If you need commercial support, please contact [Traefik.io](https://traefik.io) by mail: <mailto:support@traefik.io>.
|
If you need commercial support, please contact [Traefik.io](https://traefik.io) by mail: <mailto:support@traefik.io>.
|
||||||
@@ -129,7 +126,6 @@ We are strongly promoting a philosophy of openness and sharing, and firmly stand
|
|||||||
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.
|
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).
|
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
|
## Contributing
|
||||||
|
|
||||||
If you'd like to contribute to the project, refer to the [contributing documentation](CONTRIBUTING.md).
|
If you'd like to contribute to the project, refer to the [contributing documentation](CONTRIBUTING.md).
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
# Security Policy
|
# Security Policy
|
||||||
|
|
||||||
We strongly advise you to register your Traefik instances to [Pilot](http://pilot.traefik.io) to be notified of security advisories that apply to your Traefik version.
|
You can join our security mailing list to be aware of the latest announcements from our security team.
|
||||||
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).
|
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).
|
Reported vulnerabilities can be found on [cve.mitre.org](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=traefik).
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
FROM golang:1.16-alpine
|
FROM golang:1.19-alpine
|
||||||
|
|
||||||
RUN apk --update upgrade \
|
RUN apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar ca-certificates tzdata \
|
||||||
&& apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar ca-certificates tzdata \
|
|
||||||
&& update-ca-certificates \
|
&& update-ca-certificates \
|
||||||
&& rm -rf /var/cache/apk/*
|
&& rm -rf /var/cache/apk/*
|
||||||
|
|
||||||
@@ -13,22 +12,23 @@ RUN mkdir -p /usr/local/bin \
|
|||||||
&& curl -fL https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}.tgz \
|
&& curl -fL https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}.tgz \
|
||||||
| tar -xzC /usr/local/bin --transform 's#^.+/##x'
|
| tar -xzC /usr/local/bin --transform 's#^.+/##x'
|
||||||
|
|
||||||
# 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 golangci-lint binary to bin folder in $GOPATH
|
# Download golangci-lint 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.41.1
|
RUN curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash -s -- -b $GOPATH/bin v1.50.0
|
||||||
|
|
||||||
# Download misspell binary to bin folder in $GOPATH
|
# 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
|
RUN curl -sfL https://raw.githubusercontent.com/golangci/misspell/master/install-misspell.sh | bash -s -- -b $GOPATH/bin v0.4.0
|
||||||
|
|
||||||
# Download goreleaser binary to bin folder in $GOPATH
|
# Download goreleaser binary to bin folder in $GOPATH
|
||||||
RUN curl -sfL https://install.goreleaser.com/github.com/goreleaser/goreleaser.sh | sh
|
RUN curl -sfL https://gist.githubusercontent.com/traefiker/6d7ac019c11d011e4f131bb2cca8900e/raw/goreleaser.sh | sh
|
||||||
|
|
||||||
WORKDIR /go/src/github.com/traefik/traefik
|
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
|
# Download go modules
|
||||||
COPY go.mod .
|
COPY go.mod .
|
||||||
COPY go.sum .
|
COPY go.sum .
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ func Do(staticConfiguration static.Configuration) (*http.Response, error) {
|
|||||||
client := &http.Client{Timeout: 5 * time.Second}
|
client := &http.Client{Timeout: 5 * time.Second}
|
||||||
protocol := "http"
|
protocol := "http"
|
||||||
|
|
||||||
// FIXME Handle TLS on ping etc...
|
// TODO Handle TLS on ping etc...
|
||||||
// if pingEntryPoint.TLS != nil {
|
// if pingEntryPoint.TLS != nil {
|
||||||
// protocol = "https"
|
// protocol = "https"
|
||||||
// tr := &http.Transport{
|
// tr := &http.Transport{
|
||||||
|
|||||||
341
cmd/internal/gen/centrifuge.go
Normal file
341
cmd/internal/gen/centrifuge.go
Normal file
@@ -0,0 +1,341 @@
|
|||||||
|
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 := c.run(c.pkg.Scope(), c.rootPkg, pkgName)
|
||||||
|
|
||||||
|
err := fileWriter{baseDir: dest}.Write(files)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range c.pkg.Imports() {
|
||||||
|
if contains(c.IncludedImports, p.Path()) {
|
||||||
|
fls := c.run(p.Scope(), p.Path(), p.Name())
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
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
|
||||||
|
}
|
||||||
|
}
|
||||||
89
cmd/traefik/logger.go
Normal file
89
cmd/traefik/logger.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
stdlog "log"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/natefinch/lumberjack"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/traefik/traefik/v2/pkg/config/static"
|
||||||
|
"github.com/traefik/traefik/v2/pkg/logs"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// hide the first logs before the setup of the logger.
|
||||||
|
zerolog.SetGlobalLevel(zerolog.ErrorLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupLogger(staticConfiguration *static.Configuration) {
|
||||||
|
// configure log format
|
||||||
|
w := getLogWriter(staticConfiguration)
|
||||||
|
|
||||||
|
// configure log level
|
||||||
|
logLevel := getLogLevel(staticConfiguration)
|
||||||
|
|
||||||
|
// create logger
|
||||||
|
logCtx := zerolog.New(w).With().Timestamp()
|
||||||
|
if logLevel <= zerolog.DebugLevel {
|
||||||
|
logCtx = logCtx.Caller()
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Logger = logCtx.Logger().Level(logLevel)
|
||||||
|
zerolog.DefaultContextLogger = &log.Logger
|
||||||
|
zerolog.SetGlobalLevel(logLevel)
|
||||||
|
|
||||||
|
// Global logrus replacement (related to lib like go-rancher-metadata, docker, etc.)
|
||||||
|
logrus.StandardLogger().Out = logs.NoLevel(log.Logger, zerolog.DebugLevel)
|
||||||
|
|
||||||
|
// configure default standard log.
|
||||||
|
stdlog.SetFlags(stdlog.Lshortfile | stdlog.LstdFlags)
|
||||||
|
stdlog.SetOutput(logs.NoLevel(log.Logger, zerolog.DebugLevel))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLogWriter(staticConfiguration *static.Configuration) io.Writer {
|
||||||
|
var w io.Writer = os.Stderr
|
||||||
|
|
||||||
|
if staticConfiguration.Log != nil && len(staticConfiguration.Log.FilePath) > 0 {
|
||||||
|
_, _ = os.Create(staticConfiguration.Log.FilePath)
|
||||||
|
w = &lumberjack.Logger{
|
||||||
|
Filename: staticConfiguration.Log.FilePath,
|
||||||
|
MaxSize: staticConfiguration.Log.MaxSize,
|
||||||
|
MaxBackups: staticConfiguration.Log.MaxBackups,
|
||||||
|
MaxAge: staticConfiguration.Log.MaxAge,
|
||||||
|
Compress: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if staticConfiguration.Log == nil || staticConfiguration.Log.Format != "json" {
|
||||||
|
w = zerolog.ConsoleWriter{
|
||||||
|
Out: w,
|
||||||
|
TimeFormat: time.RFC3339,
|
||||||
|
NoColor: staticConfiguration.Log != nil && (staticConfiguration.Log.NoColor || len(staticConfiguration.Log.FilePath) > 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLogLevel(staticConfiguration *static.Configuration) zerolog.Level {
|
||||||
|
levelStr := "error"
|
||||||
|
if staticConfiguration.Log != nil && staticConfiguration.Log.Level != "" {
|
||||||
|
levelStr = strings.ToLower(staticConfiguration.Log.Level)
|
||||||
|
}
|
||||||
|
|
||||||
|
logLevel, err := zerolog.ParseLevel(strings.ToLower(levelStr))
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).
|
||||||
|
Str("logLevel", levelStr).
|
||||||
|
Msg("Unspecified or invalid log level, setting the level to default (ERROR)...")
|
||||||
|
|
||||||
|
logLevel = zerolog.ErrorLevel
|
||||||
|
}
|
||||||
|
|
||||||
|
return logLevel
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/traefik/traefik/v2/pkg/config/static"
|
"github.com/traefik/traefik/v2/pkg/config/static"
|
||||||
"github.com/traefik/traefik/v2/pkg/plugins"
|
"github.com/traefik/traefik/v2/pkg/plugins"
|
||||||
)
|
)
|
||||||
@@ -8,42 +10,74 @@ import (
|
|||||||
const outputDir = "./plugins-storage/"
|
const outputDir = "./plugins-storage/"
|
||||||
|
|
||||||
func createPluginBuilder(staticConfiguration *static.Configuration) (*plugins.Builder, error) {
|
func createPluginBuilder(staticConfiguration *static.Configuration) (*plugins.Builder, error) {
|
||||||
client, plgs, devPlugin, err := initPlugins(staticConfiguration)
|
client, plgs, localPlgs, err := initPlugins(staticConfiguration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return plugins.NewBuilder(client, plgs, devPlugin)
|
return plugins.NewBuilder(client, plgs, localPlgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initPlugins(staticCfg *static.Configuration) (*plugins.Client, map[string]plugins.Descriptor, *plugins.DevPlugin, error) {
|
func initPlugins(staticCfg *static.Configuration) (*plugins.Client, map[string]plugins.Descriptor, map[string]plugins.LocalDescriptor, error) {
|
||||||
if !isPilotEnabled(staticCfg) || !hasPlugins(staticCfg) {
|
err := checkUniquePluginNames(staticCfg.Experimental)
|
||||||
return nil, map[string]plugins.Descriptor{}, nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := plugins.ClientOptions{
|
|
||||||
Output: outputDir,
|
|
||||||
Token: staticCfg.Pilot.Token,
|
|
||||||
}
|
|
||||||
|
|
||||||
client, err := plugins.NewClient(opts)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = plugins.Setup(client, staticCfg.Experimental.Plugins, staticCfg.Experimental.DevPlugin)
|
var client *plugins.Client
|
||||||
if err != nil {
|
plgs := map[string]plugins.Descriptor{}
|
||||||
return nil, nil, nil, err
|
|
||||||
|
if hasPlugins(staticCfg) {
|
||||||
|
opts := plugins.ClientOptions{
|
||||||
|
Output: outputDir,
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
return client, staticCfg.Experimental.Plugins, staticCfg.Experimental.DevPlugin, nil
|
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 isPilotEnabled(staticCfg *static.Configuration) bool {
|
func checkUniquePluginNames(e *static.Experimental) error {
|
||||||
return staticCfg.Pilot != nil && staticCfg.Pilot.Token != ""
|
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 hasPlugins(staticCfg *static.Configuration) bool {
|
func hasPlugins(staticCfg *static.Configuration) bool {
|
||||||
return staticCfg.Experimental != nil &&
|
return staticCfg.Experimental != nil && len(staticCfg.Experimental.Plugins) > 0
|
||||||
(len(staticCfg.Experimental.Plugins) > 0 || staticCfg.Experimental.DevPlugin != nil)
|
}
|
||||||
|
|
||||||
|
func hasLocalPlugins(staticCfg *static.Configuration) bool {
|
||||||
|
return staticCfg.Experimental != nil && len(staticCfg.Experimental.LocalPlugins) > 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,23 +2,25 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/x509"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
stdlog "log"
|
stdlog "log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/coreos/go-systemd/daemon"
|
"github.com/coreos/go-systemd/daemon"
|
||||||
assetfs "github.com/elazarl/go-bindata-assetfs"
|
|
||||||
"github.com/go-acme/lego/v4/challenge"
|
"github.com/go-acme/lego/v4/challenge"
|
||||||
|
gokitmetrics "github.com/go-kit/kit/metrics"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/spiffe/go-spiffe/v2/workloadapi"
|
||||||
"github.com/traefik/paerser/cli"
|
"github.com/traefik/paerser/cli"
|
||||||
"github.com/traefik/traefik/v2/autogen/genstatic"
|
|
||||||
"github.com/traefik/traefik/v2/cmd"
|
"github.com/traefik/traefik/v2/cmd"
|
||||||
"github.com/traefik/traefik/v2/cmd/healthcheck"
|
"github.com/traefik/traefik/v2/cmd/healthcheck"
|
||||||
cmdVersion "github.com/traefik/traefik/v2/cmd/version"
|
cmdVersion "github.com/traefik/traefik/v2/cmd/version"
|
||||||
@@ -27,21 +29,23 @@ import (
|
|||||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||||
"github.com/traefik/traefik/v2/pkg/config/runtime"
|
"github.com/traefik/traefik/v2/pkg/config/runtime"
|
||||||
"github.com/traefik/traefik/v2/pkg/config/static"
|
"github.com/traefik/traefik/v2/pkg/config/static"
|
||||||
"github.com/traefik/traefik/v2/pkg/log"
|
"github.com/traefik/traefik/v2/pkg/logs"
|
||||||
"github.com/traefik/traefik/v2/pkg/metrics"
|
"github.com/traefik/traefik/v2/pkg/metrics"
|
||||||
"github.com/traefik/traefik/v2/pkg/middlewares/accesslog"
|
"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/acme"
|
||||||
"github.com/traefik/traefik/v2/pkg/provider/aggregator"
|
"github.com/traefik/traefik/v2/pkg/provider/aggregator"
|
||||||
|
"github.com/traefik/traefik/v2/pkg/provider/hub"
|
||||||
|
"github.com/traefik/traefik/v2/pkg/provider/tailscale"
|
||||||
"github.com/traefik/traefik/v2/pkg/provider/traefik"
|
"github.com/traefik/traefik/v2/pkg/provider/traefik"
|
||||||
"github.com/traefik/traefik/v2/pkg/safe"
|
"github.com/traefik/traefik/v2/pkg/safe"
|
||||||
"github.com/traefik/traefik/v2/pkg/server"
|
"github.com/traefik/traefik/v2/pkg/server"
|
||||||
"github.com/traefik/traefik/v2/pkg/server/middleware"
|
"github.com/traefik/traefik/v2/pkg/server/middleware"
|
||||||
"github.com/traefik/traefik/v2/pkg/server/service"
|
"github.com/traefik/traefik/v2/pkg/server/service"
|
||||||
traefiktls "github.com/traefik/traefik/v2/pkg/tls"
|
traefiktls "github.com/traefik/traefik/v2/pkg/tls"
|
||||||
|
"github.com/traefik/traefik/v2/pkg/tracing"
|
||||||
|
"github.com/traefik/traefik/v2/pkg/tracing/jaeger"
|
||||||
"github.com/traefik/traefik/v2/pkg/types"
|
"github.com/traefik/traefik/v2/pkg/types"
|
||||||
"github.com/traefik/traefik/v2/pkg/version"
|
"github.com/traefik/traefik/v2/pkg/version"
|
||||||
"github.com/vulcand/oxy/roundrobin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -75,7 +79,7 @@ Complete documentation is available at https://traefik.io`,
|
|||||||
|
|
||||||
err = cli.Execute(cmdTraefik)
|
err = cli.Execute(cmdTraefik)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stdlog.Println(err)
|
log.Error().Err(err).Msg("Command error")
|
||||||
logrus.Exit(1)
|
logrus.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,31 +87,24 @@ Complete documentation is available at https://traefik.io`,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runCmd(staticConfiguration *static.Configuration) error {
|
func runCmd(staticConfiguration *static.Configuration) error {
|
||||||
configureLogging(staticConfiguration)
|
setupLogger(staticConfiguration)
|
||||||
|
|
||||||
http.DefaultTransport.(*http.Transport).Proxy = http.ProxyFromEnvironment
|
http.DefaultTransport.(*http.Transport).Proxy = http.ProxyFromEnvironment
|
||||||
|
|
||||||
if err := roundrobin.SetDefaultWeight(0); err != nil {
|
|
||||||
log.WithoutContext().Errorf("Could not set round robin default weight: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
staticConfiguration.SetEffectiveConfiguration()
|
staticConfiguration.SetEffectiveConfiguration()
|
||||||
if err := staticConfiguration.ValidateConfiguration(); err != nil {
|
if err := staticConfiguration.ValidateConfiguration(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.WithoutContext().Infof("Traefik version %s built on %s", version.Version, version.BuildDate)
|
log.Info().Str("version", version.Version).
|
||||||
|
Msgf("Traefik version %s built on %s", version.Version, version.BuildDate)
|
||||||
|
|
||||||
jsonConf, err := json.Marshal(staticConfiguration)
|
jsonConf, err := json.Marshal(staticConfiguration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithoutContext().Errorf("Could not marshal static configuration: %v", err)
|
log.Error().Err(err).Msg("Could not marshal static configuration")
|
||||||
log.WithoutContext().Debugf("Static configuration loaded [struct] %#v", staticConfiguration)
|
log.Debug().Interface("staticConfiguration", staticConfiguration).Msg("Static configuration loaded [struct]")
|
||||||
} else {
|
} else {
|
||||||
log.WithoutContext().Debugf("Static configuration loaded %s", string(jsonConf))
|
log.Debug().RawJSON("staticConfiguration", jsonConf).Msg("Static configuration loaded [json]")
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
if staticConfiguration.Global.CheckNewVersion {
|
||||||
@@ -123,12 +120,6 @@ func runCmd(staticConfiguration *static.Configuration) error {
|
|||||||
|
|
||||||
ctx, _ := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
ctx, _ := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
if staticConfiguration.Experimental != nil && staticConfiguration.Experimental.DevPlugin != nil {
|
|
||||||
var cancel context.CancelFunc
|
|
||||||
ctx, cancel = context.WithTimeout(ctx, 30*time.Minute)
|
|
||||||
defer cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
if staticConfiguration.Ping != nil {
|
if staticConfiguration.Ping != nil {
|
||||||
staticConfiguration.Ping.WithContext(ctx)
|
staticConfiguration.Ping.WithContext(ctx)
|
||||||
}
|
}
|
||||||
@@ -138,16 +129,16 @@ func runCmd(staticConfiguration *static.Configuration) error {
|
|||||||
|
|
||||||
sent, err := daemon.SdNotify(false, "READY=1")
|
sent, err := daemon.SdNotify(false, "READY=1")
|
||||||
if !sent && err != nil {
|
if !sent && err != nil {
|
||||||
log.WithoutContext().Errorf("Failed to notify: %v", err)
|
log.Error().Err(err).Msg("Failed to notify")
|
||||||
}
|
}
|
||||||
|
|
||||||
t, err := daemon.SdWatchdogEnabled(false)
|
t, err := daemon.SdWatchdogEnabled(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithoutContext().Errorf("Could not enable Watchdog: %v", err)
|
log.Error().Err(err).Msg("Could not enable Watchdog")
|
||||||
} else if t != 0 {
|
} else if t != 0 {
|
||||||
// Send a ping each half time given
|
// Send a ping each half time given
|
||||||
t /= 2
|
t /= 2
|
||||||
log.WithoutContext().Infof("Watchdog activated with timer duration %s", t)
|
log.Info().Msgf("Watchdog activated with timer duration %s", t)
|
||||||
safe.Go(func() {
|
safe.Go(func() {
|
||||||
tick := time.Tick(t)
|
tick := time.Tick(t)
|
||||||
for range tick {
|
for range tick {
|
||||||
@@ -158,17 +149,17 @@ func runCmd(staticConfiguration *static.Configuration) error {
|
|||||||
|
|
||||||
if staticConfiguration.Ping == nil || errHealthCheck == nil {
|
if staticConfiguration.Ping == nil || errHealthCheck == nil {
|
||||||
if ok, _ := daemon.SdNotify(false, "WATCHDOG=1"); !ok {
|
if ok, _ := daemon.SdNotify(false, "WATCHDOG=1"); !ok {
|
||||||
log.WithoutContext().Error("Fail to tick watchdog")
|
log.Error().Msg("Fail to tick watchdog")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.WithoutContext().Error(errHealthCheck)
|
log.Error().Err(errHealthCheck).Send()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
svr.Wait()
|
svr.Wait()
|
||||||
log.WithoutContext().Info("Shutting down")
|
log.Info().Msg("Shutting down")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,8 +180,7 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
|||||||
tlsManager := traefiktls.NewManager()
|
tlsManager := traefiktls.NewManager()
|
||||||
httpChallengeProvider := acme.NewChallengeHTTP()
|
httpChallengeProvider := acme.NewChallengeHTTP()
|
||||||
|
|
||||||
// we need to wait at least 2 times the ProvidersThrottleDuration to be sure to handle the challenge.
|
tlsChallengeProvider := acme.NewChallengeTLSALPN()
|
||||||
tlsChallengeProvider := acme.NewChallengeTLSALPN(time.Duration(staticConfiguration.Providers.ProvidersThrottleDuration) * 2)
|
|
||||||
err = providerAggregator.AddProvider(tlsChallengeProvider)
|
err = providerAggregator.AddProvider(tlsChallengeProvider)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -198,9 +188,13 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
|||||||
|
|
||||||
acmeProviders := initACMEProvider(staticConfiguration, &providerAggregator, tlsManager, httpChallengeProvider, tlsChallengeProvider)
|
acmeProviders := initACMEProvider(staticConfiguration, &providerAggregator, tlsManager, httpChallengeProvider, tlsChallengeProvider)
|
||||||
|
|
||||||
|
// Tailscale
|
||||||
|
|
||||||
|
tsProviders := initTailscaleProviders(staticConfiguration, &providerAggregator)
|
||||||
|
|
||||||
// Entrypoints
|
// Entrypoints
|
||||||
|
|
||||||
serverEntryPointsTCP, err := server.NewTCPEntryPoints(staticConfiguration.EntryPoints)
|
serverEntryPointsTCP, err := server.NewTCPEntryPoints(staticConfiguration.EntryPoints, staticConfiguration.HostResolver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -210,57 +204,87 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
|||||||
return nil, err
|
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
|
// Plugins
|
||||||
|
|
||||||
pluginBuilder, err := createPluginBuilder(staticConfiguration)
|
pluginBuilder, err := createPluginBuilder(staticConfiguration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
log.Error().Err(err).Msg("Plugins are disabled because an error has occurred.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Providers plugins
|
||||||
|
|
||||||
|
for name, conf := range staticConfiguration.Providers.Plugin {
|
||||||
|
if pluginBuilder == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traefik Hub
|
||||||
|
|
||||||
|
if staticConfiguration.Hub != nil {
|
||||||
|
if err = providerAggregator.AddProvider(staticConfiguration.Hub); err != nil {
|
||||||
|
return nil, fmt.Errorf("adding Traefik Hub provider: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// API is mandatory for Traefik Hub to access the dynamic configuration.
|
||||||
|
if staticConfiguration.API == nil {
|
||||||
|
staticConfiguration.API = &static.API{}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metrics
|
// Metrics
|
||||||
|
|
||||||
metricRegistries := registerMetricClients(staticConfiguration.Metrics)
|
metricRegistries := registerMetricClients(staticConfiguration.Metrics)
|
||||||
if pilotRegistry != nil {
|
|
||||||
metricRegistries = append(metricRegistries, pilotRegistry)
|
|
||||||
}
|
|
||||||
metricsRegistry := metrics.NewMultiRegistry(metricRegistries)
|
metricsRegistry := metrics.NewMultiRegistry(metricRegistries)
|
||||||
|
|
||||||
// Service manager factory
|
// Service manager factory
|
||||||
|
|
||||||
roundTripperManager := service.NewRoundTripperManager()
|
var spiffeX509Source *workloadapi.X509Source
|
||||||
|
if staticConfiguration.Spiffe != nil && staticConfiguration.Spiffe.WorkloadAPIAddr != "" {
|
||||||
|
log.Info().Str("workloadAPIAddr", staticConfiguration.Spiffe.WorkloadAPIAddr).
|
||||||
|
Msg("Waiting on SPIFFE SVID delivery")
|
||||||
|
|
||||||
|
spiffeX509Source, err = workloadapi.NewX509Source(
|
||||||
|
ctx,
|
||||||
|
workloadapi.WithClientOptions(
|
||||||
|
workloadapi.WithAddr(
|
||||||
|
staticConfiguration.Spiffe.WorkloadAPIAddr,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to create SPIFFE x509 source: %w", err)
|
||||||
|
}
|
||||||
|
log.Info().Msg("Successfully obtained SPIFFE SVID.")
|
||||||
|
}
|
||||||
|
|
||||||
|
roundTripperManager := service.NewRoundTripperManager(spiffeX509Source)
|
||||||
acmeHTTPHandler := getHTTPChallengeHandler(acmeProviders, httpChallengeProvider)
|
acmeHTTPHandler := getHTTPChallengeHandler(acmeProviders, httpChallengeProvider)
|
||||||
managerFactory := service.NewManagerFactory(*staticConfiguration, routinesPool, metricsRegistry, roundTripperManager, acmeHTTPHandler)
|
managerFactory := service.NewManagerFactory(*staticConfiguration, routinesPool, metricsRegistry, roundTripperManager, acmeHTTPHandler)
|
||||||
|
|
||||||
// Router factory
|
// Router factory
|
||||||
|
|
||||||
accessLog := setupAccessLog(staticConfiguration.AccessLog)
|
accessLog := setupAccessLog(staticConfiguration.AccessLog)
|
||||||
chainBuilder := middleware.NewChainBuilder(*staticConfiguration, metricsRegistry, accessLog)
|
tracer := setupTracing(staticConfiguration.Tracing)
|
||||||
routerFactory := server.NewRouterFactory(*staticConfiguration, managerFactory, tlsManager, chainBuilder, pluginBuilder)
|
|
||||||
|
chainBuilder := middleware.NewChainBuilder(metricsRegistry, accessLog, tracer)
|
||||||
|
routerFactory := server.NewRouterFactory(*staticConfiguration, managerFactory, tlsManager, chainBuilder, pluginBuilder, metricsRegistry)
|
||||||
|
|
||||||
// Watcher
|
// Watcher
|
||||||
|
|
||||||
watcher := server.NewConfigurationWatcher(
|
watcher := server.NewConfigurationWatcher(
|
||||||
routinesPool,
|
routinesPool,
|
||||||
providerAggregator,
|
providerAggregator,
|
||||||
time.Duration(staticConfiguration.Providers.ProvidersThrottleDuration),
|
|
||||||
getDefaultsEntrypoints(staticConfiguration),
|
getDefaultsEntrypoints(staticConfiguration),
|
||||||
"internal",
|
"internal",
|
||||||
)
|
)
|
||||||
@@ -269,6 +293,11 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
|||||||
watcher.AddListener(func(conf dynamic.Configuration) {
|
watcher.AddListener(func(conf dynamic.Configuration) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
tlsManager.UpdateConfigs(ctx, conf.TLS.Stores, conf.TLS.Options, conf.TLS.Certificates)
|
tlsManager.UpdateConfigs(ctx, conf.TLS.Stores, conf.TLS.Options, conf.TLS.Certificates)
|
||||||
|
|
||||||
|
gauge := metricsRegistry.TLSCertsNotAfterTimestampGauge()
|
||||||
|
for _, certificate := range tlsManager.GetCertificates() {
|
||||||
|
appendCertMetric(gauge, certificate)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Metrics
|
// Metrics
|
||||||
@@ -283,7 +312,7 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Switch router
|
// Switch router
|
||||||
watcher.AddListener(switchRouter(routerFactory, serverEntryPointsTCP, serverEntryPointsUDP, aviator))
|
watcher.AddListener(switchRouter(routerFactory, serverEntryPointsTCP, serverEntryPointsUDP))
|
||||||
|
|
||||||
// Metrics
|
// Metrics
|
||||||
if metricsRegistry.IsEpEnabled() || metricsRegistry.IsSvcEnabled() {
|
if metricsRegistry.IsEpEnabled() || metricsRegistry.IsSvcEnabled() {
|
||||||
@@ -299,13 +328,22 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
|||||||
// TLS challenge
|
// TLS challenge
|
||||||
watcher.AddListener(tlsChallengeProvider.ListenConfiguration)
|
watcher.AddListener(tlsChallengeProvider.ListenConfiguration)
|
||||||
|
|
||||||
// ACME
|
// Certificate Resolvers
|
||||||
|
|
||||||
resolverNames := map[string]struct{}{}
|
resolverNames := map[string]struct{}{}
|
||||||
|
|
||||||
|
// ACME
|
||||||
for _, p := range acmeProviders {
|
for _, p := range acmeProviders {
|
||||||
resolverNames[p.ResolverName] = struct{}{}
|
resolverNames[p.ResolverName] = struct{}{}
|
||||||
watcher.AddListener(p.ListenConfiguration)
|
watcher.AddListener(p.ListenConfiguration)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tailscale
|
||||||
|
for _, p := range tsProviders {
|
||||||
|
resolverNames[p.ResolverName] = struct{}{}
|
||||||
|
watcher.AddListener(p.HandleConfigUpdate)
|
||||||
|
}
|
||||||
|
|
||||||
// Certificate resolver logs
|
// Certificate resolver logs
|
||||||
watcher.AddListener(func(config dynamic.Configuration) {
|
watcher.AddListener(func(config dynamic.Configuration) {
|
||||||
for rtName, rt := range config.HTTP.Routers {
|
for rtName, rt := range config.HTTP.Routers {
|
||||||
@@ -313,8 +351,12 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := resolverNames[rt.TLS.CertResolver]; !ok {
|
if _, ok := resolverNames[rt.TLS.CertResolver]; !ok &&
|
||||||
log.WithoutContext().Errorf("the router %s uses a non-existent resolver: %s", rtName, rt.TLS.CertResolver)
|
// "traefik-hub" is an allowed certificate resolver name in a Traefik Hub Experimental feature context.
|
||||||
|
// It is used to activate its own certificate resolution, even though it is not a "classical" traefik certificate resolver.
|
||||||
|
(staticConfiguration.Hub == nil || rt.TLS.CertResolver != "traefik-hub") {
|
||||||
|
log.Error().Err(err).Str(logs.RouterName, rtName).Str("certificateResolver", rt.TLS.CertResolver).
|
||||||
|
Msg("Router uses a non-existent certificate resolver")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -335,11 +377,32 @@ func getHTTPChallengeHandler(acmeProviders []*acme.Provider, httpChallengeProvid
|
|||||||
|
|
||||||
func getDefaultsEntrypoints(staticConfiguration *static.Configuration) []string {
|
func getDefaultsEntrypoints(staticConfiguration *static.Configuration) []string {
|
||||||
var defaultEntryPoints []string
|
var defaultEntryPoints []string
|
||||||
|
|
||||||
|
// Determines if at least one EntryPoint is configured to be used by default.
|
||||||
|
var hasDefinedDefaults bool
|
||||||
|
for _, ep := range staticConfiguration.EntryPoints {
|
||||||
|
if ep.AsDefault {
|
||||||
|
hasDefinedDefaults = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for name, cfg := range staticConfiguration.EntryPoints {
|
for name, cfg := range staticConfiguration.EntryPoints {
|
||||||
|
// By default all entrypoints are considered.
|
||||||
|
// If at least one is flagged, then only flagged entrypoints are included.
|
||||||
|
if hasDefinedDefaults && !cfg.AsDefault {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traefik Hub entryPoint should not be used as a default entryPoint.
|
||||||
|
if hub.APIEntrypoint == name || hub.TunnelEntrypoint == name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
protocol, err := cfg.GetProtocol()
|
protocol, err := cfg.GetProtocol()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Should never happen because Traefik should not start if protocol is invalid.
|
// Should never happen because Traefik should not start if protocol is invalid.
|
||||||
log.WithoutContext().Errorf("Invalid protocol: %v", err)
|
log.Error().Err(err).Msg("Invalid protocol")
|
||||||
}
|
}
|
||||||
|
|
||||||
if protocol != "udp" && name != static.DefaultInternalEntryPointName {
|
if protocol != "udp" && name != static.DefaultInternalEntryPointName {
|
||||||
@@ -351,22 +414,18 @@ func getDefaultsEntrypoints(staticConfiguration *static.Configuration) []string
|
|||||||
return defaultEntryPoints
|
return defaultEntryPoints
|
||||||
}
|
}
|
||||||
|
|
||||||
func switchRouter(routerFactory *server.RouterFactory, serverEntryPointsTCP server.TCPEntryPoints, serverEntryPointsUDP server.UDPEntryPoints, aviator *pilot.Pilot) func(conf dynamic.Configuration) {
|
func switchRouter(routerFactory *server.RouterFactory, serverEntryPointsTCP server.TCPEntryPoints, serverEntryPointsUDP server.UDPEntryPoints) func(conf dynamic.Configuration) {
|
||||||
return func(conf dynamic.Configuration) {
|
return func(conf dynamic.Configuration) {
|
||||||
rtConf := runtime.NewConfig(conf)
|
rtConf := runtime.NewConfig(conf)
|
||||||
|
|
||||||
routers, udpRouters := routerFactory.CreateRouters(rtConf)
|
routers, udpRouters := routerFactory.CreateRouters(rtConf)
|
||||||
|
|
||||||
if aviator != nil {
|
|
||||||
aviator.SetDynamicConfiguration(conf)
|
|
||||||
}
|
|
||||||
|
|
||||||
serverEntryPointsTCP.Switch(routers)
|
serverEntryPointsTCP.Switch(routers)
|
||||||
serverEntryPointsUDP.Switch(udpRouters)
|
serverEntryPointsUDP.Switch(udpRouters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// initACMEProvider creates an acme provider from the ACME part of globalConfiguration.
|
// initACMEProvider creates and registers acme.Provider instances corresponding to the configured ACME certificate resolvers.
|
||||||
func initACMEProvider(c *static.Configuration, providerAggregator *aggregator.ProviderAggregator, tlsManager *traefiktls.Manager, httpChallengeProvider, tlsChallengeProvider challenge.Provider) []*acme.Provider {
|
func initACMEProvider(c *static.Configuration, providerAggregator *aggregator.ProviderAggregator, tlsManager *traefiktls.Manager, httpChallengeProvider, tlsChallengeProvider challenge.Provider) []*acme.Provider {
|
||||||
localStores := map[string]*acme.LocalStore{}
|
localStores := map[string]*acme.LocalStore{}
|
||||||
|
|
||||||
@@ -389,7 +448,7 @@ func initACMEProvider(c *static.Configuration, providerAggregator *aggregator.Pr
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := providerAggregator.AddProvider(p); err != nil {
|
if err := providerAggregator.AddProvider(p); err != nil {
|
||||||
log.WithoutContext().Errorf("The ACME resolver %q is skipped from the resolvers list because: %v", name, err)
|
log.Error().Err(err).Str("resolver", name).Msg("The ACME resolve is skipped from the resolvers list")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -403,6 +462,27 @@ func initACMEProvider(c *static.Configuration, providerAggregator *aggregator.Pr
|
|||||||
return resolvers
|
return resolvers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// initTailscaleProviders creates and registers tailscale.Provider instances corresponding to the configured Tailscale certificate resolvers.
|
||||||
|
func initTailscaleProviders(cfg *static.Configuration, providerAggregator *aggregator.ProviderAggregator) []*tailscale.Provider {
|
||||||
|
var providers []*tailscale.Provider
|
||||||
|
for name, resolver := range cfg.CertificatesResolvers {
|
||||||
|
if resolver.Tailscale == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
tsProvider := &tailscale.Provider{ResolverName: name}
|
||||||
|
|
||||||
|
if err := providerAggregator.AddProvider(tsProvider); err != nil {
|
||||||
|
log.Error().Err(err).Str(logs.ProviderName, name).Msg("Unable to create Tailscale provider")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
providers = append(providers, tsProvider)
|
||||||
|
}
|
||||||
|
|
||||||
|
return providers
|
||||||
|
}
|
||||||
|
|
||||||
func registerMetricClients(metricsConfig *types.Metrics) []metrics.Registry {
|
func registerMetricClients(metricsConfig *types.Metrics) []metrics.Registry {
|
||||||
if metricsConfig == nil {
|
if metricsConfig == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -411,38 +491,90 @@ func registerMetricClients(metricsConfig *types.Metrics) []metrics.Registry {
|
|||||||
var registries []metrics.Registry
|
var registries []metrics.Registry
|
||||||
|
|
||||||
if metricsConfig.Prometheus != nil {
|
if metricsConfig.Prometheus != nil {
|
||||||
ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "prometheus"))
|
logger := log.With().Str(logs.MetricsProviderName, "prometheus").Logger()
|
||||||
prometheusRegister := metrics.RegisterPrometheus(ctx, metricsConfig.Prometheus)
|
|
||||||
|
prometheusRegister := metrics.RegisterPrometheus(logger.WithContext(context.Background()), metricsConfig.Prometheus)
|
||||||
if prometheusRegister != nil {
|
if prometheusRegister != nil {
|
||||||
registries = append(registries, prometheusRegister)
|
registries = append(registries, prometheusRegister)
|
||||||
log.FromContext(ctx).Debug("Configured Prometheus metrics")
|
logger.Debug().Msg("Configured Prometheus metrics")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if metricsConfig.Datadog != nil {
|
if metricsConfig.Datadog != nil {
|
||||||
ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "datadog"))
|
logger := log.With().Str(logs.MetricsProviderName, "datadog").Logger()
|
||||||
registries = append(registries, metrics.RegisterDatadog(ctx, metricsConfig.Datadog))
|
|
||||||
log.FromContext(ctx).Debugf("Configured Datadog metrics: pushing to %s once every %s",
|
registries = append(registries, metrics.RegisterDatadog(logger.WithContext(context.Background()), metricsConfig.Datadog))
|
||||||
metricsConfig.Datadog.Address, metricsConfig.Datadog.PushInterval)
|
logger.Debug().
|
||||||
|
Str("address", metricsConfig.Datadog.Address).
|
||||||
|
Str("pushInterval", metricsConfig.Datadog.PushInterval.String()).
|
||||||
|
Msgf("Configured Datadog metrics")
|
||||||
}
|
}
|
||||||
|
|
||||||
if metricsConfig.StatsD != nil {
|
if metricsConfig.StatsD != nil {
|
||||||
ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "statsd"))
|
logger := log.With().Str(logs.MetricsProviderName, "statsd").Logger()
|
||||||
registries = append(registries, metrics.RegisterStatsd(ctx, metricsConfig.StatsD))
|
|
||||||
log.FromContext(ctx).Debugf("Configured StatsD metrics: pushing to %s once every %s",
|
registries = append(registries, metrics.RegisterStatsd(logger.WithContext(context.Background()), metricsConfig.StatsD))
|
||||||
metricsConfig.StatsD.Address, metricsConfig.StatsD.PushInterval)
|
logger.Debug().
|
||||||
|
Str("address", metricsConfig.StatsD.Address).
|
||||||
|
Str("pushInterval", metricsConfig.StatsD.PushInterval.String()).
|
||||||
|
Msg("Configured StatsD metrics")
|
||||||
}
|
}
|
||||||
|
|
||||||
if metricsConfig.InfluxDB != nil {
|
if metricsConfig.InfluxDB != nil {
|
||||||
ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "influxdb"))
|
logger := log.With().Str(logs.MetricsProviderName, "influxdb").Logger()
|
||||||
registries = append(registries, metrics.RegisterInfluxDB(ctx, metricsConfig.InfluxDB))
|
|
||||||
log.FromContext(ctx).Debugf("Configured InfluxDB metrics: pushing to %s once every %s",
|
registries = append(registries, metrics.RegisterInfluxDB(logger.WithContext(context.Background()), metricsConfig.InfluxDB))
|
||||||
metricsConfig.InfluxDB.Address, metricsConfig.InfluxDB.PushInterval)
|
logger.Debug().
|
||||||
|
Str("address", metricsConfig.InfluxDB.Address).
|
||||||
|
Str("pushInterval", metricsConfig.InfluxDB.PushInterval.String()).
|
||||||
|
Msg("Configured InfluxDB metrics")
|
||||||
|
}
|
||||||
|
|
||||||
|
if metricsConfig.InfluxDB2 != nil {
|
||||||
|
logger := log.With().Str(logs.MetricsProviderName, "influxdb2").Logger()
|
||||||
|
|
||||||
|
influxDB2Register := metrics.RegisterInfluxDB2(logger.WithContext(context.Background()), metricsConfig.InfluxDB2)
|
||||||
|
if influxDB2Register != nil {
|
||||||
|
registries = append(registries, influxDB2Register)
|
||||||
|
logger.Debug().
|
||||||
|
Str("address", metricsConfig.InfluxDB2.Address).
|
||||||
|
Str("bucket", metricsConfig.InfluxDB2.Bucket).
|
||||||
|
Str("organization", metricsConfig.InfluxDB2.Org).
|
||||||
|
Str("pushInterval", metricsConfig.InfluxDB2.PushInterval.String()).
|
||||||
|
Msg("Configured InfluxDB v2 metrics")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if metricsConfig.OpenTelemetry != nil {
|
||||||
|
logger := log.With().Str(logs.MetricsProviderName, "openTelemetry").Logger()
|
||||||
|
|
||||||
|
openTelemetryRegistry := metrics.RegisterOpenTelemetry(logger.WithContext(context.Background()), metricsConfig.OpenTelemetry)
|
||||||
|
if openTelemetryRegistry != nil {
|
||||||
|
registries = append(registries, openTelemetryRegistry)
|
||||||
|
logger.Debug().
|
||||||
|
Str("address", metricsConfig.OpenTelemetry.Address).
|
||||||
|
Str("pushInterval", metricsConfig.OpenTelemetry.PushInterval.String()).
|
||||||
|
Msg("Configured OpenTelemetry metrics")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return registries
|
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 {
|
func setupAccessLog(conf *types.AccessLog) *accesslog.Handler {
|
||||||
if conf == nil {
|
if conf == nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -450,64 +582,85 @@ func setupAccessLog(conf *types.AccessLog) *accesslog.Handler {
|
|||||||
|
|
||||||
accessLoggerMiddleware, err := accesslog.NewHandler(conf)
|
accessLoggerMiddleware, err := accesslog.NewHandler(conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithoutContext().Warnf("Unable to create access logger : %v", err)
|
log.Warn().Err(err).Msg("Unable to create access logger")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return accessLoggerMiddleware
|
return accessLoggerMiddleware
|
||||||
}
|
}
|
||||||
|
|
||||||
func configureLogging(staticConfiguration *static.Configuration) {
|
func setupTracing(conf *static.Tracing) *tracing.Tracing {
|
||||||
// configure default log flags
|
if conf == nil {
|
||||||
stdlog.SetFlags(stdlog.Lshortfile | stdlog.LstdFlags)
|
return nil
|
||||||
|
|
||||||
// configure log level
|
|
||||||
// an explicitly defined log level always has precedence. if none is
|
|
||||||
// given and debug mode is disabled, the default is ERROR, and DEBUG
|
|
||||||
// otherwise.
|
|
||||||
levelStr := "error"
|
|
||||||
if staticConfiguration.Log != nil && staticConfiguration.Log.Level != "" {
|
|
||||||
levelStr = strings.ToLower(staticConfiguration.Log.Level)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
level, err := logrus.ParseLevel(levelStr)
|
var backend tracing.Backend
|
||||||
|
|
||||||
|
if conf.Jaeger != nil {
|
||||||
|
backend = conf.Jaeger
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.Zipkin != nil {
|
||||||
|
if backend != nil {
|
||||||
|
log.Error().Msg("Multiple tracing backend are not supported: cannot create Zipkin backend.")
|
||||||
|
} else {
|
||||||
|
backend = conf.Zipkin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.Datadog != nil {
|
||||||
|
if backend != nil {
|
||||||
|
log.Error().Msg("Multiple tracing backend are not supported: cannot create Datadog backend.")
|
||||||
|
} else {
|
||||||
|
backend = conf.Datadog
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.Instana != nil {
|
||||||
|
if backend != nil {
|
||||||
|
log.Error().Msg("Multiple tracing backend are not supported: cannot create Instana backend.")
|
||||||
|
} else {
|
||||||
|
backend = conf.Instana
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.Haystack != nil {
|
||||||
|
if backend != nil {
|
||||||
|
log.Error().Msg("Multiple tracing backend are not supported: cannot create Haystack backend.")
|
||||||
|
} else {
|
||||||
|
backend = conf.Haystack
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.Elastic != nil {
|
||||||
|
if backend != nil {
|
||||||
|
log.Error().Msg("Multiple tracing backend are not supported: cannot create Elastic backend.")
|
||||||
|
} else {
|
||||||
|
backend = conf.Elastic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if conf.OpenTelemetry != nil {
|
||||||
|
if backend != nil {
|
||||||
|
log.Error().Msg("Tracing backends are all mutually exclusive: cannot create OpenTelemetry backend.")
|
||||||
|
} else {
|
||||||
|
backend = conf.OpenTelemetry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if backend == nil {
|
||||||
|
log.Debug().Msg("Could not initialize tracing, using Jaeger by default")
|
||||||
|
defaultBackend := &jaeger.Config{}
|
||||||
|
defaultBackend.SetDefaults()
|
||||||
|
backend = defaultBackend
|
||||||
|
}
|
||||||
|
|
||||||
|
tracer, err := tracing.NewTracing(conf.ServiceName, conf.SpanNameLimit, backend)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithoutContext().Errorf("Error getting level: %v", err)
|
log.Warn().Err(err).Msg("Unable to create tracer")
|
||||||
}
|
return nil
|
||||||
log.SetLevel(level)
|
|
||||||
|
|
||||||
var logFile string
|
|
||||||
if staticConfiguration.Log != nil && len(staticConfiguration.Log.FilePath) > 0 {
|
|
||||||
logFile = staticConfiguration.Log.FilePath
|
|
||||||
}
|
|
||||||
|
|
||||||
// configure log format
|
|
||||||
var formatter logrus.Formatter
|
|
||||||
if staticConfiguration.Log != nil && staticConfiguration.Log.Format == "json" {
|
|
||||||
formatter = &logrus.JSONFormatter{}
|
|
||||||
} else {
|
|
||||||
disableColors := len(logFile) > 0
|
|
||||||
formatter = &logrus.TextFormatter{DisableColors: disableColors, FullTimestamp: true, DisableSorting: true}
|
|
||||||
}
|
|
||||||
log.SetFormatter(formatter)
|
|
||||||
|
|
||||||
if len(logFile) > 0 {
|
|
||||||
dir := filepath.Dir(logFile)
|
|
||||||
|
|
||||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
||||||
log.WithoutContext().Errorf("Failed to create log path %s: %s", dir, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = log.OpenFile(logFile)
|
|
||||||
logrus.RegisterExitHandler(func() {
|
|
||||||
if err := log.CloseFile(); err != nil {
|
|
||||||
log.WithoutContext().Errorf("Error while closing log: %v", err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.WithoutContext().Errorf("Error while opening log file %s: %v", logFile, err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return tracer
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkNewVersion() {
|
func checkNewVersion() {
|
||||||
@@ -520,16 +673,16 @@ func checkNewVersion() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func stats(staticConfiguration *static.Configuration) {
|
func stats(staticConfiguration *static.Configuration) {
|
||||||
logger := log.WithoutContext()
|
logger := log.Info()
|
||||||
|
|
||||||
if staticConfiguration.Global.SendAnonymousUsage {
|
if staticConfiguration.Global.SendAnonymousUsage {
|
||||||
logger.Info(`Stats collection is enabled.`)
|
logger.Msg(`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.Msg(`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.Msg(`Help us improve Traefik by leaving this feature on :)`)
|
||||||
logger.Info(`More details on: https://doc.traefik.io/traefik/contributing/data-collection/`)
|
logger.Msg(`More details on: https://doc.traefik.io/traefik/contributing/data-collection/`)
|
||||||
collect(staticConfiguration)
|
collect(staticConfiguration)
|
||||||
} else {
|
} else {
|
||||||
logger.Info(`
|
logger.Msg(`
|
||||||
Stats collection is disabled.
|
Stats collection is disabled.
|
||||||
Help us improve Traefik by turning this feature on :)
|
Help us improve Traefik by turning this feature on :)
|
||||||
More details on: https://doc.traefik.io/traefik/contributing/data-collection/
|
More details on: https://doc.traefik.io/traefik/contributing/data-collection/
|
||||||
@@ -542,7 +695,7 @@ func collect(staticConfiguration *static.Configuration) {
|
|||||||
safe.Go(func() {
|
safe.Go(func() {
|
||||||
for time.Sleep(10 * time.Minute); ; <-ticker {
|
for time.Sleep(10 * time.Minute); ; <-ticker {
|
||||||
if err := collector.Collect(staticConfiguration); err != nil {
|
if err := collector.Collect(staticConfiguration); err != nil {
|
||||||
log.WithoutContext().Debug(err)
|
log.Debug().Err(err).Send()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
193
cmd/traefik/traefik_test.go
Normal file
193
cmd/traefik/traefik_test.go
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
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"
|
||||||
|
"github.com/traefik/traefik/v2/pkg/config/static"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetDefaultsEntrypoints(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
entrypoints static.EntryPoints
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "Skips special names",
|
||||||
|
entrypoints: map[string]*static.EntryPoint{
|
||||||
|
"web": {
|
||||||
|
Address: ":80",
|
||||||
|
},
|
||||||
|
"traefik": {
|
||||||
|
Address: ":8080",
|
||||||
|
},
|
||||||
|
"traefikhub-api": {
|
||||||
|
Address: ":9900",
|
||||||
|
},
|
||||||
|
"traefikhub-tunl": {
|
||||||
|
Address: ":9901",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []string{"web"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Two EntryPoints not attachable",
|
||||||
|
entrypoints: map[string]*static.EntryPoint{
|
||||||
|
"web": {
|
||||||
|
Address: ":80",
|
||||||
|
},
|
||||||
|
"websecure": {
|
||||||
|
Address: ":443",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []string{"web", "websecure"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Two EntryPoints only one attachable",
|
||||||
|
entrypoints: map[string]*static.EntryPoint{
|
||||||
|
"web": {
|
||||||
|
Address: ":80",
|
||||||
|
},
|
||||||
|
"websecure": {
|
||||||
|
Address: ":443",
|
||||||
|
AsDefault: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []string{"websecure"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Two attachable EntryPoints",
|
||||||
|
entrypoints: map[string]*static.EntryPoint{
|
||||||
|
"web": {
|
||||||
|
Address: ":80",
|
||||||
|
AsDefault: true,
|
||||||
|
},
|
||||||
|
"websecure": {
|
||||||
|
Address: ":443",
|
||||||
|
AsDefault: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []string{"web", "websecure"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
actual := getDefaultsEntrypoints(&static.Configuration{
|
||||||
|
EntryPoints: test.entrypoints,
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.ElementsMatch(t, test.expected, actual)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
10
debug.Dockerfile
Normal file
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"]
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
"MD009": false,
|
"MD009": false,
|
||||||
"MD013": false,
|
"MD013": false,
|
||||||
"MD024": false,
|
"MD024": false,
|
||||||
|
"MD025": false,
|
||||||
"MD026": false,
|
"MD026": false,
|
||||||
"MD033": false,
|
"MD033": false,
|
||||||
"MD034": false,
|
"MD034": false,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
#######
|
#######
|
||||||
# This Makefile contains all targets related to the documentation
|
# This Makefile contains all targets related to the documentation
|
||||||
#######
|
#######
|
||||||
@@ -16,41 +15,51 @@ DOCKER_RUN_DOC_MOUNTS := -v $(CURDIR):/mkdocs
|
|||||||
DOCKER_RUN_DOC_OPTS := --rm $(DOCKER_RUN_DOC_MOUNTS) -p $(DOCKER_RUN_DOC_PORT):8000
|
DOCKER_RUN_DOC_OPTS := --rm $(DOCKER_RUN_DOC_MOUNTS) -p $(DOCKER_RUN_DOC_PORT):8000
|
||||||
|
|
||||||
# Default: generates the documentation into $(SITE_DIR)
|
# Default: generates the documentation into $(SITE_DIR)
|
||||||
|
.PHONY: docs
|
||||||
docs: docs-clean docs-image docs-lint docs-build docs-verify
|
docs: docs-clean docs-image docs-lint docs-build docs-verify
|
||||||
|
|
||||||
# Writer Mode: build and serve docs on http://localhost:8000 with livereload
|
# Writer Mode: build and serve docs on http://localhost:8000 with livereload
|
||||||
|
.PHONY: docs-serve
|
||||||
docs-serve: docs-image
|
docs-serve: docs-image
|
||||||
docker run $(DOCKER_RUN_DOC_OPTS) $(TRAEFIK_DOCS_BUILD_IMAGE) mkdocs serve
|
docker run $(DOCKER_RUN_DOC_OPTS) $(TRAEFIK_DOCS_BUILD_IMAGE) mkdocs serve
|
||||||
|
|
||||||
## Pull image for doc building
|
## Pull image for doc building
|
||||||
|
.PHONY: docs-pull-images
|
||||||
docs-pull-images:
|
docs-pull-images:
|
||||||
grep --no-filename -E '^FROM' ./*.Dockerfile | awk '{print $$2}' | sort | uniq | xargs -P 6 -n 1 docker pull
|
grep --no-filename -E '^FROM' ./*.Dockerfile \
|
||||||
|
| awk '{print $$2}' \
|
||||||
|
| sort \
|
||||||
|
| uniq \
|
||||||
|
| xargs -P 6 -n 1 docker pull
|
||||||
|
|
||||||
# Utilities Targets for each step
|
# Utilities Targets for each step
|
||||||
|
.PHONY: docs-image
|
||||||
docs-image:
|
docs-image:
|
||||||
docker build -t $(TRAEFIK_DOCS_BUILD_IMAGE) -f docs.Dockerfile ./
|
docker build -t $(TRAEFIK_DOCS_BUILD_IMAGE) -f docs.Dockerfile ./
|
||||||
|
|
||||||
|
.PHONY: docs-build
|
||||||
docs-build: docs-image
|
docs-build: docs-image
|
||||||
docker run $(DOCKER_RUN_DOC_OPTS) $(TRAEFIK_DOCS_BUILD_IMAGE) sh -c "mkdocs build \
|
docker run $(DOCKER_RUN_DOC_OPTS) $(TRAEFIK_DOCS_BUILD_IMAGE) sh -c "mkdocs build \
|
||||||
&& chown -R $(shell id -u):$(shell id -g) ./site"
|
&& chown -R $(shell id -u):$(shell id -g) ./site"
|
||||||
|
|
||||||
|
.PHONY: docs-verify
|
||||||
docs-verify: docs-build
|
docs-verify: docs-build
|
||||||
@if [ "$(DOCS_VERIFY_SKIP)" != "true" ]; then \
|
ifneq ("$(DOCS_VERIFY_SKIP)", "true")
|
||||||
docker build -t $(TRAEFIK_DOCS_CHECK_IMAGE) -f check.Dockerfile ./; \
|
docker build -t $(TRAEFIK_DOCS_CHECK_IMAGE) -f check.Dockerfile ./
|
||||||
docker run --rm -v $(CURDIR):/app $(TRAEFIK_DOCS_CHECK_IMAGE) /verify.sh; \
|
docker run --rm -v $(CURDIR):/app $(TRAEFIK_DOCS_CHECK_IMAGE) /verify.sh
|
||||||
else \
|
else
|
||||||
echo "DOCS_VERIFY_SKIP is true: no verification done."; \
|
echo "DOCS_VERIFY_SKIP is true: no verification done."
|
||||||
fi
|
endif
|
||||||
|
|
||||||
|
.PHONY: docs-lint
|
||||||
docs-lint:
|
docs-lint:
|
||||||
@if [ "$(DOCS_LINT_SKIP)" != "true" ]; then \
|
ifneq ("$(DOCS_LINT_SKIP)", "true")
|
||||||
docker build -t $(TRAEFIK_DOCS_CHECK_IMAGE) -f check.Dockerfile ./ && \
|
docker build -t $(TRAEFIK_DOCS_CHECK_IMAGE) -f check.Dockerfile ./
|
||||||
docker run --rm -v $(CURDIR):/app $(TRAEFIK_DOCS_CHECK_IMAGE) /lint.sh; \
|
docker run --rm -v $(CURDIR):/app $(TRAEFIK_DOCS_CHECK_IMAGE) /lint.sh
|
||||||
else \
|
else
|
||||||
echo "DOCS_LINT_SKIP is true: no linting done."; \
|
echo "DOCS_LINT_SKIP is true: no linting done."
|
||||||
fi
|
endif
|
||||||
|
|
||||||
|
.PHONY: docs-clean
|
||||||
docs-clean:
|
docs-clean:
|
||||||
rm -rf $(SITE_DIR)
|
rm -rf $(SITE_DIR)
|
||||||
|
|
||||||
.PHONY: all docs-verify docs docs-clean docs-build docs-lint
|
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
|
FROM alpine:3.14 as alpine
|
||||||
FROM alpine:3.13 as alpine
|
|
||||||
|
|
||||||
RUN apk --no-cache --no-progress add \
|
RUN apk --no-cache --no-progress add \
|
||||||
|
build-base \
|
||||||
libcurl \
|
libcurl \
|
||||||
|
libxml2-dev \
|
||||||
|
libxslt-dev \
|
||||||
ruby \
|
ruby \
|
||||||
ruby-bigdecimal \
|
ruby-bigdecimal \
|
||||||
|
ruby-dev \
|
||||||
ruby-etc \
|
ruby-etc \
|
||||||
ruby-ffi \
|
ruby-ffi \
|
||||||
ruby-json \
|
ruby-json \
|
||||||
ruby-nokogiri \
|
zlib-dev
|
||||||
ruby-dev \
|
|
||||||
build-base
|
|
||||||
|
|
||||||
RUN gem install html-proofer --version 3.19.0 --no-document -- --use-system-libraries
|
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!
|
# After Ruby, some NodeJS YAY!
|
||||||
RUN apk --no-cache --no-progress add \
|
RUN apk --no-cache --no-progress add \
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 58 KiB |
BIN
docs/content/assets/img/providers/nomad.png
Normal file
BIN
docs/content/assets/img/providers/nomad.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -1,10 +1,31 @@
|
|||||||
|
---
|
||||||
|
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
|
# Advocating
|
||||||
|
|
||||||
Spread the Love & Tell Us about It
|
Spread the Love & Tell Us about It
|
||||||
{: .subtitle }
|
{: .subtitle }
|
||||||
|
|
||||||
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.
|
Traefik Proxy was started by the community for the community.
|
||||||
|
You can contribute to the Traefik community in three main ways:
|
||||||
|
|
||||||
If you're talking about Traefik, [let us know](https://blog.traefik.io/spread-the-love-ba5a40aa72e7) and we'll promote your enthusiasm!
|
**Spread the word!** Guides, videos, blog posts, how-to articles, and showing off your network design all help spread the word about Traefik Proxy
|
||||||
|
and teach others in the community how to best implement it.
|
||||||
|
It always sparks joy when users share how Traefik Proxy helps them solve their problems.
|
||||||
|
If you are talking about Traefik Proxy, [let us know](https://traefik.io/submit-my-contribution/) and we will promote your work and reward your enthusiasm!
|
||||||
|
If you are giving a talk that includes or is about Traefik Proxy, [let us know](https://traefik.io/submit-my-contribution/) and we will send you swag and stickers for your time at the conference.
|
||||||
|
If you have written about Traefik or shared useful information you would like to promote, feel free to add links to the [dedicated wiki page on GitHub](https://github.com/traefik/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).
|
**Help community members!** Everyone needs a place to share their cool innovations or get help with that pesky bug that only a different pair of eyes seems to be able to see.
|
||||||
|
Join our [Community Forum](https://community.traefik.io/) where you can ask questions, help out other users, and share your neat configuration examples or snippets.
|
||||||
|
Top contributors will be asked to join the Ambassador program and get unique swag to celebrate!
|
||||||
|
|
||||||
|
**Build cool solutions!** Traefik Proxy would be so much better if only it had…
|
||||||
|
We love all the wonderful ideas that our users come up with, but we can only build so much.
|
||||||
|
Luckily, as an open source community, our users can help by [building awesome features](https://github.com/orgs/traefik/projects/9/views/7), enhancements, or bug fixes.
|
||||||
|
We are a big community, so we do need to prioritize a bit.
|
||||||
|
That is why we use the tag `contributor/wanted` to let you know which pull requests will make it to the front of the queue for design support and review.
|
||||||
|
Feel free to grab one of these and run with it.
|
||||||
|
Top contributors get unique swag to celebrate.
|
||||||
|
|||||||
@@ -1,19 +1,29 @@
|
|||||||
|
---
|
||||||
|
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
|
# Building and Testing
|
||||||
|
|
||||||
Compile and Test Your Own Traefik!
|
Compile and Test Your Own Traefik!
|
||||||
{: .subtitle }
|
{: .subtitle }
|
||||||
|
|
||||||
So you want to build your own Traefik binary from the sources?
|
You want to build your own Traefik binary from the sources?
|
||||||
Let's see how.
|
Let's see how.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
You need either [Docker](https://github.com/docker/docker) and `make` (Method 1), or `go` (Method 2) in order to build Traefik.
|
You need either [Docker](https://github.com/docker/docker "Link to website of Docker") and `make` (Method 1), or [Go](https://go.dev/ "Link to website of Go") (Method 2) in order to build Traefik.
|
||||||
For changes to its dependencies, the `dep` dependency management tool is required.
|
For changes to its dependencies, the `dep` dependency management tool is required.
|
||||||
|
|
||||||
### Method 1: Using `Docker` and `Makefile`
|
### Method 1: Using `Docker` and `Makefile`
|
||||||
|
|
||||||
Run make with the `binary` target.
|
Run make with the `binary` target.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make binary
|
||||||
|
```
|
||||||
|
|
||||||
This will create binaries for the Linux platform in the `dist` folder.
|
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.
|
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.
|
||||||
@@ -45,7 +55,7 @@ $ ls dist/
|
|||||||
traefik*
|
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-unit`
|
||||||
- `test-integration`
|
- `test-integration`
|
||||||
@@ -55,7 +65,7 @@ The following targets can be executed outside Docker by setting the variable `PR
|
|||||||
ex:
|
ex:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
PRE_TARGET= make test-unit
|
IN_DOCKER= make test-unit
|
||||||
```
|
```
|
||||||
|
|
||||||
### Method 2: Using `go`
|
### Method 2: Using `go`
|
||||||
@@ -64,7 +74,6 @@ Requirements:
|
|||||||
|
|
||||||
- `go` v1.16+
|
- `go` v1.16+
|
||||||
- environment variable `GO111MODULE=on`
|
- environment variable `GO111MODULE=on`
|
||||||
- [go-bindata](https://github.com/containous/go-bindata) `GO111MODULE=off go get -u github.com/containous/go-bindata/...`
|
|
||||||
|
|
||||||
!!! tip "Source Directory"
|
!!! tip "Source Directory"
|
||||||
|
|
||||||
@@ -101,18 +110,9 @@ Requirements:
|
|||||||
|
|
||||||
Once you've set up your go environment and cloned the source repository, you can 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](https://github.com/containous/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/traefik/traefik
|
|
||||||
|
|
||||||
# Get go-bindata. (Important: the ellipses are required.)
|
|
||||||
GO111MODULE=off go get github.com/containous/go-bindata/...
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Generate UI static files
|
# Generate UI static files
|
||||||
rm -rf static/ autogen/; make generate-webui
|
make clean-webui generate-webui
|
||||||
|
|
||||||
# required to merge non-code components into the final binary,
|
# required to merge non-code components into the final binary,
|
||||||
# such as the web dashboard/UI
|
# such as the web dashboard/UI
|
||||||
@@ -165,7 +165,7 @@ TESTFLAGS="-check.f MyTestSuite.My" make test-integration
|
|||||||
TESTFLAGS="-check.f MyTestSuite.*Test" make test-integration
|
TESTFLAGS="-check.f MyTestSuite.*Test" make test-integration
|
||||||
```
|
```
|
||||||
|
|
||||||
More: https://labix.org/gocheck
|
Check [gocheck](https://labix.org/gocheck "Link to website of gocheck") for more information.
|
||||||
|
|
||||||
### Method 2: `go`
|
### Method 2: `go`
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
# Data Collection
|
||||||
|
|
||||||
Understanding How Traefik is Being Used
|
Understanding How Traefik is Being Used
|
||||||
|
|||||||
@@ -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
|
# Documentation
|
||||||
|
|
||||||
Features Are Better When You Know How to Use Them
|
Features Are Better When You Know How to Use Them
|
||||||
@@ -10,10 +15,14 @@ Let's see how.
|
|||||||
|
|
||||||
### General
|
### General
|
||||||
|
|
||||||
This [documentation](https://doc.traefik.io/traefik/) is built with [mkdocs](https://mkdocs.org/).
|
This [documentation](https://doc.traefik.io/traefik/ "Link to the official Traefik documentation") is built with [MkDocs](https://mkdocs.org/ "Link to website of MkDocs").
|
||||||
|
|
||||||
### Method 1: `Docker` and `make`
|
### Method 1: `Docker` and `make`
|
||||||
|
|
||||||
|
Please make sure you have the following requirements installed:
|
||||||
|
|
||||||
|
- [Docker](https://www.docker.com/ "Link to website of Docker")
|
||||||
|
|
||||||
You can build the documentation and test it locally (with live reloading), using the `docs-serve` target:
|
You can build the documentation and test it locally (with live reloading), using the `docs-serve` target:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -29,7 +38,7 @@ docker run --rm -v /home/user/go/github/traefik/traefik:/mkdocs -p 8000:8000 tr
|
|||||||
|
|
||||||
!!! tip "Default URL"
|
!!! 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:
|
If you only want to build the documentation without serving it locally, you can use the following command:
|
||||||
|
|
||||||
@@ -38,9 +47,12 @@ $ make docs-build
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
### Method 2: `mkdocs`
|
### Method 2: `MkDocs`
|
||||||
|
|
||||||
First, make sure you have `python` and `pip` installed.
|
Please make sure you have the following requirements installed:
|
||||||
|
|
||||||
|
- [Python](https://www.python.org/ "Link to website of Python")
|
||||||
|
- [pip](https://pypi.org/project/pip/ "Link to the website of pip on PyPI")
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ python --version
|
$ python --version
|
||||||
@@ -49,7 +61,7 @@ $ pip --version
|
|||||||
pip 1.5.2
|
pip 1.5.2
|
||||||
```
|
```
|
||||||
|
|
||||||
Then, install mkdocs with `pip`.
|
Then, install MkDocs with `pip`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install --user -r requirements.txt
|
pip install --user -r requirements.txt
|
||||||
@@ -82,7 +94,7 @@ Running ["HtmlCheck", "ImageCheck", "ScriptCheck", "LinkCheck"] on /app/site/bas
|
|||||||
|
|
||||||
!!! note "Clean & Verify"
|
!!! 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 safer to clean it before verifying it.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ make docs
|
$ make docs
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
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
|
# Maintainer's Guidelines
|
||||||
|
|
||||||

|

|
||||||
@@ -25,7 +30,7 @@ We will be happy to answer any questions and explain all your doubts.
|
|||||||
Note: you do not have to meet all the listed requirements,
|
Note: you do not have to meet all the listed requirements,
|
||||||
but must have achieved several.
|
but must have achieved several.
|
||||||
|
|
||||||
- Enabled [2FA](https://docs.github.com/en/github/authenticating-to-github/securing-your-account-with-two-factor-authentication-2fa/configuring-two-factor-authentication) on your Github account
|
- 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 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,
|
- The contributor has participated in multiple code reviews of other PR’s,
|
||||||
including those of other maintainers and contributors.
|
including those of other maintainers and contributors.
|
||||||
@@ -55,7 +60,7 @@ but we can suggest you start with activities such as:
|
|||||||
The ability to set up a testing environment in a few minutes,
|
The ability to set up a testing environment in a few minutes,
|
||||||
using the official documentation,
|
using the official documentation,
|
||||||
is a game changer.
|
is a game changer.
|
||||||
- You will be listed on our Maintainers Github page
|
- You will be listed on our Maintainers GitHub page
|
||||||
as well as on our website in the section [maintainers](maintainers.md).
|
as well as on our website in the section [maintainers](maintainers.md).
|
||||||
- We will be promoting you on social channels (mostly on Twitter).
|
- We will be promoting you on social channels (mostly on Twitter).
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
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
|
# Maintainers
|
||||||
|
|
||||||
## The Team
|
## The Team
|
||||||
@@ -19,6 +24,7 @@
|
|||||||
* Romain Tribotté [@rtribotte](https://github.com/rtribotte)
|
* Romain Tribotté [@rtribotte](https://github.com/rtribotte)
|
||||||
* Kevin Pollet [@kevinpollet](https://github.com/kevinpollet)
|
* Kevin Pollet [@kevinpollet](https://github.com/kevinpollet)
|
||||||
* Harold Ozouf [@jspdown](https://github.com/jspdown)
|
* Harold Ozouf [@jspdown](https://github.com/jspdown)
|
||||||
|
* Tom Moulard [@tommoulard](https://github.com/tommoulard)
|
||||||
|
|
||||||
## Maintainer's Guidelines
|
## Maintainer's Guidelines
|
||||||
|
|
||||||
|
|||||||
@@ -1,43 +1,61 @@
|
|||||||
|
---
|
||||||
|
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
|
# Submitting Issues
|
||||||
|
|
||||||
Help Us Help You!
|
Help Us Help You!
|
||||||
{: .subtitle }
|
{: .subtitle }
|
||||||
|
|
||||||
|
Issues are perfect for requesting a feature/enhancement or reporting a suspected bug.
|
||||||
We use the [GitHub issue tracker](https://github.com/traefik/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).
|
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.
|
To help us (and other community members) quickly and easily understand what you need,
|
||||||
|
be sure to follow the guidelines below.
|
||||||
|
|
||||||
!!! important "Getting Help Vs Reporting an Issue"
|
!!! 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.
|
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:
|
For end-user related support questions, try using the [Traefik Community Forum](https://community.traefik.io/)
|
||||||
|
[](https://community.traefik.io/)
|
||||||
- the Traefik community forum: [](https://community.traefik.io/)
|
|
||||||
|
|
||||||
## Issue Title
|
## Issue Title
|
||||||
|
|
||||||
The title must be short and descriptive. (~60 characters)
|
The title must be short and descriptive. (~60 characters)
|
||||||
|
|
||||||
## Description
|
Examples:
|
||||||
|
|
||||||
Follow the [issue template](https://github.com/traefik/traefik/blob/master/.github/ISSUE_TEMPLATE.md) as much as possible.
|
* Bug: Duplicate requests in access logs
|
||||||
|
* Feature: Support TCP
|
||||||
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)).
|
|
||||||
|
|
||||||
## Feature Request
|
## Feature Request
|
||||||
|
|
||||||
Traefik is an open-source project and aims to be the best edge router possible.
|
Traefik is an open source project and aims to be the best edge router possible.
|
||||||
|
|
||||||
Remember when asking for new features that these must be useful to the majority (and not only useful in edge case scenarios, or hack-like setups).
|
Remember when asking for new features that these must be useful to the majority (and not only useful in edge case scenarios, or hack-like setups).
|
||||||
|
Follow the [issue template](https://github.com/traefik/traefik/blob/master/.github/ISSUE_TEMPLATE/feature-request.yml) as much as possible.
|
||||||
|
|
||||||
Do you best to explain what you're looking for, and why it would improve Traefik for everyone.
|
Do your best to explain what you're looking for, and why it would improve Traefik for everyone.
|
||||||
|
Be detailed and share the use-case(s) to allow us to see the value of your feature request as quickly as possible.
|
||||||
|
Features with a lot of positive interaction (claps, +1s, conversation about how this would impact them) indicate higher community interest and help us to prioritize.
|
||||||
|
|
||||||
|
If you are interested in creating a PR for your feature request, let us know in the the issue so we can work with you.
|
||||||
|
It can take a lot of work to make sure a PR can integrate with our existing code and planning with the team ahead of time can make sure that your PR can be accepted and merged quickly.
|
||||||
|
|
||||||
|
## Issues or Possible Bug Reports
|
||||||
|
|
||||||
|
Follow the [issue template](https://github.com/traefik/traefik/blob/master/.github/ISSUE_TEMPLATE/bug_report.yml) as much as possible.
|
||||||
|
|
||||||
|
Explain the conditions in which you encountered the issue; what is your context?
|
||||||
|
Share any logs you may have and make sure to share the steps it takes to reproduce your issue or bug.
|
||||||
|
|
||||||
|
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 your 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)).
|
||||||
|
|
||||||
## International English
|
## International English
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,230 @@
|
|||||||
# Submitting Pull Requests
|
---
|
||||||
|
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."
|
||||||
|
---
|
||||||
|
|
||||||
A Quick Guide for Efficient Contributions
|
# Before You Submit a Pull Request
|
||||||
{: .subtitle }
|
|
||||||
|
|
||||||
So you've decided to improve Traefik?
|
This guide is for contributors who already have a pull request to submit.
|
||||||
Thank You!
|
If you are looking for information on setting up your developer environment
|
||||||
|
and creating code to contribute to Traefik Proxy or related projects,
|
||||||
|
see the [development guide](https://docs.traefik.io/contributing/building-testing/).
|
||||||
|
|
||||||
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).
|
Looking for a way to contribute to Traefik Proxy?
|
||||||
|
Check out this list of [Priority Issues](https://github.com/traefik/traefik/labels/contributor%2Fwanted),
|
||||||
|
the [Good First Issue](https://github.com/traefik/traefik/labels/contributor%2Fgood-first-issue) list,
|
||||||
|
or the list of [confirmed bugs](https://github.com/traefik/traefik/labels/kind%2Fbug%2Fconfirmed) waiting to be remedied.
|
||||||
|
|
||||||
|
## How We Prioritize
|
||||||
|
|
||||||
|
We wish we could review every pull request right away.
|
||||||
|
Unfortunately, our team has to prioritize pull requests (PRs) for review
|
||||||
|
(but we are welcoming new [maintainers](https://github.com/traefik/traefik/blob/master/docs/content/contributing/maintainers-guidelines.md) to speed this up,
|
||||||
|
so if you are interested, check it out and apply).
|
||||||
|
|
||||||
|
The PRs we are able to handle fastest are:
|
||||||
|
|
||||||
|
* Documentation updates.
|
||||||
|
* Bug fixes.
|
||||||
|
* Enhancements and Features with a `contributor/wanted` tag.
|
||||||
|
|
||||||
|
PRs that take more time to address include:
|
||||||
|
|
||||||
|
* Enhancements or Features without the `contributor/wanted` tag.
|
||||||
|
|
||||||
|
If you have an idea for an enhancement or feature that you would like to build,
|
||||||
|
[create an issue](https://github.com/traefik/traefik/issues/new/choose) for it first
|
||||||
|
and tell us you are interested in writing the PR.
|
||||||
|
If an issue already exists, definitely comment on it to tell us you are interested in creating a PR.
|
||||||
|
|
||||||
|
This will allow us to communicate directly and let you know if it is something we would accept.
|
||||||
|
It also allows us to make sure you have all the information you need during the design phase
|
||||||
|
so that it can be reviewed and merged quickly.
|
||||||
|
|
||||||
|
If you have questions about the Triage process,
|
||||||
|
[read more here](https://github.com/traefik/contributors-guide/blob/master/issue_triage.md).
|
||||||
|
|
||||||
|
## The Pull Request Submit Process
|
||||||
|
|
||||||
|
Merging a PR requires the following steps to be completed before it is merged automatically.
|
||||||
|
|
||||||
|
* Make sure your pull request adheres to our best practices. These include:
|
||||||
|
* [Following project conventions](https://github.com/traefik/traefik/blob/master/docs/content/contributing/maintainers-guidelines.md); including using the PR Template.
|
||||||
|
* Make small pull requests.
|
||||||
|
* Solve only one problem at a time.
|
||||||
|
* Comment thoroughly.
|
||||||
|
* Do not open the PR from an organization repository.
|
||||||
|
* Keep "allows edit from maintainer" checked.
|
||||||
|
* Use semantic line breaks for documentation.
|
||||||
|
* Pass the validation check.
|
||||||
|
* Pass all tests.
|
||||||
|
* Receive 3 approving reviews maintainers.
|
||||||
|
|
||||||
|
## Pull Request Review Cycle
|
||||||
|
|
||||||
|
You can read about our Triage Process [here](https://github.com/traefik/contributors-guide/blob/master/issue_triage.md),
|
||||||
|
but in short, it looks like this:
|
||||||
|
|
||||||
|
* We triage every new PR or comment before entering it into the review process.
|
||||||
|
* We ensure that all prerequisites for review have been met.
|
||||||
|
* We check to make sure the use case meets our needs.
|
||||||
|
* We assign reviewers.
|
||||||
|
* Design Review.
|
||||||
|
* This takes longer than other parts of the process.
|
||||||
|
* We review that there are no obvious conflicts with our codebase.
|
||||||
|
* Code Review.
|
||||||
|
* We review the code in-depth and run tests.
|
||||||
|
* We may ask for changes here.
|
||||||
|
* During code review, we ask that you be reasonably responsive,
|
||||||
|
if a PR languishes in code review it is at risk of rejection,
|
||||||
|
or we may take ownership of the PR and the contributor will become a co-author.
|
||||||
|
* Merge.
|
||||||
|
* Success!
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
|
||||||
|
Occasionally, we may freeze our codebase when working towards a specific feature or goal that could impact other development.
|
||||||
|
During this time, your pull request could remain unmerged while the release work is completed.
|
||||||
|
|
||||||
|
## Run Local Verifications
|
||||||
|
|
||||||
|
You must run these local verifications before you submit your pull request to predict the pass or failure of continuous integration.
|
||||||
|
Your PR will not be reviewed until these are green on the CI.
|
||||||
|
|
||||||
|
* `make validate`
|
||||||
|
* `make pull-images`
|
||||||
|
* `make test`
|
||||||
|
|
||||||
|
## The Testing and Merge Workflow
|
||||||
|
|
||||||
|
Pull Requests are managed by the bot [Myrmica Lobicornis](https://github.com/traefik/lobicornis).
|
||||||
|
This bot is responsible for verifying GitHub Checks (CI, Tests, etc), mergability, and minimum reviews.
|
||||||
|
In addition, it rebases or merges with the base PR branch if needed.
|
||||||
|
It performs several other housekeeping items
|
||||||
|
and you can read more about those on the [README](https://github.com/traefik/lobicornis) for Lobicornis.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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 can be used when:
|
||||||
|
|
||||||
|
* Updating a dependency.
|
||||||
|
* Merging branches back into the next version branch.
|
||||||
|
* Submitting minor documentation changes.
|
||||||
|
* Submitting changelog PRs.
|
||||||
|
|
||||||
|
## Why Was My Pull Request Closed?
|
||||||
|
|
||||||
|
Traefik Proxy is made by the community for the community,
|
||||||
|
as such the goal is to engage the community to make Traefik the best reverse proxy available.
|
||||||
|
Part of this goal is maintaining a lean codebase and ensuring code velocity.
|
||||||
|
unfortunately, this means that sometimes we will not be able to merge a pull request.
|
||||||
|
|
||||||
|
Because we respect the work you did, you will always be told why we are closing your pull request.
|
||||||
|
If you do not agree with our decision, do not worry; closed pull requests are easy to recreate,
|
||||||
|
and little work is lost by closing a pull request that subsequently needs to be reopened.
|
||||||
|
|
||||||
|
Your pull request might be closed if:
|
||||||
|
|
||||||
|
* Your PR's design conflicts with our existing codebase in such a way that Merging is not an option
|
||||||
|
and the work needed to make your pull request usable is too high.
|
||||||
|
* To prevent this, make sure you created an issue first
|
||||||
|
and think about including Traefik Proxy maintainers in your design phase to minimize conflicts.
|
||||||
|
* Your PR is for an enhancement or feature that we will not use.
|
||||||
|
* Please remember to create an issue for any pull request **before** you create a PR
|
||||||
|
to ensure that your goal is something we can merge and that you have any design insight you might need from the team.
|
||||||
|
* Your PR has been waiting for feedback from the contributor for over 90 days.
|
||||||
|
|
||||||
|
## Why is My Pull Request Not Getting Reviewed
|
||||||
|
|
||||||
|
A few factors affect how long your pull request might wait for review.
|
||||||
|
|
||||||
|
We must prioritize which PRs we focus on.
|
||||||
|
Our first priority is PRs we have identified as having high community engagement and broad applicability.
|
||||||
|
We put our top priorities on our roadmap and you can identify them by the `contributor/wanted` tag.
|
||||||
|
These PRs will enter our review process the fastest.
|
||||||
|
|
||||||
|
Our second priority is bug fixes.
|
||||||
|
Especially for bugs that have already been tagged with `bug/confirmed`.
|
||||||
|
These reviews enter the process quickly.
|
||||||
|
|
||||||
|
If your PR does not meet the criteria above,
|
||||||
|
it will take longer for us to review as any PRs that do meet the criteria above will be prioritized.
|
||||||
|
|
||||||
|
Additionally, during the last few weeks of a milestone, we stop reviewing PRs to reduce churn and stabilize.
|
||||||
|
We will resume after the release.
|
||||||
|
|
||||||
|
The second major reason that we deprioritize your PR is that you are not following best practices.
|
||||||
|
|
||||||
|
The most common failures to follow best practices are:
|
||||||
|
|
||||||
|
* You did not create an issue for the PR you wish to make.
|
||||||
|
If you do not create an issue before submitting your PR,
|
||||||
|
we will not be able to answer any design questions and let you know how likely your PR is to be merged.
|
||||||
|
* You created pull requests that are too large to review.
|
||||||
|
* Break your pull requests up.
|
||||||
|
If you can extract whole ideas from your pull request and send those as pull requests of their own,
|
||||||
|
you should do that instead.
|
||||||
|
It is better to have many pull requests addressing one thing than one pull request addressing many things.
|
||||||
|
* Traefik Proxy is a fast-moving codebase — lock in your changes ASAP with your small pull request,
|
||||||
|
and make merges be someone else's problem.
|
||||||
|
We want every pull request to be useful on its own,
|
||||||
|
so use your best judgment on what should be a pull request vs. a commit.
|
||||||
|
* You did not comment well.
|
||||||
|
* Comment everything.
|
||||||
|
|
||||||
|
Please remember that we are working internationally, cross-culturally, and with different use-cases.
|
||||||
|
Your reviewer will not intuitively understand the problem the same way you do or solve it the same way you would.
|
||||||
|
This is why every change you make must be explained and your strategy for coding must also be explained.
|
||||||
|
|
||||||
|
* Your tests were inadequate or absent.
|
||||||
|
* If you do not know how to test your PR, please ask!
|
||||||
|
We will be happy to help you or suggest appropriate test cases.
|
||||||
|
|
||||||
|
If you have already followed the best practices and your PR still has not received a response,
|
||||||
|
here are some things you can do to move the process along:
|
||||||
|
|
||||||
|
* If you have fixed all the issues from a review,
|
||||||
|
remember to re-request a review (using the designated button) to let your reviewer know that you are ready.
|
||||||
|
You can choose to comment with the changes you made.
|
||||||
|
* Ping `@tfny` if you have not been assigned to a reviewer.
|
||||||
|
|
||||||
|
For more information on best practices, try these links:
|
||||||
|
|
||||||
|
* [How to Write a Git Commit Message - Chris Beams](https://chris.beams.io/posts/git-commit/)
|
||||||
|
* [Distributed Git - Contributing to a Project (Commit Guidelines)](https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project)
|
||||||
|
* [What’s with the 50/72 rule? - Preslav Rachev](https://preslav.me/2015/02/21/what-s-with-the-50-72-rule/)
|
||||||
|
* [A Note About Git Commit Messages - Tim Pope](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
|
||||||
|
|
||||||
|
## It's OK to Push Back
|
||||||
|
|
||||||
|
Sometimes reviewers make mistakes.
|
||||||
|
It is OK to push back on changes your reviewer requested.
|
||||||
|
If you have a good reason for doing something a certain way, you are absolutely allowed to debate the merits of a requested change.
|
||||||
|
Both the reviewer and reviewee should strive to discuss these issues in a polite and respectful manner.
|
||||||
|
|
||||||
|
You might be overruled, but you might also prevail.
|
||||||
|
We are pretty reasonable people.
|
||||||
|
|
||||||
|
Another phenomenon of open-source projects (where anyone can comment on any issue) is the dog-pile -
|
||||||
|
your pull request gets so many comments from so many people it becomes hard to follow.
|
||||||
|
In this situation, you can ask the primary reviewer (assignee) whether they want you to fork a new pull request
|
||||||
|
to clear out all the comments.
|
||||||
|
You do not have to fix every issue raised by every person who feels like commenting,
|
||||||
|
but you should answer reasonable comments with an explanation.
|
||||||
|
|
||||||
|
## Common Sense and Courtesy
|
||||||
|
|
||||||
|
No document can take the place of common sense and good taste.
|
||||||
|
Use your best judgment, while you put a bit of thought into how your work can be made easier to review.
|
||||||
|
If you do these things your pull requests will get merged with less friction.
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
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
|
||||||
|
|
||||||
## Security Advisories
|
## Security Advisories
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
|
---
|
||||||
|
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!
|
# Thank You!
|
||||||
|
|
||||||
_You_ Made It
|
_You_ Made It
|
||||||
{: .subtitle}
|
{: .subtitle}
|
||||||
|
|
||||||
Traefik truly is an [open-source project](https://github.com/traefik/traefik/),
|
Traefik Proxy 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),
|
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.
|
not accounting for people having helped with issues, tests, comments, articles, ... or just enjoy using Traefik Proxy and share with others.
|
||||||
|
|
||||||
So once again, thank you for your invaluable help on making Traefik such a good product.
|
So once again, thank you for your invaluable help in making Traefik such a good product!
|
||||||
|
|
||||||
!!! question "Where to Go Next?"
|
!!! question "Where to Go Next?"
|
||||||
If you want to:
|
If you want to:
|
||||||
|
|||||||
5
docs/content/deprecation/features.md
Normal file
5
docs/content/deprecation/features.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Feature Deprecation Notices
|
||||||
|
|
||||||
|
This page is maintained and updated periodically to reflect our roadmap and any decisions around feature deprecation.
|
||||||
|
|
||||||
|
There is no feature deprecation in Traefik v3 for now.
|
||||||
40
docs/content/deprecation/releases.md
Normal file
40
docs/content/deprecation/releases.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# Releases
|
||||||
|
|
||||||
|
## Versions
|
||||||
|
|
||||||
|
Below is a non-exhaustive list of versions and their maintenance status:
|
||||||
|
|
||||||
|
| Version | Release Date | Active Support | Security Support |
|
||||||
|
|---------|--------------|--------------------|------------------|
|
||||||
|
| 2.9 | Oct 03, 2022 | Yes | Yes |
|
||||||
|
| 2.8 | Jun 29, 2022 | Ended Oct 03, 2022 | No |
|
||||||
|
| 2.7 | May 24, 2022 | Ended Jun 29, 2022 | No |
|
||||||
|
| 2.6 | Jan 24, 2022 | Ended May 24, 2022 | No |
|
||||||
|
| 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
|
# Concepts
|
||||||
|
|
||||||
Everything You Need to Know
|
Everything You Need to Know
|
||||||
@@ -19,7 +24,7 @@ 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.
|
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.
|
You no longer need to create and synchronize configuration files cluttered with IP addresses or other rules.
|
||||||
|
|
||||||
@@ -34,3 +39,5 @@ You no longer need to create and synchronize configuration files cluttered with
|
|||||||
!!! question "How does Traefik discover the services?"
|
!!! question "How does Traefik discover the services?"
|
||||||
|
|
||||||
Traefik is able to use your cluster API to discover the services and read the attached information. In Traefik, these connectors are called [providers](../providers/overview.md) because they _provide_ the configuration to Traefik. To learn more about them, read the [provider overview](../providers/overview.md) section.
|
Traefik is able to use your cluster API to discover the services and read the attached information. In Traefik, these connectors are called [providers](../providers/overview.md) because they _provide_ the configuration to Traefik. To learn more about them, read the [provider overview](../providers/overview.md) section.
|
||||||
|
|
||||||
|
{!traefik-for-business-applications.md!}
|
||||||
|
|||||||
@@ -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
|
# Configuration Introduction
|
||||||
|
|
||||||
How the Magic Happens
|
How the Magic Happens
|
||||||
@@ -51,7 +56,7 @@ Once positioned, this option sets (and resets) all the default values of the sub
|
|||||||
|
|
||||||
### Configuration File
|
### Configuration File
|
||||||
|
|
||||||
At startup, Traefik searches for a file named `traefik.yml` (or `traefik.yaml` or `traefik.toml`) in:
|
At startup, Traefik searches for static configuration in a file named `traefik.yml` (or `traefik.yaml` or `traefik.toml`) in:
|
||||||
|
|
||||||
- `/etc/traefik/`
|
- `/etc/traefik/`
|
||||||
- `$XDG_CONFIG_HOME/`
|
- `$XDG_CONFIG_HOME/`
|
||||||
@@ -74,7 +79,7 @@ traefik --help
|
|||||||
# or
|
# or
|
||||||
|
|
||||||
docker run traefik[:version] --help
|
docker run traefik[:version] --help
|
||||||
# ex: docker run traefik:2.1 --help
|
# ex: docker run traefik:v3.0 --help
|
||||||
```
|
```
|
||||||
|
|
||||||
All available arguments can also be found [here](../reference/static-configuration/cli.md).
|
All available arguments can also be found [here](../reference/static-configuration/cli.md).
|
||||||
@@ -88,3 +93,5 @@ All available environment variables can be found [here](../reference/static-conf
|
|||||||
All the configuration options are documented in their related section.
|
All the configuration options are documented in their related section.
|
||||||
|
|
||||||
You can browse the available features in the menu, the [providers](../providers/overview.md), or the [routing section](../routing/overview.md) to see them in action.
|
You can browse the available features in the menu, the [providers](../providers/overview.md), or the [routing section](../routing/overview.md) to see them in action.
|
||||||
|
|
||||||
|
{!traefik-for-business-applications.md!}
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
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
|
# FAQ
|
||||||
|
|
||||||
## Why is Traefik Answering `XXX` HTTP Response Status Code?
|
## Why is Traefik Answering `XXX` HTTP Response Status Code?
|
||||||
@@ -125,7 +130,7 @@ http:
|
|||||||
the principle of the above example above (a catchall router) still stands,
|
the principle of the above example above (a catchall router) still stands,
|
||||||
but the `unavailable` service should be adapted to fit such a need.
|
but the `unavailable` service should be adapted to fit such a need.
|
||||||
|
|
||||||
## Why Is My TLS Certificate Not Reloaded When Its Contents Change ?
|
## Why Is My TLS Certificate Not Reloaded When Its Contents Change?
|
||||||
|
|
||||||
With the file provider,
|
With the file provider,
|
||||||
a configuration update is only triggered when one of the [watched](../providers/file.md#provider-configuration) configuration files is modified.
|
a configuration update is only triggered when one of the [watched](../providers/file.md#provider-configuration) configuration files is modified.
|
||||||
@@ -137,3 +142,42 @@ a configuration update is _not_ triggered.
|
|||||||
To take into account the new certificate contents, the update of the dynamic configuration must be forced.
|
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,
|
One way to achieve that, is to trigger a file notification,
|
||||||
for example, by using the `touch` command on the configuration file.
|
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.
|
||||||
|
|
||||||
|
## What does the "field not found" error mean?
|
||||||
|
|
||||||
|
```shell
|
||||||
|
error: field not found, node: -badField-
|
||||||
|
```
|
||||||
|
|
||||||
|
The "field not found" error occurs, when an unknown property is encountered in the dynamic or static configuration.
|
||||||
|
|
||||||
|
One easy way to check whether a configuration file is well-formed, is to validate it with:
|
||||||
|
|
||||||
|
- [JSON Schema of the static configuration](https://json.schemastore.org/traefik-v2.json)
|
||||||
|
- [JSON Schema of the dynamic configuration](https://json.schemastore.org/traefik-v2-file-provider.json)
|
||||||
|
|
||||||
|
## Why are some resources (routers, middlewares, services...) not created/applied?
|
||||||
|
|
||||||
|
As a common tip, if a resource is dropped/not created by Traefik after the dynamic configuration was evaluated,
|
||||||
|
one should look for an error in the logs.
|
||||||
|
|
||||||
|
If found, the error obviously confirms that something went wrong while creating the resource,
|
||||||
|
and the message should help in figuring out the mistake(s) in the configuration, and how to fix it.
|
||||||
|
|
||||||
|
When using the file provider,
|
||||||
|
one easy way to check if the dynamic configuration is well-formed is to validate it with the [JSON Schema of the dynamic configuration](https://json.schemastore.org/traefik-v2-file-provider.json).
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
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
|
# Install Traefik
|
||||||
|
|
||||||
You can install Traefik with the following flavors:
|
You can install Traefik with the following flavors:
|
||||||
@@ -11,12 +16,12 @@ You can install Traefik with the following flavors:
|
|||||||
|
|
||||||
Choose one of the [official Docker images](https://hub.docker.com/_/traefik) and run it with one sample configuration file:
|
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.4/traefik.sample.yml)
|
* [YAML](https://raw.githubusercontent.com/traefik/traefik/v2.9/traefik.sample.yml)
|
||||||
* [TOML](https://raw.githubusercontent.com/traefik/traefik/v2.4/traefik.sample.toml)
|
* [TOML](https://raw.githubusercontent.com/traefik/traefik/v2.9/traefik.sample.toml)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -d -p 8080:8080 -p 80:80 \
|
docker run -d -p 8080:8080 -p 80:80 \
|
||||||
-v $PWD/traefik.yml:/etc/traefik/traefik.yml traefik:v2.4
|
-v $PWD/traefik.yml:/etc/traefik/traefik.yml traefik:v3.0
|
||||||
```
|
```
|
||||||
|
|
||||||
For more details, go to the [Docker provider documentation](../providers/docker.md)
|
For more details, go to the [Docker provider documentation](../providers/docker.md)
|
||||||
@@ -24,7 +29,7 @@ For more details, go to the [Docker provider documentation](../providers/docker.
|
|||||||
!!! tip
|
!!! tip
|
||||||
|
|
||||||
* Prefer a fixed version than the latest that could be an unexpected version.
|
* Prefer a fixed version than the latest that could be an unexpected version.
|
||||||
ex: `traefik:v2.1.4`
|
ex: `traefik:v3.0`
|
||||||
* Docker images are based from the [Alpine Linux Official image](https://hub.docker.com/_/alpine).
|
* 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.
|
* Any orchestrator using docker images can fetch the official Traefik docker image.
|
||||||
|
|
||||||
@@ -39,13 +44,13 @@ Traefik can be installed in Kubernetes using the Helm chart from <https://github
|
|||||||
|
|
||||||
Ensure that the following requirements are met:
|
Ensure that the following requirements are met:
|
||||||
|
|
||||||
* Kubernetes 1.14+
|
* Kubernetes 1.16+
|
||||||
* Helm version 3.x is [installed](https://helm.sh/docs/intro/install/)
|
* Helm version 3.9+ is [installed](https://helm.sh/docs/intro/install/)
|
||||||
|
|
||||||
Add Traefik's chart repository to Helm:
|
Add Traefik Labs chart repository to Helm:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
helm repo add traefik https://helm.traefik.io/traefik
|
helm repo add traefik https://traefik.github.io/charts
|
||||||
```
|
```
|
||||||
|
|
||||||
You can update the chart repository by running:
|
You can update the chart repository by running:
|
||||||
@@ -63,6 +68,9 @@ helm install traefik traefik/traefik
|
|||||||
!!! tip "Helm Features"
|
!!! tip "Helm Features"
|
||||||
|
|
||||||
All [Helm features](https://helm.sh/docs/intro/using_helm/) are supported.
|
All [Helm features](https://helm.sh/docs/intro/using_helm/) are supported.
|
||||||
|
|
||||||
|
Examples are provided [here](https://github.com/traefik/traefik-helm-chart/blob/master/EXAMPLES.md).
|
||||||
|
|
||||||
For instance, installing the chart in a dedicated namespace:
|
For instance, installing the chart in a dedicated namespace:
|
||||||
|
|
||||||
```bash tab="Install in a Dedicated Namespace"
|
```bash tab="Install in a Dedicated Namespace"
|
||||||
@@ -78,8 +86,7 @@ helm install traefik traefik/traefik
|
|||||||
as with [any helm chart](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing).
|
as with [any helm chart](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing).
|
||||||
{: #helm-custom-values }
|
{: #helm-custom-values }
|
||||||
|
|
||||||
The values are not (yet) documented, but are self-explanatory:
|
All parameters are documented in the default [`values.yaml`](https://github.com/traefik/traefik-helm-chart/blob/master/traefik/values.yaml).
|
||||||
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`.
|
You can also set Traefik command line flags using `additionalArguments`.
|
||||||
Example of installation with logging set to `DEBUG`:
|
Example of installation with logging set to `DEBUG`:
|
||||||
@@ -101,13 +108,13 @@ helm install traefik traefik/traefik
|
|||||||
|
|
||||||
This HelmChart does not expose the Traefik dashboard by default, for security concerns.
|
This HelmChart does not expose the Traefik dashboard by default, for security concerns.
|
||||||
Thus, there are multiple ways to expose the dashboard.
|
Thus, there are multiple ways to expose the dashboard.
|
||||||
For instance, the dashboard access could be achieved through a port-forward :
|
For instance, the dashboard access could be achieved through a port-forward:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
kubectl port-forward $(kubectl get pods --selector "app.kubernetes.io/name=traefik" --output=name) 9000:9000
|
kubectl port-forward $(kubectl get pods --selector "app.kubernetes.io/name=traefik" --output=name) 9000:9000
|
||||||
```
|
```
|
||||||
|
|
||||||
Accessible with the url: http://127.0.0.1:9000/dashboard/
|
It can then be reached at: `http://127.0.0.1:9000/dashboard/`
|
||||||
|
|
||||||
Another way would be to apply your own configuration, for instance,
|
Another way would be to apply your own configuration, for instance,
|
||||||
by defining and applying an IngressRoute CRD (`kubectl apply -f dashboard.yaml`):
|
by defining and applying an IngressRoute CRD (`kubectl apply -f dashboard.yaml`):
|
||||||
@@ -173,3 +180,5 @@ And run it:
|
|||||||
## Compile your Binary from the Sources
|
## Compile your Binary from the Sources
|
||||||
|
|
||||||
All the details are available in the [Contributing Guide](../contributing/building-testing.md)
|
All the details are available in the [Contributing Guide](../contributing/building-testing.md)
|
||||||
|
|
||||||
|
{!traefik-for-business-applications.md!}
|
||||||
|
|||||||
318
docs/content/getting-started/quick-start-with-kubernetes.md
Normal file
318
docs/content/getting-started/quick-start-with-kubernetes.md
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik Getting Started With Kubernetes"
|
||||||
|
description: "Looking to get started with Traefik Proxy? Read the technical documentation to learn a simple use case that leverages Kubernetes."
|
||||||
|
---
|
||||||
|
|
||||||
|
# Quick Start
|
||||||
|
|
||||||
|
A Simple Use Case of Traefik Proxy and Kubernetes
|
||||||
|
{: .subtitle }
|
||||||
|
|
||||||
|
This guide is an introduction to using Traefik Proxy in a Kubernetes environment.
|
||||||
|
The objective is to learn how to run an application behind a Traefik reverse proxy in Kubernetes.
|
||||||
|
It presents and explains the basic blocks required to start with Traefik such as Ingress Controller, Ingresses, Deployments, static, and dynamic configuration.
|
||||||
|
|
||||||
|
## Permissions and Accesses
|
||||||
|
|
||||||
|
Traefik uses the Kubernetes API to discover running services.
|
||||||
|
|
||||||
|
In order to use the Kubernetes API, Traefik needs some permissions.
|
||||||
|
This [permission mechanism](https://kubernetes.io/docs/reference/access-authn-authz/rbac/) is based on roles defined by the cluster administrator.
|
||||||
|
The role is then bound to an account used by an application, in this case, Traefik Proxy.
|
||||||
|
|
||||||
|
The first step is to create the role.
|
||||||
|
The [`ClusterRole`](https://kubernetes.io/docs/reference/kubernetes-api/authorization-resources/cluster-role-v1/#ClusterRole) resource enumerates the resources and actions available for the role.
|
||||||
|
In a file called `00-role.yml`, put the following `ClusterRole`:
|
||||||
|
|
||||||
|
```yaml tab="00-role.yml"
|
||||||
|
kind: ClusterRole
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: traefik-role
|
||||||
|
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- services
|
||||||
|
- endpoints
|
||||||
|
- secrets
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- extensions
|
||||||
|
- networking.k8s.io
|
||||||
|
resources:
|
||||||
|
- ingresses
|
||||||
|
- ingressclasses
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- extensions
|
||||||
|
- networking.k8s.io
|
||||||
|
resources:
|
||||||
|
- ingresses/status
|
||||||
|
verbs:
|
||||||
|
- update
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! info "You can find the reference for this file [there](../../reference/dynamic-configuration/kubernetes-crd/#rbac)."
|
||||||
|
|
||||||
|
The next step is to create a dedicated service account for Traefik.
|
||||||
|
In a file called `00-account.yml`, put the following [`ServiceAccount`](https://kubernetes.io/docs/reference/kubernetes-api/authentication-resources/service-account-v1/#ServiceAccount) resource:
|
||||||
|
|
||||||
|
```yaml tab="00-account.yml"
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: traefik-account
|
||||||
|
```
|
||||||
|
|
||||||
|
And then, bind the role on the account to apply the permissions and rules on the latter. In a file called `01-role-binding.yml`, put the
|
||||||
|
following [`ClusterRoleBinding`](https://kubernetes.io/docs/reference/kubernetes-api/authorization-resources/cluster-role-binding-v1/#ClusterRoleBinding) resource:
|
||||||
|
|
||||||
|
```yaml tab="01-role-binding.yml"
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
metadata:
|
||||||
|
name: traefik-role-binding
|
||||||
|
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: traefik-role
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: traefik-account
|
||||||
|
namespace: default # Using "default" because we did not specify a namespace when creating the ClusterAccount.
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! info "`roleRef` is the Kubernetes reference to the role created in `00-role.yml`."
|
||||||
|
|
||||||
|
!!! info "`subjects` is the list of accounts reference."
|
||||||
|
|
||||||
|
In this guide, it only contains the account created in `00-account.yml`
|
||||||
|
|
||||||
|
## Deployment and Exposition
|
||||||
|
|
||||||
|
!!! info "This section can be managed with the help of the [Traefik Helm chart](../install-traefik/#use-the-helm-chart)."
|
||||||
|
|
||||||
|
The [ingress controller](https://traefik.io/glossary/kubernetes-ingress-and-ingress-controller-101/#what-is-a-kubernetes-ingress-controller)
|
||||||
|
is a software that runs in the same way as any other application on a cluster.
|
||||||
|
To start Traefik on the Kubernetes cluster,
|
||||||
|
a [`Deployment`](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/deployment-v1/) resource must exist to describe how to configure
|
||||||
|
and scale containers horizontally to support larger workloads.
|
||||||
|
|
||||||
|
Start by creating a file called `02-traefik.yml` and paste the following `Deployment` resource:
|
||||||
|
|
||||||
|
```yaml tab="02-traefik.yml"
|
||||||
|
kind: Deployment
|
||||||
|
apiVersion: apps/v1
|
||||||
|
metadata:
|
||||||
|
name: traefik-deployment
|
||||||
|
labels:
|
||||||
|
app: traefik
|
||||||
|
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: traefik
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: traefik
|
||||||
|
spec:
|
||||||
|
serviceAccountName: traefik-account
|
||||||
|
containers:
|
||||||
|
- name: traefik
|
||||||
|
image: traefik:v3.0
|
||||||
|
args:
|
||||||
|
- --api.insecure
|
||||||
|
- --providers.kubernetesingress
|
||||||
|
ports:
|
||||||
|
- name: web
|
||||||
|
containerPort: 80
|
||||||
|
- name: dashboard
|
||||||
|
containerPort: 8080
|
||||||
|
```
|
||||||
|
|
||||||
|
The deployment contains an important attribute for customizing Traefik: `args`.
|
||||||
|
These arguments are the static configuration for Traefik.
|
||||||
|
From here, it is possible to enable the dashboard,
|
||||||
|
configure entry points,
|
||||||
|
select dynamic configuration providers,
|
||||||
|
and [more](../reference/static-configuration/cli.md)...
|
||||||
|
|
||||||
|
In this deployment,
|
||||||
|
the static configuration enables the Traefik dashboard,
|
||||||
|
and uses Kubernetes native Ingress resources as router definitions to route incoming requests.
|
||||||
|
|
||||||
|
!!! info "When there is no entry point in the static configuration"
|
||||||
|
|
||||||
|
Traefik creates a default one called `web` using the port `80` routing HTTP requests.
|
||||||
|
|
||||||
|
!!! info "When enabling the [`api.insecure`](../../operations/api/#insecure) mode, Traefik exposes the dashboard on the port `8080`."
|
||||||
|
|
||||||
|
A deployment manages scaling and then can create lots of containers, called [Pods](https://kubernetes.io/docs/concepts/workloads/pods/).
|
||||||
|
Each Pod is configured following the `spec` field in the deployment.
|
||||||
|
Given that, a Deployment can run multiple Traefik Proxy Pods,
|
||||||
|
a piece is required to forward the traffic to any of the instance:
|
||||||
|
namely a [`Service`](https://kubernetes.io/docs/reference/kubernetes-api/service-resources/service-v1/#Service).
|
||||||
|
Create a file called `02-traefik-services.yml` and insert the two `Service` resources:
|
||||||
|
|
||||||
|
```yaml tab="02-traefik-services.yml"
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: traefik-dashboard-service
|
||||||
|
|
||||||
|
spec:
|
||||||
|
type: LoadBalancer
|
||||||
|
ports:
|
||||||
|
- port: 8080
|
||||||
|
targetPort: dashboard
|
||||||
|
selector:
|
||||||
|
app: traefik
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: traefik-web-service
|
||||||
|
|
||||||
|
spec:
|
||||||
|
type: LoadBalancer
|
||||||
|
ports:
|
||||||
|
- targetPort: web
|
||||||
|
port: 80
|
||||||
|
selector:
|
||||||
|
app: traefik
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! warning "It is possible to expose a service in different ways."
|
||||||
|
|
||||||
|
Depending on your working environment and use case, the `spec.type` might change.
|
||||||
|
It is strongly recommended to understand the available [service types](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) before proceeding to the next step.
|
||||||
|
|
||||||
|
It is now time to apply those files on your cluster to start Traefik.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
kubectl apply -f 00-role.yml \
|
||||||
|
-f 00-account.yml \
|
||||||
|
-f 01-role-binding.yml \
|
||||||
|
-f 02-traefik.yml \
|
||||||
|
-f 02-traefik-services.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Proxying applications
|
||||||
|
|
||||||
|
The only part still missing is the business application behind the reverse proxy.
|
||||||
|
For this guide, we use the example application [traefik/whoami](https://github.com/traefik/whoami),
|
||||||
|
but the principles are applicable to any other application.
|
||||||
|
|
||||||
|
The `whoami` application is a simple HTTP server running on port 80 which answers host-related information to the incoming requests.
|
||||||
|
As usual, start by creating a file called `03-whoami.yml` and paste the following `Deployment` resource:
|
||||||
|
|
||||||
|
```yaml tab="03-whoami.yml"
|
||||||
|
kind: Deployment
|
||||||
|
apiVersion: apps/v1
|
||||||
|
metadata:
|
||||||
|
name: whoami
|
||||||
|
labels:
|
||||||
|
app: whoami
|
||||||
|
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: whoami
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: whoami
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: whoami
|
||||||
|
image: traefik/whoami
|
||||||
|
ports:
|
||||||
|
- name: web
|
||||||
|
containerPort: 80
|
||||||
|
```
|
||||||
|
|
||||||
|
And continue by creating the following `Service` resource in a file called `03-whoami-services.yml`:
|
||||||
|
|
||||||
|
```yaml tab="03-whoami-services.yml"
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: whoami
|
||||||
|
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: web
|
||||||
|
port: 80
|
||||||
|
targetPort: web
|
||||||
|
|
||||||
|
selector:
|
||||||
|
app: whoami
|
||||||
|
```
|
||||||
|
|
||||||
|
Thanks to the Kubernetes API,
|
||||||
|
Traefik is notified when an Ingress resource is created, updated, or deleted.
|
||||||
|
This makes the process dynamic.
|
||||||
|
The ingresses are, in a way, the [dynamic configuration](../../providers/kubernetes-ingress/) for Traefik.
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
|
||||||
|
Find more information on [ingress controller](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/),
|
||||||
|
and [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) in the official Kubernetes documentation.
|
||||||
|
|
||||||
|
Create a file called `04-whoami-ingress.yml` and insert the `Ingress` resource:
|
||||||
|
|
||||||
|
```yaml tab="04-whoami-ingress.yml"
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: whoami-ingress
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- http:
|
||||||
|
paths:
|
||||||
|
- path: /
|
||||||
|
pathType: Prefix
|
||||||
|
backend:
|
||||||
|
service:
|
||||||
|
name: whoami
|
||||||
|
port:
|
||||||
|
name: web
|
||||||
|
```
|
||||||
|
|
||||||
|
This `Ingress` configures Traefik to redirect any incoming requests starting with `/` to the `whoami:80` service.
|
||||||
|
|
||||||
|
At this point, all the configurations are ready.
|
||||||
|
It is time to apply those new files:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
kubectl apply -f 03-whoami.yml \
|
||||||
|
-f 03-whoami-services.yml \
|
||||||
|
-f 04-whoami-ingress.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you should be able to access the `whoami` application and the Traefik dashboard.
|
||||||
|
Load the dashboard on a web browser: [`http://localhost:8080`](http://localhost:8080).
|
||||||
|
|
||||||
|
And now access the `whoami` application:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl -v http://localhost/
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! question "Going further"
|
||||||
|
|
||||||
|
- [Filter the ingresses](../providers/kubernetes-ingress.md#ingressclass) to use with [IngressClass](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class)
|
||||||
|
- Use [IngressRoute CRD](../providers/kubernetes-crd.md)
|
||||||
|
- Protect [ingresses with TLS](../routing/providers/kubernetes-ingress.md#enabling-tls-via-annotations)
|
||||||
@@ -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
|
# Quick Start
|
||||||
|
|
||||||
A Simple Use Case Using Docker
|
A Simple Use Case Using Docker
|
||||||
@@ -15,7 +20,7 @@ version: '3'
|
|||||||
services:
|
services:
|
||||||
reverse-proxy:
|
reverse-proxy:
|
||||||
# The official v2 Traefik docker image
|
# The official v2 Traefik docker image
|
||||||
image: traefik:v2.4
|
image: traefik:v3.0
|
||||||
# Enables the web UI and tells Traefik to listen to docker
|
# Enables the web UI and tells Traefik to listen to docker
|
||||||
command: --api.insecure=true --providers.docker
|
command: --api.insecure=true --providers.docker
|
||||||
ports:
|
ports:
|
||||||
@@ -36,7 +41,7 @@ Start your `reverse-proxy` with the following command:
|
|||||||
docker-compose up -d reverse-proxy
|
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
|
## Traefik Detects New Services and Creates the Route for You
|
||||||
|
|
||||||
@@ -45,7 +50,12 @@ Now that we have a Traefik instance up and running, we will deploy new services.
|
|||||||
Edit your `docker-compose.yml` file and add the following at the end of your file.
|
Edit your `docker-compose.yml` file and add the following at the end of your file.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# ...
|
version: '3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
whoami:
|
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
|
image: traefik/whoami
|
||||||
@@ -61,7 +71,7 @@ Start the `whoami` service with the following command:
|
|||||||
docker-compose up -d whoami
|
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)
|
When Traefik detects new services, it creates the corresponding routes so you can call them ... _let's see!_ (Here, we're using curl)
|
||||||
|
|
||||||
@@ -85,7 +95,7 @@ Run more instances of your `whoami` service with the following command:
|
|||||||
docker-compose up -d --scale whoami=2
|
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:
|
Finally, see that Traefik load-balances between the two instances of your service by running the following command twice:
|
||||||
|
|
||||||
@@ -108,4 +118,7 @@ IP: 172.27.0.4
|
|||||||
```
|
```
|
||||||
|
|
||||||
!!! question "Where to Go Next?"
|
!!! question "Where to Go Next?"
|
||||||
|
|
||||||
Now that you have a basic understanding of how Traefik can automatically create the routes to your services and load balance them, it is time to dive into [the documentation](/) and let Traefik work for you!
|
Now that you have a basic understanding of how Traefik can automatically create the routes to your services and load balance them, it is time to dive into [the documentation](/) and let Traefik work for you!
|
||||||
|
|
||||||
|
{!traefik-for-business-applications.md!}
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
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
|
# Let's Encrypt
|
||||||
|
|
||||||
Automatic HTTPS
|
Automatic HTTPS
|
||||||
@@ -23,7 +28,9 @@ Certificates are requested for domain names retrieved from the router's [dynamic
|
|||||||
|
|
||||||
You can read more about this retrieval mechanism in the following section: [ACME Domain Definition](#domain-definition).
|
You can read more about this retrieval mechanism in the following section: [ACME Domain Definition](#domain-definition).
|
||||||
|
|
||||||
!!! important "Defining a certificates resolver does not result in all routers automatically using it. Each router that is supposed to use the resolver must [reference](../routing/routers/index.md#certresolver) it."
|
!!! 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"
|
??? note "Configuration Reference"
|
||||||
|
|
||||||
@@ -114,7 +121,7 @@ Please check the [configuration examples below](#configuration-examples) for mor
|
|||||||
--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web
|
--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! important "Defining a certificates resolver does not result in all routers automatically using it. Each router that is supposed to use the resolver must [reference](../routing/routers/index.md#certresolver) it."
|
!!! 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"
|
??? example "Single Domain from Router's Rule Example"
|
||||||
|
|
||||||
@@ -140,7 +147,11 @@ Please check the [configuration examples below](#configuration-examples) for mor
|
|||||||
|
|
||||||
Traefik automatically tracks the expiry date of ACME certificates it generates.
|
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 renew it automatically.
|
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 ""
|
!!! 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.
|
Certificates that are no longer used may still be renewed, as Traefik does not currently check if the certificate is being used before renewing.
|
||||||
@@ -154,7 +165,9 @@ When using LetsEncrypt with kubernetes, there are some known caveats with both t
|
|||||||
|
|
||||||
## The Different ACME Challenges
|
## The Different ACME Challenges
|
||||||
|
|
||||||
!!! important "Defining a certificates resolver does not result in all routers automatically using it. Each router that is supposed to use the resolver must [reference](../routing/routers/index.md#certresolver) it."
|
!!! 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`
|
### `tlsChallenge`
|
||||||
|
|
||||||
@@ -280,104 +293,124 @@ For example, `CF_API_EMAIL_FILE=/run/secrets/traefik_cf-api-email` could be used
|
|||||||
|
|
||||||
For complete details, refer to your provider's _Additional configuration_ link.
|
For complete details, refer to your provider's _Additional configuration_ link.
|
||||||
|
|
||||||
| Provider Name | Provider Code | Environment Variables | |
|
| 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) |
|
| [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) |
|
| [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) |
|
||||||
| [ArvanCloud](https://www.arvancloud.com/en) | `arvancloud` | `ARVANCLOUD_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/arvancloud) |
|
| [all-inkl](https://all-inkl.com) | `allinkl` | `ALL_INKL_LOGIN`, `ALL_INKL_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/allinkl) |
|
||||||
| [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) |
|
| [ArvanCloud](https://www.arvancloud.com/en) | `arvancloud` | `ARVANCLOUD_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/arvancloud) |
|
||||||
| [Autodns](https://www.internetx.com/domains/autodns/) | `autodns` | `AUTODNS_API_USER`, `AUTODNS_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/autodns) |
|
| [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) |
|
||||||
| [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) |
|
| [Autodns](https://www.internetx.com/domains/autodns/) | `autodns` | `AUTODNS_API_USER`, `AUTODNS_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/autodns) |
|
||||||
| [Bindman](https://github.com/labbsr0x/bindman-dns-webhook) | `bindman` | `BINDMAN_MANAGER_ADDRESS` | [Additional configuration](https://go-acme.github.io/lego/dns/bindman) |
|
| [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) |
|
| [Bindman](https://github.com/labbsr0x/bindman-dns-webhook) | `bindman` | `BINDMAN_MANAGER_ADDRESS` | [Additional configuration](https://go-acme.github.io/lego/dns/bindman) |
|
||||||
| [Checkdomain](https://www.checkdomain.de/) | `checkdomain` | `CHECKDOMAIN_TOKEN`, | [Additional configuration](https://go-acme.github.io/lego/dns/checkdomain/) |
|
| [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) |
|
||||||
| [CloudDNS](https://vshosting.eu/) | `clouddns` | `CLOUDDNS_CLIENT_ID`, `CLOUDDNS_EMAIL`, `CLOUDDNS_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/clouddns) |
|
| [Checkdomain](https://www.checkdomain.de/) | `checkdomain` | `CHECKDOMAIN_TOKEN`, | [Additional configuration](https://go-acme.github.io/lego/dns/checkdomain/) |
|
||||||
| [ClouDNS](https://www.cloudns.net/) | `cloudns` | `CLOUDNS_AUTH_ID`, `CLOUDNS_AUTH_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudns) |
|
| [Civo](https://www.civo.com/) | `civo` | `CIVO_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/civo) |
|
||||||
| [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) |
|
| [CloudDNS](https://vshosting.eu/) | `clouddns` | `CLOUDDNS_CLIENT_ID`, `CLOUDDNS_EMAIL`, `CLOUDDNS_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/clouddns) |
|
||||||
| [CloudXNS](https://www.cloudxns.net) | `cloudxns` | `CLOUDXNS_API_KEY`, `CLOUDXNS_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudxns) |
|
| [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) |
|
||||||
| [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) |
|
| [ClouDNS](https://www.cloudns.net/) | `cloudns` | `CLOUDNS_AUTH_ID`, `CLOUDNS_AUTH_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudns) |
|
||||||
| [Constellix](https://constellix.com) | `constellix` | `CONSTELLIX_API_KEY`, `CONSTELLIX_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/constellix) |
|
| [CloudXNS](https://www.cloudxns.net) | `cloudxns` | `CLOUDXNS_API_KEY`, `CLOUDXNS_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudxns) |
|
||||||
| [deSEC](https://desec.io) | `desec` | `DESEC_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/desec) |
|
| [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) |
|
| [Constellix](https://constellix.com) | `constellix` | `CONSTELLIX_API_KEY`, `CONSTELLIX_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/constellix) |
|
||||||
| [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/dnsimple) |
|
| [deSEC](https://desec.io) | `desec` | `DESEC_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/desec) |
|
||||||
| [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) |
|
| [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/digitalocean) |
|
||||||
| [DNSPod](https://www.dnspod.com/) | `dnspod` | `DNSPOD_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/dnspod) |
|
| [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) |
|
||||||
| [Domain Offensive (do.de)](https://www.do.de/) | `dode` | `DODE_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/dode) |
|
| [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/dnsimple) |
|
||||||
| [Domeneshop](https://domene.shop) | `domeneshop` | `DOMENESHOP_API_TOKEN`, `DOMENESHOP_API_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/domeneshop) |
|
| [DNSPod](https://www.dnspod.com/) | `dnspod` | `DNSPOD_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/dnspod) |
|
||||||
| [DreamHost](https://www.dreamhost.com/) | `dreamhost` | `DREAMHOST_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/dreamhost) |
|
| [Domain Offensive (do.de)](https://www.do.de/) | `dode` | `DODE_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/dode) |
|
||||||
| [Duck DNS](https://www.duckdns.org/) | `duckdns` | `DUCKDNS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/duckdns) |
|
| [Domeneshop](https://domene.shop) | `domeneshop` | `DOMENESHOP_API_TOKEN`, `DOMENESHOP_API_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/domeneshop) |
|
||||||
| [Dyn](https://dyn.com) | `dyn` | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/dyn) |
|
| [DreamHost](https://www.dreamhost.com/) | `dreamhost` | `DREAMHOST_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/dreamhost) |
|
||||||
| [Dynu](https://www.dynu.com) | `dynu` | `DYNU_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/dynu) |
|
| [Duck DNS](https://www.duckdns.org/) | `duckdns` | `DUCKDNS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/duckdns) |
|
||||||
| [EasyDNS](https://easydns.com/) | `easydns` | `EASYDNS_TOKEN`, `EASYDNS_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/easydns) |
|
| [Dyn](https://dyn.com) | `dyn` | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/dyn) |
|
||||||
| [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) |
|
| [Dynu](https://www.dynu.com) | `dynu` | `DYNU_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/dynu) |
|
||||||
| External Program | `exec` | `EXEC_PATH` | [Additional configuration](https://go-acme.github.io/lego/dns/exec) |
|
| [EasyDNS](https://easydns.com/) | `easydns` | `EASYDNS_TOKEN`, `EASYDNS_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/easydns) |
|
||||||
| [Exoscale](https://www.exoscale.com) | `exoscale` | `EXOSCALE_API_KEY`, `EXOSCALE_API_SECRET`, `EXOSCALE_ENDPOINT` | [Additional configuration](https://go-acme.github.io/lego/dns/exoscale) |
|
| [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) |
|
||||||
| [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) |
|
| [Epik](https://www.epik.com) | `epik` | `EPIK_SIGNATURE` | [Additional configuration](https://go-acme.github.io/lego/dns/epik) |
|
||||||
| [Gandi](https://www.gandi.net) | `gandi` | `GANDI_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/gandi) |
|
| [Exoscale](https://www.exoscale.com) | `exoscale` | `EXOSCALE_API_KEY`, `EXOSCALE_API_SECRET`, `EXOSCALE_ENDPOINT` | [Additional configuration](https://go-acme.github.io/lego/dns/exoscale) |
|
||||||
| [Gandi v5](http://doc.livedns.gandi.net) | `gandiv5` | `GANDIV5_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/gandiv5) |
|
| [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) |
|
||||||
| [Glesys](https://glesys.com/) | `glesys` | `GLESYS_API_USER`, `GLESYS_API_KEY`, `GLESYS_DOMAIN` | [Additional configuration](https://go-acme.github.io/lego/dns/glesys) |
|
| [Freemyip.com](https://freemyip.com) | `freemyip` | `FREEMYIP_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/freemyip) |
|
||||||
| [GoDaddy](https://godaddy.com/) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/godaddy) |
|
| [G-Core Lab](https://gcorelabs.com/dns/) | `gcore` | `GCORE_PERMANENT_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/gcore) |
|
||||||
| [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) |
|
| [Gandi v5](https://doc.livedns.gandi.net) | `gandiv5` | `GANDIV5_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/gandiv5) |
|
||||||
| [Hetzner](https://hetzner.com) | `hetzner` | `HETZNER_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/hetzner) |
|
| [Gandi](https://www.gandi.net) | `gandi` | `GANDI_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/gandi) |
|
||||||
| [hosting.de](https://www.hosting.de) | `hostingde` | `HOSTINGDE_API_KEY`, `HOSTINGDE_ZONE_NAME` | [Additional configuration](https://go-acme.github.io/lego/dns/hostingde) |
|
| [Glesys](https://glesys.com/) | `glesys` | `GLESYS_API_USER`, `GLESYS_API_KEY`, `GLESYS_DOMAIN` | [Additional configuration](https://go-acme.github.io/lego/dns/glesys) |
|
||||||
| HTTP request | `httpreq` | `HTTPREQ_ENDPOINT`, `HTTPREQ_MODE`, `HTTPREQ_USERNAME`, `HTTPREQ_PASSWORD` [^1] | [Additional configuration](https://go-acme.github.io/lego/dns/httpreq) |
|
| [GoDaddy](https://godaddy.com/) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/godaddy) |
|
||||||
| [HyperOne](https://www.hyperone.com) | `hyperone` | `HYPERONE_PASSPORT_LOCATION`, `HYPERONE_LOCATION_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/hyperone) |
|
| [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) |
|
||||||
| [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) |
|
| [Hetzner](https://hetzner.com) | `hetzner` | `HETZNER_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/hetzner) |
|
||||||
| [Infoblox](https://www.infoblox.com/) | `infoblox` | `INFOBLOX_USER`, `INFOBLOX_PASSWORD`, `INFOBLOX_HOST` | [Additional configuration](https://go-acme.github.io/lego/dns/infoblox) |
|
| [hosting.de](https://www.hosting.de) | `hostingde` | `HOSTINGDE_API_KEY`, `HOSTINGDE_ZONE_NAME` | [Additional configuration](https://go-acme.github.io/lego/dns/hostingde) |
|
||||||
| [Infomaniak](https://www.infomaniak.com) | `infomaniak` | `INFOMANIAK_ACCESS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/infomaniak) |
|
| [Hosttech](https://www.hosttech.eu) | `hosttech` | `HOSTTECH_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/hosttech) |
|
||||||
| [INWX](https://www.inwx.de/en) | `inwx` | `INWX_USERNAME`, `INWX_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/inwx) |
|
| [Hurricane Electric](https://dns.he.net) | `hurricane` | `HURRICANE_TOKENS` [^6] | [Additional configuration](https://go-acme.github.io/lego/dns/hurricane) |
|
||||||
| [ionos](https://ionos.com/) | `ionos` | `IONOS_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/ionos) |
|
| [HyperOne](https://www.hyperone.com) | `hyperone` | `HYPERONE_PASSPORT_LOCATION`, `HYPERONE_LOCATION_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/hyperone) |
|
||||||
| [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) |
|
| [IBM Cloud (SoftLayer)](https://www.ibm.com/cloud/) | `ibmcloud` | `SOFTLAYER_USERNAME`, `SOFTLAYER_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/ibmcloud) |
|
||||||
| [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) |
|
| [IIJ DNS Platform Service](https://www.iij.ad.jp) | `iijdpf` | `IIJ_DPF_API_TOKEN` , `IIJ_DPF_DPM_SERVICE_CODE` | [Additional configuration](https://go-acme.github.io/lego/dns/iijdpf) |
|
||||||
| [Linode v4](https://www.linode.com) | `linode` | `LINODE_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/linode) |
|
| [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) |
|
||||||
| [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) |
|
| [Infoblox](https://www.infoblox.com/) | `infoblox` | `INFOBLOX_USERNAME`, `INFOBLOX_PASSWORD`, `INFOBLOX_HOST` | [Additional configuration](https://go-acme.github.io/lego/dns/infoblox) |
|
||||||
| [Loopia](https://loopia.com/) | `loopia` | `LOOPIA_API_PASSWORD`, `LOOPIA_API_USER` | [Additional configuration](https://go-acme.github.io/lego/dns/loopia) |
|
| [Infomaniak](https://www.infomaniak.com) | `infomaniak` | `INFOMANIAK_ACCESS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/infomaniak) |
|
||||||
| [LuaDNS](https://luadns.com) | `luadns` | `LUADNS_API_USERNAME`, `LUADNS_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/luadns) |
|
| [Internet.bs](https://internetbs.net) | `internetbs` | `INTERNET_BS_API_KEY`, `INTERNET_BS_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/internetbs) |
|
||||||
| manual | `manual` | none, but you need to run Traefik interactively [^4], turn on debug log to see instructions and press <kbd>Enter</kbd>. | |
|
| [INWX](https://www.inwx.de/en) | `inwx` | `INWX_USERNAME`, `INWX_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/inwx) |
|
||||||
| [MyDNS.jp](https://www.mydns.jp/) | `mydnsjp` | `MYDNSJP_MASTER_ID`, `MYDNSJP_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/mydnsjp) |
|
| [ionos](https://ionos.com/) | `ionos` | `IONOS_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/ionos) |
|
||||||
| [Mythic Beasts](https://www.mythic-beasts.com) | `mythicbeasts` | `MYTHICBEASTS_USER_NAME`, `MYTHICBEASTS_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/mythicbeasts) |
|
| [iwantmyname](https://iwantmyname.com) | `iwantmyname` | `IWANTMYNAME_USERNAME` , `IWANTMYNAME_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/iwantmyname) |
|
||||||
| [Namecheap](https://www.namecheap.com) | `namecheap` | `NAMECHEAP_API_USER`, `NAMECHEAP_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/namecheap) |
|
| [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) |
|
||||||
| [name.com](https://www.name.com/) | `namedotcom` | `NAMECOM_USERNAME`, `NAMECOM_API_TOKEN`, `NAMECOM_SERVER` | [Additional configuration](https://go-acme.github.io/lego/dns/namedotcom) |
|
| [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) |
|
||||||
| [Namesilo](https://www.namesilo.com/) | `namesilo` | `NAMESILO_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/namesilo) |
|
| [Linode v4](https://www.linode.com) | `linode` | `LINODE_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/linode) |
|
||||||
| [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) |
|
| [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) |
|
||||||
| [Netlify](https://www.netlify.com) | `netlify` | `NETLIFY_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/netlify) |
|
| [Loopia](https://loopia.com/) | `loopia` | `LOOPIA_API_PASSWORD`, `LOOPIA_API_USER` | [Additional configuration](https://go-acme.github.io/lego/dns/loopia) |
|
||||||
| [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) |
|
| [LuaDNS](https://luadns.com) | `luadns` | `LUADNS_API_USERNAME`, `LUADNS_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/luadns) |
|
||||||
| [Njalla](https://njal.la) | `njalla` | `NJALLA_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/njalla) |
|
| [MyDNS.jp](https://www.mydns.jp/) | `mydnsjp` | `MYDNSJP_MASTER_ID`, `MYDNSJP_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/mydnsjp) |
|
||||||
| [NS1](https://ns1.com/) | `ns1` | `NS1_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/ns1) |
|
| [Mythic Beasts](https://www.mythic-beasts.com) | `mythicbeasts` | `MYTHICBEASTS_USER_NAME`, `MYTHICBEASTS_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/mythicbeasts) |
|
||||||
| [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) |
|
| [name.com](https://www.name.com/) | `namedotcom` | `NAMECOM_USERNAME`, `NAMECOM_API_TOKEN`, `NAMECOM_SERVER` | [Additional configuration](https://go-acme.github.io/lego/dns/namedotcom) |
|
||||||
| [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) |
|
| [Namecheap](https://www.namecheap.com) | `namecheap` | `NAMECHEAP_API_USER`, `NAMECHEAP_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/namecheap) |
|
||||||
| [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) |
|
| [Namesilo](https://www.namesilo.com/) | `namesilo` | `NAMESILO_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/namesilo) |
|
||||||
| [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) |
|
| [NearlyFreeSpeech.NET](https://www.nearlyfreespeech.net/) | `nearlyfreespeech` | `NEARLYFREESPEECH_API_KEY`, `NEARLYFREESPEECH_LOGIN` | [Additional configuration](https://go-acme.github.io/lego/dns/nearlyfreespeech) |
|
||||||
| [Porkbun](https://porkbun.com/) | `porkbun` | `PORKBUN_SECRET_API_KEY`, `PORKBUN_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/porkbun) |
|
| [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) |
|
||||||
| [PowerDNS](https://www.powerdns.com) | `pdns` | `PDNS_API_KEY`, `PDNS_API_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/pdns) |
|
| [Netlify](https://www.netlify.com) | `netlify` | `NETLIFY_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/netlify) |
|
||||||
| [Rackspace](https://www.rackspace.com/cloud/dns) | `rackspace` | `RACKSPACE_USER`, `RACKSPACE_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/rackspace) |
|
| [Nicmanager](https://www.nicmanager.com) | `nicmanager` | `NICMANAGER_API_EMAIL`, `NICMANAGER_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/nicmanager) |
|
||||||
| [reg.ru](https://www.reg.ru) | `regru` | `REGRU_USERNAME`, `REGRU_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/regru) |
|
| [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) |
|
||||||
| [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) |
|
| [Njalla](https://njal.la) | `njalla` | `NJALLA_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/njalla) |
|
||||||
| [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) |
|
| [NS1](https://ns1.com/) | `ns1` | `NS1_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/ns1) |
|
||||||
| [RimuHosting](https://rimuhosting.com) | `rimuhosting` | `RIMUHOSTING_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/rimuhosting) |
|
| [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) |
|
||||||
| [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) |
|
| [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) |
|
||||||
| [Scaleway](https://www.scaleway.com) | `scaleway` | `SCALEWAY_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/scaleway) |
|
| [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) |
|
||||||
| [Selectel](https://selectel.ru/en/) | `selectel` | `SELECTEL_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/selectel) |
|
| [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) |
|
||||||
| [Servercow](https://servercow.de) | `servercow` | `SERVERCOW_USERNAME`, `SERVERCOW_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/servercow) |
|
| [Porkbun](https://porkbun.com/) | `porkbun` | `PORKBUN_SECRET_API_KEY`, `PORKBUN_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/porkbun) |
|
||||||
| [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) |
|
| [PowerDNS](https://www.powerdns.com) | `pdns` | `PDNS_API_KEY`, `PDNS_API_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/pdns) |
|
||||||
| [Sonic](https://www.sonic.com/) | `sonic` | `SONIC_USER_ID`, `SONIC_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/sonic) |
|
| [Rackspace](https://www.rackspace.com/cloud/dns) | `rackspace` | `RACKSPACE_USER`, `RACKSPACE_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/rackspace) |
|
||||||
| [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) |
|
| [reg.ru](https://www.reg.ru) | `regru` | `REGRU_USERNAME`, `REGRU_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/regru) |
|
||||||
| [TransIP](https://www.transip.nl/) | `transip` | `TRANSIP_ACCOUNT_NAME`, `TRANSIP_PRIVATE_KEY_PATH` | [Additional configuration](https://go-acme.github.io/lego/dns/transip) |
|
| [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) |
|
||||||
| [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) |
|
| [RimuHosting](https://rimuhosting.com) | `rimuhosting` | `RIMUHOSTING_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/rimuhosting) |
|
||||||
| [Versio](https://www.versio.nl/domeinnamen) | `versio` | `VERSIO_USERNAME`, `VERSIO_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/versio) |
|
| [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) |
|
||||||
| [VinylDNS](https://www.vinyldns.io) | `vinyldns` | `VINYLDNS_ACCESS_KEY`, `VINYLDNS_SECRET_KEY`, `VINYLDNS_HOST` | [Additional configuration](https://go-acme.github.io/lego/dns/vinyldns) |
|
| [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) |
|
||||||
| [Vscale](https://vscale.io/) | `vscale` | `VSCALE_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/vscale) |
|
| [Scaleway](https://www.scaleway.com) | `scaleway` | `SCALEWAY_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/scaleway) |
|
||||||
| [VULTR](https://www.vultr.com) | `vultr` | `VULTR_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/vultr) |
|
| [Selectel](https://selectel.ru/en/) | `selectel` | `SELECTEL_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/selectel) |
|
||||||
| [WEDOS](https://www.wedos.com) | `wedos` | `WEDOS_USERNAME`, `WEDOS_WAPI_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/wedos) |
|
| [Servercow](https://servercow.de) | `servercow` | `SERVERCOW_USERNAME`, `SERVERCOW_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/servercow) |
|
||||||
| [Yandex](https://yandex.com) | `yandex` | `YANDEX_PDD_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/yandex) |
|
| [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) |
|
||||||
| [Zone.ee](https://www.zone.ee) | `zoneee` | `ZONEEE_API_USER`, `ZONEEE_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/zoneee) |
|
| [Sonic](https://www.sonic.com/) | `sonic` | `SONIC_USER_ID`, `SONIC_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/sonic) |
|
||||||
| [Zonomi](https://zonomi.com) | `zonomi` | `ZONOMI_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/zonomi) |
|
| [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.ans.co.uk/cloud-and-infrastructure/dedicated-servers/dns-management/) | `safedns` | `SAFEDNS_AUTH_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/safedns) |
|
||||||
|
| [Variomedia](https://www.variomedia.de/) | `variomedia` | `VARIOMEDIA_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/variomedia) |
|
||||||
|
| [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) |
|
||||||
|
| [Vercel](https://vercel.com) | `vercel` | `VERCEL_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/vercel) |
|
||||||
|
| [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) |
|
||||||
|
| [VK Cloud](https://mcs.mail.ru/) | `vkcloud` | `VK_CLOUD_PASSWORD`, `VK_CLOUD_PROJECT_ID`, `VK_CLOUD_USERNAME` | [Additional configuration](https://go-acme.github.io/lego/dns/vkcloud) |
|
||||||
|
| [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 Cloud](https://cloud.yandex.com/en/) | `yandexcloud` | `YANDEX_CLOUD_FOLDER_ID`, `YANDEX_CLOUD_IAM_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/yandexcloud) |
|
||||||
|
| [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/)
|
[^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)
|
[^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)
|
[^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.
|
[^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`.
|
[^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`"
|
!!! info "`delayBeforeCheck`"
|
||||||
By default, the `provider` verifies the TXT record _before_ letting ACME verify.
|
By default, the `provider` verifies the TXT record _before_ letting ACME verify.
|
||||||
@@ -525,6 +558,50 @@ docker run -v "/my/host/acme:/etc/traefik/acme" traefik
|
|||||||
!!! warning
|
!!! warning
|
||||||
For concurrency reasons, this file cannot be shared across multiple instances of Traefik.
|
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`
|
### `preferredChain`
|
||||||
|
|
||||||
_Optional, Default=""_
|
_Optional, Default=""_
|
||||||
@@ -552,7 +629,7 @@ certificatesResolvers:
|
|||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
# ...
|
# ...
|
||||||
--certificatesresolvers.myresolver.acme.preferredChain="ISRG Root X1"
|
--certificatesresolvers.myresolver.acme.preferredChain=ISRG Root X1
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -580,7 +657,7 @@ certificatesResolvers:
|
|||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
# ...
|
# ...
|
||||||
--certificatesresolvers.myresolver.acme.keyType="RSA4096"
|
--certificatesresolvers.myresolver.acme.keyType=RSA4096
|
||||||
# ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -589,8 +666,10 @@ certificatesResolvers:
|
|||||||
If Let's Encrypt is not reachable, the following certificates will apply:
|
If Let's Encrypt is not reachable, the following certificates will apply:
|
||||||
|
|
||||||
1. Previously generated ACME certificates (before downtime)
|
1. Previously generated ACME certificates (before downtime)
|
||||||
1. Expired ACME certificates
|
2. Expired ACME certificates
|
||||||
1. Provided certificates
|
3. Provided certificates
|
||||||
|
|
||||||
!!! important
|
!!! important
|
||||||
For new (sub)domains which need Let's Encrypt authentication, the default Traefik certificate will be used until Traefik is restarted.
|
For new (sub)domains which need Let's Encrypt authentication, the default Traefik certificate will be used until Traefik is restarted.
|
||||||
|
|
||||||
|
{!traefik-for-business-applications.md!}
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
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
|
# HTTPS & TLS
|
||||||
|
|
||||||
Overview
|
Overview
|
||||||
@@ -14,3 +19,5 @@ The next sections of this documentation explain how to configure the TLS connect
|
|||||||
That is to say, how to obtain [TLS certificates](./tls.md#certificates-definition):
|
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).
|
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).
|
And how to configure [TLS options](./tls.md#tls-options), and [certificates stores](./tls.md#certificates-stores).
|
||||||
|
|
||||||
|
{!traefik-for-business-applications.md!}
|
||||||
|
|||||||
@@ -22,6 +22,14 @@
|
|||||||
#
|
#
|
||||||
# caServer = "https://acme-staging-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.
|
# Preferred chain to use.
|
||||||
#
|
#
|
||||||
# If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name.
|
# If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name.
|
||||||
|
|||||||
@@ -21,6 +21,14 @@
|
|||||||
#
|
#
|
||||||
--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-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.
|
# Preferred chain to use.
|
||||||
#
|
#
|
||||||
# If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name.
|
# If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name.
|
||||||
|
|||||||
@@ -24,6 +24,14 @@ certificatesResolvers:
|
|||||||
#
|
#
|
||||||
# caServer: "https://acme-staging-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.
|
# Preferred chain to use.
|
||||||
#
|
#
|
||||||
# If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name.
|
# If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name.
|
||||||
|
|||||||
54
docs/content/https/spiffe.md
Normal file
54
docs/content/https/spiffe.md
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik SPIFFE Documentation"
|
||||||
|
description: "Learn how to configure Traefik to use SPIFFE. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
|
# SPIFFE
|
||||||
|
|
||||||
|
Secure the backend connection with SPIFFE.
|
||||||
|
{: .subtitle }
|
||||||
|
|
||||||
|
[SPIFFE](https://spiffe.io/docs/latest/spiffe-about/overview/) (Secure Production Identity Framework For Everyone),
|
||||||
|
provides a secure identity in the form of a specially crafted X.509 certificate,
|
||||||
|
to every workload in an environment.
|
||||||
|
|
||||||
|
Traefik is able to connect to the Workload API to obtain an x509-SVID used to secure the connection with SPIFFE enabled backends.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### General
|
||||||
|
|
||||||
|
Enabling SPIFFE is part of the [static configuration](../getting-started/configuration-overview.md#the-static-configuration).
|
||||||
|
It can be defined by using a file (YAML or TOML) or CLI arguments.
|
||||||
|
|
||||||
|
### Workload API
|
||||||
|
|
||||||
|
The `workloadAPIAddr` configuration defines the address of the SPIFFE [Workload API](https://spiffe.io/docs/latest/spiffe-about/spiffe-concepts/#spiffe-workload-api).
|
||||||
|
|
||||||
|
!!! info "Enabling SPIFFE in ServersTransports"
|
||||||
|
|
||||||
|
Enabling SPIFFE does not imply that backend connections are going to use it automatically.
|
||||||
|
Each [ServersTransport](../routing/services/index.md#serverstransport_1) that is meant to be secured with SPIFFE must [explicitly](../routing/services/index.md#spiffe) enable it.
|
||||||
|
|
||||||
|
!!! warning "SPIFFE can cause Traefik to stall"
|
||||||
|
When using SPIFFE,
|
||||||
|
Traefik will wait for the first SVID to be delivered before starting.
|
||||||
|
If Traefik is hanging when waiting on SPIFFE SVID delivery,
|
||||||
|
please double check that it is correctly registered as workload in your SPIFFE infrastructure.
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
## Static configuration
|
||||||
|
spiffe:
|
||||||
|
workloadAPIAddr: localhost
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
## Static configuration
|
||||||
|
[spiffe]
|
||||||
|
workloadAPIAddr: localhost
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash tab="CLI"
|
||||||
|
## Static configuration
|
||||||
|
--spiffe.workloadAPIAddr=localhost
|
||||||
|
```
|
||||||
237
docs/content/https/tailscale.md
Normal file
237
docs/content/https/tailscale.md
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik Tailscale Documentation"
|
||||||
|
description: "Learn how to configure Traefik Proxy to resolve TLS certificates for your Tailscale services. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
|
# Tailscale
|
||||||
|
|
||||||
|
Provision TLS certificates for your internal Tailscale services.
|
||||||
|
{: .subtitle }
|
||||||
|
|
||||||
|
To protect a service with TLS, a certificate from a public Certificate Authority is needed.
|
||||||
|
In addition to its vpn role, Tailscale can also [provide certificates](https://tailscale.com/kb/1153/enabling-https/) for the machines in your Tailscale network.
|
||||||
|
|
||||||
|
## Certificate resolvers
|
||||||
|
|
||||||
|
To obtain a TLS certificate from the Tailscale daemon,
|
||||||
|
a Tailscale certificate resolver needs to be configured as below.
|
||||||
|
|
||||||
|
!!! info "Referencing a certificate resolver"
|
||||||
|
|
||||||
|
Defining a certificate resolver does not imply that routers are going to use it automatically.
|
||||||
|
Each router or entrypoint that is meant to use the resolver must explicitly [reference](../routing/routers/index.md#certresolver) it.
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
certificatesResolvers:
|
||||||
|
myresolver:
|
||||||
|
tailscale: {}
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
[certificatesResolvers.myresolver.tailscale]
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash tab="CLI"
|
||||||
|
--certificatesresolvers.myresolver.tailscale=true
|
||||||
|
```
|
||||||
|
|
||||||
|
## Domain Definition
|
||||||
|
|
||||||
|
A certificate resolver requests certificates for a set of domain names inferred from routers, according to the following:
|
||||||
|
|
||||||
|
- If the router has a [`tls.domains`](../routing/routers/index.md#domains) option set,
|
||||||
|
then the certificate resolver derives this router domain name from the `main` option of `tls.domains`.
|
||||||
|
|
||||||
|
- Otherwise, the certificate resolver derives the domain name from any `Host()` or `HostSNI()` matchers
|
||||||
|
in the [router's rule](../routing/routers/index.md#rule).
|
||||||
|
|
||||||
|
!!! info "Tailscale Domain Format"
|
||||||
|
|
||||||
|
The domain is only taken into account if it is a Tailscale-specific one,
|
||||||
|
i.e. of the form `machine-name.domains-alias.ts.net`.
|
||||||
|
|
||||||
|
## Configuration Example
|
||||||
|
|
||||||
|
!!! example "Enabling Tailscale certificate resolution"
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
entryPoints:
|
||||||
|
web:
|
||||||
|
address: ":80"
|
||||||
|
|
||||||
|
websecure:
|
||||||
|
address: ":443"
|
||||||
|
|
||||||
|
certificatesResolvers:
|
||||||
|
myresolver:
|
||||||
|
tailscale: {}
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
[entryPoints]
|
||||||
|
[entryPoints.web]
|
||||||
|
address = ":80"
|
||||||
|
|
||||||
|
[entryPoints.websecure]
|
||||||
|
address = ":443"
|
||||||
|
|
||||||
|
[certificatesResolvers.myresolver.tailscale]
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash tab="CLI"
|
||||||
|
--entrypoints.web.address=:80
|
||||||
|
--entrypoints.websecure.address=:443
|
||||||
|
# ...
|
||||||
|
--certificatesresolvers.myresolver.tailscale=true
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! example "Domain from Router's Rule Example"
|
||||||
|
|
||||||
|
```yaml tab="Docker"
|
||||||
|
## Dynamic configuration
|
||||||
|
labels:
|
||||||
|
- traefik.http.routers.blog.rule=Host(`monitoring.yak-bebop.ts.net`) && Path(`/metrics`)
|
||||||
|
- traefik.http.routers.blog.tls.certresolver=myresolver
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Docker (Swarm)"
|
||||||
|
## Dynamic configuration
|
||||||
|
deploy:
|
||||||
|
labels:
|
||||||
|
- traefik.http.routers.blog.rule=Host(`monitoring.yak-bebop.ts.net`) && Path(`/metrics`)
|
||||||
|
- traefik.http.routers.blog.tls.certresolver=myresolver
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Kubernetes"
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: blogtls
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- websecure
|
||||||
|
routes:
|
||||||
|
- match: Host(`monitoring.yak-bebop.ts.net`) && Path(`/metrics`)
|
||||||
|
kind: Rule
|
||||||
|
services:
|
||||||
|
- name: blog
|
||||||
|
port: 8080
|
||||||
|
tls:
|
||||||
|
certResolver: myresolver
|
||||||
|
```
|
||||||
|
|
||||||
|
```json tab="Marathon"
|
||||||
|
labels: {
|
||||||
|
"traefik.http.routers.blog.rule": "Host(`monitoring.yak-bebop.ts.net`) && Path(`/metrics`)",
|
||||||
|
"traefik.http.routers.blog.tls.certresolver": "myresolver",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Rancher"
|
||||||
|
## Dynamic configuration
|
||||||
|
labels:
|
||||||
|
- traefik.http.routers.blog.rule=Host(`monitoring.yak-bebop.ts.net`) && Path(`/metrics`)
|
||||||
|
- traefik.http.routers.blog.tls.certresolver=myresolver
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
## Dynamic configuration
|
||||||
|
http:
|
||||||
|
routers:
|
||||||
|
blog:
|
||||||
|
rule: "Host(`monitoring.yak-bebop.ts.net`) && Path(`/metrics`)"
|
||||||
|
tls:
|
||||||
|
certResolver: myresolver
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
## Dynamic configuration
|
||||||
|
[http.routers]
|
||||||
|
[http.routers.blog]
|
||||||
|
rule = "Host(`monitoring.yak-bebop.ts.net`) && Path(`/metrics`)"
|
||||||
|
[http.routers.blog.tls]
|
||||||
|
certResolver = "myresolver"
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! example "Domain from Router's tls.domain Example"
|
||||||
|
|
||||||
|
```yaml tab="Docker"
|
||||||
|
## Dynamic configuration
|
||||||
|
labels:
|
||||||
|
- traefik.http.routers.blog.rule=Path(`/metrics`)
|
||||||
|
- traefik.http.routers.blog.tls.certresolver=myresolver
|
||||||
|
- traefik.http.routers.blog.tls.domains[0].main=monitoring.yak-bebop.ts.net
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Docker (Swarm)"
|
||||||
|
## Dynamic configuration
|
||||||
|
deploy:
|
||||||
|
labels:
|
||||||
|
- traefik.http.routers.blog.rule=Path(`/metrics`)
|
||||||
|
- traefik.http.routers.blog.tls.certresolver=myresolver
|
||||||
|
- traefik.http.routers.blog.tls.domains[0].main=monitoring.yak-bebop.ts.net
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Kubernetes"
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: blogtls
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- websecure
|
||||||
|
routes:
|
||||||
|
- match: Path(`/metrics`)
|
||||||
|
kind: Rule
|
||||||
|
services:
|
||||||
|
- name: blog
|
||||||
|
port: 8080
|
||||||
|
tls:
|
||||||
|
certResolver: myresolver
|
||||||
|
domains:
|
||||||
|
- main: monitoring.yak-bebop.ts.net
|
||||||
|
```
|
||||||
|
|
||||||
|
```json tab="Marathon"
|
||||||
|
labels: {
|
||||||
|
"traefik.http.routers.blog.rule": "Path(`/metrics`)",
|
||||||
|
"traefik.http.routers.blog.tls.certresolver": "myresolver",
|
||||||
|
"traefik.http.routers.blog.tls.domains[0].main": "monitoring.yak-bebop.ts.net",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Rancher"
|
||||||
|
## Dynamic configuration
|
||||||
|
labels:
|
||||||
|
- traefik.http.routers.blog.rule=Path(`/metrics`)
|
||||||
|
- traefik.http.routers.blog.tls.certresolver=myresolver
|
||||||
|
- traefik.http.routers.blog.tls.domains[0].main=monitoring.yak-bebop.ts.net
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
## Dynamic configuration
|
||||||
|
http:
|
||||||
|
routers:
|
||||||
|
blog:
|
||||||
|
rule: "Path(`/metrics`)"
|
||||||
|
tls:
|
||||||
|
certResolver: myresolver
|
||||||
|
domains:
|
||||||
|
- main: "monitoring.yak-bebop.ts.net"
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
## Dynamic configuration
|
||||||
|
[http.routers]
|
||||||
|
[http.routers.blog]
|
||||||
|
rule = "Path(`/metrics`)"
|
||||||
|
[http.routers.blog.tls]
|
||||||
|
certResolver = "myresolver"
|
||||||
|
[[http.routers.blog.tls.domains]]
|
||||||
|
main = "monitoring.yak-bebop.ts.net"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Automatic Renewals
|
||||||
|
|
||||||
|
Traefik automatically tracks the expiry date of each Tailscale certificate it fetches,
|
||||||
|
and starts to renew a certificate 14 days before its expiry to match Tailscale daemon renew policy.
|
||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik TLS Documentation"
|
||||||
|
description: "Learn how to configure the transport layer security (TLS) connection in Traefik Proxy. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
# TLS
|
# TLS
|
||||||
|
|
||||||
Transport Layer Security
|
Transport Layer Security
|
||||||
@@ -128,7 +133,99 @@ tls:
|
|||||||
keyFile = "path/to/cert.key"
|
keyFile = "path/to/cert.key"
|
||||||
```
|
```
|
||||||
|
|
||||||
If no default certificate is provided, Traefik generates and uses a self-signed certificate.
|
```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 `defaultCertificate` is provided, Traefik will use the generated one.
|
||||||
|
|
||||||
|
### ACME Default Certificate
|
||||||
|
|
||||||
|
You can configure Traefik to use an ACME provider (like Let's Encrypt) to generate the default certificate.
|
||||||
|
The configuration to resolve the default certificate should be defined in a TLS store:
|
||||||
|
|
||||||
|
!!! important "Precedence with the `defaultGeneratedCert` option"
|
||||||
|
|
||||||
|
The `defaultGeneratedCert` definition takes precedence over the ACME default certificate configuration.
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
# Dynamic configuration
|
||||||
|
|
||||||
|
tls:
|
||||||
|
stores:
|
||||||
|
default:
|
||||||
|
defaultGeneratedCert:
|
||||||
|
resolver: myresolver
|
||||||
|
domain:
|
||||||
|
main: example.org
|
||||||
|
sans:
|
||||||
|
- foo.example.org
|
||||||
|
- bar.example.org
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
# Dynamic configuration
|
||||||
|
|
||||||
|
[tls.stores]
|
||||||
|
[tls.stores.default.defaultGeneratedCert]
|
||||||
|
resolver = "myresolver"
|
||||||
|
[tls.stores.default.defaultGeneratedCert.domain]
|
||||||
|
main = "example.org"
|
||||||
|
sans = ["foo.example.org", "bar.example.org"]
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Kubernetes"
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: TLSStore
|
||||||
|
metadata:
|
||||||
|
name: default
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
spec:
|
||||||
|
defaultGeneratedCert:
|
||||||
|
resolver: myresolver
|
||||||
|
domain:
|
||||||
|
main: example.org
|
||||||
|
sans:
|
||||||
|
- foo.example.org
|
||||||
|
- bar.example.org
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Docker"
|
||||||
|
## Dynamic configuration
|
||||||
|
labels:
|
||||||
|
- "traefik.tls.stores.default.defaultgeneratedcert.resolver=myresolver"
|
||||||
|
- "traefik.tls.stores.default.defaultgeneratedcert.domain.main=example.org"
|
||||||
|
- "traefik.tls.stores.default.defaultgeneratedcert.domain.sans=foo.example.org, bar.example.org"
|
||||||
|
```
|
||||||
|
|
||||||
|
```json tab="Marathon"
|
||||||
|
labels: {
|
||||||
|
"traefik.tls.stores.default.defaultgeneratedcert.resolver": "myresolver",
|
||||||
|
"traefik.tls.stores.default.defaultgeneratedcert.domain.main": "example.org",
|
||||||
|
"traefik.tls.stores.default.defaultgeneratedcert.domain.sans": "foo.example.org, bar.example.org",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## TLS Options
|
## TLS Options
|
||||||
|
|
||||||
@@ -143,11 +240,11 @@ The TLS options allow one to configure some parameters of the TLS connection.
|
|||||||
you must specify the provider namespace, for example:
|
you must specify the provider namespace, for example:
|
||||||
`traefik.http.routers.myrouter.tls.options=myoptions@file`
|
`traefik.http.routers.myrouter.tls.options=myoptions@file`
|
||||||
|
|
||||||
!!! important "TLSOptions in Kubernetes"
|
!!! important "TLSOption in Kubernetes"
|
||||||
|
|
||||||
When using the TLSOptions-CRD in Kubernetes, one might setup a default set of options that,
|
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.
|
if not explicitly overwritten, should apply to all ingresses.
|
||||||
To achieve that, you'll have to create a TLSOptions CR with the name `default`.
|
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.
|
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)
|
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:
|
you'll have to add an annotation to the Ingress in the following form:
|
||||||
@@ -335,8 +432,9 @@ spec:
|
|||||||
|
|
||||||
### Strict SNI Checking
|
### Strict SNI Checking
|
||||||
|
|
||||||
With strict SNI checking enabled, Traefik won't allow connections from clients
|
With strict SNI checking enabled, Traefik won't allow connections from clients that do not specify a server_name extension
|
||||||
that do not specify a server_name extension or don't match any certificate configured on the tlsOption.
|
or don't match any of the configured certificates.
|
||||||
|
The default certificate is irrelevant on that matter.
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
# Dynamic configuration
|
# Dynamic configuration
|
||||||
@@ -366,10 +464,14 @@ spec:
|
|||||||
sniStrict: true
|
sniStrict: true
|
||||||
```
|
```
|
||||||
|
|
||||||
### Prefer Server Cipher Suites
|
### ALPN Protocols
|
||||||
|
|
||||||
This option allows the server to choose its most preferred cipher suite instead of the client's.
|
_Optional, Default="h2, http/1.1, acme-tls/1"_
|
||||||
Please note that this is enabled automatically when `minVersion` or `maxVersion` are set.
|
|
||||||
|
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)"
|
```yaml tab="File (YAML)"
|
||||||
# Dynamic configuration
|
# Dynamic configuration
|
||||||
@@ -377,7 +479,9 @@ Please note that this is enabled automatically when `minVersion` or `maxVersion`
|
|||||||
tls:
|
tls:
|
||||||
options:
|
options:
|
||||||
default:
|
default:
|
||||||
preferServerCipherSuites: true
|
alpnProtocols:
|
||||||
|
- http/1.1
|
||||||
|
- h2
|
||||||
```
|
```
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
@@ -385,7 +489,7 @@ tls:
|
|||||||
|
|
||||||
[tls.options]
|
[tls.options]
|
||||||
[tls.options.default]
|
[tls.options.default]
|
||||||
preferServerCipherSuites = true
|
alpnProtocols = ["http/1.1", "h2"]
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="Kubernetes"
|
```yaml tab="Kubernetes"
|
||||||
@@ -396,7 +500,9 @@ metadata:
|
|||||||
namespace: default
|
namespace: default
|
||||||
|
|
||||||
spec:
|
spec:
|
||||||
preferServerCipherSuites: true
|
alpnProtocols:
|
||||||
|
- http/1.1
|
||||||
|
- h2
|
||||||
```
|
```
|
||||||
|
|
||||||
### Client Authentication (mTLS)
|
### Client Authentication (mTLS)
|
||||||
@@ -447,8 +553,10 @@ metadata:
|
|||||||
|
|
||||||
spec:
|
spec:
|
||||||
clientAuth:
|
clientAuth:
|
||||||
# the CA certificate is extracted from key `tls.ca` of the given secrets.
|
# the CA certificate is extracted from key `tls.ca` or `ca.crt` of the given secrets.
|
||||||
secretNames:
|
secretNames:
|
||||||
- secretCA
|
- secretCA
|
||||||
clientAuthType: RequireAndVerifyClientCert
|
clientAuthType: RequireAndVerifyClientCert
|
||||||
```
|
```
|
||||||
|
|
||||||
|
{!traefik-for-business-applications.md!}
|
||||||
|
|||||||
4
docs/content/includes/.markdownlint.json
Normal file
4
docs/content/includes/.markdownlint.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../.markdownlint.json",
|
||||||
|
"MD041": false
|
||||||
|
}
|
||||||
16
docs/content/includes/traefik-for-business-applications.md
Normal file
16
docs/content/includes/traefik-for-business-applications.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
|
||||||
|
!!! question "Using Traefik for Business Applications?"
|
||||||
|
|
||||||
|
If you are using Traefik for commercial applications,
|
||||||
|
consider the [Enterprise Edition](https://traefik.io/traefik-enterprise/).
|
||||||
|
You can use it as your:
|
||||||
|
|
||||||
|
- [Kubernetes Ingress Controller](https://traefik.io/solutions/kubernetes-ingress/)
|
||||||
|
- [Docker Swarm Ingress Controller](https://traefik.io/solutions/docker-swarm-ingress/)
|
||||||
|
- [API Gateway](https://traefik.io/solutions/api-gateway/)
|
||||||
|
|
||||||
|
Traefik Enterprise enables centralized access management,
|
||||||
|
distributed Let's Encrypt,
|
||||||
|
and other advanced capabilities.
|
||||||
|
Learn more in [this 15-minute technical walkthrough](https://info.traefik.io/watch-traefikee-demo).
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik Proxy Documentation"
|
||||||
|
description: "Traefik Proxy, an open source Edge Router, auto-discovers configurations and supports major orchestrators, like Kubernetes. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
# Welcome
|
# Welcome
|
||||||
|
|
||||||
@@ -22,7 +26,8 @@ Developing Traefik, our main goal is to make it simple to use, and we're sure yo
|
|||||||
|
|
||||||
Join our user friendly and active [Community Forum](https://community.traefik.io) to discuss, learn, and connect with the traefik community.
|
Join our user friendly and active [Community Forum](https://community.traefik.io) to discuss, learn, and connect with the traefik community.
|
||||||
|
|
||||||
If you're a business running critical services behind Traefik,
|
Using Traefik for commercial applications?
|
||||||
know that [Traefik Labs](https://traefik.io), the company that sponsors Traefik's development,
|
Consider the [Enterprise Edition](https://traefik.io/traefik-enterprise/) of Traefik as your [Kubernetes Ingress](https://traefik.io/solutions/kubernetes-ingress/),
|
||||||
can provide [commercial support](https://info.traefik.io/commercial-services)
|
your [Docker Swarm Load Balancer](https://traefik.io/solutions/docker-swarm-ingress/),
|
||||||
and develops an [Enterprise Edition](https://traefik.io/traefik-enterprise/) of Traefik.
|
or your [API gateway](https://traefik.io/solutions/api-gateway/).
|
||||||
|
Get started with a [free 30-day trial](https://info.traefik.io/get-traefik-enterprise-free-for-30-days).
|
||||||
|
|||||||
@@ -1,86 +0,0 @@
|
|||||||
# ContentType
|
|
||||||
|
|
||||||
Handling Content-Type auto-detection
|
|
||||||
{: .subtitle }
|
|
||||||
|
|
||||||
The Content-Type middleware - or rather its `autoDetect` option -
|
|
||||||
specifies whether to let the `Content-Type` header,
|
|
||||||
if it has not been defined by the backend,
|
|
||||||
be automatically set to a value derived from the contents of the response.
|
|
||||||
|
|
||||||
As a proxy, the default behavior should be to leave the header alone,
|
|
||||||
regardless of what the backend did with it.
|
|
||||||
However, the historic default was to always auto-detect and set the header if it was not already defined,
|
|
||||||
and altering this behavior would be a breaking change which would impact many users.
|
|
||||||
|
|
||||||
This middleware exists to enable the correct behavior until at least the default one can be changed in a future version.
|
|
||||||
|
|
||||||
!!! info
|
|
||||||
|
|
||||||
As explained above, for compatibility reasons the default behavior on a router (without this middleware),
|
|
||||||
is still to automatically set the `Content-Type` header.
|
|
||||||
Therefore, given the default value of the `autoDetect` option (false),
|
|
||||||
simply enabling this middleware for a router switches the router's behavior.
|
|
||||||
|
|
||||||
The scope of the Content-Type middleware is the MIME type detection done by the core of Traefik (the server part).
|
|
||||||
Therefore, it has no effect against any other `Content-Type` header modifications (e.g.: in another middleware such as compress).
|
|
||||||
|
|
||||||
## Configuration Examples
|
|
||||||
|
|
||||||
```yaml tab="Docker"
|
|
||||||
# Disable auto-detection
|
|
||||||
labels:
|
|
||||||
- "traefik.http.middlewares.autodetect.contenttype.autodetect=false"
|
|
||||||
```
|
|
||||||
|
|
||||||
```yaml tab="Kubernetes"
|
|
||||||
# Disable auto-detection
|
|
||||||
apiVersion: traefik.containo.us/v1alpha1
|
|
||||||
kind: Middleware
|
|
||||||
metadata:
|
|
||||||
name: autodetect
|
|
||||||
spec:
|
|
||||||
contentType:
|
|
||||||
autoDetect: false
|
|
||||||
```
|
|
||||||
|
|
||||||
```yaml tab="Consul Catalog"
|
|
||||||
# Disable auto-detection
|
|
||||||
- "traefik.http.middlewares.autodetect.contenttype.autodetect=false"
|
|
||||||
```
|
|
||||||
|
|
||||||
```json tab="Marathon"
|
|
||||||
"labels": {
|
|
||||||
"traefik.http.middlewares.autodetect.contenttype.autodetect": "false"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```yaml tab="Rancher"
|
|
||||||
# Disable auto-detection
|
|
||||||
labels:
|
|
||||||
- "traefik.http.middlewares.autodetect.contenttype.autodetect=false"
|
|
||||||
```
|
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
|
||||||
# Disable auto-detection
|
|
||||||
http:
|
|
||||||
middlewares:
|
|
||||||
autodetect:
|
|
||||||
contentType:
|
|
||||||
autoDetect: false
|
|
||||||
```
|
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
|
||||||
# Disable auto-detection
|
|
||||||
[http.middlewares]
|
|
||||||
[http.middlewares.autodetect.contentType]
|
|
||||||
autoDetect=false
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration Options
|
|
||||||
|
|
||||||
### `autoDetect`
|
|
||||||
|
|
||||||
`autoDetect` specifies whether to let the `Content-Type` header,
|
|
||||||
if it has not been set by the backend,
|
|
||||||
be automatically set to a value derived from the contents of the response.
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
# ErrorPage
|
|
||||||
|
|
||||||
It Has Never Been Easier to Say That Something Went Wrong
|
|
||||||
{: .subtitle }
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
The ErrorPage middleware returns a custom page in lieu of the default, according to configured ranges of HTTP Status codes.
|
|
||||||
|
|
||||||
!!! important
|
|
||||||
The error page itself is _not_ hosted by Traefik.
|
|
||||||
|
|
||||||
## Configuration Examples
|
|
||||||
|
|
||||||
```yaml tab="Docker"
|
|
||||||
# Dynamic Custom Error Page for 5XX Status Code
|
|
||||||
labels:
|
|
||||||
- "traefik.http.middlewares.test-errorpage.errors.status=500-599"
|
|
||||||
- "traefik.http.middlewares.test-errorpage.errors.service=serviceError"
|
|
||||||
- "traefik.http.middlewares.test-errorpage.errors.query=/{status}.html"
|
|
||||||
```
|
|
||||||
|
|
||||||
```yaml tab="Kubernetes"
|
|
||||||
apiVersion: traefik.containo.us/v1alpha1
|
|
||||||
kind: Middleware
|
|
||||||
metadata:
|
|
||||||
name: test-errorpage
|
|
||||||
spec:
|
|
||||||
errors:
|
|
||||||
status:
|
|
||||||
- "500-599"
|
|
||||||
query: /{status}.html
|
|
||||||
service:
|
|
||||||
name: whoami
|
|
||||||
port: 80
|
|
||||||
```
|
|
||||||
|
|
||||||
```yaml tab="Consul Catalog"
|
|
||||||
# Dynamic Custom Error Page for 5XX Status Code
|
|
||||||
- "traefik.http.middlewares.test-errorpage.errors.status=500-599"
|
|
||||||
- "traefik.http.middlewares.test-errorpage.errors.service=serviceError"
|
|
||||||
- "traefik.http.middlewares.test-errorpage.errors.query=/{status}.html"
|
|
||||||
```
|
|
||||||
|
|
||||||
```json tab="Marathon"
|
|
||||||
"labels": {
|
|
||||||
"traefik.http.middlewares.test-errorpage.errors.status": "500-599",
|
|
||||||
"traefik.http.middlewares.test-errorpage.errors.service": "serviceError",
|
|
||||||
"traefik.http.middlewares.test-errorpage.errors.query": "/{status}.html"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```yaml tab="Rancher"
|
|
||||||
# Dynamic Custom Error Page for 5XX Status Code
|
|
||||||
labels:
|
|
||||||
- "traefik.http.middlewares.test-errorpage.errors.status=500-599"
|
|
||||||
- "traefik.http.middlewares.test-errorpage.errors.service=serviceError"
|
|
||||||
- "traefik.http.middlewares.test-errorpage.errors.query=/{status}.html"
|
|
||||||
```
|
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
|
||||||
# Custom Error Page for 5XX
|
|
||||||
http:
|
|
||||||
middlewares:
|
|
||||||
test-errorpage:
|
|
||||||
errors:
|
|
||||||
status:
|
|
||||||
- "500-599"
|
|
||||||
service: serviceError
|
|
||||||
query: "/{status}.html"
|
|
||||||
|
|
||||||
services:
|
|
||||||
# ... definition of error-handler-service and my-service
|
|
||||||
```
|
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
|
||||||
# Custom Error Page for 5XX
|
|
||||||
[http.middlewares]
|
|
||||||
[http.middlewares.test-errorpage.errors]
|
|
||||||
status = ["500-599"]
|
|
||||||
service = "serviceError"
|
|
||||||
query = "/{status}.html"
|
|
||||||
|
|
||||||
[http.services]
|
|
||||||
# ... definition of error-handler-service and my-service
|
|
||||||
```
|
|
||||||
|
|
||||||
!!! note ""
|
|
||||||
|
|
||||||
In this example, the error page URL is based on the status code (`query=/{status}.html`).
|
|
||||||
|
|
||||||
## Configuration Options
|
|
||||||
|
|
||||||
### `status`
|
|
||||||
|
|
||||||
The `status` option defines which status or range of statuses should result in an error page.
|
|
||||||
|
|
||||||
The status code ranges are inclusive (`500-599` will trigger with every code between `500` and `599`, `500` and `599` included).
|
|
||||||
|
|
||||||
!!! note ""
|
|
||||||
|
|
||||||
You can define either a status code as a number (`500`),
|
|
||||||
as multiple comma-separated numbers (`500,502`),
|
|
||||||
as ranges by separating two codes with a dash (`500-599`),
|
|
||||||
or a combination of the two (`404,418,500-599`).
|
|
||||||
|
|
||||||
### `service`
|
|
||||||
|
|
||||||
The service that will serve the new requested error page.
|
|
||||||
|
|
||||||
!!! note ""
|
|
||||||
|
|
||||||
In Kubernetes, you need to reference a Kubernetes Service instead of a Traefik service.
|
|
||||||
|
|
||||||
### `query`
|
|
||||||
|
|
||||||
The URL for the error page (hosted by `service`). You can use the `{status}` variable in the `query` option in order to insert the status code in the URL.
|
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik AddPrefix Documentation"
|
||||||
|
description: "Learn how to implement the HTTP AddPrefix middleware in Traefik Proxy to updates request paths before being forwarded. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
# Add Prefix
|
# Add Prefix
|
||||||
|
|
||||||
Prefixing the Path
|
Prefixing the Path
|
||||||
{: .subtitle }
|
{: .subtitle }
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The AddPrefix middleware updates the path of a request before forwarding it.
|
The AddPrefix middleware updates the path of a request before forwarding it.
|
||||||
|
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik BasicAuth Documentation"
|
||||||
|
description: "The HTTP basic authentication (BasicAuth) middleware in Traefik Proxy restricts access to your Services to known users. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
# BasicAuth
|
# BasicAuth
|
||||||
|
|
||||||
Adding Basic Authentication
|
Adding Basic Authentication
|
||||||
{: .subtitle }
|
{: .subtitle }
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The BasicAuth middleware restricts access to your services to known users.
|
The BasicAuth middleware restricts access to your services to known users.
|
||||||
|
|
||||||
@@ -88,12 +93,21 @@ The `users` option is an array of authorized users. Each user must be declared u
|
|||||||
- If both `users` and `usersFile` are provided, the two are merged. The contents of `usersFile` have precedence over the values in `users`.
|
- If both `users` and `usersFile` are provided, the two are merged. The contents of `usersFile` have precedence over the values in `users`.
|
||||||
- For security reasons, the field `users` doesn't exist for Kubernetes IngressRoute, and one should use the `secret` field instead.
|
- For security reasons, the field `users` doesn't exist for Kubernetes IngressRoute, and one should use the `secret` field instead.
|
||||||
|
|
||||||
|
!!! note "Kubernetes kubernetes.io/basic-auth secret type"
|
||||||
|
|
||||||
|
Kubernetes supports a special `kubernetes.io/basic-auth` secret type.
|
||||||
|
This secret must contain two keys: `username` and `password`.
|
||||||
|
Please note that these keys are not hashed or encrypted in any way, and therefore is less secure than other methods.
|
||||||
|
You can find more information on the [Kubernetes Basic Authentication Secret Documentation](https://kubernetes.io/docs/concepts/configuration/secret/#basic-authentication-secret)
|
||||||
|
|
||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
# Declaring the user list
|
# Declaring the user list
|
||||||
#
|
#
|
||||||
# Note: all dollar signs in the hash need to be doubled for escaping.
|
# Note: when used in docker-compose.yml all dollar signs in the hash need to be doubled for escaping.
|
||||||
# To create a user:password pair, the following command can be used:
|
# To create a user:password pair, the following command can be used:
|
||||||
# echo $(htpasswd -nb user password) | sed -e s/\\$/\\$\\$/g
|
# echo $(htpasswd -nb user password) | sed -e s/\\$/\\$\\$/g
|
||||||
|
#
|
||||||
|
# Also note that dollar signs should NOT be doubled when they not evaluated (e.g. Ansible docker_container module).
|
||||||
labels:
|
labels:
|
||||||
- "traefik.http.middlewares.test-auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
|
- "traefik.http.middlewares.test-auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
|
||||||
```
|
```
|
||||||
@@ -118,11 +132,24 @@ kind: Secret
|
|||||||
metadata:
|
metadata:
|
||||||
name: authsecret
|
name: authsecret
|
||||||
namespace: default
|
namespace: default
|
||||||
|
|
||||||
data:
|
data:
|
||||||
users: |2
|
users: |2
|
||||||
dGVzdDokYXByMSRINnVza2trVyRJZ1hMUDZld1RyU3VCa1RycUU4d2ovCnRlc3QyOiRhcHIxJGQ5
|
dGVzdDokYXByMSRINnVza2trVyRJZ1hMUDZld1RyU3VCa1RycUU4d2ovCnRlc3QyOiRhcHIxJGQ5
|
||||||
aHI5SEJCJDRIeHdnVWlyM0hQNEVzZ2dQL1FObzAK
|
aHI5SEJCJDRIeHdnVWlyM0hQNEVzZ2dQL1FObzAK
|
||||||
|
|
||||||
|
---
|
||||||
|
# This is an alternate auth secret that demonstrates the basic-auth secret type.
|
||||||
|
# Note: the password is not hashed, and is merely base64 encoded.
|
||||||
|
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: authsecret2
|
||||||
|
namespace: default
|
||||||
|
type: kubernetes.io/basic-auth
|
||||||
|
data:
|
||||||
|
username: dXNlcg== # username: user
|
||||||
|
password: cGFzc3dvcmQ= # password: password
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="Consul Catalog"
|
```yaml tab="Consul Catalog"
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik Buffering Documentation"
|
||||||
|
description: "The HTTP buffering middleware in Traefik Proxy limits the size of requests that can be forwarded to Services. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
# Buffering
|
# Buffering
|
||||||
|
|
||||||
How to Read the Request before Forwarding It
|
How to Read the Request before Forwarding It
|
||||||
{: .subtitle }
|
{: .subtitle }
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The Buffering middleware limits the size of requests that can be forwarded to services.
|
The Buffering middleware limits the size of requests that can be forwarded to services.
|
||||||
|
|
||||||
@@ -67,9 +72,11 @@ http:
|
|||||||
|
|
||||||
### `maxRequestBodyBytes`
|
### `maxRequestBodyBytes`
|
||||||
|
|
||||||
|
_Optional, Default=0_
|
||||||
|
|
||||||
The `maxRequestBodyBytes` option configures the maximum allowed body size for the request (in bytes).
|
The `maxRequestBodyBytes` option configures the maximum allowed body size for the request (in bytes).
|
||||||
|
|
||||||
If the request exceeds the allowed size, it is not forwarded to the service, and the client gets a `413 (Request Entity Too Large)` response.
|
If the request exceeds the allowed size, it is not forwarded to the service, and the client gets a `413` (Request Entity Too Large) response.
|
||||||
|
|
||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
labels:
|
labels:
|
||||||
@@ -117,6 +124,8 @@ http:
|
|||||||
|
|
||||||
### `memRequestBodyBytes`
|
### `memRequestBodyBytes`
|
||||||
|
|
||||||
|
_Optional, Default=1048576_
|
||||||
|
|
||||||
You can configure a threshold (in bytes) from which the request will be buffered on disk instead of in memory with the `memRequestBodyBytes` option.
|
You can configure a threshold (in bytes) from which the request will be buffered on disk instead of in memory with the `memRequestBodyBytes` option.
|
||||||
|
|
||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
@@ -165,9 +174,11 @@ http:
|
|||||||
|
|
||||||
### `maxResponseBodyBytes`
|
### `maxResponseBodyBytes`
|
||||||
|
|
||||||
|
_Optional, Default=0_
|
||||||
|
|
||||||
The `maxResponseBodyBytes` option configures the maximum allowed response size from the service (in bytes).
|
The `maxResponseBodyBytes` option configures the maximum allowed response size from the service (in bytes).
|
||||||
|
|
||||||
If the response exceeds the allowed size, it is not forwarded to the client. The client gets a `413 (Request Entity Too Large) response` instead.
|
If the response exceeds the allowed size, it is not forwarded to the client. The client gets a `500` (Internal Server Error) response instead.
|
||||||
|
|
||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
labels:
|
labels:
|
||||||
@@ -215,6 +226,8 @@ http:
|
|||||||
|
|
||||||
### `memResponseBodyBytes`
|
### `memResponseBodyBytes`
|
||||||
|
|
||||||
|
_Optional, Default=1048576_
|
||||||
|
|
||||||
You can configure a threshold (in bytes) from which the response will be buffered on disk instead of in memory with the `memResponseBodyBytes` option.
|
You can configure a threshold (in bytes) from which the response will be buffered on disk instead of in memory with the `memResponseBodyBytes` option.
|
||||||
|
|
||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
@@ -263,6 +276,8 @@ http:
|
|||||||
|
|
||||||
### `retryExpression`
|
### `retryExpression`
|
||||||
|
|
||||||
|
_Optional, Default=""_
|
||||||
|
|
||||||
You can have the Buffering middleware replay the request using `retryExpression`.
|
You can have the Buffering middleware replay the request using `retryExpression`.
|
||||||
|
|
||||||
??? example "Retries once in the case of a network error"
|
??? example "Retries once in the case of a network error"
|
||||||
@@ -1,16 +1,21 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik Command Line Documentation"
|
||||||
|
description: "The HTTP chain middleware lets you define reusable combinations of other middleware, to reuse the same groups. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
# Chain
|
# Chain
|
||||||
|
|
||||||
When One Isn't Enough
|
When One Isn't Enough
|
||||||
{: .subtitle }
|
{: .subtitle }
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The Chain middleware enables you to define reusable combinations of other pieces of middleware.
|
The Chain middleware enables you to define reusable combinations of other pieces of middleware.
|
||||||
It makes reusing the same groups easier.
|
It makes reusing the same groups easier.
|
||||||
|
|
||||||
## Configuration Example
|
## Configuration Example
|
||||||
|
|
||||||
Below is an example of a Chain containing `WhiteList`, `BasicAuth`, and `RedirectScheme`.
|
Below is an example of a Chain containing `AllowList`, `BasicAuth`, and `RedirectScheme`.
|
||||||
|
|
||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
labels:
|
labels:
|
||||||
@@ -20,7 +25,7 @@ labels:
|
|||||||
- "traefik.http.middlewares.secured.chain.middlewares=https-only,known-ips,auth-users"
|
- "traefik.http.middlewares.secured.chain.middlewares=https-only,known-ips,auth-users"
|
||||||
- "traefik.http.middlewares.auth-users.basicauth.users=test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
|
- "traefik.http.middlewares.auth-users.basicauth.users=test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
|
||||||
- "traefik.http.middlewares.https-only.redirectscheme.scheme=https"
|
- "traefik.http.middlewares.https-only.redirectscheme.scheme=https"
|
||||||
- "traefik.http.middlewares.known-ips.ipwhitelist.sourceRange=192.168.1.7,127.0.0.1/32"
|
- "traefik.http.middlewares.known-ips.ipallowlist.sourceRange=192.168.1.7,127.0.0.1/32"
|
||||||
- "traefik.http.services.service1.loadbalancer.server.port=80"
|
- "traefik.http.services.service1.loadbalancer.server.port=80"
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -75,7 +80,7 @@ kind: Middleware
|
|||||||
metadata:
|
metadata:
|
||||||
name: known-ips
|
name: known-ips
|
||||||
spec:
|
spec:
|
||||||
ipWhiteList:
|
ipAllowList:
|
||||||
sourceRange:
|
sourceRange:
|
||||||
- 192.168.1.7
|
- 192.168.1.7
|
||||||
- 127.0.0.1/32
|
- 127.0.0.1/32
|
||||||
@@ -88,7 +93,7 @@ spec:
|
|||||||
- "traefik.http.middlewares.secured.chain.middlewares=https-only,known-ips,auth-users"
|
- "traefik.http.middlewares.secured.chain.middlewares=https-only,known-ips,auth-users"
|
||||||
- "traefik.http.middlewares.auth-users.basicauth.users=test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
|
- "traefik.http.middlewares.auth-users.basicauth.users=test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
|
||||||
- "traefik.http.middlewares.https-only.redirectscheme.scheme=https"
|
- "traefik.http.middlewares.https-only.redirectscheme.scheme=https"
|
||||||
- "traefik.http.middlewares.known-ips.ipwhitelist.sourceRange=192.168.1.7,127.0.0.1/32"
|
- "traefik.http.middlewares.known-ips.ipallowlist.sourceRange=192.168.1.7,127.0.0.1/32"
|
||||||
- "traefik.http.services.service1.loadbalancer.server.port=80"
|
- "traefik.http.services.service1.loadbalancer.server.port=80"
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -100,7 +105,7 @@ spec:
|
|||||||
"traefik.http.middlewares.secured.chain.middlewares": "https-only,known-ips,auth-users",
|
"traefik.http.middlewares.secured.chain.middlewares": "https-only,known-ips,auth-users",
|
||||||
"traefik.http.middlewares.auth-users.basicauth.users": "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
"traefik.http.middlewares.auth-users.basicauth.users": "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||||
"traefik.http.middlewares.https-only.redirectscheme.scheme": "https",
|
"traefik.http.middlewares.https-only.redirectscheme.scheme": "https",
|
||||||
"traefik.http.middlewares.known-ips.ipwhitelist.sourceRange": "192.168.1.7,127.0.0.1/32",
|
"traefik.http.middlewares.known-ips.ipallowlist.sourceRange": "192.168.1.7,127.0.0.1/32",
|
||||||
"traefik.http.services.service1.loadbalancer.server.port": "80"
|
"traefik.http.services.service1.loadbalancer.server.port": "80"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -113,7 +118,7 @@ labels:
|
|||||||
- "traefik.http.middlewares.secured.chain.middlewares=https-only,known-ips,auth-users"
|
- "traefik.http.middlewares.secured.chain.middlewares=https-only,known-ips,auth-users"
|
||||||
- "traefik.http.middlewares.auth-users.basicauth.users=test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
|
- "traefik.http.middlewares.auth-users.basicauth.users=test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
|
||||||
- "traefik.http.middlewares.https-only.redirectscheme.scheme=https"
|
- "traefik.http.middlewares.https-only.redirectscheme.scheme=https"
|
||||||
- "traefik.http.middlewares.known-ips.ipwhitelist.sourceRange=192.168.1.7,127.0.0.1/32"
|
- "traefik.http.middlewares.known-ips.ipallowlist.sourceRange=192.168.1.7,127.0.0.1/32"
|
||||||
- "traefik.http.services.service1.loadbalancer.server.port=80"
|
- "traefik.http.services.service1.loadbalancer.server.port=80"
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -145,7 +150,7 @@ http:
|
|||||||
scheme: https
|
scheme: https
|
||||||
|
|
||||||
known-ips:
|
known-ips:
|
||||||
ipWhiteList:
|
ipAllowList:
|
||||||
sourceRange:
|
sourceRange:
|
||||||
- "192.168.1.7"
|
- "192.168.1.7"
|
||||||
- "127.0.0.1/32"
|
- "127.0.0.1/32"
|
||||||
@@ -175,7 +180,7 @@ http:
|
|||||||
[http.middlewares.https-only.redirectScheme]
|
[http.middlewares.https-only.redirectScheme]
|
||||||
scheme = "https"
|
scheme = "https"
|
||||||
|
|
||||||
[http.middlewares.known-ips.ipWhiteList]
|
[http.middlewares.known-ips.ipAllowList]
|
||||||
sourceRange = ["192.168.1.7", "127.0.0.1/32"]
|
sourceRange = ["192.168.1.7", "127.0.0.1/32"]
|
||||||
|
|
||||||
[http.services]
|
[http.services]
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik CircuitBreaker Documentation"
|
||||||
|
description: "The HTTP circuit breaker in Traefik Proxy prevents stacking requests to unhealthy Services, resulting in cascading failures. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
# CircuitBreaker
|
# CircuitBreaker
|
||||||
|
|
||||||
Don't Waste Time Calling Unhealthy Services
|
Don't Waste Time Calling Unhealthy Services
|
||||||
{: .subtitle }
|
{: .subtitle }
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The circuit breaker protects your system from stacking requests to unhealthy services, resulting in cascading failures.
|
The circuit breaker protects your system from stacking requests to unhealthy services, resulting in cascading failures.
|
||||||
|
|
||||||
@@ -171,15 +176,18 @@ This behavior cannot be configured.
|
|||||||
|
|
||||||
### `CheckPeriod`
|
### `CheckPeriod`
|
||||||
|
|
||||||
The interval used to evaluate `expression` and decide if the state of the circuit breaker must change.
|
_Optional, Default="100ms"_
|
||||||
By default, `CheckPeriod` is 100ms. This value cannot be configured.
|
|
||||||
|
The interval between successive checks of the circuit breaker condition (when in standby state).
|
||||||
|
|
||||||
### `FallbackDuration`
|
### `FallbackDuration`
|
||||||
|
|
||||||
By default, `FallbackDuration` is 10 seconds. This value cannot be configured.
|
_Optional, Default="10s"_
|
||||||
|
|
||||||
### `RecoveringDuration`
|
The duration for which the circuit breaker will wait before trying to recover (from a tripped state).
|
||||||
|
|
||||||
The duration of the recovering mode (recovering state).
|
### `RecoveryDuration`
|
||||||
|
|
||||||
By default, `RecoveringDuration` is 10 seconds. This value cannot be configured.
|
_Optional, Default="10s"_
|
||||||
|
|
||||||
|
The duration for which the circuit breaker will try to recover (as soon as it is in recovering state).
|
||||||
@@ -1,22 +1,28 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik Compress Documentation"
|
||||||
|
description: "Traefik Proxy's HTTP middleware lets you compress responses before sending them to the client. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
# Compress
|
# Compress
|
||||||
|
|
||||||
Compress Responses before Sending them to the Client
|
Compress Allows Compressing Responses before Sending them to the Client
|
||||||
{: .subtitle }
|
{: .subtitle }
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The Compress middleware uses gzip compression.
|
The Compress middleware supports gzip and Brotli compression.
|
||||||
|
The activation of compression, and the compression method choice rely (among other things) on the request's `Accept-Encoding` header.
|
||||||
|
|
||||||
## Configuration Examples
|
## Configuration Examples
|
||||||
|
|
||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
# Enable gzip compression
|
# Enable compression
|
||||||
labels:
|
labels:
|
||||||
- "traefik.http.middlewares.test-compress.compress=true"
|
- "traefik.http.middlewares.test-compress.compress=true"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="Kubernetes"
|
```yaml tab="Kubernetes"
|
||||||
# Enable gzip compression
|
# Enable compression
|
||||||
apiVersion: traefik.containo.us/v1alpha1
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
kind: Middleware
|
kind: Middleware
|
||||||
metadata:
|
metadata:
|
||||||
@@ -26,7 +32,7 @@ spec:
|
|||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="Consul Catalog"
|
```yaml tab="Consul Catalog"
|
||||||
# Enable gzip compression
|
# Enable compression
|
||||||
- "traefik.http.middlewares.test-compress.compress=true"
|
- "traefik.http.middlewares.test-compress.compress=true"
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -37,13 +43,13 @@ spec:
|
|||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="Rancher"
|
```yaml tab="Rancher"
|
||||||
# Enable gzip compression
|
# Enable compression
|
||||||
labels:
|
labels:
|
||||||
- "traefik.http.middlewares.test-compress.compress=true"
|
- "traefik.http.middlewares.test-compress.compress=true"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
# Enable gzip compression
|
# Enable compression
|
||||||
http:
|
http:
|
||||||
middlewares:
|
middlewares:
|
||||||
test-compress:
|
test-compress:
|
||||||
@@ -51,7 +57,7 @@ http:
|
|||||||
```
|
```
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
# Enable gzip compression
|
# Enable compression
|
||||||
[http.middlewares]
|
[http.middlewares]
|
||||||
[http.middlewares.test-compress.compress]
|
[http.middlewares.test-compress.compress]
|
||||||
```
|
```
|
||||||
@@ -60,23 +66,34 @@ http:
|
|||||||
|
|
||||||
Responses are compressed when the following criteria are all met:
|
Responses are compressed when the following criteria are all met:
|
||||||
|
|
||||||
* The response body is larger than `1400` bytes.
|
* The `Accept-Encoding` request header contains `gzip`, `*`, and/or `br` with or without [quality values](https://developer.mozilla.org/en-US/docs/Glossary/Quality_values).
|
||||||
* The `Accept-Encoding` request header contains `gzip`.
|
If the `Accept-Encoding` request header is absent, it is meant as br compression is requested.
|
||||||
|
If it is present, but its value is the empty string, then compression is disabled.
|
||||||
* The response is not already compressed, i.e. the `Content-Encoding` response header is not already set.
|
* The response is not already compressed, i.e. the `Content-Encoding` response header is not already set.
|
||||||
|
* The response`Content-Type` header is not one among the [excludedContentTypes options](#excludedcontenttypes).
|
||||||
If the `Content-Type` header is not defined, or empty, the compress middleware will automatically [detect](https://mimesniff.spec.whatwg.org/) a content type.
|
* The response body is larger than the [configured minimum amount of bytes](#minresponsebodybytes) (default is `1024`).
|
||||||
It will also set the `Content-Type` header according to the detected MIME type.
|
|
||||||
|
|
||||||
## Configuration Options
|
## Configuration Options
|
||||||
|
|
||||||
### `excludedContentTypes`
|
### `excludedContentTypes`
|
||||||
|
|
||||||
|
_Optional, Default=""_
|
||||||
|
|
||||||
`excludedContentTypes` specifies a list of content types to compare the `Content-Type` header of the incoming requests and responses before compressing.
|
`excludedContentTypes` specifies a list of content types to compare the `Content-Type` header of the incoming requests and responses before compressing.
|
||||||
|
|
||||||
The responses with content types defined in `excludedContentTypes` are not compressed.
|
The responses with content types defined in `excludedContentTypes` are not compressed.
|
||||||
|
|
||||||
Content types are compared in a case-insensitive, whitespace-ignored manner.
|
Content types are compared in a case-insensitive, whitespace-ignored manner.
|
||||||
|
|
||||||
|
!!! info "In the case of gzip"
|
||||||
|
|
||||||
|
If the `Content-Type` header is not defined, or empty, the compress middleware will automatically [detect](https://mimesniff.spec.whatwg.org/) a content type.
|
||||||
|
It will also set the `Content-Type` header according to the detected MIME type.
|
||||||
|
|
||||||
|
!!! info "gRPC"
|
||||||
|
|
||||||
|
Note that `application/grpc` is never compressed.
|
||||||
|
|
||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
labels:
|
labels:
|
||||||
- "traefik.http.middlewares.test-compress.compress.excludedcontenttypes=text/event-stream"
|
- "traefik.http.middlewares.test-compress.compress.excludedcontenttypes=text/event-stream"
|
||||||
@@ -122,3 +139,55 @@ http:
|
|||||||
[http.middlewares.test-compress.compress]
|
[http.middlewares.test-compress.compress]
|
||||||
excludedContentTypes = ["text/event-stream"]
|
excludedContentTypes = ["text/event-stream"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `minResponseBodyBytes`
|
||||||
|
|
||||||
|
_Optional, Default=1024_
|
||||||
|
|
||||||
|
`minResponseBodyBytes` specifies the minimum amount of bytes a response body must have to be compressed.
|
||||||
|
|
||||||
|
Responses smaller than the specified values will not be compressed.
|
||||||
|
|
||||||
|
```yaml tab="Docker"
|
||||||
|
labels:
|
||||||
|
- "traefik.http.middlewares.test-compress.compress.minresponsebodybytes=1200"
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Kubernetes"
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: Middleware
|
||||||
|
metadata:
|
||||||
|
name: test-compress
|
||||||
|
spec:
|
||||||
|
compress:
|
||||||
|
minResponseBodyBytes: 1200
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Consul Catalog"
|
||||||
|
- "traefik.http.middlewares.test-compress.compress.minresponsebodybytes=1200"
|
||||||
|
```
|
||||||
|
|
||||||
|
```json tab="Marathon"
|
||||||
|
"labels": {
|
||||||
|
"traefik.http.middlewares.test-compress.compress.minresponsebodybytes": 1200
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Rancher"
|
||||||
|
labels:
|
||||||
|
- "traefik.http.middlewares.test-compress.compress.minresponsebodybytes=1200"
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
http:
|
||||||
|
middlewares:
|
||||||
|
test-compress:
|
||||||
|
compress:
|
||||||
|
minResponseBodyBytes: 1200
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
[http.middlewares]
|
||||||
|
[http.middlewares.test-compress.compress]
|
||||||
|
minResponseBodyBytes = 1200
|
||||||
|
```
|
||||||
66
docs/content/middlewares/http/contenttype.md
Normal file
66
docs/content/middlewares/http/contenttype.md
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik ContentType Documentation"
|
||||||
|
description: "Traefik Proxy's HTTP middleware automatically sets the `Content-Type` header value when it is not set by the backend. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
|
# ContentType
|
||||||
|
|
||||||
|
Handling Content-Type auto-detection
|
||||||
|
{: .subtitle }
|
||||||
|
|
||||||
|
The Content-Type middleware sets the `Content-Type` header value to the media type detected from the response content,
|
||||||
|
when it is not set by the backend.
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
|
||||||
|
The scope of the Content-Type middleware is the MIME type detection done by the core of Traefik (the server part).
|
||||||
|
Therefore, it has no effect against any other `Content-Type` header modifications (e.g.: in another middleware such as compress).
|
||||||
|
|
||||||
|
## Configuration Examples
|
||||||
|
|
||||||
|
```yaml tab="Docker"
|
||||||
|
# Enable auto-detection
|
||||||
|
labels:
|
||||||
|
- "traefik.http.middlewares.autodetect.contenttype=true"
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Kubernetes"
|
||||||
|
# Enable auto-detection
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: Middleware
|
||||||
|
metadata:
|
||||||
|
name: autodetect
|
||||||
|
spec:
|
||||||
|
contentType: {}
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Consul Catalog"
|
||||||
|
# Enable auto-detection
|
||||||
|
- "traefik.http.middlewares.autodetect.contenttype=true"
|
||||||
|
```
|
||||||
|
|
||||||
|
```json tab="Marathon"
|
||||||
|
"labels": {
|
||||||
|
"traefik.http.middlewares.autodetect.contenttype": "true"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Rancher"
|
||||||
|
# Enable auto-detection
|
||||||
|
labels:
|
||||||
|
- "traefik.http.middlewares.autodetect.contenttype=true"
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
# Enable auto-detection
|
||||||
|
http:
|
||||||
|
middlewares:
|
||||||
|
autodetect:
|
||||||
|
contentType: {}
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
# Enable auto-detection
|
||||||
|
[http.middlewares]
|
||||||
|
[http.middlewares.autodetect.contentType]
|
||||||
|
```
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik DigestAuth Documentation"
|
||||||
|
description: "Traefik Proxy's HTTP DigestAuth middleware restricts access to your services to known users. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
# DigestAuth
|
# DigestAuth
|
||||||
|
|
||||||
Adding Digest Authentication
|
Adding Digest Authentication
|
||||||
{: .subtitle }
|
{: .subtitle }
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The DigestAuth middleware restricts access to your services to known users.
|
The DigestAuth middleware restricts access to your services to known users.
|
||||||
|
|
||||||
137
docs/content/middlewares/http/errorpages.md
Normal file
137
docs/content/middlewares/http/errorpages.md
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik Errors Documentation"
|
||||||
|
description: "In Traefik Proxy, the Errors middleware returns custom pages according to configured ranges of HTTP Status codes. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
|
# Errors
|
||||||
|
|
||||||
|
It Has Never Been Easier to Say That Something Went Wrong
|
||||||
|
{: .subtitle }
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The Errors middleware returns a custom page in lieu of the default, according to configured ranges of HTTP Status codes.
|
||||||
|
|
||||||
|
!!! important
|
||||||
|
|
||||||
|
The error page itself is _not_ hosted by Traefik.
|
||||||
|
|
||||||
|
## Configuration Examples
|
||||||
|
|
||||||
|
```yaml tab="Docker"
|
||||||
|
# Dynamic Custom Error Page for 5XX Status Code
|
||||||
|
labels:
|
||||||
|
- "traefik.http.middlewares.test-errors.errors.status=500-599"
|
||||||
|
- "traefik.http.middlewares.test-errors.errors.service=serviceError"
|
||||||
|
- "traefik.http.middlewares.test-errors.errors.query=/{status}.html"
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Kubernetes"
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: Middleware
|
||||||
|
metadata:
|
||||||
|
name: test-errors
|
||||||
|
spec:
|
||||||
|
errors:
|
||||||
|
status:
|
||||||
|
- "500-599"
|
||||||
|
query: /{status}.html
|
||||||
|
service:
|
||||||
|
name: whoami
|
||||||
|
port: 80
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Consul Catalog"
|
||||||
|
# Dynamic Custom Error Page for 5XX Status Code
|
||||||
|
- "traefik.http.middlewares.test-errors.errors.status=500-599"
|
||||||
|
- "traefik.http.middlewares.test-errors.errors.service=serviceError"
|
||||||
|
- "traefik.http.middlewares.test-errors.errors.query=/{status}.html"
|
||||||
|
```
|
||||||
|
|
||||||
|
```json tab="Marathon"
|
||||||
|
"labels": {
|
||||||
|
"traefik.http.middlewares.test-errors.errors.status": "500-599",
|
||||||
|
"traefik.http.middlewares.test-errors.errors.service": "serviceError",
|
||||||
|
"traefik.http.middlewares.test-errors.errors.query": "/{status}.html"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Rancher"
|
||||||
|
# Dynamic Custom Error Page for 5XX Status Code
|
||||||
|
labels:
|
||||||
|
- "traefik.http.middlewares.test-errors.errors.status=500-599"
|
||||||
|
- "traefik.http.middlewares.test-errors.errors.service=serviceError"
|
||||||
|
- "traefik.http.middlewares.test-errors.errors.query=/{status}.html"
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
# Custom Error Page for 5XX
|
||||||
|
http:
|
||||||
|
middlewares:
|
||||||
|
test-errors:
|
||||||
|
errors:
|
||||||
|
status:
|
||||||
|
- "500-599"
|
||||||
|
service: serviceError
|
||||||
|
query: "/{status}.html"
|
||||||
|
|
||||||
|
services:
|
||||||
|
# ... definition of error-handler-service and my-service
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
# Custom Error Page for 5XX
|
||||||
|
[http.middlewares]
|
||||||
|
[http.middlewares.test-errors.errors]
|
||||||
|
status = ["500-599"]
|
||||||
|
service = "serviceError"
|
||||||
|
query = "/{status}.html"
|
||||||
|
|
||||||
|
[http.services]
|
||||||
|
# ... definition of error-handler-service and my-service
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
|
||||||
|
In this example, the error page URL is based on the status code (`query=/{status}.html`).
|
||||||
|
|
||||||
|
## Configuration Options
|
||||||
|
|
||||||
|
### `status`
|
||||||
|
|
||||||
|
The `status` option defines which status or range of statuses should result in an error page.
|
||||||
|
|
||||||
|
The status code ranges are inclusive (`500-599` will trigger with every code between `500` and `599`, `500` and `599` included).
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
|
||||||
|
You can define either a status code as a number (`500`),
|
||||||
|
as multiple comma-separated numbers (`500,502`),
|
||||||
|
as ranges by separating two codes with a dash (`500-599`),
|
||||||
|
or a combination of the two (`404,418,500-599`).
|
||||||
|
|
||||||
|
### `service`
|
||||||
|
|
||||||
|
The service that will serve the new requested error page.
|
||||||
|
|
||||||
|
!!! note ""
|
||||||
|
|
||||||
|
In Kubernetes, you need to reference a Kubernetes Service instead of a Traefik service.
|
||||||
|
|
||||||
|
!!! info "Host Header"
|
||||||
|
|
||||||
|
By default, the client `Host` header value is forwarded to the configured error [service](#service).
|
||||||
|
To forward the `Host` value corresponding to the configured error service URL, the [passHostHeader](../../../routing/services/#pass-host-header) option must be set to `false`.
|
||||||
|
|
||||||
|
### `query`
|
||||||
|
|
||||||
|
The URL for the error page (hosted by [`service`](#service))).
|
||||||
|
|
||||||
|
There are multiple variables that can be placed in the `query` option to insert values in the URL.
|
||||||
|
|
||||||
|
The table below lists all the available variables and their associated values.
|
||||||
|
|
||||||
|
| Variable | Value |
|
||||||
|
|------------|--------------------------------------------------------------------|
|
||||||
|
| `{status}` | The response status code. |
|
||||||
|
| `{url}` | The [escaped](https://pkg.go.dev/net/url#QueryEscape) request URL. |
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik ForwardAuth Documentation"
|
||||||
|
description: "In Traefik Proxy, the HTTP ForwardAuth middleware delegates authentication to an external Service. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
# ForwardAuth
|
# ForwardAuth
|
||||||
|
|
||||||
Using an External Service to Forward Authentication
|
Using an External Service to Forward Authentication
|
||||||
{: .subtitle }
|
{: .subtitle }
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The ForwardAuth middleware delegates authentication to an external service.
|
The ForwardAuth middleware delegates authentication to an external service.
|
||||||
If the service answers with a 2XX code, access is granted, and the original request is performed.
|
If the service answers with a 2XX code, access is granted, and the original request is performed.
|
||||||
@@ -284,6 +289,12 @@ http:
|
|||||||
authResponseHeadersRegex = "^X-"
|
authResponseHeadersRegex = "^X-"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
|
||||||
|
Regular expressions and replacements can be tested using online tools such as [Go Playground](https://play.golang.org/p/mWU9p-wk2ru) or the [Regex101](https://regex101.com/r/58sIgx/2).
|
||||||
|
|
||||||
|
When defining a regular expression within YAML, any escaped character needs to be escaped twice: `example\.com` needs to be written as `example\\.com`.
|
||||||
|
|
||||||
### `authRequestHeaders`
|
### `authRequestHeaders`
|
||||||
|
|
||||||
The `authRequestHeaders` option is the list of the headers to copy from the request to the authentication server.
|
The `authRequestHeaders` option is the list of the headers to copy from the request to the authentication server.
|
||||||
@@ -343,11 +354,16 @@ http:
|
|||||||
|
|
||||||
### `tls`
|
### `tls`
|
||||||
|
|
||||||
The `tls` option is the TLS configuration from Traefik to the authentication server.
|
_Optional_
|
||||||
|
|
||||||
#### `tls.ca`
|
Defines the TLS configuration used for the secure connection to the authentication server.
|
||||||
|
|
||||||
Certificate Authority used for the secured connection to the authentication server.
|
#### `ca`
|
||||||
|
|
||||||
|
_Optional_
|
||||||
|
|
||||||
|
`ca` is the path to the certificate authority used for the secured connection to the authentication server,
|
||||||
|
it defaults to the system bundle.
|
||||||
|
|
||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
labels:
|
labels:
|
||||||
@@ -373,7 +389,8 @@ metadata:
|
|||||||
namespace: default
|
namespace: default
|
||||||
|
|
||||||
data:
|
data:
|
||||||
ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
|
# Must contain a certificate under either a `tls.ca` or a `ca.crt` key.
|
||||||
|
tls.ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="Consul Catalog"
|
```yaml tab="Consul Catalog"
|
||||||
@@ -409,71 +426,12 @@ http:
|
|||||||
ca = "path/to/local.crt"
|
ca = "path/to/local.crt"
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `tls.caOptional`
|
#### `cert`
|
||||||
|
|
||||||
The value of `tls.caOptional` defines which policy should be used for the secure connection with TLS Client Authentication to the authentication server.
|
_Optional_
|
||||||
|
|
||||||
!!! warning ""
|
`cert` is the path to the public certificate used for the secure connection to the authentication server.
|
||||||
|
When using this option, setting the `key` option is required.
|
||||||
If `tls.ca` is undefined, this option will be ignored, and no client certificate will be requested during the handshake. Any provided certificate will thus never be verified.
|
|
||||||
|
|
||||||
When this option is set to `true`, a client certificate is requested during the handshake but is not required. If a certificate is sent, it is required to be valid.
|
|
||||||
|
|
||||||
When this option is set to `false`, a client certificate is requested during the handshake, and at least one valid certificate should be sent by the client.
|
|
||||||
|
|
||||||
```yaml tab="Docker"
|
|
||||||
labels:
|
|
||||||
- "traefik.http.middlewares.test-auth.forwardauth.tls.caOptional=true"
|
|
||||||
```
|
|
||||||
|
|
||||||
```yaml tab="Kubernetes"
|
|
||||||
apiVersion: traefik.containo.us/v1alpha1
|
|
||||||
kind: Middleware
|
|
||||||
metadata:
|
|
||||||
name: test-auth
|
|
||||||
spec:
|
|
||||||
forwardAuth:
|
|
||||||
address: https://example.com/auth
|
|
||||||
tls:
|
|
||||||
caOptional: true
|
|
||||||
```
|
|
||||||
|
|
||||||
```yaml tab="Consul Catalog"
|
|
||||||
- "traefik.http.middlewares.test-auth.forwardauth.tls.caOptional=true"
|
|
||||||
```
|
|
||||||
|
|
||||||
```json tab="Marathon"
|
|
||||||
"labels": {
|
|
||||||
"traefik.http.middlewares.test-auth.forwardauth.tls.caOptional": "true"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```yaml tab="Rancher"
|
|
||||||
labels:
|
|
||||||
- "traefik.http.middlewares.test-auth.forwardauth.tls.caOptional=true"
|
|
||||||
```
|
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
|
||||||
http:
|
|
||||||
middlewares:
|
|
||||||
test-auth:
|
|
||||||
forwardAuth:
|
|
||||||
address: "https://example.com/auth"
|
|
||||||
tls:
|
|
||||||
caOptional: true
|
|
||||||
```
|
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
|
||||||
[http.middlewares]
|
|
||||||
[http.middlewares.test-auth.forwardAuth]
|
|
||||||
address = "https://example.com/auth"
|
|
||||||
[http.middlewares.test-auth.forwardAuth.tls]
|
|
||||||
caOptional = true
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `tls.cert`
|
|
||||||
|
|
||||||
The public certificate used for the secure connection to the authentication server.
|
|
||||||
|
|
||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
labels:
|
labels:
|
||||||
@@ -546,9 +504,12 @@ http:
|
|||||||
|
|
||||||
For security reasons, the field does not exist for Kubernetes IngressRoute, and one should use the `secret` field instead.
|
For security reasons, the field does not exist for Kubernetes IngressRoute, and one should use the `secret` field instead.
|
||||||
|
|
||||||
#### `tls.key`
|
#### `key`
|
||||||
|
|
||||||
The private certificate used for the secure connection to the authentication server.
|
_Optional_
|
||||||
|
|
||||||
|
`key` is the path to the private key used for the secure connection to the authentication server.
|
||||||
|
When using this option, setting the `cert` option is required.
|
||||||
|
|
||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
labels:
|
labels:
|
||||||
@@ -621,7 +582,9 @@ http:
|
|||||||
|
|
||||||
For security reasons, the field does not exist for Kubernetes IngressRoute, and one should use the `secret` field instead.
|
For security reasons, the field does not exist for Kubernetes IngressRoute, and one should use the `secret` field instead.
|
||||||
|
|
||||||
#### `tls.insecureSkipVerify`
|
#### `insecureSkipVerify`
|
||||||
|
|
||||||
|
_Optional, Default=false_
|
||||||
|
|
||||||
If `insecureSkipVerify` is `true`, the TLS connection to the authentication server accepts any certificate presented by the server regardless of the hostnames it covers.
|
If `insecureSkipVerify` is `true`, the TLS connection to the authentication server accepts any certificate presented by the server regardless of the hostnames it covers.
|
||||||
|
|
||||||
77
docs/content/middlewares/http/grpcweb.md
Normal file
77
docs/content/middlewares/http/grpcweb.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik GrpcWeb Documentation"
|
||||||
|
description: "In Traefik Proxy's HTTP middleware, GrpcWeb converts a gRPC Web requests to HTTP/2 gRPC requests. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
|
# GrpcWeb
|
||||||
|
|
||||||
|
Converting gRPC Web requests to HTTP/2 gRPC requests.
|
||||||
|
{: .subtitle }
|
||||||
|
|
||||||
|
The GrpcWeb middleware converts gRPC Web requests to HTTP/2 gRPC requests before forwarding them to the backends.
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
|
||||||
|
Please note, that Traefik needs to communicate using gRPC with the backends (h2c or HTTP/2 over TLS).
|
||||||
|
Check out the [gRPC](../../user-guides/grpc.md) user guide for more details.
|
||||||
|
|
||||||
|
## Configuration Examples
|
||||||
|
|
||||||
|
```yaml tab="Docker"
|
||||||
|
labels:
|
||||||
|
- "traefik.http.middlewares.test-grpcweb.grpcweb.allowOrigins=*"
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Kubernetes"
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: Middleware
|
||||||
|
metadata:
|
||||||
|
name: test-grpcweb
|
||||||
|
spec:
|
||||||
|
grpcWeb:
|
||||||
|
allowOrigins:
|
||||||
|
- "*"
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Consul Catalog"
|
||||||
|
- "traefik.http.middlewares.test-grpcweb.grpcWeb.allowOrigins=*"
|
||||||
|
```
|
||||||
|
|
||||||
|
```json tab="Marathon"
|
||||||
|
"labels": {
|
||||||
|
"traefik.http.middlewares.test-grpcweb.grpcweb.alloworigins": "*"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Rancher"
|
||||||
|
labels:
|
||||||
|
- "traefik.http.middlewares.test-grpcweb.grpcweb.alloworigins=*"
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
http:
|
||||||
|
middlewares:
|
||||||
|
test-grpcweb:
|
||||||
|
grpcWeb:
|
||||||
|
allowOrigins:
|
||||||
|
- "*"
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
[http.middlewares]
|
||||||
|
[http.middlewares.test-grpcweb.grpcWeb]
|
||||||
|
allowOrigins = ["*"]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration Options
|
||||||
|
|
||||||
|
### `allowOrigins`
|
||||||
|
|
||||||
|
The `allowOrigins` contains the list of allowed origins.
|
||||||
|
A wildcard origin `*` can also be configured to match all requests.
|
||||||
|
|
||||||
|
More information including how to use the settings can be found at:
|
||||||
|
|
||||||
|
- [Mozilla.org](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin)
|
||||||
|
- [w3](https://fetch.spec.whatwg.org/#http-access-control-allow-origin)
|
||||||
|
- [IETF](https://tools.ietf.org/html/rfc6454#section-7.1)
|
||||||
@@ -1,12 +1,19 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik Headers Documentation"
|
||||||
|
description: "In Traefik Proxy, the HTTP headers middleware manages the headers of requests and responses. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
# Headers
|
# Headers
|
||||||
|
|
||||||
Managing Request/Response headers
|
Managing Request/Response headers
|
||||||
{: .subtitle }
|
{: .subtitle }
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The Headers middleware manages the headers of requests and responses.
|
The Headers middleware manages the headers of requests and responses.
|
||||||
|
|
||||||
|
A set of forwarded headers are automatically added by default. See the [FAQ](../../getting-started/faq.md#what-are-the-forwarded-headers-when-proxying-http-requests) for more information.
|
||||||
|
|
||||||
## Configuration Examples
|
## Configuration Examples
|
||||||
|
|
||||||
### Adding Headers to the Request and the Response
|
### Adding Headers to the Request and the Response
|
||||||
@@ -141,13 +148,13 @@ http:
|
|||||||
|
|
||||||
### Using Security Headers
|
### Using Security Headers
|
||||||
|
|
||||||
Security-related headers (HSTS headers, SSL redirection, Browser XSS filter, etc) can be managed similarly to custom headers as shown above.
|
Security-related headers (HSTS headers, Browser XSS filter, etc) can be managed similarly to custom headers as shown above.
|
||||||
This functionality makes it possible to easily use security features by adding headers.
|
This functionality makes it possible to easily use security features by adding headers.
|
||||||
|
|
||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
labels:
|
labels:
|
||||||
- "traefik.http.middlewares.testHeader.headers.framedeny=true"
|
- "traefik.http.middlewares.testHeader.headers.framedeny=true"
|
||||||
- "traefik.http.middlewares.testHeader.headers.sslredirect=true"
|
- "traefik.http.middlewares.testHeader.headers.browserxssfilter=true"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="Kubernetes"
|
```yaml tab="Kubernetes"
|
||||||
@@ -158,25 +165,25 @@ metadata:
|
|||||||
spec:
|
spec:
|
||||||
headers:
|
headers:
|
||||||
frameDeny: true
|
frameDeny: true
|
||||||
sslRedirect: true
|
browserXssFilter: true
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="Consul Catalog"
|
```yaml tab="Consul Catalog"
|
||||||
- "traefik.http.middlewares.testheader.headers.framedeny=true"
|
- "traefik.http.middlewares.testheader.headers.framedeny=true"
|
||||||
- "traefik.http.middlewares.testheader.headers.sslredirect=true"
|
- "traefik.http.middlewares.testheader.headers.browserxssfilter=true"
|
||||||
```
|
```
|
||||||
|
|
||||||
```json tab="Marathon"
|
```json tab="Marathon"
|
||||||
"labels": {
|
"labels": {
|
||||||
"traefik.http.middlewares.testheader.headers.framedeny": "true",
|
"traefik.http.middlewares.testheader.headers.framedeny": "true",
|
||||||
"traefik.http.middlewares.testheader.headers.sslredirect": "true"
|
"traefik.http.middlewares.testheader.headers.browserxssfilter": "true"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="Rancher"
|
```yaml tab="Rancher"
|
||||||
labels:
|
labels:
|
||||||
- "traefik.http.middlewares.testheader.headers.framedeny=true"
|
- "traefik.http.middlewares.testheader.headers.framedeny=true"
|
||||||
- "traefik.http.middlewares.testheader.headers.sslredirect=true"
|
- "traefik.http.middlewares.testheader.headers.browserxssfilter=true"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
@@ -185,20 +192,22 @@ http:
|
|||||||
testHeader:
|
testHeader:
|
||||||
headers:
|
headers:
|
||||||
frameDeny: true
|
frameDeny: true
|
||||||
sslRedirect: true
|
browserXssFilter: true
|
||||||
```
|
```
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
[http.middlewares]
|
[http.middlewares]
|
||||||
[http.middlewares.testHeader.headers]
|
[http.middlewares.testHeader.headers]
|
||||||
frameDeny = true
|
frameDeny = true
|
||||||
sslRedirect = true
|
browserXssFilter = true
|
||||||
```
|
```
|
||||||
|
|
||||||
### CORS Headers
|
### CORS Headers
|
||||||
|
|
||||||
CORS (Cross-Origin Resource Sharing) headers can be added and configured in a manner similar to the custom headers above.
|
CORS (Cross-Origin Resource Sharing) headers can be added and configured in a manner similar to the custom headers above.
|
||||||
This functionality allows for more advanced security features to quickly be set.
|
This functionality allows for more advanced security features to quickly be set.
|
||||||
|
If CORS headers are set, then the middleware does not pass preflight requests to any service,
|
||||||
|
instead the response will be generated and sent back to the client directly.
|
||||||
|
|
||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
labels:
|
labels:
|
||||||
@@ -331,7 +340,9 @@ It allows all origins that contain any match of a regular expression in the `acc
|
|||||||
|
|
||||||
!!! tip
|
!!! tip
|
||||||
|
|
||||||
Regular expressions can be tested using online tools such as [Go Playground](https://play.golang.org/p/mWU9p-wk2ru) or the [Regex101](https://regex101.com/r/58sIgx/2).
|
Regular expressions and replacements can be tested using online tools such as [Go Playground](https://play.golang.org/p/mWU9p-wk2ru) or the [Regex101](https://regex101.com/r/58sIgx/2).
|
||||||
|
|
||||||
|
When defining a regular expression within YAML, any escaped character needs to be escaped twice: `example\.com` needs to be written as `example\\.com`.
|
||||||
|
|
||||||
### `accessControlExposeHeaders`
|
### `accessControlExposeHeaders`
|
||||||
|
|
||||||
@@ -353,27 +364,11 @@ The `allowedHosts` option lists fully qualified domain names that are allowed.
|
|||||||
|
|
||||||
The `hostsProxyHeaders` option is a set of header keys that may hold a proxied hostname value for the request.
|
The `hostsProxyHeaders` option is a set of header keys that may hold a proxied hostname value for the request.
|
||||||
|
|
||||||
### `sslRedirect`
|
|
||||||
|
|
||||||
The `sslRedirect` only allow HTTPS requests when set to `true`.
|
|
||||||
|
|
||||||
### `sslTemporaryRedirect`
|
|
||||||
|
|
||||||
Set `sslTemporaryRedirect` to `true` to force an SSL redirection using a 302 (instead of a 301).
|
|
||||||
|
|
||||||
### `sslHost`
|
|
||||||
|
|
||||||
The `sslHost` option is the host name that is used to redirect HTTP requests to HTTPS.
|
|
||||||
|
|
||||||
### `sslProxyHeaders`
|
### `sslProxyHeaders`
|
||||||
|
|
||||||
The `sslProxyHeaders` option is set of header keys with associated values that would indicate a valid HTTPS request.
|
The `sslProxyHeaders` option is set of header keys with associated values that would indicate a valid HTTPS request.
|
||||||
It can be useful when using other proxies (example: `"X-Forwarded-Proto": "https"`).
|
It can be useful when using other proxies (example: `"X-Forwarded-Proto": "https"`).
|
||||||
|
|
||||||
### `sslForceHost`
|
|
||||||
|
|
||||||
Set `sslForceHost` to `true` and set `sslHost` to force requests to use `SSLHost` regardless of whether they already use SSL.
|
|
||||||
|
|
||||||
### `stsSeconds`
|
### `stsSeconds`
|
||||||
|
|
||||||
The `stsSeconds` is the max-age of the `Strict-Transport-Security` header.
|
The `stsSeconds` is the max-age of the `Strict-Transport-Security` header.
|
||||||
@@ -425,12 +420,14 @@ The `publicKey` implements HPKP to prevent MITM attacks with forged certificates
|
|||||||
|
|
||||||
The `referrerPolicy` allows sites to control whether browsers forward the `Referer` header to other sites.
|
The `referrerPolicy` allows sites to control whether browsers forward the `Referer` header to other sites.
|
||||||
|
|
||||||
### `featurePolicy`
|
### `permissionsPolicy`
|
||||||
|
|
||||||
The `featurePolicy` allows sites to control browser features.
|
The `permissionsPolicy` allows sites to control browser features.
|
||||||
|
|
||||||
### `isDevelopment`
|
### `isDevelopment`
|
||||||
|
|
||||||
Set `isDevelopment` to `true` when developing to mitigate the unwanted effects of the `AllowedHosts`, SSL, and STS options.
|
Set `isDevelopment` to `true` when developing to mitigate the unwanted effects of the `AllowedHosts`, SSL, and STS options.
|
||||||
Usually testing takes place using HTTP, not HTTPS, and on `localhost`, not your production domain.
|
Usually testing takes place using HTTP, not HTTPS, and on `localhost`, not your production domain.
|
||||||
If you would like your development environment to mimic production with complete Host blocking, SSL redirects, and STS headers, leave this as `false`.
|
If you would like your development environment to mimic production with complete Host blocking, SSL redirects, and STS headers, leave this as `false`.
|
||||||
|
|
||||||
|
{!traefik-for-business-applications.md!}
|
||||||
@@ -1,9 +1,14 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik InFlightReq Documentation"
|
||||||
|
description: "Traefik Proxy's HTTP middleware lets you limit the number of simultaneous in-flight requests. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
# InFlightReq
|
# InFlightReq
|
||||||
|
|
||||||
Limiting the Number of Simultaneous In-Flight Requests
|
Limiting the Number of Simultaneous In-Flight Requests
|
||||||
{: .subtitle }
|
{: .subtitle }
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
To proactively prevent services from being overwhelmed with high load, the number of allowed simultaneous in-flight requests can be limited.
|
To proactively prevent services from being overwhelmed with high load, the number of allowed simultaneous in-flight requests can be limited.
|
||||||
|
|
||||||
@@ -115,7 +120,7 @@ http:
|
|||||||
### `sourceCriterion`
|
### `sourceCriterion`
|
||||||
|
|
||||||
The `sourceCriterion` option defines what criterion is used to group requests as originating from a common source.
|
The `sourceCriterion` option defines what criterion is used to group requests as originating from a common source.
|
||||||
The precedence order is `ipStrategy`, then `requestHeaderName`, then `requestHost`.
|
If several strategies are defined at the same time, an error will be raised.
|
||||||
If none are set, the default is to use the `requestHost`.
|
If none are set, the default is to use the `requestHost`.
|
||||||
|
|
||||||
#### `sourceCriterion.ipStrategy`
|
#### `sourceCriterion.ipStrategy`
|
||||||
@@ -1,27 +1,30 @@
|
|||||||
# IPWhiteList
|
---
|
||||||
|
title: "Traefik HTTP Middlewares IPAllowList"
|
||||||
|
description: "Learn how to use IPAllowList in HTTP middleware for limiting clients to specific IPs in Traefik Proxy. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
|
# IPAllowList
|
||||||
|
|
||||||
Limiting Clients to Specific IPs
|
Limiting Clients to Specific IPs
|
||||||
{: .subtitle }
|
{: .subtitle }
|
||||||
|
|
||||||

|
IPAllowList accepts / refuses requests based on the client IP.
|
||||||
|
|
||||||
IPWhitelist accepts / refuses requests based on the client IP.
|
|
||||||
|
|
||||||
## Configuration Examples
|
## Configuration Examples
|
||||||
|
|
||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
# Accepts request from defined IP
|
# Accepts request from defined IP
|
||||||
labels:
|
labels:
|
||||||
- "traefik.http.middlewares.test-ipwhitelist.ipwhitelist.sourcerange=127.0.0.1/32, 192.168.1.7"
|
- "traefik.http.middlewares.test-ipallowlist.ipallowlist.sourcerange=127.0.0.1/32, 192.168.1.7"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="Kubernetes"
|
```yaml tab="Kubernetes"
|
||||||
apiVersion: traefik.containo.us/v1alpha1
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
kind: Middleware
|
kind: Middleware
|
||||||
metadata:
|
metadata:
|
||||||
name: test-ipwhitelist
|
name: test-ipallowlist
|
||||||
spec:
|
spec:
|
||||||
ipWhiteList:
|
ipAllowList:
|
||||||
sourceRange:
|
sourceRange:
|
||||||
- 127.0.0.1/32
|
- 127.0.0.1/32
|
||||||
- 192.168.1.7
|
- 192.168.1.7
|
||||||
@@ -29,27 +32,27 @@ spec:
|
|||||||
|
|
||||||
```yaml tab="Consul Catalog"
|
```yaml tab="Consul Catalog"
|
||||||
# Accepts request from defined IP
|
# Accepts request from defined IP
|
||||||
- "traefik.http.middlewares.test-ipwhitelist.ipwhitelist.sourcerange=127.0.0.1/32, 192.168.1.7"
|
- "traefik.http.middlewares.test-ipallowlist.ipallowlist.sourcerange=127.0.0.1/32, 192.168.1.7"
|
||||||
```
|
```
|
||||||
|
|
||||||
```json tab="Marathon"
|
```json tab="Marathon"
|
||||||
"labels": {
|
"labels": {
|
||||||
"traefik.http.middlewares.test-ipwhitelist.ipwhitelist.sourcerange": "127.0.0.1/32,192.168.1.7"
|
"traefik.http.middlewares.test-ipallowlist.ipallowlist.sourcerange": "127.0.0.1/32,192.168.1.7"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="Rancher"
|
```yaml tab="Rancher"
|
||||||
# Accepts request from defined IP
|
# Accepts request from defined IP
|
||||||
labels:
|
labels:
|
||||||
- "traefik.http.middlewares.test-ipwhitelist.ipwhitelist.sourcerange=127.0.0.1/32, 192.168.1.7"
|
- "traefik.http.middlewares.test-ipallowlist.ipallowlist.sourcerange=127.0.0.1/32, 192.168.1.7"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
# Accepts request from defined IP
|
# Accepts request from defined IP
|
||||||
http:
|
http:
|
||||||
middlewares:
|
middlewares:
|
||||||
test-ipwhitelist:
|
test-ipallowlist:
|
||||||
ipWhiteList:
|
ipAllowList:
|
||||||
sourceRange:
|
sourceRange:
|
||||||
- "127.0.0.1/32"
|
- "127.0.0.1/32"
|
||||||
- "192.168.1.7"
|
- "192.168.1.7"
|
||||||
@@ -58,7 +61,7 @@ http:
|
|||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
# Accepts request from defined IP
|
# Accepts request from defined IP
|
||||||
[http.middlewares]
|
[http.middlewares]
|
||||||
[http.middlewares.test-ipwhitelist.ipWhiteList]
|
[http.middlewares.test-ipallowlist.ipAllowList]
|
||||||
sourceRange = ["127.0.0.1/32", "192.168.1.7"]
|
sourceRange = ["127.0.0.1/32", "192.168.1.7"]
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -81,7 +84,7 @@ The `depth` option tells Traefik to use the `X-Forwarded-For` header and take th
|
|||||||
|
|
||||||
!!! example "Examples of Depth & X-Forwarded-For"
|
!!! example "Examples of Depth & X-Forwarded-For"
|
||||||
|
|
||||||
If `depth` is set to 2, and the request `X-Forwarded-For` header is `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` then the "real" client IP is `"10.0.0.1"` (at depth 4) but the IP used for the whitelisting is `"12.0.0.1"` (`depth=2`).
|
If `depth` is set to 2, and the request `X-Forwarded-For` header is `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` then the "real" client IP is `"10.0.0.1"` (at depth 4) but the IP used is `"12.0.0.1"` (`depth=2`).
|
||||||
|
|
||||||
| `X-Forwarded-For` | `depth` | clientIP |
|
| `X-Forwarded-For` | `depth` | clientIP |
|
||||||
|-----------------------------------------|---------|--------------|
|
|-----------------------------------------|---------|--------------|
|
||||||
@@ -90,20 +93,20 @@ The `depth` option tells Traefik to use the `X-Forwarded-For` header and take th
|
|||||||
| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `5` | `""` |
|
| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `5` | `""` |
|
||||||
|
|
||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
# Whitelisting Based on `X-Forwarded-For` with `depth=2`
|
# Allowlisting Based on `X-Forwarded-For` with `depth=2`
|
||||||
labels:
|
labels:
|
||||||
- "traefik.http.middlewares.test-ipwhitelist.ipwhitelist.sourcerange=127.0.0.1/32, 192.168.1.7"
|
- "traefik.http.middlewares.test-ipallowlist.ipallowlist.sourcerange=127.0.0.1/32, 192.168.1.7"
|
||||||
- "traefik.http.middlewares.test-ipwhitelist.ipwhitelist.ipstrategy.depth=2"
|
- "traefik.http.middlewares.test-ipallowlist.ipallowlist.ipstrategy.depth=2"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="Kubernetes"
|
```yaml tab="Kubernetes"
|
||||||
# Whitelisting Based on `X-Forwarded-For` with `depth=2`
|
# Allowlisting Based on `X-Forwarded-For` with `depth=2`
|
||||||
apiVersion: traefik.containo.us/v1alpha1
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
kind: Middleware
|
kind: Middleware
|
||||||
metadata:
|
metadata:
|
||||||
name: test-ipwhitelist
|
name: test-ipallowlist
|
||||||
spec:
|
spec:
|
||||||
ipWhiteList:
|
ipAllowList:
|
||||||
sourceRange:
|
sourceRange:
|
||||||
- 127.0.0.1/32
|
- 127.0.0.1/32
|
||||||
- 192.168.1.7
|
- 192.168.1.7
|
||||||
@@ -112,31 +115,31 @@ spec:
|
|||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="Consul Catalog"
|
```yaml tab="Consul Catalog"
|
||||||
# Whitelisting Based on `X-Forwarded-For` with `depth=2`
|
# Allowlisting Based on `X-Forwarded-For` with `depth=2`
|
||||||
- "traefik.http.middlewares.test-ipwhitelist.ipwhitelist.sourcerange=127.0.0.1/32, 192.168.1.7"
|
- "traefik.http.middlewares.test-ipallowlist.ipallowlist.sourcerange=127.0.0.1/32, 192.168.1.7"
|
||||||
- "traefik.http.middlewares.test-ipwhitelist.ipwhitelist.ipstrategy.depth=2"
|
- "traefik.http.middlewares.test-ipallowlist.ipallowlist.ipstrategy.depth=2"
|
||||||
```
|
```
|
||||||
|
|
||||||
```json tab="Marathon"
|
```json tab="Marathon"
|
||||||
"labels": {
|
"labels": {
|
||||||
"traefik.http.middlewares.test-ipwhitelist.ipwhitelist.sourcerange": "127.0.0.1/32, 192.168.1.7",
|
"traefik.http.middlewares.test-ipallowlist.ipallowlist.sourcerange": "127.0.0.1/32, 192.168.1.7",
|
||||||
"traefik.http.middlewares.test-ipwhitelist.ipwhitelist.ipstrategy.depth": "2"
|
"traefik.http.middlewares.test-ipallowlist.ipallowlist.ipstrategy.depth": "2"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="Rancher"
|
```yaml tab="Rancher"
|
||||||
# Whitelisting Based on `X-Forwarded-For` with `depth=2`
|
# Allowlisting Based on `X-Forwarded-For` with `depth=2`
|
||||||
labels:
|
labels:
|
||||||
- "traefik.http.middlewares.test-ipwhitelist.ipwhitelist.sourcerange=127.0.0.1/32, 192.168.1.7"
|
- "traefik.http.middlewares.test-ipallowlist.ipallowlist.sourcerange=127.0.0.1/32, 192.168.1.7"
|
||||||
- "traefik.http.middlewares.test-ipwhitelist.ipwhitelist.ipstrategy.depth=2"
|
- "traefik.http.middlewares.test-ipallowlist.ipallowlist.ipstrategy.depth=2"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
# Whitelisting Based on `X-Forwarded-For` with `depth=2`
|
# Allowlisting Based on `X-Forwarded-For` with `depth=2`
|
||||||
http:
|
http:
|
||||||
middlewares:
|
middlewares:
|
||||||
test-ipwhitelist:
|
test-ipallowlist:
|
||||||
ipWhiteList:
|
ipAllowList:
|
||||||
sourceRange:
|
sourceRange:
|
||||||
- "127.0.0.1/32"
|
- "127.0.0.1/32"
|
||||||
- "192.168.1.7"
|
- "192.168.1.7"
|
||||||
@@ -145,11 +148,11 @@ http:
|
|||||||
```
|
```
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
# Whitelisting Based on `X-Forwarded-For` with `depth=2`
|
# Allowlisting Based on `X-Forwarded-For` with `depth=2`
|
||||||
[http.middlewares]
|
[http.middlewares]
|
||||||
[http.middlewares.test-ipwhitelist.ipWhiteList]
|
[http.middlewares.test-ipallowlist.ipAllowList]
|
||||||
sourceRange = ["127.0.0.1/32", "192.168.1.7"]
|
sourceRange = ["127.0.0.1/32", "192.168.1.7"]
|
||||||
[http.middlewares.test-ipwhitelist.ipWhiteList.ipStrategy]
|
[http.middlewares.test-ipallowlist.ipAllowList.ipStrategy]
|
||||||
depth = 2
|
depth = 2
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -172,7 +175,7 @@ http:
|
|||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
# Exclude from `X-Forwarded-For`
|
# Exclude from `X-Forwarded-For`
|
||||||
labels:
|
labels:
|
||||||
- "traefik.http.middlewares.test-ipwhitelist.ipwhitelist.ipstrategy.excludedips=127.0.0.1/32, 192.168.1.7"
|
- "traefik.http.middlewares.test-ipallowlist.ipallowlist.ipstrategy.excludedips=127.0.0.1/32, 192.168.1.7"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="Kubernetes"
|
```yaml tab="Kubernetes"
|
||||||
@@ -180,9 +183,9 @@ labels:
|
|||||||
apiVersion: traefik.containo.us/v1alpha1
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
kind: Middleware
|
kind: Middleware
|
||||||
metadata:
|
metadata:
|
||||||
name: test-ipwhitelist
|
name: test-ipallowlist
|
||||||
spec:
|
spec:
|
||||||
ipWhiteList:
|
ipAllowList:
|
||||||
ipStrategy:
|
ipStrategy:
|
||||||
excludedIPs:
|
excludedIPs:
|
||||||
- 127.0.0.1/32
|
- 127.0.0.1/32
|
||||||
@@ -191,27 +194,27 @@ spec:
|
|||||||
|
|
||||||
```yaml tab="Consul Catalog"
|
```yaml tab="Consul Catalog"
|
||||||
# Exclude from `X-Forwarded-For`
|
# Exclude from `X-Forwarded-For`
|
||||||
- "traefik.http.middlewares.test-ipwhitelist.ipwhitelist.ipstrategy.excludedips=127.0.0.1/32, 192.168.1.7"
|
- "traefik.http.middlewares.test-ipallowlist.ipallowlist.ipstrategy.excludedips=127.0.0.1/32, 192.168.1.7"
|
||||||
```
|
```
|
||||||
|
|
||||||
```json tab="Marathon"
|
```json tab="Marathon"
|
||||||
"labels": {
|
"labels": {
|
||||||
"traefik.http.middlewares.test-ipwhitelist.ipwhitelist.ipstrategy.excludedips": "127.0.0.1/32, 192.168.1.7"
|
"traefik.http.middlewares.test-ipallowlist.ipallowlist.ipstrategy.excludedips": "127.0.0.1/32, 192.168.1.7"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="Rancher"
|
```yaml tab="Rancher"
|
||||||
# Exclude from `X-Forwarded-For`
|
# Exclude from `X-Forwarded-For`
|
||||||
labels:
|
labels:
|
||||||
- "traefik.http.middlewares.test-ipwhitelist.ipwhitelist.ipstrategy.excludedips=127.0.0.1/32, 192.168.1.7"
|
- "traefik.http.middlewares.test-ipallowlist.ipallowlist.ipstrategy.excludedips=127.0.0.1/32, 192.168.1.7"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
# Exclude from `X-Forwarded-For`
|
# Exclude from `X-Forwarded-For`
|
||||||
http:
|
http:
|
||||||
middlewares:
|
middlewares:
|
||||||
test-ipwhitelist:
|
test-ipallowlist:
|
||||||
ipWhiteList:
|
ipAllowList:
|
||||||
ipStrategy:
|
ipStrategy:
|
||||||
excludedIPs:
|
excludedIPs:
|
||||||
- "127.0.0.1/32"
|
- "127.0.0.1/32"
|
||||||
@@ -221,7 +224,7 @@ http:
|
|||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
# Exclude from `X-Forwarded-For`
|
# Exclude from `X-Forwarded-For`
|
||||||
[http.middlewares]
|
[http.middlewares]
|
||||||
[http.middlewares.test-ipwhitelist.ipWhiteList]
|
[http.middlewares.test-ipallowlist.ipAllowList]
|
||||||
[http.middlewares.test-ipwhitelist.ipWhiteList.ipStrategy]
|
[http.middlewares.test-ipallowlist.ipAllowList.ipStrategy]
|
||||||
excludedIPs = ["127.0.0.1/32", "192.168.1.7"]
|
excludedIPs = ["127.0.0.1/32", "192.168.1.7"]
|
||||||
```
|
```
|
||||||
161
docs/content/middlewares/http/overview.md
Normal file
161
docs/content/middlewares/http/overview.md
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik Proxy HTTP Middleware Overview"
|
||||||
|
description: "Read the official Traefik Proxy documentation for an overview of the available HTTP middleware."
|
||||||
|
---
|
||||||
|
|
||||||
|
# HTTP Middlewares
|
||||||
|
|
||||||
|
Controlling connections
|
||||||
|
{: .subtitle }
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Configuration Example
|
||||||
|
|
||||||
|
```yaml tab="Docker"
|
||||||
|
# As a Docker Label
|
||||||
|
whoami:
|
||||||
|
# A container that exposes an API to show its IP address
|
||||||
|
image: traefik/whoami
|
||||||
|
labels:
|
||||||
|
# Create a middleware named `foo-add-prefix`
|
||||||
|
- "traefik.http.middlewares.foo-add-prefix.addprefix.prefix=/foo"
|
||||||
|
# Apply the middleware named `foo-add-prefix` to the router named `router1`
|
||||||
|
- "traefik.http.routers.router1.middlewares=foo-add-prefix@docker"
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Kubernetes IngressRoute"
|
||||||
|
# As a Kubernetes Traefik IngressRoute
|
||||||
|
apiVersion: apiextensions.k8s.io/v1beta1
|
||||||
|
kind: CustomResourceDefinition
|
||||||
|
metadata:
|
||||||
|
name: middlewares.traefik.containo.us
|
||||||
|
spec:
|
||||||
|
group: traefik.containo.us
|
||||||
|
version: v1alpha1
|
||||||
|
names:
|
||||||
|
kind: Middleware
|
||||||
|
plural: middlewares
|
||||||
|
singular: middleware
|
||||||
|
scope: Namespaced
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: Middleware
|
||||||
|
metadata:
|
||||||
|
name: stripprefix
|
||||||
|
spec:
|
||||||
|
stripPrefix:
|
||||||
|
prefixes:
|
||||||
|
- /stripit
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: ingressroute
|
||||||
|
spec:
|
||||||
|
# more fields...
|
||||||
|
routes:
|
||||||
|
# more fields...
|
||||||
|
middlewares:
|
||||||
|
- name: stripprefix
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Consul Catalog"
|
||||||
|
# Create a middleware named `foo-add-prefix`
|
||||||
|
- "traefik.http.middlewares.foo-add-prefix.addprefix.prefix=/foo"
|
||||||
|
# Apply the middleware named `foo-add-prefix` to the router named `router1`
|
||||||
|
- "traefik.http.routers.router1.middlewares=foo-add-prefix@consulcatalog"
|
||||||
|
```
|
||||||
|
|
||||||
|
```json tab="Marathon"
|
||||||
|
"labels": {
|
||||||
|
"traefik.http.middlewares.foo-add-prefix.addprefix.prefix": "/foo",
|
||||||
|
"traefik.http.routers.router1.middlewares": "foo-add-prefix@marathon"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Rancher"
|
||||||
|
# As a Rancher Label
|
||||||
|
labels:
|
||||||
|
# Create a middleware named `foo-add-prefix`
|
||||||
|
- "traefik.http.middlewares.foo-add-prefix.addprefix.prefix=/foo"
|
||||||
|
# Apply the middleware named `foo-add-prefix` to the router named `router1`
|
||||||
|
- "traefik.http.routers.router1.middlewares=foo-add-prefix@rancher"
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
# As TOML Configuration File
|
||||||
|
[http.routers]
|
||||||
|
[http.routers.router1]
|
||||||
|
service = "service1"
|
||||||
|
middlewares = ["foo-add-prefix"]
|
||||||
|
rule = "Host(`example.com`)"
|
||||||
|
|
||||||
|
[http.middlewares]
|
||||||
|
[http.middlewares.foo-add-prefix.addPrefix]
|
||||||
|
prefix = "/foo"
|
||||||
|
|
||||||
|
[http.services]
|
||||||
|
[http.services.service1]
|
||||||
|
[http.services.service1.loadBalancer]
|
||||||
|
|
||||||
|
[[http.services.service1.loadBalancer.servers]]
|
||||||
|
url = "http://127.0.0.1:80"
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
# As YAML Configuration File
|
||||||
|
http:
|
||||||
|
routers:
|
||||||
|
router1:
|
||||||
|
service: service1
|
||||||
|
middlewares:
|
||||||
|
- "foo-add-prefix"
|
||||||
|
rule: "Host(`example.com`)"
|
||||||
|
|
||||||
|
middlewares:
|
||||||
|
foo-add-prefix:
|
||||||
|
addPrefix:
|
||||||
|
prefix: "/foo"
|
||||||
|
|
||||||
|
services:
|
||||||
|
service1:
|
||||||
|
loadBalancer:
|
||||||
|
servers:
|
||||||
|
- url: "http://127.0.0.1:80"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Available HTTP Middlewares
|
||||||
|
|
||||||
|
| Middleware | Purpose | Area |
|
||||||
|
|-------------------------------------------|---------------------------------------------------|-----------------------------|
|
||||||
|
| [AddPrefix](addprefix.md) | Adds a Path Prefix | Path Modifier |
|
||||||
|
| [BasicAuth](basicauth.md) | Adds Basic Authentication | Security, Authentication |
|
||||||
|
| [Buffering](buffering.md) | Buffers the request/response | Request Lifecycle |
|
||||||
|
| [Chain](chain.md) | Combines multiple pieces of middleware | Misc |
|
||||||
|
| [CircuitBreaker](circuitbreaker.md) | Prevents calling unhealthy services | Request Lifecycle |
|
||||||
|
| [Compress](compress.md) | Compresses the response | Content Modifier |
|
||||||
|
| [ContentType](contenttype.md) | Handles Content-Type auto-detection | Misc |
|
||||||
|
| [DigestAuth](digestauth.md) | Adds Digest Authentication | Security, Authentication |
|
||||||
|
| [Errors](errorpages.md) | Defines custom error pages | Request Lifecycle |
|
||||||
|
| [ForwardAuth](forwardauth.md) | Delegates Authentication | Security, Authentication |
|
||||||
|
| [Headers](headers.md) | Adds / Updates headers | Security |
|
||||||
|
| [IPAllowList](ipallowlist.md) | Limits the allowed client IPs | Security, Request lifecycle |
|
||||||
|
| [InFlightReq](inflightreq.md) | Limits the number of simultaneous connections | Security, Request lifecycle |
|
||||||
|
| [PassTLSClientCert](passtlsclientcert.md) | Adds Client Certificates in a Header | Security |
|
||||||
|
| [RateLimit](ratelimit.md) | Limits the call frequency | Security, Request lifecycle |
|
||||||
|
| [RedirectScheme](redirectscheme.md) | Redirects based on scheme | Request lifecycle |
|
||||||
|
| [RedirectRegex](redirectregex.md) | Redirects based on regex | Request lifecycle |
|
||||||
|
| [ReplacePath](replacepath.md) | Changes the path of the request | Path Modifier |
|
||||||
|
| [ReplacePathRegex](replacepathregex.md) | Changes the path of the request | Path Modifier |
|
||||||
|
| [Retry](retry.md) | Automatically retries in case of error | Request lifecycle |
|
||||||
|
| [StripPrefix](stripprefix.md) | Changes the path of the request | Path Modifier |
|
||||||
|
| [StripPrefixRegex](stripprefixregex.md) | Changes the path of the request | Path Modifier |
|
||||||
|
|
||||||
|
## Community Middlewares
|
||||||
|
|
||||||
|
Please take a look at the community-contributed plugins in the [plugin catalog](https://plugins.traefik.io/plugins).
|
||||||
|
|
||||||
|
{!traefik-for-business-applications.md!}
|
||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik PassTLSClientCert Documentation"
|
||||||
|
description: "In Traefik Proxy's HTTP middleware, the PassTLSClientCert adds selected data from passed client TLS certificates to headers. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
# PassTLSClientCert
|
# PassTLSClientCert
|
||||||
|
|
||||||
Adding Client Certificates in a Header
|
Adding Client Certificates in a Header
|
||||||
@@ -11,10 +16,10 @@ PassTLSClientCert adds the selected data from the passed client TLS certificate
|
|||||||
|
|
||||||
## Configuration Examples
|
## Configuration Examples
|
||||||
|
|
||||||
Pass the escaped pem in the `X-Forwarded-Tls-Client-Cert` header.
|
Pass the pem in the `X-Forwarded-Tls-Client-Cert` header.
|
||||||
|
|
||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
# Pass the escaped pem in the `X-Forwarded-Tls-Client-Cert` header.
|
# Pass the pem in the `X-Forwarded-Tls-Client-Cert` header.
|
||||||
labels:
|
labels:
|
||||||
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.pem=true"
|
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.pem=true"
|
||||||
```
|
```
|
||||||
@@ -23,14 +28,14 @@ labels:
|
|||||||
apiVersion: traefik.containo.us/v1alpha1
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
kind: Middleware
|
kind: Middleware
|
||||||
metadata:
|
metadata:
|
||||||
name: addprefix
|
name: test-passtlsclientcert
|
||||||
spec:
|
spec:
|
||||||
passTLSClientCert:
|
passTLSClientCert:
|
||||||
pem: true
|
pem: true
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="Consul Catalog"
|
```yaml tab="Consul Catalog"
|
||||||
# Pass the escaped pem in the `X-Forwarded-Tls-Client-Cert` header
|
# Pass the pem in the `X-Forwarded-Tls-Client-Cert` header
|
||||||
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.pem=true"
|
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.pem=true"
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -41,13 +46,13 @@ spec:
|
|||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="Rancher"
|
```yaml tab="Rancher"
|
||||||
# Pass the escaped pem in the `X-Forwarded-Tls-Client-Cert` header.
|
# Pass the pem in the `X-Forwarded-Tls-Client-Cert` header.
|
||||||
labels:
|
labels:
|
||||||
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.pem=true"
|
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.pem=true"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
# Pass the escaped pem in the `X-Forwarded-Tls-Client-Cert` header.
|
# Pass the pem in the `X-Forwarded-Tls-Client-Cert` header.
|
||||||
http:
|
http:
|
||||||
middlewares:
|
middlewares:
|
||||||
test-passtlsclientcert:
|
test-passtlsclientcert:
|
||||||
@@ -56,13 +61,13 @@ http:
|
|||||||
```
|
```
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
# Pass the escaped pem in the `X-Forwarded-Tls-Client-Cert` header.
|
# Pass the pem in the `X-Forwarded-Tls-Client-Cert` header.
|
||||||
[http.middlewares]
|
[http.middlewares]
|
||||||
[http.middlewares.test-passtlsclientcert.passTLSClientCert]
|
[http.middlewares.test-passtlsclientcert.passTLSClientCert]
|
||||||
pem = true
|
pem = true
|
||||||
```
|
```
|
||||||
|
|
||||||
??? example "Pass the escaped pem in the `X-Forwarded-Tls-Client-Cert` header"
|
??? example "Pass the pem in the `X-Forwarded-Tls-Client-Cert` header"
|
||||||
|
|
||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
# Pass all the available info in the `X-Forwarded-Tls-Client-Cert-Info` header
|
# Pass all the available info in the `X-Forwarded-Tls-Client-Cert-Info` header
|
||||||
@@ -76,6 +81,7 @@ http:
|
|||||||
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.domaincomponent=true"
|
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.domaincomponent=true"
|
||||||
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.locality=true"
|
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.locality=true"
|
||||||
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organization=true"
|
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organization=true"
|
||||||
|
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organizationalunit=true"
|
||||||
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.province=true"
|
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.province=true"
|
||||||
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.serialnumber=true"
|
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.serialnumber=true"
|
||||||
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.commonname=true"
|
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.commonname=true"
|
||||||
@@ -104,6 +110,7 @@ http:
|
|||||||
province: true
|
province: true
|
||||||
locality: true
|
locality: true
|
||||||
organization: true
|
organization: true
|
||||||
|
organizationalUnit: true
|
||||||
commonName: true
|
commonName: true
|
||||||
serialNumber: true
|
serialNumber: true
|
||||||
domainComponent: true
|
domainComponent: true
|
||||||
@@ -127,6 +134,7 @@ http:
|
|||||||
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.domaincomponent=true"
|
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.domaincomponent=true"
|
||||||
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.locality=true"
|
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.locality=true"
|
||||||
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organization=true"
|
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organization=true"
|
||||||
|
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organizationalunit=true"
|
||||||
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.province=true"
|
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.province=true"
|
||||||
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.serialnumber=true"
|
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.serialnumber=true"
|
||||||
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.commonname=true"
|
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.commonname=true"
|
||||||
@@ -148,6 +156,7 @@ http:
|
|||||||
"traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.domaincomponent": "true",
|
"traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.domaincomponent": "true",
|
||||||
"traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.locality": "true",
|
"traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.locality": "true",
|
||||||
"traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organization": "true",
|
"traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organization": "true",
|
||||||
|
"traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organizationalunit": "true",
|
||||||
"traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.province": "true",
|
"traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.province": "true",
|
||||||
"traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.serialnumber": "true",
|
"traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.serialnumber": "true",
|
||||||
"traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.commonname": "true",
|
"traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.commonname": "true",
|
||||||
@@ -171,6 +180,7 @@ http:
|
|||||||
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.domaincomponent=true"
|
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.domaincomponent=true"
|
||||||
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.locality=true"
|
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.locality=true"
|
||||||
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organization=true"
|
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organization=true"
|
||||||
|
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.organizationalunit=true"
|
||||||
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.province=true"
|
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.province=true"
|
||||||
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.serialnumber=true"
|
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.subject.serialnumber=true"
|
||||||
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.commonname=true"
|
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.info.issuer.commonname=true"
|
||||||
@@ -197,6 +207,7 @@ http:
|
|||||||
province: true
|
province: true
|
||||||
locality: true
|
locality: true
|
||||||
organization: true
|
organization: true
|
||||||
|
organizationalUnit: true
|
||||||
commonName: true
|
commonName: true
|
||||||
serialNumber: true
|
serialNumber: true
|
||||||
domainComponent: true
|
domainComponent: true
|
||||||
@@ -223,6 +234,7 @@ http:
|
|||||||
province = true
|
province = true
|
||||||
locality = true
|
locality = true
|
||||||
organization = true
|
organization = true
|
||||||
|
organizationalUnit = true
|
||||||
commonName = true
|
commonName = true
|
||||||
serialNumber = true
|
serialNumber = true
|
||||||
domainComponent = true
|
domainComponent = true
|
||||||
@@ -242,13 +254,13 @@ http:
|
|||||||
|
|
||||||
PassTLSClientCert can add two headers to the request:
|
PassTLSClientCert can add two headers to the request:
|
||||||
|
|
||||||
- `X-Forwarded-Tls-Client-Cert` that contains the escaped pem.
|
- `X-Forwarded-Tls-Client-Cert` that contains the pem.
|
||||||
- `X-Forwarded-Tls-Client-Cert-Info` that contains all the selected certificate information in an escaped string.
|
- `X-Forwarded-Tls-Client-Cert-Info` that contains all the selected certificate information in an escaped string.
|
||||||
|
|
||||||
!!! info
|
!!! info
|
||||||
|
|
||||||
* The headers are filled with escaped string so it can be safely placed inside a URL query.
|
* `X-Forwarded-Tls-Client-Cert-Info` header value is a string that has been escaped in order to be a valid URL query.
|
||||||
* These options only work accordingly to the [MutualTLS configuration](../https/tls.md#client-authentication-mtls).
|
* These options only work accordingly to the [MutualTLS configuration](../../https/tls.md#client-authentication-mtls).
|
||||||
That is to say, only the certificates that match the `clientAuth.clientAuthType` policy are passed.
|
That is to say, only the certificates that match the `clientAuth.clientAuthType` policy are passed.
|
||||||
|
|
||||||
The following example shows a complete certificate and explains each of the middleware options.
|
The following example shows a complete certificate and explains each of the middleware options.
|
||||||
@@ -359,7 +371,7 @@ The following example shows a complete certificate and explains each of the midd
|
|||||||
|
|
||||||
### `pem`
|
### `pem`
|
||||||
|
|
||||||
The `pem` option sets the `X-Forwarded-Tls-Client-Cert` header with the escaped certificate.
|
The `pem` option sets the `X-Forwarded-Tls-Client-Cert` header with the certificate.
|
||||||
|
|
||||||
In the example, it is the part between `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----` delimiters:
|
In the example, it is the part between `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----` delimiters:
|
||||||
|
|
||||||
@@ -412,15 +424,18 @@ In the example, it is the part between `-----BEGIN CERTIFICATE-----` and `-----E
|
|||||||
!!! warning "`X-Forwarded-Tls-Client-Cert` value could exceed the web server header size limit"
|
!!! warning "`X-Forwarded-Tls-Client-Cert` value could exceed the web server header size limit"
|
||||||
|
|
||||||
The header size limit of web servers is commonly between 4kb and 8kb.
|
The header size limit of web servers is commonly between 4kb and 8kb.
|
||||||
You could change the server configuration to allow bigger header or use the `info` option with the needed field(s).
|
If that turns out to be a problem, and if reconfiguring the server to allow larger headers is not an option,
|
||||||
|
one can alleviate the problem by selecting only the interesting parts of the cert,
|
||||||
|
through the use of the `info` options described below. (And by setting `pem` to false).
|
||||||
|
|
||||||
### `info`
|
### `info`
|
||||||
|
|
||||||
The `info` option selects the specific client certificate details you want to add to the `X-Forwarded-Tls-Client-Cert-Info` header.
|
The `info` option selects the specific client certificate details you want to add to the `X-Forwarded-Tls-Client-Cert-Info` header.
|
||||||
|
|
||||||
The value of the header is an escaped concatenation of all the selected certificate details.
|
The value of the header is an escaped concatenation of all the selected certificate details.
|
||||||
|
But in the following, unless specified otherwise, all the header values examples are shown unescaped, for readability.
|
||||||
|
|
||||||
The following example shows an unescaped result that uses all the available fields:
|
The following example shows such a concatenation, when all the available fields are selected:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Subject="DC=org,DC=cheese,C=FR,C=US,ST=Cheese org state,ST=Cheese com state,L=TOULOUSE,L=LYON,O=Cheese,O=Cheese 2,CN=*.example.com";Issuer="DC=org,DC=cheese,C=FR,C=US,ST=Signing State,ST=Signing State 2,L=TOULOUSE,L=LYON,O=Cheese,O=Cheese 2,CN=Simple Signing CA 2";NB="1544094616";NA="1607166616";SAN="*.example.org,*.example.net,*.example.com,test@example.org,test@example.net,10.0.1.0,10.0.1.2"
|
Subject="DC=org,DC=cheese,C=FR,C=US,ST=Cheese org state,ST=Cheese com state,L=TOULOUSE,L=LYON,O=Cheese,O=Cheese 2,CN=*.example.com";Issuer="DC=org,DC=cheese,C=FR,C=US,ST=Signing State,ST=Signing State 2,L=TOULOUSE,L=LYON,O=Cheese,O=Cheese 2,CN=Simple Signing CA 2";NB="1544094616";NA="1607166616";SAN="*.example.org,*.example.net,*.example.com,test@example.org,test@example.net,10.0.1.0,10.0.1.2"
|
||||||
@@ -430,6 +445,23 @@ Subject="DC=org,DC=cheese,C=FR,C=US,ST=Cheese org state,ST=Cheese com state,L=TO
|
|||||||
|
|
||||||
If there are more than one certificate, they are separated by a `,`.
|
If there are more than one certificate, they are separated by a `,`.
|
||||||
|
|
||||||
|
#### `info.serialNumber`
|
||||||
|
|
||||||
|
Set the `info.serialNumber` option to `true` to add the `Serial Number` of the certificate.
|
||||||
|
|
||||||
|
The data is taken from the following certificate part:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Serial Number:
|
||||||
|
6a:2f:20:f8:ce:8d:48:52:ba:d9:bb:be:60:ec:bf:79
|
||||||
|
```
|
||||||
|
|
||||||
|
And it is formatted as follows in the header (decimal representation):
|
||||||
|
|
||||||
|
```text
|
||||||
|
SerialNumber="141142874255168551917600297745052909433"
|
||||||
|
```
|
||||||
|
|
||||||
#### `info.notAfter`
|
#### `info.notAfter`
|
||||||
|
|
||||||
Set the `info.notAfter` option to `true` to add the `Not After` information from the `Validity` part.
|
Set the `info.notAfter` option to `true` to add the `Not After` information from the `Validity` part.
|
||||||
@@ -437,11 +469,11 @@ Set the `info.notAfter` option to `true` to add the `Not After` information from
|
|||||||
The data is taken from the following certificate part:
|
The data is taken from the following certificate part:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Validity
|
Validity
|
||||||
Not After : Dec 5 11:10:16 2020 GMT
|
Not After : Dec 5 11:10:16 2020 GMT
|
||||||
```
|
```
|
||||||
|
|
||||||
The escaped `notAfter` info part is formatted as below:
|
And it is formatted as follows in the header:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
NA="1607166616"
|
NA="1607166616"
|
||||||
@@ -458,7 +490,7 @@ Validity
|
|||||||
Not Before: Dec 6 11:10:16 2018 GMT
|
Not Before: Dec 6 11:10:16 2018 GMT
|
||||||
```
|
```
|
||||||
|
|
||||||
The escaped `notBefore` info part is formatted as below:
|
And it is formatted as follows in the header:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
NB="1544094616"
|
NB="1544094616"
|
||||||
@@ -471,11 +503,11 @@ Set the `info.sans` option to `true` to add the `Subject Alternative Name` infor
|
|||||||
The data is taken from the following certificate part:
|
The data is taken from the following certificate part:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
X509v3 Subject Alternative Name:
|
X509v3 Subject Alternative Name:
|
||||||
DNS:*.example.org, DNS:*.example.net, DNS:*.example.com, IP Address:10.0.1.0, IP Address:10.0.1.2, email:test@example.org, email:test@example.net
|
DNS:*.example.org, DNS:*.example.net, DNS:*.example.com, IP Address:10.0.1.0, IP Address:10.0.1.2, email:test@example.org, email:test@example.net
|
||||||
```
|
```
|
||||||
|
|
||||||
The escape SANs info part is formatted as below:
|
And it is formatted as follows in the header:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
SAN="*.example.org,*.example.net,*.example.com,test@example.org,test@example.net,10.0.1.0,10.0.1.2"
|
SAN="*.example.org,*.example.net,*.example.com,test@example.org,test@example.net,10.0.1.0,10.0.1.2"
|
||||||
@@ -501,7 +533,7 @@ Set the `info.subject.country` option to `true` to add the `country` information
|
|||||||
|
|
||||||
The data is taken from the subject part with the `C` key.
|
The data is taken from the subject part with the `C` key.
|
||||||
|
|
||||||
The escape country info in the subject part is formatted as below:
|
And it is formatted as follows in the header:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
C=FR,C=US
|
C=FR,C=US
|
||||||
@@ -513,7 +545,7 @@ Set the `info.subject.province` option to `true` to add the `province` informati
|
|||||||
|
|
||||||
The data is taken from the subject part with the `ST` key.
|
The data is taken from the subject part with the `ST` key.
|
||||||
|
|
||||||
The escape province info in the subject part is formatted as below:
|
And it is formatted as follows in the header:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
ST=Cheese org state,ST=Cheese com state
|
ST=Cheese org state,ST=Cheese com state
|
||||||
@@ -525,7 +557,7 @@ Set the `info.subject.locality` option to `true` to add the `locality` informati
|
|||||||
|
|
||||||
The data is taken from the subject part with the `L` key.
|
The data is taken from the subject part with the `L` key.
|
||||||
|
|
||||||
The escape locality info in the subject part is formatted as below:
|
And it is formatted as follows in the header:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
L=TOULOUSE,L=LYON
|
L=TOULOUSE,L=LYON
|
||||||
@@ -537,19 +569,31 @@ Set the `info.subject.organization` option to `true` to add the `organization` i
|
|||||||
|
|
||||||
The data is taken from the subject part with the `O` key.
|
The data is taken from the subject part with the `O` key.
|
||||||
|
|
||||||
The escape organization info in the subject part is formatted as below:
|
And it is formatted as follows in the header:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
O=Cheese,O=Cheese 2
|
O=Cheese,O=Cheese 2
|
||||||
```
|
```
|
||||||
|
|
||||||
|
##### `info.subject.organizationalUnit`
|
||||||
|
|
||||||
|
Set the `info.subject.organizationalUnit` option to `true` to add the `organizationalUnit` information into the subject.
|
||||||
|
|
||||||
|
The data is taken from the subject part with the `OU` key.
|
||||||
|
|
||||||
|
And it is formatted as follows in the header:
|
||||||
|
|
||||||
|
```text
|
||||||
|
OU=Cheese Section,OU=Cheese Section 2
|
||||||
|
```
|
||||||
|
|
||||||
##### `info.subject.commonName`
|
##### `info.subject.commonName`
|
||||||
|
|
||||||
Set the `info.subject.commonName` option to `true` to add the `commonName` information into the subject.
|
Set the `info.subject.commonName` option to `true` to add the `commonName` information into the subject.
|
||||||
|
|
||||||
The data is taken from the subject part with the `CN` key.
|
The data is taken from the subject part with the `CN` key.
|
||||||
|
|
||||||
The escape common name info in the subject part is formatted as below:
|
And it is formatted as follows in the header:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
CN=*.example.com
|
CN=*.example.com
|
||||||
@@ -561,7 +605,7 @@ Set the `info.subject.serialNumber` option to `true` to add the `serialNumber` i
|
|||||||
|
|
||||||
The data is taken from the subject part with the `SN` key.
|
The data is taken from the subject part with the `SN` key.
|
||||||
|
|
||||||
The escape serial number info in the subject part is formatted as below:
|
And it is formatted as follows in the header:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
SN=1234567890
|
SN=1234567890
|
||||||
@@ -573,7 +617,7 @@ Set the `info.subject.domainComponent` option to `true` to add the `domainCompon
|
|||||||
|
|
||||||
The data is taken from the subject part with the `DC` key.
|
The data is taken from the subject part with the `DC` key.
|
||||||
|
|
||||||
The escape domain component info in the subject part is formatted as below:
|
And it is formatted as follows in the header:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
DC=org,DC=cheese
|
DC=org,DC=cheese
|
||||||
@@ -595,7 +639,7 @@ Set the `info.issuer.country` option to `true` to add the `country` information
|
|||||||
|
|
||||||
The data is taken from the issuer part with the `C` key.
|
The data is taken from the issuer part with the `C` key.
|
||||||
|
|
||||||
The escape country info in the issuer part is formatted as below:
|
And it is formatted as follows in the header:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
C=FR,C=US
|
C=FR,C=US
|
||||||
@@ -607,7 +651,7 @@ Set the `info.issuer.province` option to `true` to add the `province` informatio
|
|||||||
|
|
||||||
The data is taken from the issuer part with the `ST` key.
|
The data is taken from the issuer part with the `ST` key.
|
||||||
|
|
||||||
The escape province info in the issuer part is formatted as below:
|
And it is formatted as follows in the header:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
ST=Signing State,ST=Signing State 2
|
ST=Signing State,ST=Signing State 2
|
||||||
@@ -619,7 +663,7 @@ Set the `info.issuer.locality` option to `true` to add the `locality` informatio
|
|||||||
|
|
||||||
The data is taken from the issuer part with the `L` key.
|
The data is taken from the issuer part with the `L` key.
|
||||||
|
|
||||||
The escape locality info in the issuer part is formatted as below:
|
And it is formatted as follows in the header:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
L=TOULOUSE,L=LYON
|
L=TOULOUSE,L=LYON
|
||||||
@@ -631,7 +675,7 @@ Set the `info.issuer.organization` option to `true` to add the `organization` in
|
|||||||
|
|
||||||
The data is taken from the issuer part with the `O` key.
|
The data is taken from the issuer part with the `O` key.
|
||||||
|
|
||||||
The escape organization info in the issuer part is formatted as below:
|
And it is formatted as follows in the header:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
O=Cheese,O=Cheese 2
|
O=Cheese,O=Cheese 2
|
||||||
@@ -643,7 +687,7 @@ Set the `info.issuer.commonName` option to `true` to add the `commonName` inform
|
|||||||
|
|
||||||
The data is taken from the issuer part with the `CN` key.
|
The data is taken from the issuer part with the `CN` key.
|
||||||
|
|
||||||
The escape common name info in the issuer part is formatted as below:
|
And it is formatted as follows in the header:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
CN=Simple Signing CA 2
|
CN=Simple Signing CA 2
|
||||||
@@ -655,7 +699,7 @@ Set the `info.issuer.serialNumber` option to `true` to add the `serialNumber` in
|
|||||||
|
|
||||||
The data is taken from the issuer part with the `SN` key.
|
The data is taken from the issuer part with the `SN` key.
|
||||||
|
|
||||||
The escape serial number info in the issuer part is formatted as below:
|
And it is formatted as follows in the header:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
SN=1234567890
|
SN=1234567890
|
||||||
@@ -667,7 +711,7 @@ Set the `info.issuer.domainComponent` option to `true` to add the `domainCompone
|
|||||||
|
|
||||||
The data is taken from the issuer part with the `DC` key.
|
The data is taken from the issuer part with the `DC` key.
|
||||||
|
|
||||||
The escape domain component info in the issuer part is formatted as below:
|
And it is formatted as follows in the header:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
DC=org,DC=cheese
|
DC=org,DC=cheese
|
||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik RateLimit Documentation"
|
||||||
|
description: "Traefik Proxy's HTTP RateLimit middleware ensures Services receive fair amounts of requests. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
# RateLimit
|
# RateLimit
|
||||||
|
|
||||||
To Control the Number of Requests Going to a Service
|
To Control the Number of Requests Going to a Service
|
||||||
@@ -250,7 +255,7 @@ http:
|
|||||||
### `sourceCriterion`
|
### `sourceCriterion`
|
||||||
|
|
||||||
The `sourceCriterion` option defines what criterion is used to group requests as originating from a common source.
|
The `sourceCriterion` option defines what criterion is used to group requests as originating from a common source.
|
||||||
The precedence order is `ipStrategy`, then `requestHeaderName`, then `requestHost`.
|
If several strategies are defined at the same time, an error will be raised.
|
||||||
If none are set, the default is to use the request's remote address field (as an `ipStrategy`).
|
If none are set, the default is to use the request's remote address field (as an `ipStrategy`).
|
||||||
|
|
||||||
#### `sourceCriterion.ipStrategy`
|
#### `sourceCriterion.ipStrategy`
|
||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik RedirectRegex Documentation"
|
||||||
|
description: "In Traefik Proxy's HTTP middleware, RedirectRegex redirecting clients to different locations. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
# RedirectRegex
|
# RedirectRegex
|
||||||
|
|
||||||
Redirecting the Client to a Different Location
|
Redirecting the Client to a Different Location
|
||||||
@@ -73,10 +78,6 @@ http:
|
|||||||
|
|
||||||
## Configuration Options
|
## Configuration Options
|
||||||
|
|
||||||
!!! tip
|
|
||||||
|
|
||||||
Regular expressions and replacements can be tested using online tools such as [Go Playground](https://play.golang.org/p/mWU9p-wk2ru) or the [Regex101](https://regex101.com/r/58sIgx/2).
|
|
||||||
|
|
||||||
### `permanent`
|
### `permanent`
|
||||||
|
|
||||||
Set the `permanent` option to `true` to apply a permanent redirection.
|
Set the `permanent` option to `true` to apply a permanent redirection.
|
||||||
@@ -85,6 +86,12 @@ Set the `permanent` option to `true` to apply a permanent redirection.
|
|||||||
|
|
||||||
The `regex` option is the regular expression to match and capture elements from the request URL.
|
The `regex` option is the regular expression to match and capture elements from the request URL.
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
|
||||||
|
Regular expressions and replacements can be tested using online tools such as [Go Playground](https://play.golang.org/p/mWU9p-wk2ru) or the [Regex101](https://regex101.com/r/58sIgx/2).
|
||||||
|
|
||||||
|
When defining a regular expression within YAML, any escaped character needs to be escaped twice: `example\.com` needs to be written as `example\\.com`.
|
||||||
|
|
||||||
### `replacement`
|
### `replacement`
|
||||||
|
|
||||||
The `replacement` option defines how to modify the URL to have the new target URL.
|
The `replacement` option defines how to modify the URL to have the new target URL.
|
||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik RedirectScheme Documentation"
|
||||||
|
description: "In Traefik Proxy's HTTP middleware, RedirectScheme redirects clients to different schemes/ports. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
# RedirectScheme
|
# RedirectScheme
|
||||||
|
|
||||||
Redirecting the Client to a Different Scheme/Port
|
Redirecting the Client to a Different Scheme/Port
|
||||||
@@ -7,7 +12,16 @@ Redirecting the Client to a Different Scheme/Port
|
|||||||
TODO: add schema
|
TODO: add schema
|
||||||
-->
|
-->
|
||||||
|
|
||||||
RedirectScheme redirects requests from a scheme/port to another.
|
The RedirectScheme middleware redirects the request if the request scheme is different from the configured scheme.
|
||||||
|
|
||||||
|
!!! warning "When behind another reverse-proxy"
|
||||||
|
|
||||||
|
When there is at least one other reverse-proxy between the client and Traefik,
|
||||||
|
the other reverse-proxy (i.e. the last hop) needs to be a [trusted](../../routing/entrypoints.md#forwarded-headers) one.
|
||||||
|
|
||||||
|
Otherwise, Traefik would clean up the X-Forwarded headers coming from this last hop,
|
||||||
|
and as the RedirectScheme middleware relies on them to determine the scheme used,
|
||||||
|
it would not function as intended.
|
||||||
|
|
||||||
## Configuration Examples
|
## Configuration Examples
|
||||||
|
|
||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik ReplacePath Documentation"
|
||||||
|
description: "In Traefik Proxy's HTTP middleware, ReplacePath updates paths before forwarding requests. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
# ReplacePath
|
# ReplacePath
|
||||||
|
|
||||||
Updating the Path Before Forwarding the Request
|
Updating the Path Before Forwarding the Request
|
||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik ReplacePathRegex Documentation"
|
||||||
|
description: "In Traefik Proxy's HTTP middleware, ReplacePathRegex updates paths before forwarding requests, using a regex. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
# ReplacePathRegex
|
# ReplacePathRegex
|
||||||
|
|
||||||
Updating the Path Before Forwarding the Request (Using a Regex)
|
Updating the Path Before Forwarding the Request (Using a Regex)
|
||||||
@@ -79,7 +84,9 @@ The ReplacePathRegex middleware will:
|
|||||||
|
|
||||||
!!! tip
|
!!! tip
|
||||||
|
|
||||||
Regular expressions and replacements can be tested using online tools such as [Go Playground](https://play.golang.org/p/mWU9p-wk2ru) or [Regex101](https://regex101.com/r/58sIgx/2).
|
Regular expressions and replacements can be tested using online tools such as [Go Playground](https://play.golang.org/p/mWU9p-wk2ru) or the [Regex101](https://regex101.com/r/58sIgx/2).
|
||||||
|
|
||||||
|
When defining a regular expression within YAML, any escaped character needs to be escaped twice: `example\.com` needs to be written as `example\\.com`.
|
||||||
|
|
||||||
### `regex`
|
### `regex`
|
||||||
|
|
||||||
@@ -1,3 +1,8 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik HTTP Retry Documentation"
|
||||||
|
description: "Configure Traefik Proxy's HTTP Retry middleware, so you can retry requests to a backend server until it succeeds. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
# Retry
|
# Retry
|
||||||
|
|
||||||
Retrying until it Succeeds
|
Retrying until it Succeeds
|
||||||
90
docs/content/middlewares/http/stripprefix.md
Normal file
90
docs/content/middlewares/http/stripprefix.md
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
---
|
||||||
|
title: "Traefik StripPrefix Documentation"
|
||||||
|
description: "In Traefik Proxy's HTTP middleware, StripPrefix removes prefixes from paths before forwarding requests. Read the technical documentation."
|
||||||
|
---
|
||||||
|
|
||||||
|
# StripPrefix
|
||||||
|
|
||||||
|
Removing Prefixes From the Path Before Forwarding the Request
|
||||||
|
{: .subtitle }
|
||||||
|
|
||||||
|
<!--
|
||||||
|
TODO: add schema
|
||||||
|
-->
|
||||||
|
|
||||||
|
Remove the specified prefixes from the URL path.
|
||||||
|
|
||||||
|
## Configuration Examples
|
||||||
|
|
||||||
|
```yaml tab="Docker"
|
||||||
|
# Strip prefix /foobar and /fiibar
|
||||||
|
labels:
|
||||||
|
- "traefik.http.middlewares.test-stripprefix.stripprefix.prefixes=/foobar,/fiibar"
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Kubernetes"
|
||||||
|
# Strip prefix /foobar and /fiibar
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: Middleware
|
||||||
|
metadata:
|
||||||
|
name: test-stripprefix
|
||||||
|
spec:
|
||||||
|
stripPrefix:
|
||||||
|
prefixes:
|
||||||
|
- /foobar
|
||||||
|
- /fiibar
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Consul Catalog"
|
||||||
|
# Strip prefix /foobar and /fiibar
|
||||||
|
- "traefik.http.middlewares.test-stripprefix.stripprefix.prefixes=/foobar,/fiibar"
|
||||||
|
```
|
||||||
|
|
||||||
|
```json tab="Marathon"
|
||||||
|
"labels": {
|
||||||
|
"traefik.http.middlewares.test-stripprefix.stripprefix.prefixes": "/foobar,/fiibar"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Rancher"
|
||||||
|
# Strip prefix /foobar and /fiibar
|
||||||
|
labels:
|
||||||
|
- "traefik.http.middlewares.test-stripprefix.stripprefix.prefixes=/foobar,/fiibar"
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
# Strip prefix /foobar and /fiibar
|
||||||
|
http:
|
||||||
|
middlewares:
|
||||||
|
test-stripprefix:
|
||||||
|
stripPrefix:
|
||||||
|
prefixes:
|
||||||
|
- "/foobar"
|
||||||
|
- "/fiibar"
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
# Strip prefix /foobar and /fiibar
|
||||||
|
[http.middlewares]
|
||||||
|
[http.middlewares.test-stripprefix.stripPrefix]
|
||||||
|
prefixes = ["/foobar", "/fiibar"]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration Options
|
||||||
|
|
||||||
|
### General
|
||||||
|
|
||||||
|
The StripPrefix middleware strips the matching path prefix and stores it in a `X-Forwarded-Prefix` header.
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
|
||||||
|
Use a `StripPrefix` middleware if your backend listens on the root path (`/`) but should be exposed on a specific prefix.
|
||||||
|
|
||||||
|
### `prefixes`
|
||||||
|
|
||||||
|
The `prefixes` option defines the prefixes to strip from the request URL.
|
||||||
|
|
||||||
|
For instance, `/products` also matches `/products/shoes` and `/products/shirts`.
|
||||||
|
|
||||||
|
If your backend is serving assets (e.g., images or JavaScript files), it can use the `X-Forwarded-Prefix` header to properly construct relative URLs.
|
||||||
|
Using the previous example, the backend should return `/products/shoes/image.png` (and not `/image.png`, which Traefik would likely not be able to associate with the same backend).
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user