Compare commits
688 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ec80d1145c | |||
| 2391520b50 | |||
| 6cc3dd8d40 | |||
| bcf768ee09 | |||
| 51b9a37615 | |||
| 26c96a3935 | |||
| cb9e8ab510 | |||
| b46e795f41 | |||
| e53a37b869 | |||
| ad1c1fc2f2 | |||
| 0209f984eb | |||
| e043982244 | |||
| 149e62d6db | |||
| 4ef4c09300 | |||
| d5ad3eb63b | |||
| 8447bfc71e | |||
| dc4b6fe2c6 | |||
| 15ecff2bbd | |||
| 8773d7ead4 | |||
| 74b6408475 | |||
| 708aa38f36 | |||
| 1137f1f807 | |||
| 15c47f9cb4 | |||
| ba8830fdef | |||
| b6bb80f8ff | |||
| 6ddda32184 | |||
| 5404f6fb25 | |||
| 5d123f52e1 | |||
| d653ee3d73 | |||
| f32f05f811 | |||
| 5ea71f1c3a | |||
| 48ba249ba7 | |||
| 281ce42477 | |||
| 4c71ee3d18 | |||
| 2c436f3c23 | |||
| a664812e9c | |||
| 85290af77d | |||
| e38281d8ad | |||
| 4aa82efc76 | |||
| 32da599169 | |||
| 468e0c8d55 | |||
| dcbe752df5 | |||
| 855561306f | |||
| f25d48e039 | |||
| d468a3e4d0 | |||
| a669522eca | |||
| 3697409701 | |||
| 9a276c3aeb | |||
| af31587841 | |||
| 8c05c1b1a7 | |||
| 9c94e3b493 | |||
| fc83948b1e | |||
| 83b36871c3 | |||
| 892bcc288b | |||
| 5026ca97d0 | |||
| 2c03850b90 | |||
| f9d9b72380 | |||
| 29406d4289 | |||
| 6e0198ca1e | |||
| 9d5b03d2ea | |||
| 1db7d439a4 | |||
| ee07d0f4d2 | |||
| 1c9a3d21ed | |||
| 30c118e552 | |||
| 743a63369c | |||
| a6b16426f2 | |||
| ecf5182056 | |||
| d58fd9ac89 | |||
| 8d2e7db6e3 | |||
| eec68dce06 | |||
| 6736725654 | |||
| f2b11cd50d | |||
| f3c6d14caa | |||
| 4d9031bdb2 | |||
| fa49e2bcad | |||
| e116b8b859 | |||
| 32c0861937 | |||
| c876ed1191 | |||
| 22460f0a62 | |||
| 1337363cf6 | |||
| 72fcf30d6b | |||
| 83cc8fee5d | |||
| fc482fda97 | |||
| 36a565a599 | |||
| 8c7634bcde | |||
| 28604083a4 | |||
| ff824c2333 | |||
| 04aa6bb4f9 | |||
| 2861d0efe1 | |||
| 801eb2f149 | |||
| 9f3f1725d2 | |||
| ee94dc5343 | |||
| 7be1506b42 | |||
| 961c383a88 | |||
| e4537f8b04 | |||
| 8f99bbcfba | |||
| 29d2340ca4 | |||
| bd6a685be3 | |||
| ead1c84fae | |||
| edd7d2eb33 | |||
| e6abf7c3c8 | |||
| 80103b1db2 | |||
| 3854630763 | |||
| f7c0fdea57 | |||
| f91bae1257 | |||
| 47851c212f | |||
| 2433b18fef | |||
| c1d3c08390 | |||
| de40e88d31 | |||
| 53b9a314b8 | |||
| c5ce3f847a | |||
| 59357eeaf8 | |||
| 5ece7043c0 | |||
| 98cf988d30 | |||
| ad4abde146 | |||
| 23513cff14 | |||
| 72c55e7e1d | |||
| 6a6d96d7fe | |||
| 08ecb17a36 | |||
| 0fdea20eb1 | |||
| 9560f0d815 | |||
| 590429d821 | |||
| 28e655452c | |||
| 2e80ad282a | |||
| 7ccaa0b420 | |||
| 9893e89628 | |||
| a47e15f129 | |||
| da808bda43 | |||
| a6664c95b5 | |||
| 42e69bcd67 | |||
| 379d691e09 | |||
| a9ce32a8dc | |||
| a6141798f2 | |||
| 6161e3040c | |||
| 53752744d5 | |||
| 13302a212e | |||
| 332f5a929f | |||
| 4aea15feea | |||
| 5e1de22584 | |||
| 4262cb5466 | |||
| 184de70aef | |||
| 211ec53661 | |||
| f4766d74c7 | |||
| 1a43505387 | |||
| eb22d72b48 | |||
| 7cacf027a1 | |||
| 036114bf17 | |||
| 4b678ce9fd | |||
| df00d82fc7 | |||
| 0ed3b8ddb1 | |||
| 61b5bc4ad1 | |||
| 2b9ffc4261 | |||
| 85b9b6903e | |||
| e3db821484 | |||
| 8c4fc89579 | |||
| afeedb2885 | |||
| 091d13b5e4 | |||
| 79ec5fd65e | |||
| ba3afa5e2b | |||
| 786f7192e1 | |||
| 7c5b3e8853 | |||
| 210344f950 | |||
| 1db00b974b | |||
| 5ab893f01d | |||
| 081818f537 | |||
| 94764103c4 | |||
| 64495e424c | |||
| 756e91acdd | |||
| d31ce5df13 | |||
| 8b17fc1667 | |||
| e24a61c14c | |||
| 0cbb4a99bb | |||
| e32a5a04bc | |||
| f964291f02 | |||
| 9d9f0d465d | |||
| 3ce463ddd1 | |||
| 012b483fbe | |||
| ea7f300c85 | |||
| ea92a3e150 | |||
| 9a8ff969ac | |||
| cc195a3f87 | |||
| f15b836c86 | |||
| 70dd1517e8 | |||
| 174e5d8111 | |||
| 9990cfc613 | |||
| cdb4ce0a52 | |||
| b4aa35e0fb | |||
| c32177e1ec | |||
| b782bd32d4 | |||
| 1b2e82453c | |||
| e4b2c648bf | |||
| 7816ed8e34 | |||
| 30b442a363 | |||
| 8eb4d34294 | |||
| cf29feb81c | |||
| f5eb6c9de5 | |||
| 28fc23d656 | |||
| 3c74d627d5 | |||
| 6a61ff5965 | |||
| 14c489b77c | |||
| 25ab6f46d0 | |||
| f19aaa769c | |||
| 30d5258c75 | |||
| c60ef319ba | |||
| 930ff2aa09 | |||
| 51f6b0435f | |||
| 2ab0514034 | |||
| 70c45a6f9c | |||
| d7de8ee4f3 | |||
| 444e096d3c | |||
| 6c7c056b28 | |||
| 4fa1f31f79 | |||
| 86db5c2777 | |||
| 33219a0af8 | |||
| b1b520b186 | |||
| f2c198c9f3 | |||
| 3166deb308 | |||
| f79caa5717 | |||
| a06eca2b99 | |||
| 4fe0bea069 | |||
| 31400876de | |||
| 122175ac2f | |||
| 11d251415a | |||
| 154fcf86ae | |||
| d1a6841275 | |||
| 69a738ccf0 | |||
| f143e15595 | |||
| f66b616aeb | |||
| d7cc334238 | |||
| c16988ebf3 | |||
| a377b3aba1 | |||
| 6d6374d499 | |||
| 67c64ed9b2 | |||
| 832f48d9bf | |||
| 65cca1291d | |||
| b460351f7e | |||
| b9739c20f9 | |||
| 1122842ca3 | |||
| 2033a2e8b6 | |||
| 3b97d37c98 | |||
| d82bcf3c74 | |||
| b8132e00ad | |||
| ee07a31ae3 | |||
| efcc60fbdb | |||
| 043583490b | |||
| 469ee709d1 | |||
| 25b06d0199 | |||
| c7d5507462 | |||
| bb7bb49226 | |||
| 9ab7fb860d | |||
| 8291b1d049 | |||
| 10992086b8 | |||
| fc32e6dc0b | |||
| 86a8f87acd | |||
| d5745c3807 | |||
| f3413f840a | |||
| b29c804c25 | |||
| 1268d9bc22 | |||
| c662b9ac6b | |||
| ef1d71f786 | |||
| b643cd1508 | |||
| 2b71f6f518 | |||
| d680fef7f1 | |||
| 6163601db0 | |||
| 7a95bac64f | |||
| a71d4d640f | |||
| 8b3111c42a | |||
| c605e5e139 | |||
| 734cc21fb4 | |||
| cd0763170a | |||
| 27095a3365 | |||
| 2329de1f62 | |||
| 3872ea8d18 | |||
| 88deb9b211 | |||
| 3ec1ee585f | |||
| f5efe1e69b | |||
| fc65ec8839 | |||
| 1ee0c8b4f0 | |||
| a0654afa97 | |||
| 24ac779a5c | |||
| 0aedf85236 | |||
| b9525e53a8 | |||
| 0664dadfbd | |||
| ff1a6786cd | |||
| 0d60499f13 | |||
| 65f9d06632 | |||
| 13dc9a6892 | |||
| 7c55452b21 | |||
| 95b3f45311 | |||
| c98fddbd03 | |||
| 7a3ffcc3d9 | |||
| 4595c7a920 | |||
| 288e4e2e2b | |||
| 7494b5c9ff | |||
| 3d8908860f | |||
| 3d8373b944 | |||
| 827f5ca8c7 | |||
| d39036f454 | |||
| 34ce2e9dbd | |||
| b8c30c2b2d | |||
| cdd28169d3 | |||
| 3d3aff10eb | |||
| 4c9c70b7f5 | |||
| f0da74e641 | |||
| b97d27f7b7 | |||
| 526a34c8ec | |||
| 2924e9ad5a | |||
| b8fca6e460 | |||
| 9eea5c4152 | |||
| 2f215ab9a1 | |||
| f6ce751a06 | |||
| a28da8a226 | |||
| 7747b40310 | |||
| 31e566e9f1 | |||
| 72e2454e42 | |||
| 256fcbed5e | |||
| 0beed101ec | |||
| 4b3c971ea3 | |||
| 4a4be524bb | |||
| 34ae66b9ab | |||
| d337748873 | |||
| ef546944b1 | |||
| 29d1c751c1 | |||
| f1e850ae0b | |||
| 6b2bb3cb24 | |||
| f2b68f29a1 | |||
| c320bb4adb | |||
| a4a91344ed | |||
| 8425e09806 | |||
| 5969d1680d | |||
| 75ac25bb31 | |||
| b19e4a435b | |||
| b3528b4f4d | |||
| 7d13368150 | |||
| f32d58c577 | |||
| 1bc9569399 | |||
| 72ba481bbc | |||
| 54fca86901 | |||
| dd8045ad4e | |||
| 3678d2332f | |||
| f4f129a279 | |||
| 2c47d71666 | |||
| 50faaf298a | |||
| 121dfa6060 | |||
| 5c97bfbbbc | |||
| ed990f279a | |||
| 731d8c0ba7 | |||
| a9c5a3828b | |||
| fb54793b93 | |||
| 7e5ee8988e | |||
| d4d61dbd80 | |||
| 91381590d3 | |||
| 85cd5485b7 | |||
| 27912e3849 | |||
| 9ac5d3ac1c | |||
| d675b163b3 | |||
| 139b59def9 | |||
| 94eba471f1 | |||
| ef5d040fd6 | |||
| 954eaab5f7 | |||
| 7cb25da31c | |||
| 82c756006b | |||
| 63820e1d78 | |||
| bb35197d5a | |||
| 08b1336af0 | |||
| 6cd85caa99 | |||
| 3315a9fbec | |||
| 77af7e4dea | |||
| 27b27e9b1f | |||
| 34d8491ac2 | |||
| 97158ac770 | |||
| 51343bc15f | |||
| 105bf1cfd6 | |||
| a6516d36eb | |||
| 1728364341 | |||
| 8479d66d18 | |||
| 9e5d4ba5a1 | |||
| adf47fba31 | |||
| 5492079915 | |||
| 794916a183 | |||
| 5d3706468d | |||
| 2e6dfbae57 | |||
| dc6d54532d | |||
| ee265a8509 | |||
| 5a9f3e6999 | |||
| fc67185987 | |||
| d054299ed0 | |||
| 93f7cb1082 | |||
| df50421b53 | |||
| dc04dc1940 | |||
| 26f4a669b8 | |||
| e8067f4e01 | |||
| 1881434ac6 | |||
| 862488569d | |||
| 2b710f05b3 | |||
| c7487c4a69 | |||
| 12d792cdef | |||
| e9f3089e90 | |||
| 7e703742cb | |||
| 4de6d6b902 | |||
| 1778ff3bac | |||
| 7e1654ae27 | |||
| 5d00096f82 | |||
| be27044099 | |||
| dbebe5fa3e | |||
| 413b7c8cca | |||
| 3b6949c18c | |||
| 66bf4632e2 | |||
| 1de72c715d | |||
| f7280439e6 | |||
| cabcf19303 | |||
| 47d7094dfb | |||
| 0e360966a0 | |||
| c2b21d3719 | |||
| 5b17f0116e | |||
| d1765c7768 | |||
| 23788e90cb | |||
| 90ce858347 | |||
| 6af404b9da | |||
| 50c254a522 | |||
| 8e6ce08f33 | |||
| 97f94349d5 | |||
| 278d903bb4 | |||
| 1d445d5c9d | |||
| a16c2326b3 | |||
| f726a50038 | |||
| b4abd8dc2c | |||
| f71b941995 | |||
| 3ef6c06b51 | |||
| e6b9f14022 | |||
| 78e2dab155 | |||
| 023adeff12 | |||
| e40d8c3d11 | |||
| 704f69272c | |||
| 8ebab1b243 | |||
| 5fe10e2098 | |||
| fd36de5a0a | |||
| 50e5720464 | |||
| 4854dee208 | |||
| 60b19b7b81 | |||
| e4f0c3051c | |||
| 1e0e03edc7 | |||
| 0a3239463b | |||
| 653b105cb7 | |||
| e0e49533ab | |||
| 928f7ed8ce | |||
| 950e957b03 | |||
| 351dcbd186 | |||
| f0957c8df4 | |||
| 7f40f3cd58 | |||
| 4e441f8b18 | |||
| cd562a0451 | |||
| c63be08b07 | |||
| 8a621274b8 | |||
| e931a71660 | |||
| 61ad0f13e8 | |||
| 63a6172ec4 | |||
| 206427c4ea | |||
| 4d7d627319 | |||
| c3d428a16e | |||
| d6b127ba91 | |||
| 7314f7ddc9 | |||
| 4b50f27d6e | |||
| ef03ed5875 | |||
| 14a1aedf57 | |||
| e5a3a23c02 | |||
| d76a4e36ee | |||
| 0b6438b7c0 | |||
| 34b91218f4 | |||
| b9c2e757c7 | |||
| e15c11961f | |||
| d7406ccb4b | |||
| f9b943a7ee | |||
| 042feacf3e | |||
| 9638c0b8b0 | |||
| 759e82c3c8 | |||
| 2d7262515d | |||
| ae86cb7d11 | |||
| 155b29edbe | |||
| 4ed8f08757 | |||
| 44219f9a1c | |||
| 8bdcd72042 | |||
| f3e199cd47 | |||
| 9232535cf6 | |||
| 00b64f0ca6 | |||
| f6ea1f473a | |||
| e0d9b060cf | |||
| ade2306a60 | |||
| 67fef270af | |||
| 79bb320f4c | |||
| 9722e2bd6a | |||
| dd67dac537 | |||
| baba5da88b | |||
| 058b194604 | |||
| d271750062 | |||
| a01c73d506 | |||
| 77b1282570 | |||
| f45317c9c9 | |||
| 2ad42cd0ec | |||
| 06db5168c0 | |||
| 9e04dd6a3c | |||
| 83beb8a19c | |||
| bb10b9df91 | |||
| b1834122a1 | |||
| 12887f992a | |||
| effca0a603 | |||
| 4e3022628d | |||
| e1e350f5aa | |||
| 1d8cd5a89b | |||
| c4c3968109 | |||
| d9deb21eac | |||
| 2e2fe7a817 | |||
| 05db0895cb | |||
| d5a8ff919a | |||
| 5033aaafde | |||
| 06f401d472 | |||
| 3b9eaed9c9 | |||
| 998868450f | |||
| db4f262916 | |||
| 8914057766 | |||
| e1b6668a84 | |||
| e67fbcc5c2 | |||
| ea3e08ec3b | |||
| 10be359327 | |||
| 5c489c05fc | |||
| a754236ce5 | |||
| 067c7e7152 | |||
| f3ecfa82bc | |||
| d1f46cb02b | |||
| 5e2b393ceb | |||
| 0880cc672f | |||
| c50216919a | |||
| 822f349fa1 | |||
| d6598f370c | |||
| 8392503df7 | |||
| 6c4dfaa56e | |||
| ecf08b91a3 | |||
| b4847d74bc | |||
| d1ab6ed489 | |||
| 6e0012cb0a | |||
| 862116c050 | |||
| 3a7d331967 | |||
| 9dfcded534 | |||
| ffd82c92cb | |||
| 05de0670ea | |||
| f7e59510b4 | |||
| 835899f4bc | |||
| 34d7091f31 | |||
| 0ea8cbdfbf | |||
| c18ba96f87 | |||
| cc1cb77abb | |||
| 463ffadb6a | |||
| b04759a80b | |||
| b2f9996fa4 | |||
| d28d719276 | |||
| fe730d3ad9 | |||
| c948417866 | |||
| b61df559d2 | |||
| cd028267ef | |||
| 13bcdebc89 | |||
| 3f23afb2c6 | |||
| 2a3b696d85 | |||
| 5688b1777d | |||
| c61fb89d3f | |||
| 7cc8b099d2 | |||
| c5ed376d5f | |||
| 0b7f0b4042 | |||
| ad566ee9ef | |||
| 5d830477b7 | |||
| 83b28270a4 | |||
| 8441c476f1 | |||
| 5f415615eb | |||
| 5dfb832921 | |||
| a030638183 | |||
| 5878238077 | |||
| 614ba391fa | |||
| 92d06b733c | |||
| 98121cb081 | |||
| 52fa989d00 | |||
| 6aaae0e6f7 | |||
| b0a6c40c33 | |||
| 7c30752d21 | |||
| 284d665aa0 | |||
| 5ab001b55b | |||
| 6df82676aa | |||
| 7e3dbc22f7 | |||
| 53df34e070 | |||
| e4f0f7be35 | |||
| 2580d0f95c | |||
| 5df4c270a7 | |||
| 634f892370 | |||
| 660acf3b42 | |||
| fed86bd816 | |||
| ffd01fc88a | |||
| 27a820950a | |||
| f9f825163a | |||
| cff924f4fd | |||
| 01bc0a0a0a | |||
| c09d3fb03c | |||
| a090452807 | |||
| c294b87a45 | |||
| 0b240ca97a | |||
| ff848c74f9 | |||
| 02443545e7 | |||
| e2282b1379 | |||
| a051f20876 | |||
| e96034f494 | |||
| f685b3f258 | |||
| 7ab17d228f | |||
| 5f28c56437 | |||
| 2023ffe2d3 | |||
| cc7f409d46 | |||
| 19ed2346cb | |||
| f9fbcfbb42 | |||
| 9b42b5b930 | |||
| 30b0666219 | |||
| 3f1b5216f0 | |||
| 4ff8eca572 | |||
| c2db9db1aa | |||
| 90702d93ab | |||
| 0bf6442c5d | |||
| 1986610363 | |||
| 09c11532ac | |||
| 1997bc7432 | |||
| 8ac8473554 | |||
| fcae39bf13 | |||
| fd8a64ca95 | |||
| 86422af988 | |||
| 50f95dd909 | |||
| fc0fac8543 | |||
| 3b33ffa245 | |||
| 5cc2a8344c | |||
| fc5359b6f6 | |||
| c5d448fba9 | |||
| c60815ed08 | |||
| aac3d70fa1 | |||
| c450306c5a | |||
| c820d18ada | |||
| c20802b07e | |||
| 16c536e83a | |||
| 1827652258 | |||
| 19a2e2efc5 | |||
| b350ad7f7c | |||
| bcdb70b689 | |||
| 860159315d | |||
| a274f52924 | |||
| cf1e582af5 | |||
| 9896192efb | |||
| 40bdea4db8 | |||
| 31db97cbe4 | |||
| 5d85e6d088 | |||
| c0edcc09bb | |||
| 24cede62ee | |||
| 2cbd96e64c | |||
| 4576155005 | |||
| 2dcc1c16b7 | |||
| 2ed2123fc0 | |||
| 117e0b4471 | |||
| 028e8ca0b0 | |||
| 78cc85283c | |||
| 7b78128d4e | |||
| 8c23eb6833 | |||
| 5c94bbf122 | |||
| 77ef7fe490 | |||
| ff992fb7f9 | |||
| 91331415ce | |||
| 9862cd6780 | |||
| 4af188242c | |||
| c02ec6857f | |||
| 2d98795cc5 | |||
| cbfecc5d49 | |||
| 56a95d6c16 | |||
| 9bd5c61782 | |||
| b39ee8ede5 | |||
| 2949995abc | |||
| bf72b9768c | |||
| fd5796ac39 | |||
| ce1b13f228 | |||
| 289d6e5dca | |||
| f16fff577a | |||
| 4790e4910f | |||
| 8c6ed23c5f | |||
| c5f7381c80 | |||
| dddb68cd5f | |||
| 8f37c8f0c5 | |||
| a092c4f535 | |||
| d7d0017545 | |||
| 6c3b099c25 |
@@ -1,17 +1,18 @@
|
||||
<!--
|
||||
PLEASE READ THIS MESSAGE.
|
||||
|
||||
Documentation fixes or enhancements:
|
||||
- for Traefik v2: use branch v2.11
|
||||
- for Traefik v3: use branch v3.4
|
||||
Documentation:
|
||||
- for Traefik v2: use branch v2.11 (fixes only)
|
||||
- for Traefik v3.6: use branch v3.6
|
||||
- for Traefik v3.7: use branch v3.7
|
||||
|
||||
Bug fixes:
|
||||
- for Traefik v2: use branch v2.11
|
||||
- for Traefik v3: use branch v3.4
|
||||
Bug:
|
||||
- for Traefik v2: use branch v2.11 (security fixes only)
|
||||
- for Traefik v3.6: use branch v3.6
|
||||
- for Traefik v3.7: use branch v3.7
|
||||
|
||||
Enhancements:
|
||||
- for Traefik v2: we only accept bug fixes
|
||||
- for Traefik v3: use branch master
|
||||
- use branch master
|
||||
|
||||
HOW TO WRITE A GOOD PULL REQUEST? https://doc.traefik.io/traefik/contributing/submitting-pull-requests/
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ on:
|
||||
- 'script/gcg/**'
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.24'
|
||||
CGO_ENABLED: 0
|
||||
|
||||
jobs:
|
||||
@@ -20,6 +19,7 @@ jobs:
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -51,20 +51,20 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v5
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
env:
|
||||
ImageOS: ${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.goarm }}
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
go-version-file: '.go-version'
|
||||
check-latest: true
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: webui.tar.gz
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
name: Check Documentation
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
paths:
|
||||
- '.github/workflows/check_doc.yaml'
|
||||
- 'docs/**'
|
||||
|
||||
jobs:
|
||||
|
||||
docs:
|
||||
name: lint, build and verify
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install markdownlint
|
||||
run: |
|
||||
npm install --global markdownlint@0.29.0 markdownlint-cli@0.35.0
|
||||
|
||||
- name: Lint
|
||||
run: ./docs/scripts/lint.sh docs
|
||||
|
||||
- name: Setup python
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: '3.12'
|
||||
cache: 'pip'
|
||||
cache-dependency-path: "./docs/requirements.txt"
|
||||
|
||||
- name: Build documentation
|
||||
working-directory: ./docs
|
||||
run: |
|
||||
pip install -r requirements.txt
|
||||
mkdocs build --strict
|
||||
|
||||
- name: Setup ruby
|
||||
uses: ruby/setup-ruby@90be1154f987f4dc0fe0dd0feedac9e473aa4ba8 # v1.286.0
|
||||
with:
|
||||
ruby-version: '3.4'
|
||||
|
||||
- name: Install html-proofer
|
||||
run: |
|
||||
gem install nokogiri --version 1.18.6 --no-document -- --use-system-libraries
|
||||
gem install html-proofer --version 5.0.10 --no-document -- --use-system-libraries
|
||||
env:
|
||||
NOKOGIRI_USE_SYSTEM_LIBRARIES: "true"
|
||||
|
||||
# Comes from https://github.com/gjtorikian/html-proofer?tab=readme-ov-file#caching-with-continuous-integration
|
||||
- name: Cache HTMLProofer
|
||||
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||
with:
|
||||
path: tmp/.htmlproofer
|
||||
key: ${{ runner.os }}-htmlproofer
|
||||
|
||||
- name: Verify
|
||||
run: ./docs/scripts/verify.sh docs/site
|
||||
@@ -1,25 +0,0 @@
|
||||
name: Check Documentation
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
|
||||
docs:
|
||||
name: Check, verify and build documentation
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check documentation
|
||||
run: make docs-pull-images docs
|
||||
env:
|
||||
# These variables are not passed to workflows that are triggered by a pull request from a fork.
|
||||
DOCS_VERIFY_SKIP: ${{ vars.DOCS_VERIFY_SKIP }}
|
||||
DOCS_LINT_SKIP: ${{ vars.DOCS_LINT_SKIP }}
|
||||
@@ -12,6 +12,7 @@ jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
@@ -28,17 +29,18 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
|
||||
- name: setup go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
if: ${{ matrix.language == 'go' }}
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
go-version-file: '.go-version'
|
||||
check-latest: true
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
uses: github/codeql-action/init@38e701f46e33fb233075bf4238cb1e5d68e429e4 # v3.31.11
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -52,7 +54,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
uses: github/codeql-action/autobuild@38e701f46e33fb233075bf4238cb1e5d68e429e4 # v3.31.11
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
@@ -65,6 +67,6 @@ jobs:
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
uses: github/codeql-action/analyze@38e701f46e33fb233075bf4238cb1e5d68e429e4 # v3.31.11
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
name: Build and Publish Documentation
|
||||
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
@@ -15,16 +16,17 @@ jobs:
|
||||
docs:
|
||||
name: Doc Process
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
if: github.repository == 'traefik/traefik'
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
@@ -39,9 +41,9 @@ jobs:
|
||||
run: curl -sSfL https://raw.githubusercontent.com/traefik/mixtus/master/godownloader.sh | sh -s -- -b $HOME/bin ${MIXTUS_VERSION}
|
||||
|
||||
- name: Build documentation
|
||||
run: $HOME/bin/structor -o traefik -r traefik --dockerfile-url="https://raw.githubusercontent.com/traefik/traefik/v1.7/docs.Dockerfile" --menu.js-url="https://raw.githubusercontent.com/traefik/structor/master/traefik-menu.js.gotmpl" --rqts-url="https://raw.githubusercontent.com/traefik/structor/master/requirements-override.txt" --force-edit-url --exp-branch=master --debug
|
||||
env:
|
||||
STRUCTOR_LATEST_TAG: ${{ vars.STRUCTOR_LATEST_TAG }}
|
||||
run: |
|
||||
STRUCTOR_LATEST_TAG=$(curl -s https://api.github.com/repos/traefik/traefik/releases/latest | jq -r '.tag_name')
|
||||
$HOME/bin/structor -o traefik -r traefik --dockerfile-url="https://raw.githubusercontent.com/traefik/traefik/v1.7/docs.Dockerfile" --menu.js-url="https://raw.githubusercontent.com/traefik/structor/master/traefik-menu.js.gotmpl" --rqts-url="https://raw.githubusercontent.com/traefik/structor/master/requirements-override.txt" --force-edit-url --exp-branch=master --debug
|
||||
|
||||
- name: Apply seo
|
||||
run: $HOME/bin/seo -path=./site -product=traefik
|
||||
@@ -7,7 +7,6 @@ on:
|
||||
- v*
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.24'
|
||||
CGO_ENABLED: 0
|
||||
|
||||
jobs:
|
||||
@@ -20,19 +19,20 @@ jobs:
|
||||
if: github.repository == 'traefik/traefik'
|
||||
name: Build experimental image on branch
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v5
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
env:
|
||||
ImageOS: ${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.goarm }}
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
go-version-file: '.go-version'
|
||||
check-latest: true
|
||||
|
||||
- name: Build
|
||||
@@ -42,19 +42,19 @@ jobs:
|
||||
run: echo ${GITHUB_REF##*/}
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: webui.tar.gz
|
||||
|
||||
|
||||
@@ -6,11 +6,10 @@ on:
|
||||
- 'v*.*.*'
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.24'
|
||||
CGO_ENABLED: 0
|
||||
VERSION: ${{ github.ref_name }}
|
||||
TRAEFIKER_EMAIL: "traefiker@traefik.io"
|
||||
CODENAME: chaource
|
||||
CODENAME: langres
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -21,30 +20,31 @@ jobs:
|
||||
build:
|
||||
if: github.ref_type == 'tag' && github.repository == 'traefik/traefik'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ linux-amd64, linux-386, linux-arm, linux-arm64, linux-ppc64le, linux-s390x, linux-riscv64, darwin, windows-amd64, windows-arm64, windows-386, freebsd, openbsd ]
|
||||
os: [ linux-amd64, linux-386, linux-arm, linux-arm64, linux-ppc64le, linux-s390x, linux-riscv64, darwin-amd64, darwin-arm64, windows-amd64, windows-arm64, windows-386, freebsd-amd64, freebsd-386, openbsd-amd64, openbsd-386, openbsd-riscv64 ]
|
||||
needs:
|
||||
- build-webui
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v5
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
env:
|
||||
# Ensure cache consistency on Linux, see https://github.com/actions/setup-go/pull/383
|
||||
ImageOS: ${{ matrix.os }}
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
go-version-file: '.go-version'
|
||||
check-latest: true
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: webui.tar.gz
|
||||
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
echo "GORELEASER_CONFIG_FILE_PATH=$GORELEASER_CONFIG_FILE_PATH" >> $GITHUB_ENV
|
||||
|
||||
- name: Build with goreleaser
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
|
||||
with:
|
||||
distribution: goreleaser
|
||||
# 'latest', 'nightly', or a semver
|
||||
@@ -71,7 +71,7 @@ jobs:
|
||||
args: release --clean --timeout="90m" --config "${{ env.GORELEASER_CONFIG_FILE_PATH }}"
|
||||
|
||||
- name: Artifact binaries
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: ${{ matrix.os }}-binaries
|
||||
path: |
|
||||
@@ -83,18 +83,19 @@ jobs:
|
||||
release:
|
||||
if: github.ref_type == 'tag' && github.repository == 'traefik/traefik'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
|
||||
needs:
|
||||
- build
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: webui.tar.gz
|
||||
|
||||
@@ -111,7 +112,7 @@ jobs:
|
||||
echo "${TRAEFIKER_RSA}" | base64 --decode > ~/.ssh/traefiker_rsa
|
||||
|
||||
- name: Download All Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
path: dist/
|
||||
pattern: "*-binaries"
|
||||
@@ -126,13 +127,10 @@ jobs:
|
||||
tar cfz "dist/traefik-${VERSION}.src.tar.gz" \
|
||||
--exclude-vcs \
|
||||
--exclude .idea \
|
||||
--exclude .travis \
|
||||
--exclude .semaphoreci \
|
||||
--exclude .github \
|
||||
--exclude dist .
|
||||
|
||||
chown -R "$(id -u)":"$(id -g)" dist/
|
||||
gh release create ${VERSION} ./dist/**/traefik*.{zip,tar.gz} ./dist/traefik*.{tar.gz,txt} --repo traefik/traefik --title ${VERSION} --notes ${VERSION}
|
||||
|
||||
./script/deploy.sh
|
||||
|
||||
chown -R "$(id -u)":"$(id -g)" dist/
|
||||
gh release create ${VERSION} ./dist/**/traefik*.{zip,tar.gz} ./dist/traefik*.{tar.gz,txt} --repo traefik/traefik --title ${VERSION} --notes ${VERSION} --latest=false
|
||||
|
||||
./script/deploy.sh
|
||||
|
||||
@@ -8,15 +8,16 @@ on:
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
if: github.repository == 'traefik/traefik'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
|
||||
- uses: imjasonh/setup-crane@v0.4
|
||||
- uses: imjasonh/setup-crane@31b88efe9de28ae0ffa220711af4b60be9435f6e # v0.4
|
||||
|
||||
- name: Sync
|
||||
run: |
|
||||
|
||||
@@ -1,28 +1,42 @@
|
||||
name: Build Web UI
|
||||
on:
|
||||
workflow_call: {}
|
||||
env:
|
||||
SAFE_CHAIN_MINIMUM_PACKAGE_AGE_HOURS: 48 # 2 days
|
||||
jobs:
|
||||
|
||||
build-webui:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Enable corepack
|
||||
run: corepack enable
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
with:
|
||||
node-version-file: webui/.nvmrc
|
||||
cache: yarn
|
||||
cache-dependency-path: webui/yarn.lock
|
||||
|
||||
- name: Setup safe-chain
|
||||
working-directory: ./webui
|
||||
run: |
|
||||
npm i -g @aikidosec/safe-chain
|
||||
safe-chain setup-ci
|
||||
|
||||
- name: Build webui
|
||||
working-directory: ./webui
|
||||
run: |
|
||||
yarn install
|
||||
yarn tsc
|
||||
yarn lint
|
||||
yarn build
|
||||
|
||||
- name: Package webui
|
||||
@@ -30,7 +44,7 @@ jobs:
|
||||
tar czvf webui.tar.gz ./webui/static/
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: webui.tar.gz
|
||||
path: webui.tar.gz
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
name: Test K8s Gateway API conformance
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
paths:
|
||||
- '.github/workflows/test-conformance.yaml'
|
||||
- 'pkg/provider/kubernetes/gateway/**'
|
||||
- 'integration/fixtures/k8s-conformance/**'
|
||||
- 'integration/k8s_conformance_test.go'
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.23'
|
||||
CGO_ENABLED: 0
|
||||
|
||||
jobs:
|
||||
|
||||
test-conformance:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Avoid generating webui
|
||||
run: touch webui/static/index.html
|
||||
|
||||
- name: K8s Gateway API conformance test and report
|
||||
run: |
|
||||
make test-gateway-api-conformance
|
||||
git diff --exit-code
|
||||
@@ -0,0 +1,42 @@
|
||||
name: Test K8s Gateway API conformance
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
paths:
|
||||
- '.github/workflows/test-gateway-api-conformance.yaml'
|
||||
- 'pkg/provider/kubernetes/gateway/**'
|
||||
- 'integration/fixtures/gateway-api-conformance/**'
|
||||
- 'integration/gateway_api_conformance_test.go'
|
||||
- 'integration/integration_test.go'
|
||||
|
||||
env:
|
||||
CGO_ENABLED: 0
|
||||
|
||||
jobs:
|
||||
|
||||
test-gateway-api-conformance:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
go-version-file: '.go-version'
|
||||
check-latest: true
|
||||
|
||||
- name: Avoid generating webui
|
||||
run: |
|
||||
touch webui/static/index.html
|
||||
|
||||
- name: Gateway API conformance test and report
|
||||
run: |
|
||||
make test-gateway-api-conformance
|
||||
git diff --exit-code
|
||||
@@ -10,41 +10,42 @@ on:
|
||||
- 'script/gcg/**'
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.24'
|
||||
CGO_ENABLED: 0
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v5
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
go-version-file: '.go-version'
|
||||
check-latest: true
|
||||
|
||||
- name: Avoid generating webui
|
||||
run: touch webui/static/index.html
|
||||
run: |
|
||||
touch webui/static/index.html
|
||||
|
||||
- name: Build binary
|
||||
run: make binary-linux-amd64
|
||||
|
||||
- name: Save go cache build
|
||||
uses: actions/cache/save@v4
|
||||
uses: actions/cache/save@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-cache-${{ env.GO_VERSION }}-${{ hashFiles('**/go.sum') }}
|
||||
key: ${{ runner.os }}-go-build-cache-${{ hashFiles('**/go.mod', '**/go.sum') }}
|
||||
|
||||
- name: Artifact traefik binary
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: traefik
|
||||
path: ./dist/linux/amd64/traefik
|
||||
@@ -52,6 +53,7 @@ jobs:
|
||||
|
||||
test-integration:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 90
|
||||
needs:
|
||||
- build
|
||||
strategy:
|
||||
@@ -62,21 +64,18 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v5
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
go-version-file: '.go-version'
|
||||
check-latest: true
|
||||
|
||||
- name: Avoid generating webui
|
||||
run: touch webui/static/index.html
|
||||
|
||||
- name: Download traefik binary
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: traefik
|
||||
path: ./dist/linux/amd64/
|
||||
@@ -85,15 +84,15 @@ jobs:
|
||||
run: chmod +x ./dist/linux/amd64/traefik
|
||||
|
||||
- name: Restore go cache build
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5.0.2
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
key: ${{ runner.os }}-go-build-cache-${{ env.GO_VERSION }}-${{ hashFiles('**/go.sum') }}
|
||||
key: ${{ runner.os }}-go-build-cache-${{ hashFiles('**/go.mod', '**/go.sum') }}
|
||||
|
||||
- name: Generate go test Slice
|
||||
id: test_split
|
||||
uses: hashicorp-forge/go-test-split-action@v2.0.0
|
||||
uses: hashicorp-forge/go-test-split-action@ddb2685fb140c29505663b405af7eb2cd953dd13 # v2.0.1
|
||||
with:
|
||||
packages: ./integration
|
||||
total: ${{ matrix.parallel }}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
name: Test Knative conformance
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
paths:
|
||||
- '.github/workflows/test-knative-conformance.yaml'
|
||||
- 'pkg/provider/kubernetes/knative/**'
|
||||
- 'integration/fixtures/knative/**'
|
||||
- 'integration/knative_conformance_test.go'
|
||||
- 'integration/integration_test.go'
|
||||
|
||||
env:
|
||||
CGO_ENABLED: 0
|
||||
|
||||
jobs:
|
||||
|
||||
test-knative-conformance:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
go-version-file: '.go-version'
|
||||
check-latest: true
|
||||
|
||||
- name: Set up KO
|
||||
uses: ko-build/setup-ko@ace48d793556083a76f1e3e6068850c1f4a369aa # v0.6
|
||||
env:
|
||||
KO_DOCKER_REPO: ko.local
|
||||
|
||||
- name: Upload Test Images
|
||||
run: |
|
||||
# Download the test image templates.
|
||||
go mod vendor
|
||||
./integration/fixtures/knative/upload-test-images.sh
|
||||
|
||||
- name: Avoid generating webui
|
||||
run: |
|
||||
touch webui/static/index.html
|
||||
|
||||
- name: Knative conformance test
|
||||
run: |
|
||||
make test-knative-conformance
|
||||
@@ -9,26 +9,23 @@ on:
|
||||
- '**.md'
|
||||
- 'script/gcg/**'
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.24'
|
||||
|
||||
jobs:
|
||||
|
||||
generate-packages:
|
||||
name: List Go Packages
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v5
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
go-version-file: '.go-version'
|
||||
check-latest: true
|
||||
|
||||
- name: Generate matrix
|
||||
@@ -40,6 +37,7 @@ jobs:
|
||||
|
||||
test-unit:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
needs: generate-packages
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -47,40 +45,49 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v5
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
go-version-file: '.go-version'
|
||||
check-latest: true
|
||||
|
||||
- name: Avoid generating webui
|
||||
run: touch webui/static/index.html
|
||||
|
||||
- name: Tests
|
||||
run: |
|
||||
go test -v -parallel 8 ${{ matrix.package.group }}
|
||||
|
||||
test-ui-unit:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Enable corepack
|
||||
run: corepack enable
|
||||
|
||||
- name: Set up Node.js ${{ env.NODE_VERSION }}
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
with:
|
||||
node-version-file: webui/.nvmrc
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: webui/yarn.lock
|
||||
|
||||
- name: UI unit tests
|
||||
- name: Setup safe-chain
|
||||
run: |
|
||||
yarn --cwd webui install
|
||||
yarn --cwd webui test:unit:ci
|
||||
npm i -g @aikidosec/safe-chain
|
||||
safe-chain setup-ci
|
||||
|
||||
- name: UI unit tests
|
||||
working-directory: ./webui
|
||||
env:
|
||||
VITE_APP_BASE_API_URL: "/api"
|
||||
run: |
|
||||
yarn install
|
||||
yarn test:unit:ci
|
||||
|
||||
@@ -6,69 +6,68 @@ on:
|
||||
- '*'
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.24'
|
||||
GOLANGCI_LINT_VERSION: v2.0.2
|
||||
MISSPELL_VERSION: v0.6.0
|
||||
GOLANGCI_LINT_VERSION: v2.10.1
|
||||
MISSPELL_VERSION: v0.7.0
|
||||
|
||||
jobs:
|
||||
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v5
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
go-version-file: '.go-version'
|
||||
check-latest: true
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v7
|
||||
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
|
||||
with:
|
||||
version: "${{ env.GOLANGCI_LINT_VERSION }}"
|
||||
|
||||
validate:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v5
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
go-version-file: '.go-version'
|
||||
check-latest: true
|
||||
|
||||
- name: Install misspell ${{ env.MISSPELL_VERSION }}
|
||||
run: curl -sfL https://raw.githubusercontent.com/golangci/misspell/HEAD/install-misspell.sh | sh -s -- -b $(go env GOPATH)/bin ${MISSPELL_VERSION}
|
||||
|
||||
- name: Avoid generating webui
|
||||
run: touch webui/static/index.html
|
||||
|
||||
- name: Validate
|
||||
run: make validate-files
|
||||
|
||||
validate-generate:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v5
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
go-version-file: '.go-version'
|
||||
check-latest: true
|
||||
|
||||
- name: go generate
|
||||
@@ -76,11 +75,6 @@ jobs:
|
||||
make 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
|
||||
|
||||
@@ -19,4 +19,4 @@ plugins-storage/
|
||||
plugins-local/
|
||||
traefik_changelog.md
|
||||
integration/tailscale.secret
|
||||
integration/conformance-reports/**/experimental-dev-default-report.yaml
|
||||
integration/gateway-api-conformance-reports/**/experimental-dev-default-report.yaml
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
1.25
|
||||
@@ -36,6 +36,7 @@ linters:
|
||||
- nilnil # Not relevant
|
||||
- nlreturn # Not relevant
|
||||
- noctx # Too strict
|
||||
- noinlineerr # Too strict
|
||||
- nonamedreturns # Too strict
|
||||
- paralleltest # Not relevant
|
||||
- prealloc # Too many false-positive.
|
||||
@@ -47,6 +48,7 @@ linters:
|
||||
- varnamelen # Not relevant
|
||||
- wrapcheck # Too strict
|
||||
- wsl # Too strict
|
||||
- wsl_v5 # Too strict
|
||||
|
||||
settings:
|
||||
depguard:
|
||||
@@ -80,6 +82,7 @@ linters:
|
||||
toolchain-pattern: go1\.\d+\.\d+$
|
||||
tool-forbidden: true
|
||||
go-version-pattern: ^1\.\d+(\.0)?$
|
||||
replace-local: true
|
||||
replace-allow-list:
|
||||
- github.com/abbot/go-http-auth
|
||||
- github.com/gorilla/mux
|
||||
@@ -87,6 +90,7 @@ linters:
|
||||
- github.com/mailgun/multibuf
|
||||
- github.com/jaguilar/vt100
|
||||
- github.com/cucumber/godog
|
||||
- github.com/vulcand/oxy/v2
|
||||
govet:
|
||||
enable-all: true
|
||||
disable:
|
||||
@@ -245,6 +249,10 @@ linters:
|
||||
text: Function 'buildConstructor' has too many statements
|
||||
linters:
|
||||
- funlen
|
||||
- path: pkg/provider/kubernetes/ingress-nginx/kubernetes.go
|
||||
text: Function 'loadConfiguration' has too many statements
|
||||
linters:
|
||||
- funlen
|
||||
- path: pkg/tracing/haystack/logger.go
|
||||
linters:
|
||||
- goprintffuncname
|
||||
@@ -259,7 +267,7 @@ linters:
|
||||
- path: pkg/provider/kubernetes/(crd|gateway)/client.go
|
||||
linters:
|
||||
- interfacebloat
|
||||
- path: pkg/metrics/metrics.go
|
||||
- path: pkg/observability/metrics/metrics.go
|
||||
linters:
|
||||
- interfacebloat
|
||||
- path: integration/healthcheck_test.go
|
||||
@@ -291,21 +299,39 @@ linters:
|
||||
source: 'errors.New\("Nomad provider'
|
||||
text: 'ST1005: error strings should not be capitalized'
|
||||
- path: (.+)\.go
|
||||
text: 'struct-tag: unknown option ''inline'' in JSON tag'
|
||||
text: 'omitzero: Omitempty has no effect on nested struct field'
|
||||
linters:
|
||||
- modernize
|
||||
- path: (.+)\.go
|
||||
text: 'struct-tag: unknown option "inline" in json tag'
|
||||
linters:
|
||||
- revive
|
||||
- path: (.+)\.go
|
||||
text: 'struct-tag: unknown option ''omitzero'' in TOML tag'
|
||||
text: 'struct-tag: unknown option "omitzero" in toml tag'
|
||||
linters:
|
||||
- revive
|
||||
- path: (pkg/types/.+|pkg/api/.+|pkg/observability/types/.+)\.go
|
||||
text: 'var-naming: avoid meaningless package names'
|
||||
linters:
|
||||
- revive
|
||||
- path: ((cmd|pkg)/version/.*|pkg/config/runtime/.*|pkg/log/.*|pkg/(middlewares/)?metrics/.*|pkg/muxer/http/.+|pkg/provider/http/.+|pkg/tls/.+|pkg/proxy/httputil/.+|pkg/observability/metrics/.+)\.go
|
||||
text: 'var-naming: avoid package names that conflict with Go standard library package names'
|
||||
linters:
|
||||
- revive
|
||||
- path: (.+)\.go$
|
||||
text: 'SA1019: http.CloseNotifier has been deprecated' # FIXME must be fixed
|
||||
- path: (.+)\.go$
|
||||
text: 'SA1019: dynamic.(TCPIPWhiteList|IPWhiteList) is deprecated: please use IPAllowList instead.'
|
||||
- path: (.+)\.go$
|
||||
text: 'SA1019: middlewareTCP.Spec.IPWhiteList is deprecated: please use IPAllowList instead.'
|
||||
- path: (.+)\.go$
|
||||
text: 'SA1019: cfg.(SSLRedirect|SSLTemporaryRedirect|SSLHost|SSLForceHost|FeaturePolicy) is deprecated'
|
||||
- path: (.+)\.go$
|
||||
text: 'SA1019: c.Providers.(ConsulCatalog|Consul|Nomad).Namespace is deprecated'
|
||||
- path: (.+)\.go$
|
||||
text: 'SA1019: dockertypes.ContainerNode is deprecated'
|
||||
- path: pkg/middlewares/auth/basic_auth_test.go
|
||||
text: 'SA1008: keys in http.Header are canonicalized, "x-user" is not canonical; fix the constant or use http.CanonicalHeaderKey'
|
||||
- path: pkg/middlewares/auth/digest_auth_test.go
|
||||
text: 'SA1008: keys in http.Header are canonicalized, "x-user" is not canonical; fix the constant or use http.CanonicalHeaderKey'
|
||||
- path: pkg/provider/kubernetes/crd/kubernetes.go
|
||||
text: "Function 'loadConfigurationFromCRD' has too many statements"
|
||||
linters:
|
||||
@@ -316,6 +342,15 @@ linters:
|
||||
- recvcheck
|
||||
- path: pkg/proxy/httputil/bufferpool.go
|
||||
text: 'SA6002: argument should be pointer-like to avoid allocations'
|
||||
- path: integration/integration_test.go
|
||||
text: 'var (gatewayAPIConformanceRunTest|traefikVersion) is unused'
|
||||
- path: pkg/server/router/router.go
|
||||
text: 'appendAssign: append result not assigned to the same slice'
|
||||
linters:
|
||||
- gocritic
|
||||
- path: pkg/server/conncontext.go
|
||||
linters:
|
||||
- fatcontext
|
||||
paths:
|
||||
- pkg/provider/kubernetes/crd/generated/
|
||||
|
||||
|
||||
@@ -54,10 +54,12 @@ changelog:
|
||||
archives:
|
||||
- id: traefik
|
||||
name_template: '{{ .ProjectName }}_v{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
|
||||
format: tar.gz
|
||||
formats:
|
||||
- tar.gz
|
||||
format_overrides:
|
||||
- goos: windows
|
||||
format: zip
|
||||
formats:
|
||||
- zip
|
||||
files:
|
||||
- LICENSE.md
|
||||
- CHANGELOG.md
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
version: v1.0
|
||||
name: Traefik Release - deprecated
|
||||
agent:
|
||||
machine:
|
||||
type: f1-standard-2
|
||||
os_image: ubuntu2204
|
||||
blocks:
|
||||
- name: 'Do nothing'
|
||||
task:
|
||||
jobs:
|
||||
- name: 'Do nothing'
|
||||
commands:
|
||||
- echo "Do nothing"
|
||||
@@ -0,0 +1,113 @@
|
||||
# Traefik — Contributor Guide for AI Agents
|
||||
|
||||
Traefik is a modern HTTP reverse proxy and load balancer that discovers services from orchestrators (Kubernetes, Docker, Nomad, ...) and wires up routing dynamically. This file is the canonical guide for AI coding agents (Claude Code, Codex, Gemini, Cursor, ...) working in this repository; `CLAUDE.md` is a thin pointer to this file. For everything not covered here, defer to [`CONTRIBUTING.md`](./CONTRIBUTING.md) and [`docs/content/contributing/`](./docs/content/contributing/).
|
||||
|
||||
> **Training-data notice.** Traefik evolved significantly between v2 and v3 (label formats, provider names, CRD shapes, middleware names). If anything you think you know about Traefik contradicts this file or the current code, trust this file and the code — not your training data.
|
||||
|
||||
## Core vocabulary
|
||||
|
||||
These terms appear everywhere in the code and configuration. Use them precisely; they are not interchangeable.
|
||||
|
||||
- **EntryPoint** — a network listener (port + protocol).
|
||||
- **Router** — matches an incoming request and selects a service.
|
||||
- **Middleware** — transforms a request or response in the routing chain (auth, headers, rate limiting, ...).
|
||||
- **Service** — defines how to load-balance to backend servers.
|
||||
- **Provider** — a source of dynamic configuration (Kubernetes CRD, Docker labels, a file, an HTTP endpoint, ...).
|
||||
- **Static vs Dynamic configuration** — two distinct domains:
|
||||
- *Static* is set at startup (entrypoints, providers, global options) and lives under [`pkg/config/static`](./pkg/config/static).
|
||||
- *Dynamic* is produced by providers at runtime (routers, services, middlewares) and lives under [`pkg/config/dynamic`](./pkg/config/dynamic).
|
||||
|
||||
These terms are accurate for the code, but user-facing docs deliberately hide the distinction to keep things simpler for readers: when writing or editing under [`docs/content/`](./docs/content), prefer **install configuration** (over *static*) and **routing configuration** (over *dynamic*).
|
||||
|
||||
At request time the components chain in this order:
|
||||
|
||||
```
|
||||
Client → EntryPoint → Router → Middleware chain → Service → Backend
|
||||
```
|
||||
|
||||
The middleware chain is ordered: middlewares run in the sequence declared on the router, and the router match happens *before* any middleware runs.
|
||||
|
||||
## Where things live
|
||||
|
||||
- `cmd/traefik/` — main.
|
||||
- `pkg/provider/` — one subpackage per provider (Kubernetes, Docker, file, ...).
|
||||
- `pkg/server/` — routing core, middleware chain, configuration watcher.
|
||||
- `pkg/middlewares/` — HTTP and TCP middleware implementations.
|
||||
- `pkg/config/static`, `pkg/config/dynamic` — the two config domains above.
|
||||
- `pkg/plugins/` — Yaegi and WASM plugin runtimes.
|
||||
- `pkg/observability/logs/` — logging helpers; the project uses `github.com/rs/zerolog` exclusively.
|
||||
- `webui/` — React dashboard. Built assets under `webui/static/` are embedded into the Go binary via `//go:embed` (see `webui/embed.go`) and must be regenerated with `make generate-webui` (Docker required) — they are not meant to be hand-edited.
|
||||
- `integration/` — integration tests; reusable fixtures under `integration/fixtures/`.
|
||||
- `docs/content/` — MkDocs sources for the public documentation.
|
||||
|
||||
## Before you edit
|
||||
|
||||
Read two or three existing files in the same package before adding a new one, and copy their structure. Do not invent new directory layouts, file-naming conventions, or abstraction boundaries — match the neighbours. When adding a new provider, read two existing providers under `pkg/provider/`; when adding a middleware, read two under `pkg/middlewares/`.
|
||||
|
||||
## Build, test, lint
|
||||
|
||||
The Go version is declared in [`go.mod`](./go.mod) — check there rather than hard-coding a version. All day-to-day commands go through `make`:
|
||||
|
||||
```bash
|
||||
make binary # build the traefik binary (runs generate-webui first)
|
||||
make test-unit # run Go unit tests
|
||||
make test-integration # run integration tests (requires Docker)
|
||||
make lint # run golangci-lint
|
||||
make validate-files # misspell, shellcheck, generated-files check
|
||||
make validate # lint + validate-files (run this before pushing)
|
||||
make fmt # gofumpt / goimports
|
||||
make generate # regenerate non-CRD generated code (deepcopy, etc.)
|
||||
make generate-crd # regenerate Kubernetes CRD clientset + deepcopy
|
||||
make generate-webui # rebuild the embedded WebUI assets (Docker required)
|
||||
make docs-serve # preview the documentation locally
|
||||
```
|
||||
|
||||
Full environment setup (Docker, `GOPATH` layout, Tailscale for Docker Desktop users, how to target a single integration test via `TESTFLAGS`) is documented in [`docs/content/contributing/building-testing.md`](./docs/content/contributing/building-testing.md). CI runs `make validate` and fails if `make generate` or `make generate-crd` leave the tree dirty — always commit regenerated files alongside the source change that triggered them.
|
||||
|
||||
## Code style
|
||||
|
||||
Standard Go formatting (`gofumpt`/`goimports`) and `golangci-lint` cover most rules automatically; run `make lint` to catch them. Two project-specific rules that tooling does **not** enforce:
|
||||
|
||||
- **Comments answer *why*, not *what*.** Comments that restate what the code already says are noise: they go stale and waste review time. Only add a comment when it records *why* the code exists — a constraint, a past incident, a spec reference, an edge case. Comments explaining *how* should be rare and usually indicate the code needs to be clearer. When a comment is present, it **must end with a period**.
|
||||
- **Assertion messages are minimal.** Prefer `assert.Equal(t, expected, actual)` over `assert.Equal(t, expected, actual, "detailed explanation")`. The test name provides the context; a descriptive message is usually noise.
|
||||
|
||||
Prefer modern standard-library packages (`slices`, `maps`, `cmp`, ...) over hand-rolled helpers or third-party libraries when the Go version in `go.mod` supports them.
|
||||
|
||||
## Common patterns
|
||||
|
||||
- **Logging.** The project uses `github.com/rs/zerolog` exclusively — do not import `log`, `slog`, or `logrus`. Inside a middleware, get a logger via `middlewares.GetLogger(ctx, name, typeName)` (see [`pkg/middlewares/middleware.go`](./pkg/middlewares/middleware.go)) where `typeName` is a package-level `const` like `const typeNameForward = "ForwardAuth"`. Elsewhere, extract the logger from the context with `log.Ctx(ctx)` and attach it to a new context with `.WithContext(ctx)`.
|
||||
- **Context propagation.** `context.Context` is always the first argument, named `ctx`. Avoid `context.Background()` in request paths; propagate from the caller. Define custom context keys as unexported struct types (`type myKey struct{}`) to prevent collisions.
|
||||
|
||||
## Testing conventions
|
||||
|
||||
- Unit tests live next to the code as `*_test.go` files using `testing.T` with `testify/assert` and `testify/require`.
|
||||
- Use `require.*` for preconditions that must stop the test on failure (setup, must-not-be-nil). Use `assert.*` for independent checks where you want the test to keep running and report every failure.
|
||||
- Integration tests under `integration/` are built on `testify/suite` (see `integration/integration_test.go`) and reuse fixtures from `integration/fixtures/`. New fixtures should follow the pattern of the existing ones.
|
||||
- New providers require integration tests.
|
||||
- Prefer running a focused test over the whole suite while iterating. When iterating on a failing test, capture the output to a file once and grep it (`... > /tmp/out.log 2>&1`) rather than re-running the suite with different `TESTFLAGS`. See [`docs/content/contributing/building-testing.md`](./docs/content/contributing/building-testing.md) for the `TESTFLAGS` invocation.
|
||||
|
||||
## Documentation
|
||||
|
||||
User-facing features need matching documentation updates under `docs/content/`. Integrate new pages into the existing structure rather than creating parallel sections. Preview locally with `make docs-serve`.
|
||||
|
||||
## Contributing etiquette
|
||||
|
||||
- **Target the right branch** (the [PR template](./.github/PULL_REQUEST_TEMPLATE.md) is authoritative): enhancements go to `master`; bug fixes and documentation updates go to the current maintenance branches (`v3.6` for v3, `v2.11` for v2, security-fixes only). Forward-ports from the maintenance branches up to `master` are handled by maintainers.
|
||||
- Keep pull requests small and focused; one logical change per PR.
|
||||
- For anything beyond a bug fix, open an issue first and wait for a maintainer to confirm the direction before investing significant work.
|
||||
- Follow the full guide in [`docs/content/contributing/submitting-pull-requests.md`](./docs/content/contributing/submitting-pull-requests.md).
|
||||
|
||||
## AI assistance disclosure
|
||||
|
||||
Traefik welcomes AI-assisted contributions, provided a few simple rules are followed:
|
||||
|
||||
- **Declare substantial AI assistance** with an `Assisted-by:` trailer at the bottom of the commit message whenever an agent produced a meaningful portion of the diff — for example `Assisted-by: Claude Opus 4.6`. Trivial edits such as a typo fix or a one-line rename do not need a trailer.
|
||||
- **Keep issue and PR conversations human.** Do not let an agent post comments, review replies, or triage messages on your behalf. If an agent drafted a message for you, rewrite it in your own voice before sending — maintainers need to know they are talking to a person, not a bot.
|
||||
- **Align with a maintainer before generating code for anything larger than a bug fix.** An agent can produce thousands of lines in minutes; maintainer review capacity cannot scale the same way. Open an issue, state the intended approach, and wait for confirmation before asking an agent to implement it.
|
||||
|
||||
## Things to avoid
|
||||
|
||||
- Do not hand-edit generated files — notably `**/zz_generated*.go`, everything under `pkg/provider/kubernetes/crd/generated/`, and `webui/static/`. Regenerate them via `make generate`, `make generate-crd`, or `make generate-webui` and commit the result.
|
||||
- Do not skip `make lint` and `make validate-files` (or `make validate`) before pushing.
|
||||
- Do not opportunistically reformat, rename, or refactor files you did not otherwise need to touch. Drive-by changes turn a reviewable diff into noise — scope every PR to one logical change.
|
||||
- Do not include unrelated refactors, formatting-only changes to untouched files, or speculative abstractions in a feature PR.
|
||||
@@ -1,5 +1,5 @@
|
||||
# syntax=docker/dockerfile:1.2
|
||||
FROM alpine:3.22
|
||||
FROM alpine:3.23
|
||||
|
||||
RUN apk add --no-cache --no-progress ca-certificates tzdata
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ dist:
|
||||
.PHONY: build-webui-image
|
||||
#? build-webui-image: Build WebUI Docker image
|
||||
build-webui-image:
|
||||
docker build -t traefik-webui -f webui/Dockerfile webui
|
||||
docker build -t traefik-webui -f webui/buildx.Dockerfile webui
|
||||
|
||||
.PHONY: clean-webui
|
||||
#? clean-webui: Clean WebUI static generated assets
|
||||
@@ -41,8 +41,9 @@ clean-webui:
|
||||
|
||||
webui/static/index.html:
|
||||
$(MAKE) build-webui-image
|
||||
docker run --rm -v "$(PWD)/webui/static":'/src/webui/static' traefik-webui npm run build:nc
|
||||
docker run --rm -v "$(PWD)/webui/static":'/src/webui/static' traefik-webui yarn build:prod
|
||||
docker run --rm -v "$(PWD)/webui/static":'/src/webui/static' traefik-webui chown -R $(shell id -u):$(shell id -g) ./static
|
||||
printf 'For more information see `webui/readme.md`' > webui/static/DONT-EDIT-FILES-IN-THIS-DIRECTORY.md
|
||||
|
||||
.PHONY: generate-webui
|
||||
#? generate-webui: Generate WebUI
|
||||
@@ -57,7 +58,7 @@ generate:
|
||||
#? binary: Build the binary
|
||||
binary: generate-webui dist
|
||||
@echo SHA: $(VERSION) $(CODENAME) $(DATE)
|
||||
CGO_ENABLED=0 GOGC=${GOGC} GOOS=${GOOS} GOARCH=${GOARCH} go build ${FLAGS[*]} -ldflags "-s -w \
|
||||
CGO_ENABLED=0 GOGC=${GOGC} GOOS=${GOOS} GOARCH=${GOARCH} go build ${FLAGS} -ldflags "-s -w \
|
||||
-X github.com/traefik/traefik/v3/pkg/version.Version=$(VERSION) \
|
||||
-X github.com/traefik/traefik/v3/pkg/version.Codename=$(CODENAME) \
|
||||
-X github.com/traefik/traefik/v3/pkg/version.BuildDate=$(DATE)" \
|
||||
@@ -99,10 +100,15 @@ test-integration:
|
||||
GOOS=$(GOOS) GOARCH=$(GOARCH) go test ./integration -test.timeout=20m -failfast -v $(TESTFLAGS)
|
||||
|
||||
.PHONY: test-gateway-api-conformance
|
||||
#? test-gateway-api-conformance: Run the conformance tests
|
||||
#? test-gateway-api-conformance: Run the Gateway API conformance tests
|
||||
test-gateway-api-conformance: build-image-dirty
|
||||
# In case of a new Minor/Major version, the k8sConformanceTraefikVersion needs to be updated.
|
||||
GOOS=$(GOOS) GOARCH=$(GOARCH) go test ./integration -v -test.run K8sConformanceSuite -k8sConformance -k8sConformanceTraefikVersion="v3.4" $(TESTFLAGS)
|
||||
# In case of a new Minor/Major version, the traefikVersion needs to be updated.
|
||||
GOOS=$(GOOS) GOARCH=$(GOARCH) go test ./integration -v -tags gatewayAPIConformance -test.run GatewayAPIConformanceSuite -traefikVersion="v3.7" $(TESTFLAGS)
|
||||
|
||||
.PHONY: test-knative-conformance
|
||||
#? test-knative-conformance: Run the Knative conformance tests
|
||||
test-knative-conformance: build-image-dirty
|
||||
GOOS=$(GOOS) GOARCH=$(GOARCH) go test ./integration/integration_test.go ./integration/knative_conformance_test.go -v -tags knativeConformance -test.run KnativeConformanceSuite
|
||||
|
||||
.PHONY: test-ui-unit
|
||||
#? test-ui-unit: Run the unit tests for the webui
|
||||
@@ -183,11 +189,6 @@ generate-crd:
|
||||
generate-genconf:
|
||||
go run ./cmd/internal/gen/
|
||||
|
||||
.PHONY: release-packages
|
||||
#? release-packages: Create packages for the release
|
||||
release-packages: generate-webui
|
||||
$(CURDIR)/script/release-packages.sh
|
||||
|
||||
.PHONY: fmt
|
||||
#? fmt: Format the Code
|
||||
fmt:
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
</picture>
|
||||
</p>
|
||||
|
||||
[](https://traefik-oss.semaphoreci.com/projects/traefik)
|
||||
[](https://doc.traefik.io/traefik)
|
||||
[](https://goreportcard.com/report/traefik/traefik)
|
||||
[](https://github.com/traefik/traefik/blob/master/LICENSE.md)
|
||||
@@ -35,7 +34,7 @@ Pointing Traefik at your orchestrator should be the _only_ configuration step yo
|
||||
|
||||
---
|
||||
|
||||
:warning: When migrating to a new major version of Traefik, please refer to the [migration guide](https://doc.traefik.io/traefik/migration/v2-to-v3/) to ensure a smooth transition and to be aware of any breaking changes.
|
||||
:warning: When migrating to a new major version of Traefik, please refer to the [migration guide](https://doc.traefik.io/traefik/migrate/v2-to-v3/) to ensure a smooth transition and to be aware of any breaking changes.
|
||||
|
||||
|
||||
## Overview
|
||||
@@ -152,7 +151,7 @@ We use [Semantic Versioning](https://semver.org/).
|
||||
|
||||
## Credits
|
||||
|
||||
Kudos to [Peka](http://peka.byethost11.com/photoblog/) for his awesome work on the gopher's logo!.
|
||||
Kudos to [Peka](https://www.instagram.com/pierroks/) for his awesome work on the gopher's logo!.
|
||||
|
||||
The gopher's logo of Traefik is licensed under the Creative Commons 3.0 Attributions license.
|
||||
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
# Security Policy
|
||||
|
||||
You can join our security mailing list to be aware of the latest announcements from our security team.
|
||||
You can subscribe by sending an email to security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
|
||||
|
||||
Reported vulnerabilities can be found on [cve.mitre.org](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=traefik).
|
||||
|
||||
## Supported Versions
|
||||
|
||||
- We usually release 3/4 new versions (e.g. 1.1.0, 1.2.0, 1.3.0) per year.
|
||||
@@ -17,10 +12,10 @@ We use [Semantic Versioning](https://semver.org/).
|
||||
|
||||
| Version | Supported |
|
||||
|-----------|--------------------|
|
||||
| `2.2.x` | :white_check_mark: |
|
||||
| `< 2.2.x` | :x: |
|
||||
| `1.7.x` | :white_check_mark: |
|
||||
| `< 1.7.x` | :x: |
|
||||
| `3.6.x` | :white_check_mark: |
|
||||
| `< 3.6.x` | :x: |
|
||||
| `2.11.x` | :white_check_mark: |
|
||||
| `< 2.11.x` | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
@@ -28,3 +23,5 @@ We want to keep Traefik safe for everyone.
|
||||
If you've discovered a security vulnerability in Traefik,
|
||||
we appreciate your help in disclosing it to us in a responsible manner,
|
||||
by creating a [security advisory](https://github.com/traefik/traefik/security/advisories).
|
||||
|
||||
Reported vulnerabilities can be found on [cve.mitre.org](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=traefik).
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
// TraefikCmdConfiguration wraps the static configuration and extra parameters.
|
||||
type TraefikCmdConfiguration struct {
|
||||
static.Configuration `export:"true"`
|
||||
|
||||
// ConfigFile is the path to the configuration file.
|
||||
ConfigFile string `description:"Configuration file to use. If specified all other flags are ignored." export:"true"`
|
||||
}
|
||||
|
||||
@@ -61,7 +61,12 @@ func Do(staticConfiguration static.Configuration) (*http.Response, error) {
|
||||
return nil, fmt.Errorf("ping: missing %s entry point", ep)
|
||||
}
|
||||
|
||||
client := &http.Client{Timeout: 5 * time.Second}
|
||||
client := &http.Client{
|
||||
Timeout: 5 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
Proxy: nil,
|
||||
},
|
||||
}
|
||||
protocol := "http"
|
||||
|
||||
// TODO Handle TLS on ping etc...
|
||||
|
||||
@@ -158,7 +158,7 @@ func (c Centrifuge) run(sc *types.Scope, rootPkg string, pkgName string) map[str
|
||||
|
||||
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))
|
||||
fmt.Fprintf(&b, "type %s struct {\n", name)
|
||||
|
||||
for i := range obj.NumFields() {
|
||||
field := obj.Field(i)
|
||||
@@ -175,7 +175,7 @@ func (c Centrifuge) writeStruct(name string, obj *types.Struct, rootPkg string,
|
||||
fType := c.TypeCleaner(field.Type(), rootPkg)
|
||||
|
||||
if field.Embedded() {
|
||||
b.WriteString(fmt.Sprintf("\t%s\n", fType))
|
||||
fmt.Fprintf(&b, "\t%s\n", fType)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -184,10 +184,10 @@ func (c Centrifuge) writeStruct(name string, obj *types.Struct, rootPkg string,
|
||||
continue
|
||||
}
|
||||
|
||||
b.WriteString(fmt.Sprintf("\t%s %s", field.Name(), fType))
|
||||
fmt.Fprintf(&b, "\t%s %s", field.Name(), fType)
|
||||
|
||||
if ok {
|
||||
b.WriteString(fmt.Sprintf(" `json:\"%s\"`", strings.Join(values, ",")))
|
||||
fmt.Fprintf(&b, " `json:\"%s\"`", strings.Join(values, ","))
|
||||
}
|
||||
|
||||
b.WriteString("\n")
|
||||
|
||||
@@ -83,7 +83,7 @@ func run(dest string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.WriteFile(filepath.Join(dest, "marshaler.go"), []byte(fmt.Sprintf(marsh, destPkg)), 0o666)
|
||||
return os.WriteFile(filepath.Join(dest, "marshaler.go"), fmt.Appendf(nil, marsh, destPkg), 0o666)
|
||||
}
|
||||
|
||||
func cleanType(typ types.Type, base string) string {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -13,7 +14,7 @@ import (
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/traefik/traefik/v3/pkg/config/static"
|
||||
"github.com/traefik/traefik/v3/pkg/logs"
|
||||
"github.com/traefik/traefik/v3/pkg/observability/logs"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
@@ -22,7 +23,7 @@ func init() {
|
||||
zerolog.SetGlobalLevel(zerolog.ErrorLevel)
|
||||
}
|
||||
|
||||
func setupLogger(staticConfiguration *static.Configuration) error {
|
||||
func setupLogger(ctx context.Context, staticConfiguration *static.Configuration) error {
|
||||
// Validate that the experimental flag is set up at this point,
|
||||
// rather than validating the static configuration before the setupLogger call.
|
||||
// This ensures that validation messages are not logged using an un-configured logger.
|
||||
@@ -39,16 +40,16 @@ func setupLogger(staticConfiguration *static.Configuration) error {
|
||||
zerolog.SetGlobalLevel(logLevel)
|
||||
|
||||
// create logger
|
||||
logCtx := zerolog.New(w).With().Timestamp()
|
||||
logger := zerolog.New(w).With().Timestamp()
|
||||
if logLevel <= zerolog.DebugLevel {
|
||||
logCtx = logCtx.Caller()
|
||||
logger = logger.Caller()
|
||||
}
|
||||
|
||||
log.Logger = logCtx.Logger().Level(logLevel)
|
||||
log.Logger = logger.Logger().Level(logLevel)
|
||||
|
||||
if staticConfiguration.Log != nil && staticConfiguration.Log.OTLP != nil {
|
||||
var err error
|
||||
log.Logger, err = logs.SetupOTelLogger(log.Logger, staticConfiguration.Log.OTLP)
|
||||
log.Logger, err = logs.SetupOTelLogger(ctx, log.Logger, staticConfiguration.Log.OTLP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting up OpenTelemetry logger: %w", err)
|
||||
}
|
||||
@@ -67,10 +68,6 @@ func setupLogger(staticConfiguration *static.Configuration) error {
|
||||
}
|
||||
|
||||
func getLogWriter(staticConfiguration *static.Configuration) io.Writer {
|
||||
if staticConfiguration.Log != nil && staticConfiguration.Log.OTLP != nil {
|
||||
return io.Discard
|
||||
}
|
||||
|
||||
var w io.Writer = os.Stdout
|
||||
if staticConfiguration.Log != nil && len(staticConfiguration.Log.FilePath) > 0 {
|
||||
_, _ = os.OpenFile(staticConfiguration.Log.FilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o666)
|
||||
|
||||
@@ -2,43 +2,62 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-retryablehttp"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/traefik/traefik/v3/pkg/config/static"
|
||||
"github.com/traefik/traefik/v3/pkg/observability/logs"
|
||||
"github.com/traefik/traefik/v3/pkg/plugins"
|
||||
)
|
||||
|
||||
const outputDir = "./plugins-storage/"
|
||||
|
||||
func createPluginBuilder(staticConfiguration *static.Configuration) (*plugins.Builder, error) {
|
||||
client, plgs, localPlgs, err := initPlugins(staticConfiguration)
|
||||
manager, plgs, localPlgs, err := initPlugins(staticConfiguration)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return plugins.NewBuilder(client, plgs, localPlgs)
|
||||
return plugins.NewBuilder(manager, plgs, localPlgs)
|
||||
}
|
||||
|
||||
func initPlugins(staticCfg *static.Configuration) (*plugins.Client, map[string]plugins.Descriptor, map[string]plugins.LocalDescriptor, error) {
|
||||
func initPlugins(staticCfg *static.Configuration) (*plugins.Manager, map[string]plugins.Descriptor, map[string]plugins.LocalDescriptor, error) {
|
||||
err := checkUniquePluginNames(staticCfg.Experimental)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
var client *plugins.Client
|
||||
var manager *plugins.Manager
|
||||
plgs := map[string]plugins.Descriptor{}
|
||||
|
||||
if hasPlugins(staticCfg) {
|
||||
opts := plugins.ClientOptions{
|
||||
httpClient := retryablehttp.NewClient()
|
||||
httpClient.Logger = logs.NewRetryableHTTPLogger(log.Logger)
|
||||
httpClient.HTTPClient = &http.Client{Timeout: 10 * time.Second}
|
||||
httpClient.RetryMax = 3
|
||||
|
||||
// Create separate downloader for HTTP operations
|
||||
archivesPath := filepath.Join(outputDir, "archives")
|
||||
downloader, err := plugins.NewRegistryDownloader(plugins.RegistryDownloaderOptions{
|
||||
HTTPClient: httpClient.HTTPClient,
|
||||
ArchivesPath: archivesPath,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("unable to create plugin downloader: %w", err)
|
||||
}
|
||||
|
||||
opts := plugins.ManagerOptions{
|
||||
Output: outputDir,
|
||||
}
|
||||
|
||||
var err error
|
||||
client, err = plugins.NewClient(opts)
|
||||
manager, err = plugins.NewManager(downloader, opts)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("unable to create plugins client: %w", err)
|
||||
return nil, nil, nil, fmt.Errorf("unable to create plugins manager: %w", err)
|
||||
}
|
||||
|
||||
err = plugins.SetupRemotePlugins(client, staticCfg.Experimental.Plugins)
|
||||
err = plugins.SetupRemotePlugins(manager, staticCfg.Experimental.Plugins)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("unable to set up plugins environment: %w", err)
|
||||
}
|
||||
@@ -57,7 +76,7 @@ func initPlugins(staticCfg *static.Configuration) (*plugins.Client, map[string]p
|
||||
localPlgs = staticCfg.Experimental.LocalPlugins
|
||||
}
|
||||
|
||||
return client, plgs, localPlgs, nil
|
||||
return manager, plgs, localPlgs, nil
|
||||
}
|
||||
|
||||
func checkUniquePluginNames(e *static.Experimental) error {
|
||||
|
||||
@@ -30,9 +30,11 @@ import (
|
||||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v3/pkg/config/runtime"
|
||||
"github.com/traefik/traefik/v3/pkg/config/static"
|
||||
"github.com/traefik/traefik/v3/pkg/logs"
|
||||
"github.com/traefik/traefik/v3/pkg/metrics"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares/accesslog"
|
||||
"github.com/traefik/traefik/v3/pkg/observability/logs"
|
||||
"github.com/traefik/traefik/v3/pkg/observability/metrics"
|
||||
"github.com/traefik/traefik/v3/pkg/observability/tracing"
|
||||
otypes "github.com/traefik/traefik/v3/pkg/observability/types"
|
||||
"github.com/traefik/traefik/v3/pkg/provider/acme"
|
||||
"github.com/traefik/traefik/v3/pkg/provider/aggregator"
|
||||
"github.com/traefik/traefik/v3/pkg/provider/tailscale"
|
||||
@@ -46,8 +48,6 @@ import (
|
||||
"github.com/traefik/traefik/v3/pkg/server/service"
|
||||
"github.com/traefik/traefik/v3/pkg/tcp"
|
||||
traefiktls "github.com/traefik/traefik/v3/pkg/tls"
|
||||
"github.com/traefik/traefik/v3/pkg/tracing"
|
||||
"github.com/traefik/traefik/v3/pkg/types"
|
||||
"github.com/traefik/traefik/v3/pkg/version"
|
||||
)
|
||||
|
||||
@@ -90,10 +90,18 @@ Complete documentation is available at https://traefik.io`,
|
||||
}
|
||||
|
||||
func runCmd(staticConfiguration *static.Configuration) error {
|
||||
if err := setupLogger(staticConfiguration); err != nil {
|
||||
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer cancel()
|
||||
|
||||
if err := setupLogger(ctx, staticConfiguration); err != nil {
|
||||
return fmt.Errorf("setting up logger: %w", err)
|
||||
}
|
||||
|
||||
log.Warn().Msg("Traefik can reject some encoded characters in the request path." +
|
||||
"When your backend is not fully compliant with [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986)," +
|
||||
"it is recommended to set these options to `false` to avoid split-view situation." +
|
||||
"Refer to the documentation for more details: https://doc.traefik.io/traefik/v3.7/migrate/v3/#encoded-characters-configuration-default-values")
|
||||
|
||||
http.DefaultTransport.(*http.Transport).Proxy = http.ProxyFromEnvironment
|
||||
|
||||
staticConfiguration.SetEffectiveConfiguration()
|
||||
@@ -111,9 +119,7 @@ func runCmd(staticConfiguration *static.Configuration) error {
|
||||
log.Debug().RawJSON("staticConfiguration", []byte(redactedStaticConfiguration)).Msg("Static configuration loaded [json]")
|
||||
}
|
||||
|
||||
if staticConfiguration.Global.CheckNewVersion {
|
||||
checkNewVersion()
|
||||
}
|
||||
checkNewVersion(staticConfiguration)
|
||||
|
||||
stats(staticConfiguration)
|
||||
|
||||
@@ -122,8 +128,6 @@ func runCmd(staticConfiguration *static.Configuration) error {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, _ := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
if staticConfiguration.Ping != nil {
|
||||
staticConfiguration.Ping.WithContext(ctx)
|
||||
}
|
||||
@@ -181,7 +185,9 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
||||
|
||||
// ACME
|
||||
|
||||
tlsManager := traefiktls.NewManager()
|
||||
tlsManager := traefiktls.NewManager(staticConfiguration.OCSP)
|
||||
routinesPool.GoCtx(tlsManager.Run)
|
||||
|
||||
httpChallengeProvider := acme.NewChallengeHTTP()
|
||||
|
||||
tlsChallengeProvider := acme.NewChallengeTLSALPN()
|
||||
@@ -207,8 +213,8 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
||||
}
|
||||
}
|
||||
metricsRegistry := metrics.NewMultiRegistry(metricRegistries)
|
||||
accessLog := setupAccessLog(staticConfiguration.AccessLog)
|
||||
tracer, tracerCloser := setupTracing(staticConfiguration.Tracing)
|
||||
accessLog := setupAccessLog(ctx, staticConfiguration.AccessLog)
|
||||
tracer, tracerCloser := setupTracing(ctx, staticConfiguration.Tracing)
|
||||
observabilityMgr := middleware.NewObservabilityMgr(*staticConfiguration, metricsRegistry, semConvMetricRegistry, accessLog, tracer, tracerCloser)
|
||||
|
||||
// Entrypoints
|
||||
@@ -225,6 +231,7 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
||||
|
||||
if staticConfiguration.API != nil {
|
||||
version.DisableDashboardAd = staticConfiguration.API.DisableDashboardAd
|
||||
version.DashboardName = staticConfiguration.API.DashboardName
|
||||
}
|
||||
|
||||
// Plugins
|
||||
@@ -296,7 +303,7 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
||||
|
||||
dialerManager := tcp.NewDialerManager(spiffeX509Source)
|
||||
acmeHTTPHandler := getHTTPChallengeHandler(acmeProviders, httpChallengeProvider)
|
||||
managerFactory := service.NewManagerFactory(*staticConfiguration, routinesPool, observabilityMgr, transportManager, proxyBuilder, acmeHTTPHandler)
|
||||
managerFactory := service.NewManagerFactory(*staticConfiguration, routinesPool, observabilityMgr, transportManager, proxyBuilder, acmeHTTPHandler, tlsManager)
|
||||
|
||||
// Router factory
|
||||
|
||||
@@ -502,7 +509,7 @@ func initTailscaleProviders(cfg *static.Configuration, providerAggregator *aggre
|
||||
return providers
|
||||
}
|
||||
|
||||
func registerMetricClients(metricsConfig *types.Metrics) []metrics.Registry {
|
||||
func registerMetricClients(metricsConfig *otypes.Metrics) []metrics.Registry {
|
||||
if metricsConfig == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -583,12 +590,12 @@ func appendCertMetric(gauge gokitmetrics.Gauge, certificate *x509.Certificate) {
|
||||
gauge.With(labels...).Set(notAfter)
|
||||
}
|
||||
|
||||
func setupAccessLog(conf *types.AccessLog) *accesslog.Handler {
|
||||
func setupAccessLog(ctx context.Context, conf *otypes.AccessLog) *accesslog.Handler {
|
||||
if conf == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
accessLoggerMiddleware, err := accesslog.NewHandler(conf)
|
||||
accessLoggerMiddleware, err := accesslog.NewHandler(ctx, conf)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("Unable to create access logger")
|
||||
return nil
|
||||
@@ -597,12 +604,12 @@ func setupAccessLog(conf *types.AccessLog) *accesslog.Handler {
|
||||
return accessLoggerMiddleware
|
||||
}
|
||||
|
||||
func setupTracing(conf *static.Tracing) (*tracing.Tracer, io.Closer) {
|
||||
func setupTracing(ctx context.Context, conf *static.Tracing) (*tracing.Tracer, io.Closer) {
|
||||
if conf == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
tracer, closer, err := tracing.NewTracing(conf)
|
||||
tracer, closer, err := tracing.NewTracing(ctx, conf)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("Unable to create tracer")
|
||||
return nil, nil
|
||||
@@ -611,13 +618,28 @@ func setupTracing(conf *static.Tracing) (*tracing.Tracer, io.Closer) {
|
||||
return tracer, closer
|
||||
}
|
||||
|
||||
func checkNewVersion() {
|
||||
ticker := time.Tick(24 * time.Hour)
|
||||
safe.Go(func() {
|
||||
for time.Sleep(10 * time.Minute); ; <-ticker {
|
||||
version.CheckNewVersion()
|
||||
}
|
||||
})
|
||||
func checkNewVersion(staticConfiguration *static.Configuration) {
|
||||
logger := log.With().Logger()
|
||||
|
||||
if staticConfiguration.Global.CheckNewVersion {
|
||||
logger.Info().Msg(`Version check is enabled.`)
|
||||
logger.Info().Msg(`Traefik checks for new releases to notify you if your version is out of date.`)
|
||||
logger.Info().Msg(`It also collects usage data during this process.`)
|
||||
logger.Info().Msg(`Check the documentation to get more info: https://doc.traefik.io/traefik/contributing/data-collection/`)
|
||||
|
||||
ticker := time.Tick(24 * time.Hour)
|
||||
safe.Go(func() {
|
||||
for time.Sleep(10 * time.Minute); ; <-ticker {
|
||||
version.CheckNewVersion()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
logger.Info().Msg(`
|
||||
Version check is disabled.
|
||||
You will not be notified if a new version is available.
|
||||
More details: https://doc.traefik.io/traefik/contributing/data-collection/
|
||||
`)
|
||||
}
|
||||
}
|
||||
|
||||
func stats(staticConfiguration *static.Configuration) {
|
||||
|
||||
@@ -21,7 +21,7 @@ docs: docs-clean docs-image docs-lint docs-build docs-verify
|
||||
# Writer Mode: build and serve docs on http://localhost:8000 with livereload
|
||||
.PHONY: docs-serve
|
||||
docs-serve: docs-image
|
||||
docker run $(DOCKER_RUN_DOC_OPTS) $(TRAEFIK_DOCS_BUILD_IMAGE) mkdocs serve
|
||||
docker run $(DOCKER_RUN_DOC_OPTS) $(TRAEFIK_DOCS_BUILD_IMAGE) mkdocs serve -a 0.0.0.0:8000
|
||||
|
||||
## Pull image for doc building
|
||||
.PHONY: docs-pull-images
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.22
|
||||
FROM alpine:3.23
|
||||
|
||||
RUN apk --no-cache --no-progress add \
|
||||
build-base \
|
||||
@@ -34,6 +34,7 @@ RUN apk --no-cache --no-progress add \
|
||||
|
||||
COPY ./scripts/verify.sh /verify.sh
|
||||
COPY ./scripts/lint.sh /lint.sh
|
||||
COPY ./scripts/lint-yaml.sh /lint-yaml.sh
|
||||
|
||||
WORKDIR /app
|
||||
VOLUME ["/tmp","/app"]
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
#migration-doc-banner {
|
||||
display: block;
|
||||
position: relative;
|
||||
padding: 0.6em 1.2em;
|
||||
border: 1px solid #00b3ce;
|
||||
border-radius: 8px;
|
||||
background-color: #00b3ce1a;
|
||||
color: #00b3ce;
|
||||
font-size: 0.85em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
#migration-doc-banner a {
|
||||
color: #00b3ce;
|
||||
font-weight: 600;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#migration-doc-banner p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#migration-doc-banner-close {
|
||||
position: absolute;
|
||||
top: 0.4em;
|
||||
right: 0.6em;
|
||||
background: none;
|
||||
border: none;
|
||||
color: #00b3ce;
|
||||
font-size: 1.5em;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Breakpoint for the collapsed sidebar */
|
||||
@media (min-width: 1220px) {
|
||||
#migration-doc-banner {
|
||||
margin-top: -24px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/* Fix positioning of the built-in clipboard button for code blocks.
|
||||
* In this theme, the button can end up positioned relative to <body>,
|
||||
* so anchor it to the code block container instead.
|
||||
*/
|
||||
|
||||
.md-typeset pre.highlight {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.md-typeset pre.highlight > button.md-clipboard {
|
||||
position: absolute;
|
||||
top: .25rem;
|
||||
right: .25rem;
|
||||
z-index: 10;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
/* Use a wider grid to accommodate table content and code blocks. */
|
||||
.md-grid {
|
||||
max-width: 1800px;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/* Traefik Hub Menu icon base styles */
|
||||
.menu-icon {
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
vertical-align: middle;
|
||||
margin-left: 6px;
|
||||
transition: all 0.2s ease;
|
||||
filter: drop-shadow(0 1px 1px rgba(0,0,0,0.1));
|
||||
display: inline;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Ensure parent container keeps items inline */
|
||||
.nav-link-with-icon {
|
||||
white-space: nowrap !important;
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
}
|
||||
|
||||
/* Hover effects */
|
||||
.menu-icon:hover {
|
||||
transform: scale(1.05);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* Tablet responsive */
|
||||
@media (max-width: 1024px) {
|
||||
.menu-icon {
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile responsive */
|
||||
@media (max-width: 768px) {
|
||||
.menu-icon {
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
margin-left: 3px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* Keep mobile navigation items inline */
|
||||
.nav-link-with-icon {
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
width: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* High DPI displays */
|
||||
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
|
||||
.menu-icon {
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
image-rendering: crisp-edges;
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 307 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 241 KiB |
@@ -0,0 +1,38 @@
|
||||
(function () {
|
||||
var BANNER_ID = 'migration-doc-banner';
|
||||
var SESSION_KEY = 'migration-doc-banner-dismissed';
|
||||
|
||||
function createBanner() {
|
||||
if (document.getElementById(BANNER_ID)) return;
|
||||
if (sessionStorage.getItem(SESSION_KEY)) return;
|
||||
|
||||
var banner = document.createElement('div');
|
||||
banner.id = BANNER_ID;
|
||||
banner.innerHTML =
|
||||
'<p><strong>Moving from ingress-nginx?</strong></p>' +
|
||||
'<p>No need to start over. Traefik supports your existing ingress-nginx annotations as-is — no rewrites, no downtime.</p>' +
|
||||
'<p>See our <a href="/traefik/migrate/nginx-to-traefik/">migration guide</a> and <a href="/traefik/reference/routing-configuration/kubernetes/ingress-nginx/">annotation reference</a> to get started.</p>' +
|
||||
'<button id="migration-doc-banner-close" aria-label="Dismiss banner">×</button>';
|
||||
|
||||
var target =
|
||||
document.querySelector('.md-content__inner') ||
|
||||
document.querySelector('.md-main__inner') ||
|
||||
document.querySelector('article') ||
|
||||
document.querySelector('main');
|
||||
|
||||
if (target) {
|
||||
target.insertBefore(banner, target.firstChild);
|
||||
}
|
||||
|
||||
document.getElementById('migration-doc-banner-close').addEventListener('click', function () {
|
||||
banner.remove();
|
||||
sessionStorage.setItem(SESSION_KEY, '1');
|
||||
});
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', createBanner);
|
||||
} else {
|
||||
createBanner();
|
||||
}
|
||||
})();
|
||||
@@ -1,4 +1,14 @@
|
||||
/* Highlight */
|
||||
(function(hljs) {
|
||||
hljs.initHighlightingOnLoad();
|
||||
})(hljs);
|
||||
})(hljs);
|
||||
|
||||
/* Scarf Analytics - cookieless, anonymous company-level intelligence */
|
||||
(function() {
|
||||
var img = document.createElement('img');
|
||||
img.src = 'https://static.scarf.sh/a.png?x-pxid=1a49232a-b165-4015-8ed2-a1092f1f0d83';
|
||||
img.referrerPolicy = 'no-referrer-when-downgrade';
|
||||
img.loading = 'eager';
|
||||
img.style.cssText = 'visibility:hidden;position:absolute;width:1px;height:1px;';
|
||||
document.body.appendChild(img);
|
||||
})();
|
||||
@@ -1,17 +1,72 @@
|
||||
---
|
||||
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."
|
||||
description: "Learn what data Traefik shares, how it is used, and how you can control it. This documentation explains both version check and anonymous usage statistics data. Read the technical documentation."
|
||||
---
|
||||
|
||||
# Data Collection
|
||||
|
||||
Understanding How Traefik is Being Used
|
||||
Understanding the data Traefik shares and how it is used
|
||||
{: .subtitle }
|
||||
|
||||
## Configuration Example
|
||||
## Introduction
|
||||
|
||||
Understanding how you use Traefik is very important to us: it helps us improve the solution in many different ways.
|
||||
For this very reason, the sendAnonymousUsage option is mandatory: we want you to take time to consider whether or not you wish to share anonymous data with us, so we can benefit from your experience and use cases.
|
||||
Protecting user privacy is essential to Traefik Labs, and we design every data-sharing mechanism with transparency and minimalism in mind.
|
||||
This page describes the two types of data exchanged by Traefik and how to configure them.
|
||||
|
||||
For more details on how your data is handled, please refer to our [Privacy and Cookie Policy](https://traefik.io/legal/privacy-and-cookie-policy).
|
||||
|
||||
## Configuration Overview
|
||||
|
||||
Traefik provides two independent mechanisms:
|
||||
|
||||
- `checkNewVersion`, enabled by default. You may disable it at any time.
|
||||
- `sendAnonymousUsage`, which requires explicit opt‑in.
|
||||
|
||||
Examples below show how to activate or deactivate both of them.
|
||||
|
||||
```yaml tab="YAML"
|
||||
global:
|
||||
checkNewVersion: true # set to false to disable
|
||||
sendAnonymousUsage: false # set to true to enable
|
||||
```
|
||||
|
||||
```toml tab="TOML"
|
||||
[global]
|
||||
checkNewVersion = true # set to false to disable
|
||||
sendAnonymousUsage = false # set to true to enable
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--global.checkNewVersion=true # set to false to disable
|
||||
--global.sendAnonymousUsage=false # set to true to enable
|
||||
```
|
||||
|
||||
A log message at startup clearly indicates whether each of those options are enabled or disabled.
|
||||
|
||||
## Version Check (`checkNewVersion`) – Opt-out
|
||||
|
||||
Traefik periodically contacts `update.traefik.io` to determine whether a newer version is available.
|
||||
When this request is made, Traefik shares the **running version** and the **public IP** of the instance.
|
||||
The IP is used to build global usage statistics and does not influence the version comparison.
|
||||
|
||||
This mechanism helps you stay informed about updates and provides TraefikLabs with a broad view of which versions are deployed in the wild.
|
||||
|
||||
The collected IP addresses are also used for marketing purposes, specifically to detect companies running Traefik and offer them adapted support contracts, enterprise features, and tailored services.
|
||||
|
||||
If you want to explore the implementation, you can read the version check source code: [version.go](https://github.com/traefik/traefik/blob/master/pkg/version/version.go)
|
||||
|
||||
## Anonymous Usage Data (`sendAnonymousUsage`) – Opt‑in
|
||||
|
||||
Traefik can also collect anonymous usage statistics once per day, starting 10 minutes after it starts running.
|
||||
These statistics include:
|
||||
|
||||
- the Traefik version,
|
||||
- a hash of the configuration,
|
||||
- an anonymized version of the static configuration (all sensitive fields removed: tokens, passwords, URLs, IP addresses, domains, emails, etc.).
|
||||
|
||||
This feature comes from this [public proposal](https://github.com/traefik/traefik/issues/2369).
|
||||
|
||||
This information helps TraefikLabs understand how Traefik is used in general and prioritize features and provider support accordingly. Dynamic configuration (routers and services) is never collected.
|
||||
|
||||
!!! example "Enabling Data Collection"
|
||||
|
||||
@@ -32,21 +87,6 @@ For this very reason, the sendAnonymousUsage option is mandatory: we want you to
|
||||
--global.sendAnonymousUsage
|
||||
```
|
||||
|
||||
## Collected Data
|
||||
|
||||
This feature comes from this [public proposal](https://github.com/traefik/traefik/issues/2369).
|
||||
|
||||
In order to help us learn more about how Traefik is being used and improve it, we collect anonymous usage statistics from running instances.
|
||||
Those data help us prioritize our developments and focus on what's important for our users (for example, which provider is popular, and which is not).
|
||||
|
||||
### What's collected / when ?
|
||||
|
||||
Once a day (the first call begins 10 minutes after the start of Traefik), we collect:
|
||||
|
||||
- the Traefik version number
|
||||
- a hash of the configuration
|
||||
- an **anonymized version** of the static configuration (token, username, password, URL, IP, domain, email, etc., are removed).
|
||||
|
||||
!!! info
|
||||
|
||||
- We do not collect the dynamic configuration information (routers & services).
|
||||
@@ -93,8 +133,9 @@ providers:
|
||||
insecureSkipVerify: true
|
||||
```
|
||||
|
||||
## The Code for Data Collection
|
||||
### The Code for Anonymous Usage Collection
|
||||
|
||||
If you want to dig into more details, here is the source code of the collecting system: [collector.go](https://github.com/traefik/traefik/blob/master/pkg/collector/collector.go)
|
||||
If you want to explore the implementation, you can read the collector source code:
|
||||
[collector.go](https://github.com/traefik/traefik/blob/master/pkg/collector/collector.go)
|
||||
|
||||
By default, we anonymize all configuration fields, except fields tagged with `export=true`.
|
||||
Traefik anonymizes all configuration fields by default, except those explicitly marked with `export=true`.
|
||||
|
||||
@@ -15,7 +15,7 @@ Let's see how.
|
||||
|
||||
### General
|
||||
|
||||
This [documentation](../../ "Link to the official Traefik documentation") is built with [MkDocs](https://mkdocs.org/ "Link to the website of MkDocs").
|
||||
This [documentation](../index.md "Link to the official Traefik documentation") is built with [MkDocs](https://mkdocs.org/ "Link to the website of MkDocs").
|
||||
|
||||
### Method 1: `Docker` and `make`
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ As a maintainer, you are granted a vote for the following:
|
||||
- [Proposals](https://github.com/traefik/contributors-guide/blob/master/proposals.md).
|
||||
|
||||
Maintainers are also added to the maintainer's Discord server where happens the [issue triage](https://github.com/traefik/contributors-guide/blob/master/issue_triage.md)
|
||||
and appear on the [Maintainers](maintainers.md) page.
|
||||
and appear on the [Maintainers](./maintainers.md) page.
|
||||
|
||||
As a maintainer, you should:
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ description: "Traefik Proxy is an open source software with a thriving community
|
||||
* Simon Delicata [@sdelicata](https://github.com/sdelicata)
|
||||
* Baptiste Mayelle [@youkoulayley](https://github.com/youkoulayley)
|
||||
* Jesper Noordsij [@jnoordsij](https://github.com/jnoordsij)
|
||||
* Gina Adzani [@gndz07](https://github.com/gndz07)
|
||||
* Mathis Urien [@LBF38](https://github.com/LBF38)
|
||||
|
||||
## Past Maintainers
|
||||
|
||||
@@ -37,4 +39,4 @@ People who have had an incredibly positive impact on the project, and are now fo
|
||||
|
||||
## Maintainer's Guidelines
|
||||
|
||||
Please read the [maintainer's guidelines](maintainers-guidelines.md).
|
||||
Please read the [maintainer's guidelines](./maintainers-guidelines.md).
|
||||
|
||||
@@ -8,7 +8,7 @@ description: "Looking to contribute to Traefik Proxy? This guide will show you t
|
||||
This guide is for contributors who already have a pull request to submit.
|
||||
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/).
|
||||
see the [development guide](./building-testing.md).
|
||||
|
||||
Looking for a way to contribute to Traefik Proxy?
|
||||
Check out this list of [Priority Issues](https://github.com/traefik/traefik/labels/contributor%2Fwanted),
|
||||
@@ -46,7 +46,7 @@ Read more about the [Triage process](https://github.com/traefik/contributors-gui
|
||||
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.
|
||||
* [Following project conventions](./maintainers-guidelines.md); including using the PR Template.
|
||||
* Make small pull requests.
|
||||
* Solve only one problem at a time.
|
||||
* Comment thoroughly.
|
||||
|
||||
@@ -15,9 +15,65 @@ You can subscribe by sending an email to security+subscribe@traefik.io or on [th
|
||||
Reported vulnerabilities can be found on
|
||||
[cve.mitre.org](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=traefik).
|
||||
|
||||
CVEs are only created for vulnerabilities affecting **Generally Available (GA) versions** of Traefik.
|
||||
Vulnerabilities discovered in non-GA versions (release candidates, betas, early access, or development branches)
|
||||
will be fixed without creating a CVE.
|
||||
|
||||
## Report a Vulnerability
|
||||
|
||||
We want to keep Traefik safe for everyone.
|
||||
If you've discovered a security vulnerability in Traefik,
|
||||
we appreciate your help in disclosing it to us in a responsible manner,
|
||||
by creating a [security advisory](https://github.com/traefik/traefik/security/advisories).
|
||||
|
||||
## Code of Conduct for Vulnerability Submissions
|
||||
|
||||
We are committed to handling every legitimate report responsibly,
|
||||
and we expect submitters to engage with our security team in a respectful and collaborative manner.
|
||||
|
||||
The following behaviors are **not acceptable** and will not be tolerated:
|
||||
|
||||
- **Threats** to publicly disclose the vulnerability if it is not fixed within a timeframe you set unilaterally.
|
||||
- **Ultimatums** or pressure tactics intended to force a faster response than our normal triage and remediation process allows.
|
||||
- **Demands** for payment, bug bounties, or any form of compensation in exchange for not disclosing the issue
|
||||
(Traefik does not operate a paid bug bounty program).
|
||||
- **Aggressive, abusive, or disrespectful communication** with our security team.
|
||||
|
||||
Submitters who engage in any of the above may face the following consequences:
|
||||
|
||||
- The submitter **will not be credited** in the security advisory or any subsequent communication.
|
||||
- The submitter's GitHub profile may be **reported to GitHub** for violation of platform terms of service.
|
||||
- We may **decline to engage further** on the report, while still addressing the underlying issue if it is legitimate.
|
||||
|
||||
We take security seriously and act on legitimate reports as quickly as our resources allow.
|
||||
Patience and constructive dialogue help us protect users effectively.
|
||||
|
||||
## Submission Quality Guidelines
|
||||
|
||||
We have been receiving an increasing number of low-quality vulnerability reports that are not actual security issues.
|
||||
Many of these reports originate from AI/LLM tools and are submitted without any human validation or testing.
|
||||
This wastes the time of our security team and delays the handling of legitimate vulnerabilities.
|
||||
|
||||
Before submitting a security advisory, you **must**:
|
||||
|
||||
- **Carefully test and validate** the vulnerability yourself before submitting.
|
||||
You must be able to demonstrate a working proof of concept with clear reproduction steps.
|
||||
- **Understand the impact** of the vulnerability and explain how it can be exploited in a realistic scenario.
|
||||
- **Verify that the issue is not a false positive**.
|
||||
Ensure the behavior you are reporting is actually a security concern and not expected behavior.
|
||||
|
||||
### Policy on AI-Generated Reports
|
||||
|
||||
Security reports that are **directly generated by AI/LLM tools without proper human validation** will be **closed immediately**.
|
||||
|
||||
Indicators of unvalidated AI-generated reports include (but are not limited to):
|
||||
|
||||
- No working proof of concept or reproduction steps.
|
||||
- Generic or theoretical vulnerability descriptions with no evidence of actual testing.
|
||||
- Misunderstanding of Traefik's architecture or threat model.
|
||||
- Hallucinated code paths, configuration options, or behaviors that do not exist.
|
||||
|
||||
**Contributors who repeatedly submit low-quality or unvalidated reports may have their accounts blocked.**
|
||||
|
||||
We appreciate the work of security researchers who take the time to rigorously validate their findings.
|
||||
Quality over quantity helps keep Traefik safe for everyone.
|
||||
|
||||
@@ -6,24 +6,14 @@ Below is a non-exhaustive list of versions and their maintenance status:
|
||||
|
||||
| Version | Release Date | Active Support | Security Support |
|
||||
|---------|--------------|--------------------|-------------------|
|
||||
| 3.4 | May 05, 2025 | Yes | Yes |
|
||||
| 3.6 | Nov 07, 2025 | Yes | Yes |
|
||||
| 3.5 | Jul 23, 2025 | Ended Nov 07, 2025 | No |
|
||||
| 3.4 | May 05, 2025 | Ended Jul 23, 2025 | No |
|
||||
| 3.3 | Jan 06, 2025 | Ended May 05, 2025 | No |
|
||||
| 3.2 | Oct 28, 2024 | Ended Jan 06, 2025 | No |
|
||||
| 3.1 | Jul 15, 2024 | Ended Oct 28, 2024 | No |
|
||||
| 3.0 | Apr 29, 2024 | Ended Jul 15, 2024 | No |
|
||||
| 2.11 | Feb 12, 2024 | Ended Apr 29, 2025 | Ends Feb 01, 2026 |
|
||||
| 2.10 | Apr 24, 2023 | Ended Feb 12, 2024 | No |
|
||||
| 2.9 | Oct 03, 2022 | Ended Apr 24, 2023 | No |
|
||||
| 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 | No |
|
||||
|
||||
??? example "Active Support / Security Support"
|
||||
|
||||
|
||||
@@ -1,462 +0,0 @@
|
||||
# Exposing Services with Traefik on Docker
|
||||
|
||||
This guide will help you expose your services securely through Traefik Proxy using Docker. We'll cover routing HTTP and HTTPS traffic, implementing TLS, adding middlewares, Let's Encrypt integration, and sticky sessions.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Docker and Docker Compose installed
|
||||
- Basic understanding of Docker concepts
|
||||
- Traefik deployed using the Traefik Docker Setup guide
|
||||
|
||||
## Expose Your First HTTP Service
|
||||
|
||||
Let's expose a simple HTTP service using the [whoami](https://hub.docker.com/r/traefik/whoami) application. This will demonstrate basic routing to a backend service.
|
||||
|
||||
First, create a `docker-compose.yml` file:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
traefik:
|
||||
image: "traefik:v3.4"
|
||||
container_name: "traefik"
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
networks:
|
||||
- proxy
|
||||
command:
|
||||
- "--providers.docker=true"
|
||||
- "--providers.docker.exposedbydefault=false"
|
||||
- "--providers.docker.network=proxy"
|
||||
- "--entryPoints.web.address=:80"
|
||||
ports:
|
||||
- "80:80"
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
|
||||
whoami:
|
||||
image: "traefik/whoami"
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- proxy
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.whoami.rule=Host(`whoami.docker.localhost`)"
|
||||
- "traefik.http.routers.whoami.entrypoints=web"
|
||||
|
||||
networks:
|
||||
proxy:
|
||||
name: proxy
|
||||
```
|
||||
|
||||
Save this as `docker-compose.yml` and start the services:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Verify Your Service
|
||||
|
||||
Your service is now available at http://whoami.docker.localhost/. Test that it works:
|
||||
|
||||
```bash
|
||||
curl -H "Host: whoami.docker.localhost" http://localhost/
|
||||
```
|
||||
|
||||
You should see output similar to:
|
||||
|
||||
```bash
|
||||
Hostname: whoami
|
||||
IP: 127.0.0.1
|
||||
IP: ::1
|
||||
IP: 172.18.0.3
|
||||
IP: fe80::215:5dff:fe00:c9e
|
||||
RemoteAddr: 172.18.0.2:55108
|
||||
GET / HTTP/1.1
|
||||
Host: whoami.docker.localhost
|
||||
User-Agent: curl/7.68.0
|
||||
Accept: */*
|
||||
Accept-Encoding: gzip
|
||||
X-Forwarded-For: 172.18.0.1
|
||||
X-Forwarded-Host: whoami.docker.localhost
|
||||
X-Forwarded-Port: 80
|
||||
X-Forwarded-Proto: http
|
||||
X-Forwarded-Server: 5789f594e7d5
|
||||
X-Real-Ip: 172.18.0.1
|
||||
```
|
||||
|
||||
This confirms that Traefik is successfully routing requests to your whoami application.
|
||||
|
||||
## Add Routing Rules
|
||||
|
||||
Now we'll enhance our routing by directing traffic to different services based on [URL paths](../reference/routing-configuration/http/router/rules-and-priority.md#path-pathprefix-and-pathregexp). This is useful for API versioning, frontend/backend separation, or organizing microservices.
|
||||
|
||||
Update your `docker-compose.yml` to add another service:
|
||||
|
||||
```yaml
|
||||
# ...
|
||||
|
||||
# New service
|
||||
whoami-api:
|
||||
image: "traefik/whoami"
|
||||
networks:
|
||||
- proxy
|
||||
container_name: "whoami-api"
|
||||
environment:
|
||||
- WHOAMI_NAME=API Service
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
# Path-based routing
|
||||
- "traefik.http.routers.whoami-api.rule=Host(`whoami.docker.localhost`) && PathPrefix(`/api`)"
|
||||
- "traefik.http.routers.whoami-api.entrypoints=web"
|
||||
```
|
||||
|
||||
Apply the changes:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Test the Path-Based Routing
|
||||
|
||||
Verify that different paths route to different services:
|
||||
|
||||
```bash
|
||||
# Root path should go to the main whoami service
|
||||
curl -H "Host: whoami.docker.localhost" http://localhost/
|
||||
|
||||
# /api path should go to the whoami-api service
|
||||
curl -H "Host: whoami.docker.localhost" http://localhost/api
|
||||
```
|
||||
|
||||
For the `/api` requests, you should see the response showing "API Service" in the environment variables section, confirming that your path-based routing is working correctly.
|
||||
|
||||
## Enable TLS
|
||||
|
||||
Let's secure our service with HTTPS by adding TLS. We'll start with a self-signed certificate for local development.
|
||||
|
||||
### Create a Self-Signed Certificate
|
||||
|
||||
Generate a self-signed certificate:
|
||||
|
||||
```bash
|
||||
mkdir -p certs
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
-keyout certs/local.key -out certs/local.crt \
|
||||
-subj "/CN=*.docker.localhost"
|
||||
```
|
||||
|
||||
Create a directory for dynamic configuration and add a TLS configuration file:
|
||||
|
||||
```bash
|
||||
mkdir -p dynamic
|
||||
cat > dynamic/tls.yml << EOF
|
||||
tls:
|
||||
certificates:
|
||||
- certFile: /certs/local.crt
|
||||
keyFile: /certs/local.key
|
||||
EOF
|
||||
```
|
||||
|
||||
Update your `docker-compose.yml` file with the following changes:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
traefik:
|
||||
image: "traefik:v3.4"
|
||||
container_name: "traefik"
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
networks:
|
||||
- proxy
|
||||
command:
|
||||
- "--api.insecure=false"
|
||||
- "--api.dashboard=true"
|
||||
- "--providers.docker=true"
|
||||
- "--providers.docker.exposedbydefault=false"
|
||||
- "--providers.docker.network=proxy"
|
||||
- "--providers.file.directory=/etc/traefik/dynamic"
|
||||
- "--entryPoints.web.address=:80"
|
||||
- "--entryPoints.websecure.address=:443"
|
||||
- "--entryPoints.websecure.http.tls=true"
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
# Add the following volumes
|
||||
- "./certs:/certs:ro"
|
||||
- "./dynamic:/etc/traefik/dynamic:ro"
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.dashboard.rule=Host(`dashboard.docker.localhost`)"
|
||||
- "traefik.http.routers.dashboard.entrypoints=websecure"
|
||||
- "traefik.http.routers.dashboard.service=api@internal"
|
||||
# Add the following label
|
||||
- "traefik.http.routers.dashboard.tls=true"
|
||||
|
||||
whoami:
|
||||
image: "traefik/whoami"
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- proxy
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.whoami.rule=Host(`whoami.docker.localhost`)"
|
||||
- "traefik.http.routers.whoami.entrypoints=websecure"
|
||||
# Add the following label
|
||||
- "traefik.http.routers.whoami.tls=true"
|
||||
|
||||
whoami-api:
|
||||
image: "traefik/whoami"
|
||||
container_name: "whoami-api"
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- proxy
|
||||
environment:
|
||||
- WHOAMI_NAME=API Service
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.whoami-api.rule=Host(`whoami.docker.localhost`) && PathPrefix(`/api`)"
|
||||
- "traefik.http.routers.whoami-api.entrypoints=websecure"
|
||||
# Add the following label
|
||||
- "traefik.http.routers.whoami-api.tls=true"
|
||||
|
||||
networks:
|
||||
proxy:
|
||||
name: proxy
|
||||
```
|
||||
|
||||
Apply the changes:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Your browser can access https://whoami.docker.localhost/ for the service. You'll need to accept the security warning for the self-signed certificate.
|
||||
|
||||
## Add Middlewares
|
||||
|
||||
Middlewares allow you to modify requests or responses as they pass through Traefik. Let's add two useful middlewares: [Headers](../reference/routing-configuration/http/middlewares/headers.md) for security and [IP allowlisting](../reference/routing-configuration/http/middlewares/ipallowlist.md) for access control.
|
||||
|
||||
Add the following labels to your whoami service in `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
|
||||
# Secure Headers Middleware
|
||||
- "traefik.http.middlewares.secure-headers.headers.frameDeny=true"
|
||||
- "traefik.http.middlewares.secure-headers.headers.sslRedirect=true"
|
||||
- "traefik.http.middlewares.secure-headers.headers.browserXssFilter=true"
|
||||
- "traefik.http.middlewares.secure-headers.headers.contentTypeNosniff=true"
|
||||
- "traefik.http.middlewares.secure-headers.headers.stsIncludeSubdomains=true"
|
||||
- "traefik.http.middlewares.secure-headers.headers.stsPreload=true"
|
||||
- "traefik.http.middlewares.secure-headers.headers.stsSeconds=31536000"
|
||||
|
||||
# IP Allowlist Middleware
|
||||
- "traefik.http.middlewares.ip-allowlist.ipallowlist.sourceRange=127.0.0.1/32,192.168.0.0/16,10.0.0.0/8"
|
||||
```
|
||||
|
||||
Add the same middleware to your whoami-api service:
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.http.routers.whoami-api.middlewares=secure-headers,ip-allowlist"
|
||||
```
|
||||
|
||||
Apply the changes:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Test the Middlewares
|
||||
|
||||
Now let's verify that our middlewares are working correctly:
|
||||
|
||||
Test the Secure Headers middleware:
|
||||
|
||||
```bash
|
||||
curl -k -I -H "Host: whoami.docker.localhost" https://localhost/
|
||||
```
|
||||
|
||||
In the response headers, you should see security headers set by the middleware:
|
||||
|
||||
- `X-Frame-Options: DENY`
|
||||
- `X-Content-Type-Options: nosniff`
|
||||
- `X-XSS-Protection: 1; mode=block`
|
||||
- `Strict-Transport-Security` with the appropriate settings
|
||||
|
||||
Test the IP Allowlist middleware:
|
||||
|
||||
If your request comes from an IP that's in the allow list (e.g., 127.0.0.1), it should succeed:
|
||||
|
||||
```bash
|
||||
curl -k -I -H "Host: whoami.docker.localhost" https://localhost/
|
||||
```
|
||||
|
||||
If you try to access from an IP not in the allow list, the request will be rejected with a `403` Forbidden response. To simulate this in a local environment, you can modify the middleware configuration temporarily to exclude your IP address, then test again.
|
||||
|
||||
## Generate Certificates with Let's Encrypt
|
||||
|
||||
Let's Encrypt provides free, automated TLS certificates. Let's configure Traefik to automatically obtain and renew certificates for our services.
|
||||
|
||||
Instead of using self-signed certificates, update your existing `docker-compose.yml` file with the following changes:
|
||||
|
||||
Add the Let's Encrypt certificate resolver to the Traefik service command section:
|
||||
|
||||
```yaml
|
||||
command:
|
||||
- "--api.insecure=false"
|
||||
- "--api.dashboard=true"
|
||||
- "--providers.docker=true"
|
||||
- "--providers.docker.exposedbydefault=false"
|
||||
- "--providers.docker.network=proxy"
|
||||
- "--entryPoints.web.address=:80"
|
||||
- "--entryPoints.websecure.address=:443"
|
||||
- "--entryPoints.websecure.http.tls=true"
|
||||
- "--entryPoints.web.http.redirections.entryPoint.to=websecure"
|
||||
- "--entryPoints.web.http.redirections.entryPoint.scheme=https"
|
||||
# Let's Encrypt configuration
|
||||
- "--certificatesresolvers.le.acme.email=your-email@example.com" # replace with your actual email
|
||||
- "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
|
||||
- "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
|
||||
```
|
||||
|
||||
Add a volume for Let's Encrypt certificates:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
# ...Existing volumes...
|
||||
- "./letsencrypt:/letsencrypt"
|
||||
```
|
||||
|
||||
Update your service labels to use the certificate resolver:
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.http.routers.whoami.tls.certresolver=le"
|
||||
```
|
||||
|
||||
Do the same for any other services you want to secure:
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.http.routers.whoami-api.tls.certresolver=le"
|
||||
```
|
||||
|
||||
Create a directory for storing Let's Encrypt certificates:
|
||||
|
||||
```bash
|
||||
mkdir -p letsencrypt
|
||||
```
|
||||
|
||||
Apply the changes:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
!!! important "Public DNS Required"
|
||||
Let's Encrypt may require a publicly accessible domain to validate domain ownership. For testing with local domains like `whoami.docker.localhost`, the certificate will remain self-signed. In production, replace it with a real domain that has a publicly accessible DNS record pointing to your Traefik instance.
|
||||
|
||||
Once the certificate is issued, you can verify it:
|
||||
|
||||
```bash
|
||||
# Verify the certificate chain
|
||||
curl -v https://whoami.docker.localhost/ 2>&1 | grep -i "server certificate"
|
||||
```
|
||||
|
||||
You should see that your certificate is issued by Let's Encrypt.
|
||||
|
||||
## Configure Sticky Sessions
|
||||
|
||||
Sticky sessions ensure that a user's requests always go to the same backend server, which is essential for applications that maintain session state. Let's implement sticky sessions for our whoami service.
|
||||
|
||||
### First, Add Sticky Session Labels
|
||||
|
||||
Add the following labels to your whoami service in the `docker-compose.yml` file:
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.http.services.whoami.loadbalancer.sticky.cookie=true"
|
||||
- "traefik.http.services.whoami.loadbalancer.sticky.cookie.name=sticky_cookie"
|
||||
- "traefik.http.services.whoami.loadbalancer.sticky.cookie.secure=true"
|
||||
- "traefik.http.services.whoami.loadbalancer.sticky.cookie.httpOnly=true"
|
||||
```
|
||||
|
||||
Apply the changes:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Then, Scale Up the Service
|
||||
|
||||
To demonstrate sticky sessions with Docker, use Docker Compose's scale feature:
|
||||
|
||||
```bash
|
||||
docker compose up -d --scale whoami=3
|
||||
```
|
||||
|
||||
This creates multiple instances of the whoami service.
|
||||
|
||||
!!! important "Scaling After Configuration Changes"
|
||||
If you run `docker compose up -d` after scaling, it will reset the number of whoami instances back to 1. Always scale after applying configuration changes and starting the services.
|
||||
|
||||
### Test Sticky Sessions
|
||||
|
||||
You can test the sticky sessions by making multiple requests and observing that they all go to the same backend container:
|
||||
|
||||
```bash
|
||||
# First request - save cookies to a file
|
||||
curl -k -c cookies.txt -H "Host: whoami.docker.localhost" https://localhost/
|
||||
|
||||
# Subsequent requests - use the cookies
|
||||
curl -k -b cookies.txt -H "Host: whoami.docker.localhost" https://localhost/
|
||||
curl -k -b cookies.txt -H "Host: whoami.docker.localhost" https://localhost/
|
||||
```
|
||||
|
||||
Pay attention to the `Hostname` field in each response - it should remain the same across all requests when using the cookie file, confirming that sticky sessions are working.
|
||||
|
||||
For comparison, try making requests without the cookie:
|
||||
|
||||
```bash
|
||||
# Requests without cookies should be load-balanced across different containers
|
||||
curl -k -H "Host: whoami.docker.localhost" https://localhost/
|
||||
curl -k -H "Host: whoami.docker.localhost" https://localhost/
|
||||
```
|
||||
|
||||
You should see different `Hostname` values in these responses, as each request is load-balanced to a different container.
|
||||
|
||||
!!! important "Browser Testing"
|
||||
When testing in browsers, you need to use the same browser session to maintain the cookie. The cookie is set with `httpOnly` and `secure` flags for security, so it will only be sent over HTTPS connections and won't be accessible via JavaScript.
|
||||
|
||||
For more advanced configuration options, see the [reference documentation](../reference/routing-configuration/http/load-balancing/service.md).
|
||||
|
||||
## Conclusion
|
||||
|
||||
In this guide, you've learned how to:
|
||||
|
||||
- Expose HTTP services through Traefik in Docker
|
||||
- Set up path-based routing to direct traffic to different backend services
|
||||
- Secure your services with TLS using self-signed certificates
|
||||
- Add security with middlewares like secure headers and IP allow listing
|
||||
- Automate certificate management with Let's Encrypt
|
||||
- Implement sticky sessions for stateful applications
|
||||
|
||||
These fundamental capabilities provide a solid foundation for exposing any application through Traefik Proxy in Docker. Each of these can be further customized to meet your specific requirements.
|
||||
|
||||
### Next Steps
|
||||
|
||||
Now that you understand the basics of exposing services with Traefik Proxy, you might want to explore:
|
||||
|
||||
- [Advanced routing options](../reference/routing-configuration/http/router/rules-and-priority.md) like query parameter matching, header-based routing, and more
|
||||
- [Additional middlewares](../reference/routing-configuration/http/middlewares/overview.md) for authentication, rate limiting, and request modifications
|
||||
- [Observability features](../reference/install-configuration/observability/metrics.md) for monitoring and debugging your Traefik deployment
|
||||
- [TCP services](../reference/routing-configuration/tcp/service.md) for exposing TCP services
|
||||
- [UDP services](../reference/routing-configuration/udp/service.md) for exposing UDP services
|
||||
- [Docker provider documentation](../reference/install-configuration/providers/docker.md) for more details about the Docker integration
|
||||
@@ -0,0 +1,472 @@
|
||||
# Exposing Services with Traefik on Docker - Advanced
|
||||
|
||||
This guide builds on the concepts and setup from the [Basic Guide](basic.md). Make sure you've completed the basic guide and have a working Traefik setup with Docker before proceeding.
|
||||
|
||||
In this advanced guide, you'll learn how to enhance your Traefik deployment with:
|
||||
|
||||
- **Middlewares** for security headers and access control
|
||||
- **Let's Encrypt** for automated certificate management
|
||||
- **Sticky sessions** for stateful applications
|
||||
- **Multi-layer routing** for hierarchical routing with a complex authentication based routing example
|
||||
- **Service middlewares** for applying middleware at the service level
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Completed the [Basic Guide](basic.md)
|
||||
- Docker and Docker Compose installed
|
||||
- Working Traefik setup from the basic guide
|
||||
|
||||
## Add Middlewares
|
||||
|
||||
Middlewares allow you to modify requests or responses as they pass through Traefik. Let's add two useful middlewares: [Headers](../../reference/routing-configuration/http/middlewares/headers.md) for security and [IP allowlisting](../../reference/routing-configuration/http/middlewares/ipallowlist.md) for access control.
|
||||
|
||||
Add the following labels to your whoami service in `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
|
||||
# Secure Headers Middleware
|
||||
- "traefik.http.middlewares.secure-headers.headers.frameDeny=true"
|
||||
- "traefik.http.middlewares.secure-headers.headers.sslRedirect=true"
|
||||
- "traefik.http.middlewares.secure-headers.headers.browserXssFilter=true"
|
||||
- "traefik.http.middlewares.secure-headers.headers.contentTypeNosniff=true"
|
||||
- "traefik.http.middlewares.secure-headers.headers.stsIncludeSubdomains=true"
|
||||
- "traefik.http.middlewares.secure-headers.headers.stsPreload=true"
|
||||
- "traefik.http.middlewares.secure-headers.headers.stsSeconds=31536000"
|
||||
|
||||
# IP Allowlist Middleware
|
||||
- "traefik.http.middlewares.ip-allowlist.ipallowlist.sourceRange=127.0.0.1/32,192.168.0.0/16,10.0.0.0/8"
|
||||
|
||||
# Apply middlewares to whoami router
|
||||
- "traefik.http.routers.whoami.middlewares=secure-headers,ip-allowlist"
|
||||
```
|
||||
|
||||
Add the same middleware to your whoami-api service:
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.http.routers.whoami-api.middlewares=secure-headers,ip-allowlist"
|
||||
```
|
||||
|
||||
Apply the changes:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Test the Middlewares
|
||||
|
||||
Now let's verify that our middlewares are working correctly:
|
||||
|
||||
Test the Secure Headers middleware:
|
||||
|
||||
```bash
|
||||
curl -k -I -H "Host: whoami.docker.localhost" https://localhost/
|
||||
```
|
||||
|
||||
In the response headers, you should see security headers set by the middleware:
|
||||
|
||||
- `X-Frame-Options: DENY`
|
||||
- `X-Content-Type-Options: nosniff`
|
||||
- `X-XSS-Protection: 1; mode=block`
|
||||
- `Strict-Transport-Security` with the appropriate settings
|
||||
|
||||
Test the IP Allowlist middleware:
|
||||
|
||||
If your request comes from an IP that's in the allow list (e.g., 127.0.0.1), it should succeed:
|
||||
|
||||
```bash
|
||||
curl -k -I -H "Host: whoami.docker.localhost" https://localhost/
|
||||
```
|
||||
|
||||
If you try to access from an IP not in the allow list, the request will be rejected with a `403` Forbidden response. To simulate this in a local environment, you can modify the middleware configuration temporarily to exclude your IP address, then test again.
|
||||
|
||||
## Generate Certificates with Let's Encrypt
|
||||
|
||||
Let's Encrypt provides free, automated TLS certificates. Let's configure Traefik to automatically obtain and renew certificates for our services.
|
||||
|
||||
Instead of using self-signed certificates, update your existing `docker-compose.yml` file with the following changes:
|
||||
|
||||
Add the Let's Encrypt certificate resolver to the Traefik service command section:
|
||||
|
||||
```yaml
|
||||
command:
|
||||
- "--api.insecure=false"
|
||||
- "--api.dashboard=true"
|
||||
- "--providers.docker=true"
|
||||
- "--providers.docker.exposedbydefault=false"
|
||||
- "--providers.docker.network=proxy"
|
||||
- "--entryPoints.web.address=:80"
|
||||
- "--entryPoints.websecure.address=:443"
|
||||
- "--entryPoints.websecure.http.tls=true"
|
||||
- "--entryPoints.web.http.redirections.entryPoint.to=websecure"
|
||||
- "--entryPoints.web.http.redirections.entryPoint.scheme=https"
|
||||
# Let's Encrypt configuration
|
||||
- "--certificatesresolvers.le.acme.email=your-email@example.com" # replace with your actual email
|
||||
- "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
|
||||
- "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
|
||||
```
|
||||
|
||||
Add a volume for Let's Encrypt certificates:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
# ...Existing volumes...
|
||||
- "./letsencrypt:/letsencrypt"
|
||||
```
|
||||
|
||||
Update your service labels to use the certificate resolver:
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.http.routers.whoami.tls.certresolver=le"
|
||||
```
|
||||
|
||||
Do the same for any other services you want to secure:
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.http.routers.whoami-api.tls.certresolver=le"
|
||||
```
|
||||
|
||||
Create a directory for storing Let's Encrypt certificates:
|
||||
|
||||
```bash
|
||||
mkdir -p letsencrypt
|
||||
```
|
||||
|
||||
Apply the changes:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
!!! important "Public DNS Required"
|
||||
Let's Encrypt may require a publicly accessible domain to validate domain ownership. For testing with local domains like `whoami.docker.localhost`, the certificate will remain self-signed. In production, replace it with a real domain that has a publicly accessible DNS record pointing to your Traefik instance.
|
||||
|
||||
Once the certificate is issued, you can verify it:
|
||||
|
||||
```bash
|
||||
# Verify the certificate chain
|
||||
curl -v https://whoami.docker.localhost/ 2>&1 | grep -i "server certificate"
|
||||
```
|
||||
|
||||
You should see that your certificate is issued by Let's Encrypt.
|
||||
|
||||
## Configure Sticky Sessions
|
||||
|
||||
Sticky sessions ensure that a user's requests always go to the same backend server, which is essential for applications that maintain session state. Let's implement sticky sessions for our whoami service.
|
||||
|
||||
### First, Add Sticky Session Labels
|
||||
|
||||
Add the following labels to your whoami service in the `docker-compose.yml` file:
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
- "traefik.http.services.whoami.loadbalancer.sticky.cookie=true"
|
||||
- "traefik.http.services.whoami.loadbalancer.sticky.cookie.name=sticky_cookie"
|
||||
- "traefik.http.services.whoami.loadbalancer.sticky.cookie.secure=true"
|
||||
- "traefik.http.services.whoami.loadbalancer.sticky.cookie.httpOnly=true"
|
||||
```
|
||||
|
||||
Apply the changes:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Then, Scale Up the Service
|
||||
|
||||
To demonstrate sticky sessions with Docker, use Docker Compose's scale feature:
|
||||
|
||||
```bash
|
||||
docker compose up -d --scale whoami=3
|
||||
```
|
||||
|
||||
This creates multiple instances of the whoami service.
|
||||
|
||||
!!! important "Scaling After Configuration Changes"
|
||||
If you run `docker compose up -d` after scaling, it will reset the number of whoami instances back to 1. Always scale after applying configuration changes and starting the services.
|
||||
|
||||
### Test Sticky Sessions
|
||||
|
||||
You can test the sticky sessions by making multiple requests and observing that they all go to the same backend container:
|
||||
|
||||
```bash
|
||||
# First request - save cookies to a file
|
||||
curl -k -c cookies.txt -H "Host: whoami.docker.localhost" https://localhost/
|
||||
|
||||
# Subsequent requests - use the cookies
|
||||
curl -k -b cookies.txt -H "Host: whoami.docker.localhost" https://localhost/
|
||||
curl -k -b cookies.txt -H "Host: whoami.docker.localhost" https://localhost/
|
||||
```
|
||||
|
||||
Pay attention to the `Hostname` field in each response - it should remain the same across all requests when using the cookie file, confirming that sticky sessions are working.
|
||||
|
||||
For comparison, try making requests without the cookie:
|
||||
|
||||
```bash
|
||||
# Requests without cookies should be load-balanced across different containers
|
||||
curl -k -H "Host: whoami.docker.localhost" https://localhost/
|
||||
curl -k -H "Host: whoami.docker.localhost" https://localhost/
|
||||
```
|
||||
|
||||
You should see different `Hostname` values in these responses, as each request is load-balanced to a different container.
|
||||
|
||||
!!! important "Browser Testing"
|
||||
When testing in browsers, you need to use the same browser session to maintain the cookie. The cookie is set with `httpOnly` and `secure` flags for security, so it will only be sent over HTTPS connections and won't be accessible via JavaScript.
|
||||
|
||||
For more advanced configuration options, see the [reference documentation](../../reference/routing-configuration/http/load-balancing/service.md).
|
||||
|
||||
## Multi-Layer Routing
|
||||
|
||||
Multi-layer routing enables hierarchical relationships between routers, where parent routers can process requests through middleware before child routers make final routing decisions. This is particularly useful for authentication-based routing or staged middleware application.
|
||||
|
||||
!!! info "Provider Requirement"
|
||||
Multi-layer routing requires the File provider, as Docker labels do not support the `parentRefs` field. However, you can use **both Docker and File providers together** - Docker labels for service discovery and File configuration for multi-layer routing.
|
||||
|
||||
### Setup Multi-Layer Routing with Docker
|
||||
|
||||
To use multi-layer routing with Docker, you need to enable the File provider alongside the Docker provider.
|
||||
|
||||
Update your Traefik service in `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
traefik:
|
||||
image: "traefik:latest"
|
||||
container_name: "traefik"
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
networks:
|
||||
- proxy
|
||||
command:
|
||||
- "--providers.docker=true"
|
||||
- "--providers.docker.exposedbydefault=false"
|
||||
- "--providers.docker.network=proxy"
|
||||
- "--providers.file.directory=/etc/traefik/dynamic" # Enable File provider
|
||||
- "--entryPoints.web.address=:80"
|
||||
- "--entryPoints.websecure.address=:443"
|
||||
- "--entryPoints.websecure.http.tls=true"
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
- "./dynamic:/etc/traefik/dynamic:ro" # Mount directory for dynamic config
|
||||
```
|
||||
|
||||
### Authentication-Based Routing Example
|
||||
|
||||
Let's create a multi-layer routing setup where a parent router authenticates requests, and child routers direct traffic based on user roles.
|
||||
|
||||
First, keep your Docker services defined with labels as usual:
|
||||
|
||||
```yaml
|
||||
# In docker-compose.yml
|
||||
services:
|
||||
# ... traefik service from above ...
|
||||
|
||||
# Mock authentication service that adds X-User-Role header
|
||||
auth-service:
|
||||
image: "traefik/whoami"
|
||||
networks:
|
||||
- proxy
|
||||
environment:
|
||||
- WHOAMI_NAME=Auth Service
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.auth-service.loadbalancer.server.port=80"
|
||||
|
||||
# Admin backend service
|
||||
admin-backend:
|
||||
image: "traefik/whoami"
|
||||
networks:
|
||||
- proxy
|
||||
environment:
|
||||
- WHOAMI_NAME=Admin Backend
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.admin-backend.loadbalancer.server.port=80"
|
||||
|
||||
# User backend service
|
||||
user-backend:
|
||||
image: "traefik/whoami"
|
||||
networks:
|
||||
- proxy
|
||||
environment:
|
||||
- WHOAMI_NAME=User Backend
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.user-backend.loadbalancer.server.port=80"
|
||||
```
|
||||
|
||||
Now create the multi-layer routing configuration in a file. Create `dynamic/mlr.yml`:
|
||||
|
||||
```yaml
|
||||
http:
|
||||
routers:
|
||||
# Parent router with authentication middleware
|
||||
api-parent:
|
||||
rule: "Host(`api.docker.localhost`) && PathPrefix(`/api`)"
|
||||
middlewares:
|
||||
- auth-middleware
|
||||
entryPoints:
|
||||
- websecure
|
||||
# Note: No service and no TLS config - this is a parent router
|
||||
|
||||
# Child router for admin users
|
||||
api-admin:
|
||||
rule: "HeadersRegexp(`X-Auth-User`, `admin`)"
|
||||
service: admin-backend@docker # Reference Docker service
|
||||
parentRefs:
|
||||
- api-parent@file # Explicit reference to parent in file provider
|
||||
|
||||
# Child router for regular users
|
||||
api-user:
|
||||
rule: "HeadersRegexp(`X-Auth-User`, `user`)"
|
||||
service: user-backend@docker # Reference Docker service
|
||||
parentRefs:
|
||||
- api-parent@file # Explicit reference to parent in file provider
|
||||
|
||||
middlewares:
|
||||
auth-middleware:
|
||||
basicAuth:
|
||||
users:
|
||||
- "admin:$apr1$DmXR3Add$wfdbGw6RWIhFb0ffXMM4d0"
|
||||
- "user:$apr1$GJtcIY1o$mSLdsWYeXpPHVsxGDqadI."
|
||||
headerField: X-Auth-User
|
||||
```
|
||||
|
||||
!!! note "Generating Password Hashes"
|
||||
The password hashes above are generated using `htpasswd`. To create your own user credentials:
|
||||
|
||||
```bash
|
||||
# Using htpasswd (Apache utils)
|
||||
htpasswd -nb admin yourpassword
|
||||
```
|
||||
|
||||
!!! important "Cross-Provider References"
|
||||
Notice the `@docker` suffix on service names and the `@file` suffix in `parentRefs`. When using the File provider to orchestrate multi-layer routing with Docker services:
|
||||
|
||||
- Use `service-name@docker` to reference Docker services
|
||||
- Use `parent-name@file` in `parentRefs` to reference the parent router in the File provider
|
||||
|
||||
The `@provider` suffix tells Traefik which provider namespace to look in for the resource.
|
||||
|
||||
Apply the changes:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Test Multi-Layer Routing
|
||||
|
||||
Test the routing behavior:
|
||||
|
||||
```bash
|
||||
# Request goes through parent router → auth middleware → admin child router
|
||||
curl -k -u admin:test -H "Host: api.docker.localhost" https://localhost/api
|
||||
```
|
||||
|
||||
You should see the response from the admin-backend service when authenticating as `admin`. Try with `user:test` credentials to reach the user-backend service instead.
|
||||
|
||||
### How It Works
|
||||
|
||||
1. **Request arrives** at `api.docker.localhost/api`
|
||||
2. **Parent router** (`api-parent`) matches based on host and path
|
||||
3. **BasicAuth middleware** authenticates the user and sets the `X-Auth-User` header with the username
|
||||
4. **Child router** (`api-admin` or `api-user`) matches based on the header value
|
||||
5. **Request forwarded** to the appropriate Docker service
|
||||
|
||||
For more details about multi-layer routing, see the [Multi-Layer Routing documentation](../../reference/routing-configuration/http/routing/multi-layer-routing.md).
|
||||
|
||||
## Service Middlewares
|
||||
|
||||
Service middlewares allow you to apply middleware to a service rather than to individual routers. This means the middleware takes effect for all requests handled by the service, regardless of which router forwards the request.
|
||||
|
||||
This is useful when you want to apply the same middleware (like headers, rate limiting, or authentication) to all traffic reaching a service without having to configure it on each router.
|
||||
|
||||
### When to Use Service Middlewares
|
||||
|
||||
Use service middlewares when:
|
||||
|
||||
- Multiple routers forward traffic to the same service, and all should have the same middleware applied
|
||||
- You want to ensure a middleware is always applied to a service regardless of how traffic reaches it
|
||||
- You're centralizing middleware configuration at the service level for easier management
|
||||
|
||||
### Add Service Middleware Labels
|
||||
|
||||
Add the following labels to your whoami service in `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
whoami:
|
||||
image: traefik/whoami
|
||||
networks:
|
||||
- proxy
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.whoami.rule=Host(`whoami.docker.localhost`)"
|
||||
- "traefik.http.routers.whoami.entrypoints=websecure"
|
||||
- "traefik.http.routers.whoami.tls=true"
|
||||
# Define the middleware
|
||||
- "traefik.http.middlewares.service-headers.headers.customRequestHeaders.X-Service-Middleware=applied"
|
||||
# Attach middleware at the SERVICE level (not the router level)
|
||||
- "traefik.http.services.whoami.middlewares=service-headers"
|
||||
- "traefik.http.services.whoami.loadbalancer.server.port=80"
|
||||
```
|
||||
|
||||
!!! info "Service-Level vs Router-Level Middlewares"
|
||||
|
||||
- **Router-level middleware** (`traefik.http.routers.<name>.middlewares`): Applied only when traffic matches that specific router's rule
|
||||
- **Service-level middleware** (`traefik.http.services.<name>.middlewares`): Applied to all traffic reaching the service, regardless of which router forwarded it
|
||||
|
||||
When both are configured, router middlewares execute first, followed by service middlewares.
|
||||
|
||||
Apply the changes:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Test Service Middleware
|
||||
|
||||
Verify the service middleware is working:
|
||||
|
||||
```bash
|
||||
curl -k -H "Host: whoami.docker.localhost" https://localhost/
|
||||
```
|
||||
|
||||
In the response from whoami, you should see the custom header that was added by the service middleware:
|
||||
|
||||
```text
|
||||
X-Service-Middleware: applied
|
||||
```
|
||||
|
||||
For more details on service middlewares, see the [reference documentation](../../reference/routing-configuration/http/load-balancing/service.md#middlewares).
|
||||
|
||||
## Conclusion
|
||||
|
||||
In this advanced guide, you've learned how to:
|
||||
|
||||
- Add security with middlewares like secure headers and IP allow listing
|
||||
- Automate certificate management with Let's Encrypt
|
||||
- Implement sticky sessions for stateful applications
|
||||
- Setup multi-layer routing for authentication-based routing
|
||||
- Apply middlewares at the service level for centralized middleware management
|
||||
|
||||
These advanced capabilities allow you to build production-ready Traefik deployments with Docker. Each of these can be further customized to meet your specific requirements.
|
||||
|
||||
### Next Steps
|
||||
|
||||
Now that you've mastered both basic and advanced Traefik features with Docker, you might want to explore:
|
||||
|
||||
- [Advanced routing options](../../reference/routing-configuration/http/routing/rules-and-priority.md) like query parameter matching, header-based routing, and more
|
||||
- [Additional middlewares](../../reference/routing-configuration/http/middlewares/overview.md) for authentication, rate limiting, and request modifications
|
||||
- [Observability features](../../reference/install-configuration/observability/metrics.md) for monitoring and debugging your Traefik deployment
|
||||
- [TCP services](../../reference/routing-configuration/tcp/service.md) for exposing TCP services
|
||||
- [UDP services](../../reference/routing-configuration/udp/service.md) for exposing UDP services
|
||||
- [Docker provider documentation](../../reference/install-configuration/providers/docker.md) for more details about the Docker integration
|
||||
@@ -0,0 +1,250 @@
|
||||
# Exposing Services with Traefik on Docker - Basic
|
||||
|
||||
This guide will help you get started with exposing your services through Traefik Proxy using Docker. You'll learn the fundamentals of routing HTTP traffic, setting up path-based routing, and securing your services with TLS.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Docker and Docker Compose installed
|
||||
- Basic understanding of Docker concepts
|
||||
- Traefik deployed using the [Traefik Docker Setup guide](../../setup/docker.md)
|
||||
|
||||
## Expose Your First HTTP Service
|
||||
|
||||
Let's expose a simple HTTP service using the [whoami](https://hub.docker.com/r/traefik/whoami) application. This will demonstrate basic routing to a backend service.
|
||||
|
||||
First, create a `docker-compose.yml` file:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
traefik:
|
||||
image: "traefik:v3.4"
|
||||
container_name: "traefik"
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
networks:
|
||||
- proxy
|
||||
command:
|
||||
- "--providers.docker=true"
|
||||
- "--providers.docker.exposedbydefault=false"
|
||||
- "--providers.docker.network=proxy"
|
||||
- "--entryPoints.web.address=:80"
|
||||
ports:
|
||||
- "80:80"
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
|
||||
whoami:
|
||||
image: "traefik/whoami"
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- proxy
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.whoami.rule=Host(`whoami.docker.localhost`)"
|
||||
- "traefik.http.routers.whoami.entrypoints=web"
|
||||
|
||||
networks:
|
||||
proxy:
|
||||
name: proxy
|
||||
```
|
||||
|
||||
Save this as `docker-compose.yml` and start the services:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Verify Your Service
|
||||
|
||||
Your service is now available at http://whoami.docker.localhost/. Test that it works:
|
||||
|
||||
```bash
|
||||
curl -H "Host: whoami.docker.localhost" http://localhost/
|
||||
```
|
||||
|
||||
You should see output similar to:
|
||||
|
||||
```bash
|
||||
Hostname: whoami
|
||||
IP: 127.0.0.1
|
||||
IP: ::1
|
||||
IP: 172.18.0.3
|
||||
IP: fe80::215:5dff:fe00:c9e
|
||||
RemoteAddr: 172.18.0.2:55108
|
||||
GET / HTTP/1.1
|
||||
Host: whoami.docker.localhost
|
||||
User-Agent: curl/7.68.0
|
||||
Accept: */*
|
||||
Accept-Encoding: gzip
|
||||
X-Forwarded-For: 172.18.0.1
|
||||
X-Forwarded-Host: whoami.docker.localhost
|
||||
X-Forwarded-Port: 80
|
||||
X-Forwarded-Proto: http
|
||||
X-Forwarded-Server: 5789f594e7d5
|
||||
X-Real-Ip: 172.18.0.1
|
||||
```
|
||||
|
||||
This confirms that Traefik is successfully routing requests to your whoami application.
|
||||
|
||||
## Add Routing Rules
|
||||
|
||||
Now we'll enhance our routing by directing traffic to different services based on [URL paths](../../reference/routing-configuration/http/routing/rules-and-priority.md#path-pathprefix-and-pathregexp). This is useful for API versioning, frontend/backend separation, or organizing microservices.
|
||||
|
||||
Update your `docker-compose.yml` to add another service:
|
||||
|
||||
```yaml
|
||||
# ...
|
||||
|
||||
# New service
|
||||
whoami-api:
|
||||
image: "traefik/whoami"
|
||||
networks:
|
||||
- proxy
|
||||
container_name: "whoami-api"
|
||||
environment:
|
||||
- WHOAMI_NAME=API Service
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
# Path-based routing
|
||||
- "traefik.http.routers.whoami-api.rule=Host(`whoami.docker.localhost`) && PathPrefix(`/api`)"
|
||||
- "traefik.http.routers.whoami-api.entrypoints=web"
|
||||
```
|
||||
|
||||
Apply the changes:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Test the Path-Based Routing
|
||||
|
||||
Verify that different paths route to different services:
|
||||
|
||||
```bash
|
||||
# Root path should go to the main whoami service
|
||||
curl -H "Host: whoami.docker.localhost" http://localhost/
|
||||
|
||||
# /api path should go to the whoami-api service
|
||||
curl -H "Host: whoami.docker.localhost" http://localhost/api
|
||||
```
|
||||
|
||||
For the `/api` requests, you should see the response showing "API Service" in the environment variables section, confirming that your path-based routing is working correctly.
|
||||
|
||||
## Enable TLS
|
||||
|
||||
Let's secure our service with HTTPS by adding TLS. We'll start with a self-signed certificate for local development.
|
||||
|
||||
### Create a Self-Signed Certificate
|
||||
|
||||
Generate a self-signed certificate:
|
||||
|
||||
```bash
|
||||
mkdir -p certs
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
-keyout certs/local.key -out certs/local.crt \
|
||||
-subj "/CN=*.docker.localhost"
|
||||
```
|
||||
|
||||
Create a directory for dynamic configuration and add a TLS configuration file:
|
||||
|
||||
```bash
|
||||
mkdir -p dynamic
|
||||
cat > dynamic/tls.yml << EOF
|
||||
tls:
|
||||
certificates:
|
||||
- certFile: /certs/local.crt
|
||||
keyFile: /certs/local.key
|
||||
EOF
|
||||
```
|
||||
|
||||
Update your `docker-compose.yml` file with the following changes:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
traefik:
|
||||
image: "traefik:v3.4"
|
||||
container_name: "traefik"
|
||||
restart: unless-stopped
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
networks:
|
||||
- proxy
|
||||
command:
|
||||
- "--api.insecure=false"
|
||||
- "--api.dashboard=true"
|
||||
- "--providers.docker=true"
|
||||
- "--providers.docker.exposedbydefault=false"
|
||||
- "--providers.docker.network=proxy"
|
||||
- "--providers.file.directory=/etc/traefik/dynamic"
|
||||
- "--entryPoints.web.address=:80"
|
||||
- "--entryPoints.websecure.address=:443"
|
||||
- "--entryPoints.websecure.http.tls=true"
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
# Add the following volumes
|
||||
- "./certs:/certs:ro"
|
||||
- "./dynamic:/etc/traefik/dynamic:ro"
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.dashboard.rule=Host(`dashboard.docker.localhost`)"
|
||||
- "traefik.http.routers.dashboard.entrypoints=websecure"
|
||||
- "traefik.http.routers.dashboard.service=api@internal"
|
||||
# Add the following label
|
||||
- "traefik.http.routers.dashboard.tls=true"
|
||||
|
||||
whoami:
|
||||
image: "traefik/whoami"
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- proxy
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.whoami.rule=Host(`whoami.docker.localhost`)"
|
||||
- "traefik.http.routers.whoami.entrypoints=websecure"
|
||||
# Add the following label
|
||||
- "traefik.http.routers.whoami.tls=true"
|
||||
|
||||
whoami-api:
|
||||
image: "traefik/whoami"
|
||||
container_name: "whoami-api"
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- proxy
|
||||
environment:
|
||||
- WHOAMI_NAME=API Service
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.whoami-api.rule=Host(`whoami.docker.localhost`) && PathPrefix(`/api`)"
|
||||
- "traefik.http.routers.whoami-api.entrypoints=websecure"
|
||||
# Add the following label
|
||||
- "traefik.http.routers.whoami-api.tls=true"
|
||||
|
||||
networks:
|
||||
proxy:
|
||||
name: proxy
|
||||
```
|
||||
|
||||
Apply the changes:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Your browser can access https://whoami.docker.localhost/ for the service. You'll need to accept the security warning for the self-signed certificate.
|
||||
|
||||
## Next Steps
|
||||
|
||||
Now that you've mastered the basics of exposing services with Traefik on Docker, you're ready to explore more advanced features like middlewares, Let's Encrypt certificates, sticky sessions, and multi-layer routing.
|
||||
|
||||
Continue to the [Advanced Guide](advanced.md) to learn about:
|
||||
|
||||
- Adding middlewares for security and access control
|
||||
- Generating certificates with Let's Encrypt
|
||||
- Configuring sticky sessions for stateful applications
|
||||
- Setting up multi-layer routing for authentication-based routing
|
||||
@@ -1,430 +1,26 @@
|
||||
# Exposing Services with Traefik on Kubernetes
|
||||
# Exposing Services with Traefik on Kubernetes - Advanced
|
||||
|
||||
This guide will help you expose your services securely through Traefik Proxy on Kubernetes. We'll cover routing HTTP and HTTPS traffic, implementing TLS, adding security middleware, and configuring sticky sessions. For routing, this guide gives you two options:
|
||||
This guide builds on the concepts and setup from the [Basic Guide](basic.md). Make sure you've completed the basic guide and have a working Traefik setup with Kubernetes before proceeding.
|
||||
|
||||
- [Gateway API](../reference/routing-configuration/kubernetes/gateway-api.md)
|
||||
- [IngressRoute](../reference/routing-configuration/kubernetes/crd/http/ingressroute.md)
|
||||
In this advanced guide, you'll learn how to enhance your Traefik deployment with:
|
||||
|
||||
Feel free to choose the one that fits your needs best.
|
||||
- **Middlewares** for security headers and access control
|
||||
- **Let's Encrypt** for automated certificate management (IngressRoute)
|
||||
- **cert-manager** for automated certificate management (Gateway API)
|
||||
- **Sticky sessions** for stateful applications
|
||||
- **Multi-layer routing** for hierarchical routing with complex authentication scenarios (IngressRoute only)
|
||||
- **Service middlewares** for applying middleware at the service level
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Completed the [Basic Guide](basic.md)
|
||||
- A Kubernetes cluster with Traefik Proxy installed
|
||||
- `kubectl` configured to interact with your cluster
|
||||
- Traefik deployed using the Traefik Kubernetes Setup guide
|
||||
|
||||
## Expose Your First HTTP Service
|
||||
|
||||
Let's expose a simple HTTP service using the [whoami](https://github.com/traefik/whoami) application. This will demonstrate basic routing to a backend service.
|
||||
|
||||
First, create the deployment and service:
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: whoami
|
||||
namespace: default
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: whoami
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: whoami
|
||||
spec:
|
||||
containers:
|
||||
- name: whoami
|
||||
image: traefik/whoami
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: whoami
|
||||
namespace: default
|
||||
spec:
|
||||
selector:
|
||||
app: whoami
|
||||
ports:
|
||||
- port: 80
|
||||
```
|
||||
|
||||
Save this as `whoami.yaml` and apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f whoami.yaml
|
||||
```
|
||||
|
||||
Now, let's create routes using either Gateway API or IngressRoute.
|
||||
|
||||
### Using Gateway API
|
||||
|
||||
```yaml
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: whoami
|
||||
namespace: default
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: traefik-gateway # This Gateway is automatically created by Traefik
|
||||
hostnames:
|
||||
- "whoami.docker.localhost"
|
||||
rules:
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /
|
||||
backendRefs:
|
||||
- name: whoami
|
||||
port: 80
|
||||
```
|
||||
|
||||
Save this as `whoami-route.yaml` and apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f whoami-route.yaml
|
||||
```
|
||||
|
||||
### Using IngressRoute
|
||||
|
||||
```yaml
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: whoami
|
||||
namespace: default
|
||||
spec:
|
||||
entryPoints:
|
||||
- web
|
||||
routes:
|
||||
- match: Host(`whoami.docker.localhost`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: whoami
|
||||
port: 80
|
||||
```
|
||||
|
||||
Save this as `whoami-ingressroute.yaml` and apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f whoami-ingressroute.yaml
|
||||
```
|
||||
|
||||
### Verify Your Service
|
||||
|
||||
Your service is now available at http://whoami.docker.localhost/. Test that it works:
|
||||
|
||||
```bash
|
||||
curl -H "Host: whoami.docker.localhost" http://localhost/
|
||||
```
|
||||
|
||||
!!! info
|
||||
Make sure to remove the `ports.web.redirections` block from the `values.yaml` file if you followed the Kubernetes Setup Guide to install Traefik otherwise you will be redirected to the HTTPS entrypoint:
|
||||
|
||||
```yaml
|
||||
redirections:
|
||||
entryPoint:
|
||||
to: websecure
|
||||
```
|
||||
|
||||
You should see output similar to:
|
||||
|
||||
```bash
|
||||
Hostname: whoami-6d5d964cb-8pv4k
|
||||
IP: 127.0.0.1
|
||||
IP: ::1
|
||||
IP: 10.42.0.18
|
||||
IP: fe80::d4c0:3bff:fe20:b0a3
|
||||
RemoteAddr: 10.42.0.17:39872
|
||||
GET / HTTP/1.1
|
||||
Host: whoami.docker.localhost
|
||||
User-Agent: curl/7.68.0
|
||||
Accept: */*
|
||||
Accept-Encoding: gzip
|
||||
X-Forwarded-For: 10.42.0.1
|
||||
X-Forwarded-Host: whoami.docker.localhost
|
||||
X-Forwarded-Port: 80
|
||||
X-Forwarded-Proto: http
|
||||
X-Forwarded-Server: traefik-76cbd5b89c-rx5xn
|
||||
X-Real-Ip: 10.42.0.1
|
||||
```
|
||||
|
||||
This confirms that Traefik is successfully routing requests to your whoami application.
|
||||
|
||||
## Add Routing Rules
|
||||
|
||||
Now we'll enhance our routing by directing traffic to different services based on URL paths. This is useful for API versioning, frontend/backend separation, or organizing microservices.
|
||||
|
||||
First, deploy a second service to represent an API:
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: whoami-api
|
||||
namespace: default
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: whoami-api
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: whoami-api
|
||||
spec:
|
||||
containers:
|
||||
- name: whoami
|
||||
image: traefik/whoami
|
||||
env:
|
||||
- name: WHOAMI_NAME
|
||||
value: "API Service"
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: whoami-api
|
||||
namespace: default
|
||||
spec:
|
||||
selector:
|
||||
app: whoami-api
|
||||
ports:
|
||||
- port: 80
|
||||
```
|
||||
|
||||
Save this as `whoami-api.yaml` and apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f whoami-api.yaml
|
||||
```
|
||||
|
||||
Now set up path-based routing:
|
||||
|
||||
### Gateway API with Path Rules
|
||||
|
||||
Update your existing `HTTPRoute` to include path-based routing:
|
||||
|
||||
```yaml
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: whoami
|
||||
namespace: default
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: traefik-gateway
|
||||
hostnames:
|
||||
- "whoami.docker.localhost"
|
||||
rules:
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /api
|
||||
backendRefs:
|
||||
- name: whoami-api
|
||||
port: 80
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /
|
||||
backendRefs:
|
||||
- name: whoami
|
||||
port: 80
|
||||
```
|
||||
|
||||
Update the file `whoami-route.yaml` and apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f whoami-route.yaml
|
||||
```
|
||||
|
||||
### IngressRoute with Path Rules
|
||||
|
||||
Update your existing IngressRoute to include path-based routing:
|
||||
|
||||
```yaml
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: whoami
|
||||
namespace: default
|
||||
spec:
|
||||
entryPoints:
|
||||
- web
|
||||
routes:
|
||||
- match: Host(`whoami.docker.localhost`) && Path(`/api`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: whoami-api
|
||||
port: 80
|
||||
- match: Host(`whoami.docker.localhost`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: whoami
|
||||
port: 80
|
||||
```
|
||||
|
||||
Save this as `whoami-ingressroute.yaml` and apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f whoami-ingressroute.yaml
|
||||
```
|
||||
|
||||
### Test the Path-Based Routing
|
||||
|
||||
Verify that different paths route to different services:
|
||||
|
||||
```bash
|
||||
# Root path should go to the main whoami service
|
||||
curl -H "Host: whoami.docker.localhost" http://localhost/
|
||||
|
||||
# /api path should go to the whoami-api service
|
||||
curl -H "Host: whoami.docker.localhost" http://localhost/api
|
||||
```
|
||||
|
||||
For the `/api` requests, you should see the response showing "API Service" in the environment variables section, confirming that your path-based routing is working correctly:
|
||||
|
||||
```bash
|
||||
{"hostname":"whoami-api-67d97b4868-dvvll","ip":["127.0.0.1","::1","10.42.0.9","fe80::10aa:37ff:fe74:31f2"],"headers":{"Accept":["*/*"],"Accept-Encoding":["gzip"],"User-Agent":["curl/8.7.1"],"X-Forwarded-For":["10.42.0.1"],"X-Forwarded-Host":["whoami.docker.localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["traefik-669c479df8-vkj22"],"X-Real-Ip":["10.42.0.1"]},"url":"/api","host":"whoami.docker.localhost","method":"GET","name":"API Service","remoteAddr":"10.42.0.13:36592"}
|
||||
```
|
||||
|
||||
## Enable TLS
|
||||
|
||||
Let's secure our service with HTTPS by adding TLS. We'll start with a self-signed certificate for local development.
|
||||
|
||||
### Create a Self-Signed Certificate
|
||||
|
||||
Generate a self-signed certificate:
|
||||
|
||||
```bash
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
-keyout tls.key -out tls.crt \
|
||||
-subj "/CN=whoami.docker.localhost"
|
||||
```
|
||||
|
||||
Create a TLS secret in Kubernetes:
|
||||
|
||||
```bash
|
||||
kubectl create secret tls whoami-tls --cert=tls.crt --key=tls.key
|
||||
```
|
||||
|
||||
!!! important "Prerequisite for Gateway API with TLS"
|
||||
Before using the Gateway API with TLS, you must define the `websecure` listener in your Traefik installation. This is typically done in your Helm values.
|
||||
|
||||
Example configuration in `values.yaml`:
|
||||
```yaml
|
||||
gateway:
|
||||
listeners:
|
||||
web:
|
||||
port: 80
|
||||
protocol: HTTP
|
||||
namespacePolicy: All
|
||||
websecure:
|
||||
port: 443
|
||||
protocol: HTTPS
|
||||
namespacePolicy: All
|
||||
mode: Terminate
|
||||
certificateRefs:
|
||||
- kind: Secret
|
||||
name: local-selfsigned-tls
|
||||
group: ""
|
||||
```
|
||||
|
||||
See the Traefik Kubernetes Setup Guide for complete installation details.
|
||||
|
||||
### Gateway API with TLS
|
||||
|
||||
Update your existing `HTTPRoute` to use the secured gateway listener:
|
||||
|
||||
```yaml
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: whoami
|
||||
namespace: default
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: traefik-gateway
|
||||
sectionName: websecure # The HTTPS listener
|
||||
hostnames:
|
||||
- "whoami.docker.localhost"
|
||||
rules:
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /api
|
||||
backendRefs:
|
||||
- name: whoami-api
|
||||
port: 80
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /
|
||||
backendRefs:
|
||||
- name: whoami
|
||||
port: 80
|
||||
```
|
||||
|
||||
Update the file `whoami-route.yaml` and apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f whoami-route.yaml
|
||||
```
|
||||
|
||||
### IngressRoute with TLS
|
||||
|
||||
Update your existing IngressRoute to use TLS:
|
||||
|
||||
```yaml
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: whoami
|
||||
namespace: default
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure # Changed from 'web' to 'websecure'
|
||||
routes:
|
||||
- match: Host(`whoami.docker.localhost`) && Path(`/api`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: whoami-api
|
||||
port: 80
|
||||
- match: Host(`whoami.docker.localhost`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: whoami
|
||||
port: 80
|
||||
tls:
|
||||
secretName: whoami-tls # Added TLS configuration
|
||||
```
|
||||
|
||||
Update the file `whoami-ingressroute.yaml` and apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f whoami-ingressroute.yaml
|
||||
```
|
||||
|
||||
### Verify HTTPS Access
|
||||
|
||||
Now you can access your service securely. Since we're using a self-signed certificate, you'll need to skip certificate verification:
|
||||
|
||||
```bash
|
||||
curl -k -H "Host: whoami.docker.localhost" https://localhost/
|
||||
```
|
||||
|
||||
Your browser can also access https://whoami.docker.localhost/ (you'll need to accept the security warning for the self-signed certificate).
|
||||
- Working Traefik setup from the basic guide
|
||||
|
||||
## Add Middlewares
|
||||
|
||||
Middlewares allow you to modify requests or responses as they pass through Traefik. Let's add two useful middlewares: [Headers](../reference/routing-configuration/http/middlewares/headers.md) for security and [IP allowlisting](../reference/routing-configuration/http/middlewares/ipallowlist.md) for access control.
|
||||
Middlewares allow you to modify requests or responses as they pass through Traefik. Let's add two useful middlewares: [Headers](../../reference/routing-configuration/http/middlewares/headers.md) for security and [IP allowlisting](../../reference/routing-configuration/http/middlewares/ipallowlist.md) for access control.
|
||||
|
||||
### Create Middlewares
|
||||
|
||||
@@ -467,37 +63,6 @@ kubectl apply -f middlewares.yaml
|
||||
|
||||
In Gateway API, you can apply middlewares using the `ExtensionRef` filter type. This is the preferred and standard way to use Traefik middlewares with Gateway API, as it integrates directly with the HTTPRoute specification.
|
||||
|
||||
First, make sure you have the same middlewares defined:
|
||||
|
||||
```yaml
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: secure-headers
|
||||
namespace: default
|
||||
spec:
|
||||
headers:
|
||||
frameDeny: true
|
||||
sslRedirect: true
|
||||
browserXssFilter: true
|
||||
contentTypeNosniff: true
|
||||
stsIncludeSubdomains: true
|
||||
stsPreload: true
|
||||
stsSeconds: 31536000
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: ip-allowlist
|
||||
namespace: default
|
||||
spec:
|
||||
ipAllowList:
|
||||
sourceRange:
|
||||
- 127.0.0.1/32
|
||||
- 10.0.0.0/8 # Typical cluster network range
|
||||
- 192.168.0.0/16 # Common local network range
|
||||
```
|
||||
|
||||
Now, update your `HTTPRoute` to reference these middlewares using the `ExtensionRef` filter:
|
||||
|
||||
```yaml
|
||||
@@ -523,7 +88,7 @@ spec:
|
||||
group: traefik.io
|
||||
kind: Middleware
|
||||
name: secure-headers
|
||||
- type: ExtensionRef
|
||||
- type: ExtensionRef
|
||||
extensionRef: # IP AllowList Middleware Definition
|
||||
group: traefik.io
|
||||
kind: Middleware
|
||||
@@ -541,7 +106,7 @@ spec:
|
||||
group: traefik.io
|
||||
kind: Middleware
|
||||
name: secure-headers
|
||||
- type: ExtensionRef
|
||||
- type: ExtensionRef
|
||||
extensionRef: # IP AllowList Middleware Definition
|
||||
group: traefik.io
|
||||
kind: Middleware
|
||||
@@ -852,7 +417,7 @@ spec:
|
||||
group: traefik.io
|
||||
kind: Middleware
|
||||
name: secure-headers
|
||||
- type: ExtensionRef
|
||||
- type: ExtensionRef
|
||||
extensionRef: # IP AllowList Middleware Definition
|
||||
group: traefik.io
|
||||
kind: Middleware
|
||||
@@ -874,7 +439,7 @@ spec:
|
||||
group: traefik.io
|
||||
kind: Middleware
|
||||
name: secure-headers
|
||||
- type: ExtensionRef
|
||||
- type: ExtensionRef
|
||||
extensionRef: # IP AllowList Middleware Definition
|
||||
group: traefik.io
|
||||
kind: Middleware
|
||||
@@ -984,29 +549,460 @@ You should see different `Hostname` values in these responses, as each request i
|
||||
!!! important "Browser Testing"
|
||||
When testing in browsers, you need to use the same browser session to maintain the cookie. The cookie is set with `httpOnly` and `secure` flags for security, so it will only be sent over HTTPS connections and won't be accessible via JavaScript.
|
||||
|
||||
For more advanced configuration options, see the [reference documentation](../reference/routing-configuration/http/load-balancing/service.md).
|
||||
For more advanced configuration options, see the [reference documentation](../../reference/routing-configuration/http/load-balancing/service.md).
|
||||
|
||||
## Setup Multi-Layer Routing
|
||||
|
||||
Multi-layer routing enables hierarchical relationships between routers, where parent routers can process requests through middleware before child routers make final routing decisions. This is particularly useful for authentication-based routing or staged middleware application.
|
||||
|
||||
!!! info "IngressRoute Support"
|
||||
Multi-layer routing is **natively supported** by Kubernetes IngressRoute (CRD) using the `spec.parentRefs` field. This feature is not available when using standard Kubernetes Ingress or Gateway API resources.
|
||||
|
||||
### Authentication-Based Routing Example
|
||||
|
||||
Let's create a multi-layer routing setup where a parent IngressRoute authenticates requests, and child IngressRoutes direct traffic based on user roles.
|
||||
|
||||
!!! important "Parent Router Requirements"
|
||||
Parent routers in multi-layer routing must not have a service defined. The child routers will handle the service selection based on their matching rules. Make sure all child IngressRoutes reference the parent correctly using `parentRefs`.
|
||||
|
||||
First, deploy your backend services:
|
||||
|
||||
```yaml
|
||||
# whoami-backends.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: admin-backend
|
||||
namespace: default
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: admin-backend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: admin-backend
|
||||
spec:
|
||||
containers:
|
||||
- name: whoami
|
||||
image: traefik/whoami
|
||||
env:
|
||||
- name: WHOAMI_NAME
|
||||
value: "Admin Backend"
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: admin-backend
|
||||
namespace: default
|
||||
spec:
|
||||
selector:
|
||||
app: admin-backend
|
||||
ports:
|
||||
- port: 80
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: user-backend
|
||||
namespace: default
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: user-backend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: user-backend
|
||||
spec:
|
||||
containers:
|
||||
- name: whoami
|
||||
image: traefik/whoami
|
||||
env:
|
||||
- name: WHOAMI_NAME
|
||||
value: "User Backend"
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: user-backend
|
||||
namespace: default
|
||||
spec:
|
||||
selector:
|
||||
app: user-backend
|
||||
ports:
|
||||
- port: 80
|
||||
```
|
||||
|
||||
Apply the backend services:
|
||||
|
||||
```bash
|
||||
kubectl apply -f whoami-backends.yaml
|
||||
```
|
||||
|
||||
Now create the middleware and IngressRoutes for multi-layer routing:
|
||||
|
||||
```yaml
|
||||
# mlr-ingressroute.yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: auth-secret
|
||||
namespace: default
|
||||
type: Opaque
|
||||
stringData:
|
||||
users: |
|
||||
admin:$apr1$DmXR3Add$wfdbGw6RWIhFb0ffXMM4d0
|
||||
user:$apr1$GJtcIY1o$mSLdsWYeXpPHVsxGDqadI.
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: auth-middleware
|
||||
namespace: default
|
||||
spec:
|
||||
basicAuth:
|
||||
secret: auth-secret
|
||||
headerField: X-Auth-User
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: api-parent
|
||||
namespace: default
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`api.docker.localhost`) && PathPrefix(`/api`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: auth-middleware
|
||||
# Note: No services and no TLS config - this is a parent IngressRoute
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: api-admin
|
||||
namespace: default
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: api-parent
|
||||
namespace: default # Optional, defaults to same namespace
|
||||
routes:
|
||||
- match: HeadersRegexp(`X-Auth-User`, `admin`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: admin-backend
|
||||
port: 80
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: api-user
|
||||
namespace: default
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: api-parent
|
||||
namespace: default # Optional, defaults to same namespace
|
||||
routes:
|
||||
- match: HeadersRegexp(`X-Auth-User`, `user`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: user-backend
|
||||
port: 80
|
||||
```
|
||||
|
||||
!!! note "Generating Password Hashes"
|
||||
The password hashes above are generated using `htpasswd`. To create your own user credentials:
|
||||
|
||||
```bash
|
||||
# Using htpasswd (Apache utils)
|
||||
htpasswd -nb admin yourpassword
|
||||
```
|
||||
|
||||
Apply the multi-layer routing configuration:
|
||||
|
||||
```bash
|
||||
kubectl apply -f mlr-ingressroute.yaml
|
||||
```
|
||||
|
||||
### Test Multi-Layer Routing
|
||||
|
||||
Test the routing behavior:
|
||||
|
||||
```bash
|
||||
# Request goes through parent router → auth middleware → admin child router
|
||||
curl -k -u admin:test -H "Host: api.docker.localhost" https://localhost/api
|
||||
```
|
||||
|
||||
You should see the response from the admin-backend service when authenticating as `admin`. Try with `user:test` credentials to reach the user-backend service instead.
|
||||
|
||||
### How It Works
|
||||
|
||||
1. **Request arrives** at `api.docker.localhost/api`
|
||||
2. **Parent IngressRoute** (`api-parent`) matches based on host and path
|
||||
3. **BasicAuth middleware** authenticates the user and sets the `X-Auth-User` header with the username
|
||||
4. **Child IngressRoute** (`api-admin` or `api-user`) matches based on the header value
|
||||
5. **Request forwarded** to the appropriate Kubernetes service
|
||||
|
||||
### Cross-Namespace Parent References
|
||||
|
||||
You can reference parent IngressRoutes in different namespaces by specifying the `namespace` field:
|
||||
|
||||
```yaml
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: api-child
|
||||
namespace: app-namespace
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: api-parent
|
||||
namespace: shared-namespace # Parent in different namespace
|
||||
routes:
|
||||
- match: Path(`/child`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: child-service
|
||||
port: 80
|
||||
```
|
||||
|
||||
!!! important "Cross-Namespace Requirement"
|
||||
To use cross-namespace parent references, you must enable the `allowCrossNamespace` option in your Traefik Helm values:
|
||||
|
||||
```yaml
|
||||
providers:
|
||||
kubernetesCRD:
|
||||
allowCrossNamespace: true
|
||||
```
|
||||
|
||||
### Multiple Parent References
|
||||
|
||||
Child IngressRoutes can reference multiple parent IngressRoutes:
|
||||
|
||||
```yaml
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: api-child
|
||||
namespace: default
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: parent-one
|
||||
- name: parent-two
|
||||
routes:
|
||||
- match: Path(`/api`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: child-service
|
||||
port: 80
|
||||
```
|
||||
|
||||
For more details about multi-layer routing, see the [Multi-Layer Routing documentation](../../reference/routing-configuration/http/routing/multi-layer-routing.md).
|
||||
|
||||
## Service Middlewares
|
||||
|
||||
Service middlewares allow you to apply middleware to a service rather than to individual routers. This means the middleware takes effect for all requests handled by the service, regardless of which router forwards the request.
|
||||
|
||||
This is useful when you want to apply the same middleware (like headers, rate limiting, or authentication) to all traffic reaching a service without having to configure it on each router.
|
||||
|
||||
### When to Use Service Middlewares
|
||||
|
||||
Use service middlewares when:
|
||||
|
||||
- Multiple routers forward traffic to the same service, and all should have the same middleware applied
|
||||
- You want to ensure a middleware is always applied to a service regardless of how traffic reaches it
|
||||
- You're centralizing middleware configuration at the service level for easier management
|
||||
|
||||
!!! info "Service-Level vs Router-Level Middlewares"
|
||||
|
||||
- **Router-level middleware**: Applied only when traffic matches that specific router's rule
|
||||
- **Service-level middleware**: Applied to all traffic reaching the service, regardless of which router forwarded it
|
||||
|
||||
When both are configured, router middlewares execute first, followed by service middlewares.
|
||||
|
||||
### Using IngressRoute with Service Middlewares
|
||||
|
||||
With IngressRoute, you can attach middlewares directly to a service reference within a route:
|
||||
|
||||
```yaml
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: service-headers
|
||||
namespace: default
|
||||
spec:
|
||||
headers:
|
||||
customRequestHeaders:
|
||||
X-Service-Middleware: "applied"
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: whoami
|
||||
namespace: default
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`whoami.docker.localhost`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: whoami
|
||||
port: 80
|
||||
middlewares:
|
||||
- name: service-headers
|
||||
tls: {}
|
||||
```
|
||||
|
||||
Save this as `service-middleware-ingressroute.yaml` and apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f service-middleware-ingressroute.yaml
|
||||
```
|
||||
|
||||
### Using Gateway API with Backend Filters
|
||||
|
||||
Gateway API supports applying filters directly to individual backends through the `backendRefs[].filters` field. This enables backend-level request modifications.
|
||||
|
||||
```yaml
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: service-headers
|
||||
namespace: default
|
||||
spec:
|
||||
headers:
|
||||
customRequestHeaders:
|
||||
X-Service-Middleware: "applied"
|
||||
---
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: whoami
|
||||
namespace: default
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: traefik-gateway
|
||||
sectionName: websecure
|
||||
hostnames:
|
||||
- "whoami.docker.localhost"
|
||||
rules:
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /
|
||||
backendRefs:
|
||||
- name: whoami
|
||||
port: 80
|
||||
filters:
|
||||
- type: ExtensionRef
|
||||
extensionRef:
|
||||
group: traefik.io
|
||||
kind: Middleware
|
||||
name: service-headers
|
||||
```
|
||||
|
||||
Gateway API also supports the native `RequestHeaderModifier` filter type for simpler header modifications:
|
||||
|
||||
```yaml
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: whoami
|
||||
namespace: default
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: traefik-gateway
|
||||
sectionName: websecure
|
||||
hostnames:
|
||||
- "whoami.docker.localhost"
|
||||
rules:
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /
|
||||
backendRefs:
|
||||
- name: whoami
|
||||
port: 80
|
||||
filters:
|
||||
- type: RequestHeaderModifier
|
||||
requestHeaderModifier:
|
||||
add:
|
||||
- name: X-Backend-Header
|
||||
value: "gateway-api-filter"
|
||||
```
|
||||
|
||||
Save and apply:
|
||||
|
||||
```bash
|
||||
kubectl apply -f service-middleware-gateway.yaml
|
||||
```
|
||||
|
||||
### Using Kubernetes Ingress with Service Annotation
|
||||
|
||||
For standard Kubernetes Ingress, you can apply middlewares to a service using annotations:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: whoami
|
||||
namespace: default
|
||||
annotations:
|
||||
traefik.ingress.kubernetes.io/service.middlewares: default-service-headers@kubernetescrd
|
||||
spec:
|
||||
selector:
|
||||
app: whoami
|
||||
ports:
|
||||
- port: 80
|
||||
```
|
||||
|
||||
The annotation value follows the format `<namespace>-<middleware-name>@kubernetescrd`.
|
||||
|
||||
### Test Service Middleware
|
||||
|
||||
Verify the service middleware is working:
|
||||
|
||||
```bash
|
||||
curl -k -H "Host: whoami.docker.localhost" https://localhost/
|
||||
```
|
||||
|
||||
In the response from whoami, you should see the custom header that was added by the service middleware:
|
||||
|
||||
```text
|
||||
X-Service-Middleware: applied
|
||||
```
|
||||
|
||||
For more details on service middlewares, see the [reference documentation](../../reference/routing-configuration/http/load-balancing/service.md#middlewares).
|
||||
|
||||
## Conclusion
|
||||
|
||||
In this guide, you've learned how to:
|
||||
In this advanced guide, you've learned how to:
|
||||
|
||||
- Expose HTTP services through Traefik in Kubernetes using both Gateway API and IngressRoute
|
||||
- Set up path-based routing to direct traffic to different backend services
|
||||
- Secure your services with TLS using self-signed certificates
|
||||
- Add security with middlewares like secure headers and IP allow listing
|
||||
- Automate certificate management with Let's Encrypt
|
||||
- Automate certificate management with Let's Encrypt (IngressRoute) and cert-manager (Gateway API)
|
||||
- Implement sticky sessions for stateful applications
|
||||
- Setup multi-layer routing for authentication-based routing (IngressRoute only)
|
||||
- Apply middlewares at the service level for centralized middleware management
|
||||
|
||||
These fundamental capabilities provide a solid foundation for exposing any application through Traefik Proxy in Kubernetes. Each of these can be further customized to meet your specific requirements.
|
||||
These advanced capabilities allow you to build production-ready Traefik deployments with Kubernetes. Each of these can be further customized to meet your specific requirements.
|
||||
|
||||
### Next Steps
|
||||
|
||||
Now that you understand the basics of exposing services with Traefik Proxy, you might want to explore:
|
||||
Now that you've mastered both basic and advanced Traefik features with Kubernetes, you might want to explore:
|
||||
|
||||
- [Advanced routing options](../reference/routing-configuration/http/router/rules-and-priority.md) like query parameter matching, header-based routing, and more
|
||||
- [Additional middlewares](../reference/routing-configuration/http/middlewares/overview.md) for authentication, rate limiting, and request modifications
|
||||
- [Observability features](../reference/install-configuration/observability/metrics.md) for monitoring and debugging your Traefik deployment
|
||||
- [TCP services](../reference/routing-configuration/tcp/service.md) for exposing TCP services
|
||||
- [UDP services](../reference/routing-configuration/udp/service.md) for exposing UDP services
|
||||
- [Kubernetes Provider documentation](../reference/install-configuration/providers/kubernetes/kubernetes-crd.md) for more details about the Kubernetes integration.
|
||||
- [Gateway API provider documentation](../reference/install-configuration/providers/kubernetes/kubernetes-gateway.md) for more details about the Gateway API integration.
|
||||
- [Advanced routing options](../../reference/routing-configuration/http/routing/rules-and-priority.md) like query parameter matching, header-based routing, and more
|
||||
- [Additional middlewares](../../reference/routing-configuration/http/middlewares/overview.md) for authentication, rate limiting, and request modifications
|
||||
- [Observability features](../../reference/install-configuration/observability/metrics.md) for monitoring and debugging your Traefik deployment
|
||||
- [TCP services](../../reference/routing-configuration/tcp/service.md) for exposing TCP services
|
||||
- [UDP services](../../reference/routing-configuration/udp/service.md) for exposing UDP services
|
||||
- [Kubernetes Provider documentation](../../reference/install-configuration/providers/kubernetes/kubernetes-crd.md) for more details about the Kubernetes integration
|
||||
- [Gateway API provider documentation](../../reference/install-configuration/providers/kubernetes/kubernetes-gateway.md) for more details about the Gateway API integration
|
||||
@@ -0,0 +1,438 @@
|
||||
# Exposing Services with Traefik on Kubernetes - Basic
|
||||
|
||||
This guide will help you get started with exposing your services through Traefik Proxy on Kubernetes. You'll learn the fundamentals of routing HTTP traffic, setting up path-based routing, and securing your services with TLS.
|
||||
|
||||
For routing, this guide gives you two options:
|
||||
|
||||
- [Gateway API](../../reference/routing-configuration/kubernetes/gateway-api.md)
|
||||
- [IngressRoute](../../reference/routing-configuration/kubernetes/crd/http/ingressroute.md)
|
||||
|
||||
Feel free to choose the one that fits your needs best.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- A Kubernetes cluster with Traefik Proxy installed
|
||||
- `kubectl` configured to interact with your cluster
|
||||
- Traefik deployed using the [Traefik Kubernetes Setup guide](../../setup/kubernetes.md)
|
||||
|
||||
## Expose Your First HTTP Service
|
||||
|
||||
Let's expose a simple HTTP service using the [whoami](https://github.com/traefik/whoami) application. This will demonstrate basic routing to a backend service.
|
||||
|
||||
First, create the deployment and service:
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: whoami
|
||||
namespace: default
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: whoami
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: whoami
|
||||
spec:
|
||||
containers:
|
||||
- name: whoami
|
||||
image: traefik/whoami
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: whoami
|
||||
namespace: default
|
||||
spec:
|
||||
selector:
|
||||
app: whoami
|
||||
ports:
|
||||
- port: 80
|
||||
```
|
||||
|
||||
Save this as `whoami.yaml` and apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f whoami.yaml
|
||||
```
|
||||
|
||||
Now, let's create routes using either Gateway API or IngressRoute.
|
||||
|
||||
### Using Gateway API
|
||||
|
||||
```yaml
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: whoami
|
||||
namespace: default
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: traefik-gateway # This Gateway is automatically created by Traefik
|
||||
hostnames:
|
||||
- "whoami.docker.localhost"
|
||||
rules:
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /
|
||||
backendRefs:
|
||||
- name: whoami
|
||||
port: 80
|
||||
```
|
||||
|
||||
Save this as `whoami-route.yaml` and apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f whoami-route.yaml
|
||||
```
|
||||
|
||||
### Using IngressRoute
|
||||
|
||||
```yaml
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: whoami
|
||||
namespace: default
|
||||
spec:
|
||||
entryPoints:
|
||||
- web
|
||||
routes:
|
||||
- match: Host(`whoami.docker.localhost`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: whoami
|
||||
port: 80
|
||||
```
|
||||
|
||||
Save this as `whoami-ingressroute.yaml` and apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f whoami-ingressroute.yaml
|
||||
```
|
||||
|
||||
### Verify Your Service
|
||||
|
||||
Your service is now available at http://whoami.docker.localhost/. Test that it works:
|
||||
|
||||
```bash
|
||||
curl -H "Host: whoami.docker.localhost" http://localhost/
|
||||
```
|
||||
|
||||
!!! info
|
||||
Make sure to remove the `ports.web.redirections` block from the `values.yaml` file if you followed the Kubernetes Setup Guide to install Traefik otherwise you will be redirected to the HTTPS entrypoint:
|
||||
|
||||
```yaml
|
||||
redirections:
|
||||
entryPoint:
|
||||
to: websecure
|
||||
```
|
||||
|
||||
You should see output similar to:
|
||||
|
||||
```bash
|
||||
Hostname: whoami-6d5d964cb-8pv4k
|
||||
IP: 127.0.0.1
|
||||
IP: ::1
|
||||
IP: 10.42.0.18
|
||||
IP: fe80::d4c0:3bff:fe20:b0a3
|
||||
RemoteAddr: 10.42.0.17:39872
|
||||
GET / HTTP/1.1
|
||||
Host: whoami.docker.localhost
|
||||
User-Agent: curl/7.68.0
|
||||
Accept: */*
|
||||
Accept-Encoding: gzip
|
||||
X-Forwarded-For: 10.42.0.1
|
||||
X-Forwarded-Host: whoami.docker.localhost
|
||||
X-Forwarded-Port: 80
|
||||
X-Forwarded-Proto: http
|
||||
X-Forwarded-Server: traefik-76cbd5b89c-rx5xn
|
||||
X-Real-Ip: 10.42.0.1
|
||||
```
|
||||
|
||||
This confirms that Traefik is successfully routing requests to your whoami application.
|
||||
|
||||
## Add Routing Rules
|
||||
|
||||
Now we'll enhance our routing by directing traffic to different services based on URL paths. This is useful for API versioning, frontend/backend separation, or organizing microservices.
|
||||
|
||||
First, deploy a second service to represent an API:
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: whoami-api
|
||||
namespace: default
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: whoami-api
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: whoami-api
|
||||
spec:
|
||||
containers:
|
||||
- name: whoami
|
||||
image: traefik/whoami
|
||||
env:
|
||||
- name: WHOAMI_NAME
|
||||
value: "API Service"
|
||||
ports:
|
||||
- containerPort: 80
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: whoami-api
|
||||
namespace: default
|
||||
spec:
|
||||
selector:
|
||||
app: whoami-api
|
||||
ports:
|
||||
- port: 80
|
||||
```
|
||||
|
||||
Save this as `whoami-api.yaml` and apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f whoami-api.yaml
|
||||
```
|
||||
|
||||
Now set up path-based routing:
|
||||
|
||||
### Gateway API with Path Rules
|
||||
|
||||
Update your existing `HTTPRoute` to include path-based routing:
|
||||
|
||||
```yaml
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: whoami
|
||||
namespace: default
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: traefik-gateway
|
||||
hostnames:
|
||||
- "whoami.docker.localhost"
|
||||
rules:
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /api
|
||||
backendRefs:
|
||||
- name: whoami-api
|
||||
port: 80
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /
|
||||
backendRefs:
|
||||
- name: whoami
|
||||
port: 80
|
||||
```
|
||||
|
||||
Update the file `whoami-route.yaml` and apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f whoami-route.yaml
|
||||
```
|
||||
|
||||
### IngressRoute with Path Rules
|
||||
|
||||
Update your existing IngressRoute to include path-based routing:
|
||||
|
||||
```yaml
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: whoami
|
||||
namespace: default
|
||||
spec:
|
||||
entryPoints:
|
||||
- web
|
||||
routes:
|
||||
- match: Host(`whoami.docker.localhost`) && Path(`/api`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: whoami-api
|
||||
port: 80
|
||||
- match: Host(`whoami.docker.localhost`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: whoami
|
||||
port: 80
|
||||
```
|
||||
|
||||
Save this as `whoami-ingressroute.yaml` and apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f whoami-ingressroute.yaml
|
||||
```
|
||||
|
||||
### Test the Path-Based Routing
|
||||
|
||||
Verify that different paths route to different services:
|
||||
|
||||
```bash
|
||||
# Root path should go to the main whoami service
|
||||
curl -H "Host: whoami.docker.localhost" http://localhost/
|
||||
|
||||
# /api path should go to the whoami-api service
|
||||
curl -H "Host: whoami.docker.localhost" http://localhost/api
|
||||
```
|
||||
|
||||
For the `/api` requests, you should see the response showing "API Service" in the environment variables section, confirming that your path-based routing is working correctly:
|
||||
|
||||
```bash
|
||||
{"hostname":"whoami-api-67d97b4868-dvvll","ip":["127.0.0.1","::1","10.42.0.9","fe80::10aa:37ff:fe74:31f2"],"headers":{"Accept":["*/*"],"Accept-Encoding":["gzip"],"User-Agent":["curl/8.7.1"],"X-Forwarded-For":["10.42.0.1"],"X-Forwarded-Host":["whoami.docker.localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["traefik-669c479df8-vkj22"],"X-Real-Ip":["10.42.0.1"]},"url":"/api","host":"whoami.docker.localhost","method":"GET","name":"API Service","remoteAddr":"10.42.0.13:36592"}
|
||||
```
|
||||
|
||||
## Enable TLS
|
||||
|
||||
Let's secure our service with HTTPS by adding TLS. We'll start with a self-signed certificate for local development.
|
||||
|
||||
### Create a Self-Signed Certificate
|
||||
|
||||
Generate a self-signed certificate:
|
||||
|
||||
```bash
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
-keyout tls.key -out tls.crt \
|
||||
-subj "/CN=whoami.docker.localhost"
|
||||
```
|
||||
|
||||
Create a TLS secret in Kubernetes:
|
||||
|
||||
```bash
|
||||
kubectl create secret tls whoami-tls --cert=tls.crt --key=tls.key
|
||||
```
|
||||
|
||||
!!! important "Prerequisite for Gateway API with TLS"
|
||||
Before using the Gateway API with TLS, you must define the `websecure` listener in your Traefik installation. This is typically done in your Helm values.
|
||||
|
||||
Example configuration in `values.yaml`:
|
||||
```yaml
|
||||
gateway:
|
||||
listeners:
|
||||
web:
|
||||
port: 80
|
||||
protocol: HTTP
|
||||
namespacePolicy:
|
||||
from: All
|
||||
websecure:
|
||||
port: 443
|
||||
protocol: HTTPS
|
||||
namespacePolicy:
|
||||
from: All
|
||||
mode: Terminate
|
||||
certificateRefs:
|
||||
- kind: Secret
|
||||
name: local-selfsigned-tls
|
||||
group: ""
|
||||
```
|
||||
|
||||
See the Traefik Kubernetes Setup Guide for complete installation details.
|
||||
|
||||
### Gateway API with TLS
|
||||
|
||||
Update your existing `HTTPRoute` to use the secured gateway listener:
|
||||
|
||||
```yaml
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: whoami
|
||||
namespace: default
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: traefik-gateway
|
||||
sectionName: websecure # The HTTPS listener
|
||||
hostnames:
|
||||
- "whoami.docker.localhost"
|
||||
rules:
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /api
|
||||
backendRefs:
|
||||
- name: whoami-api
|
||||
port: 80
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /
|
||||
backendRefs:
|
||||
- name: whoami
|
||||
port: 80
|
||||
```
|
||||
|
||||
Update the file `whoami-route.yaml` and apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f whoami-route.yaml
|
||||
```
|
||||
|
||||
### IngressRoute with TLS
|
||||
|
||||
Update your existing IngressRoute to use TLS:
|
||||
|
||||
```yaml
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: whoami
|
||||
namespace: default
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure # Changed from 'web' to 'websecure'
|
||||
routes:
|
||||
- match: Host(`whoami.docker.localhost`) && Path(`/api`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: whoami-api
|
||||
port: 80
|
||||
- match: Host(`whoami.docker.localhost`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: whoami
|
||||
port: 80
|
||||
tls:
|
||||
secretName: whoami-tls # Added TLS configuration
|
||||
```
|
||||
|
||||
Update the file `whoami-ingressroute.yaml` and apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f whoami-ingressroute.yaml
|
||||
```
|
||||
|
||||
### Verify HTTPS Access
|
||||
|
||||
Now you can access your service securely. Since we're using a self-signed certificate, you'll need to skip certificate verification:
|
||||
|
||||
```bash
|
||||
curl -k -H "Host: whoami.docker.localhost" https://localhost/
|
||||
```
|
||||
|
||||
Your browser can also access https://whoami.docker.localhost/ (you'll need to accept the security warning for the self-signed certificate).
|
||||
|
||||
## Next Steps
|
||||
|
||||
Now that you've mastered the basics of exposing services with Traefik on Kubernetes, you're ready to explore more advanced features like middlewares, Let's Encrypt certificates, sticky sessions, and multi-layer routing.
|
||||
|
||||
Continue to the [Advanced Guide](advanced.md) to learn about:
|
||||
|
||||
- Adding middlewares for security and access control
|
||||
- Generating certificates with Let's Encrypt (IngressRoute) or cert-manager (Gateway API)
|
||||
- Configuring sticky sessions for stateful applications
|
||||
- Setting up multi-layer routing for authentication-based routing with IngressRoute
|
||||
@@ -17,6 +17,100 @@ Following these guides, you'll learn how to:
|
||||
|
||||
For detailed steps tailored to your environment, follow the guide for your platform:
|
||||
|
||||
- [Kubernetes](./kubernetes.md)
|
||||
- [Docker](./docker.md)
|
||||
- [Docker Swarm](./swarm.md)
|
||||
- [Kubernetes](./kubernetes/basic.md)
|
||||
- [Docker](./docker/basic.md)
|
||||
- [Docker Swarm](./swarm/basic.md)
|
||||
|
||||
## Advanced Use Cases
|
||||
|
||||
### Exposing gRPC Services
|
||||
|
||||
Traefik Proxy supports gRPC applications without requiring specific configuration. You can expose gRPC services using either HTTP (h2c) or HTTPS.
|
||||
|
||||
??? example "Using HTTP (h2c)"
|
||||
|
||||
For unencrypted gRPC communication, configure your service to use the `h2c://` protocol scheme:
|
||||
|
||||
```yaml
|
||||
http:
|
||||
routers:
|
||||
grpc-router:
|
||||
service: grpc-service
|
||||
rule: Host(`grpc.example.com`)
|
||||
|
||||
services:
|
||||
grpc-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: h2c://backend:8080
|
||||
```
|
||||
|
||||
!!! note
|
||||
For providers with labels (Docker, Kubernetes), specify the scheme using:
|
||||
`traefik.http.services.<service-name>.loadbalancer.server.scheme=h2c`
|
||||
|
||||
??? example "Using HTTPS"
|
||||
|
||||
For encrypted gRPC communication, use standard HTTPS URLs. Traefik will use HTTP/2 over TLS to communicate with your gRPC backend:
|
||||
|
||||
```yaml
|
||||
http:
|
||||
routers:
|
||||
grpc-router:
|
||||
service: grpc-service
|
||||
rule: Host(`grpc.example.com`)
|
||||
tls: {}
|
||||
|
||||
services:
|
||||
grpc-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: https://backend:8080
|
||||
```
|
||||
|
||||
Traefik handles the protocol negotiation automatically. Configure TLS certificates for your backends using [ServersTransport](../reference/routing-configuration/http/load-balancing/serverstransport.md) if needed.
|
||||
|
||||
### Exposing WebSocket Services
|
||||
|
||||
Traefik Proxy supports WebSocket (WS) and WebSocket Secure (WSS) connections out of the box. No special configuration is required beyond standard HTTP routing.
|
||||
|
||||
??? example "Basic WebSocket"
|
||||
|
||||
Configure a router and service pointing to your WebSocket server. Traefik automatically detects and handles the WebSocket upgrade:
|
||||
|
||||
```yaml
|
||||
http:
|
||||
routers:
|
||||
websocket-router:
|
||||
rule: Host(`ws.example.com`)
|
||||
service: websocket-service
|
||||
|
||||
services:
|
||||
websocket-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: http://websocket-backend:8000
|
||||
```
|
||||
|
||||
??? example "WebSocket Secure (WSS)"
|
||||
|
||||
For encrypted WebSocket connections, enable TLS on your router. Clients connect using `wss://` while you can choose whether backends use encrypted or unencrypted connections:
|
||||
|
||||
```yaml
|
||||
http:
|
||||
routers:
|
||||
websocket-secure-router:
|
||||
rule: Host(`wss.example.com`)
|
||||
service: websocket-service
|
||||
tls: {}
|
||||
|
||||
services:
|
||||
websocket-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: http://websocket-backend:8000 # SSL termination at Traefik
|
||||
# OR
|
||||
# - url: https://websocket-backend:8443 # End-to-end encryption
|
||||
```
|
||||
|
||||
Traefik preserves WebSocket headers including `Origin`, `Sec-WebSocket-Key`, and `Sec-WebSocket-Version`. Use the [Headers middleware](../reference/routing-configuration/http/middlewares/headers.md) if you need to modify headers for origin checking or other requirements.
|
||||
|
||||
@@ -1,401 +0,0 @@
|
||||
# Exposing Services with Traefik on Docker Swarm
|
||||
|
||||
This guide will help you expose your services securely through Traefik Proxy using Docker Swarm. We'll cover routing HTTP and HTTPS traffic, implementing TLS, adding middlewares, Let's Encrypt integration, and sticky sessions.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Docker Swarm cluster initialized
|
||||
- Basic understanding of Docker Swarm concepts
|
||||
- Traefik deployed using the Traefik Docker Swarm Setup guide
|
||||
|
||||
## Expose Your First HTTP Service
|
||||
|
||||
Let's expose a simple HTTP service using the [whoami](https://hub.docker.com/r/traefik/whoami) application. This will demonstrate basic routing to a backend service.
|
||||
|
||||
First, update your existing `docker-compose.yml` file if you haven't already:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
whoami:
|
||||
image: traefik/whoami
|
||||
networks:
|
||||
- traefik_proxy
|
||||
deploy:
|
||||
replicas: 3
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.whoami.rule=Host(`whoami.swarm.localhost`)"
|
||||
- "traefik.http.routers.whoami.entrypoints=web,websecure"
|
||||
```
|
||||
|
||||
Save this as `docker-compose.yml` and deploy the stack:
|
||||
|
||||
```bash
|
||||
docker stack deploy -c docker-compose.yml traefik
|
||||
```
|
||||
|
||||
### Verify Your Service
|
||||
|
||||
Your service is now available at http://whoami.swarm.localhost/. Test that it works:
|
||||
|
||||
```bash
|
||||
curl -H "Host: whoami.swarm.localhost" http://localhost/
|
||||
```
|
||||
|
||||
You should see output similar to:
|
||||
|
||||
```bash
|
||||
Hostname: whoami.1.7c8f7tr56q3p949rscxrkp80e
|
||||
IP: 127.0.0.1
|
||||
IP: ::1
|
||||
IP: 10.0.1.8
|
||||
IP: fe80::215:5dff:fe00:c9e
|
||||
RemoteAddr: 10.0.1.2:45098
|
||||
GET / HTTP/1.1
|
||||
Host: whoami.swarm.localhost
|
||||
User-Agent: curl/7.68.0
|
||||
Accept: */*
|
||||
Accept-Encoding: gzip
|
||||
X-Forwarded-For: 10.0.1.1
|
||||
X-Forwarded-Host: whoami.swarm.localhost
|
||||
X-Forwarded-Port: 80
|
||||
X-Forwarded-Proto: http
|
||||
X-Forwarded-Server: 5789f594e7d5
|
||||
X-Real-Ip: 10.0.1.1
|
||||
```
|
||||
|
||||
This confirms that Traefik is successfully routing requests to your whoami application.
|
||||
|
||||
## Add Routing Rules
|
||||
|
||||
Now we'll enhance our routing by directing traffic to different services based on [URL paths](../reference/routing-configuration/http/router/rules-and-priority.md#path-pathprefix-and-pathregexp). This is useful for API versioning, frontend/backend separation, or organizing microservices.
|
||||
|
||||
Update your `docker-compose.yml` to add another service:
|
||||
|
||||
```yaml
|
||||
# ...
|
||||
|
||||
# New service
|
||||
whoami-api:
|
||||
image: traefik/whoami
|
||||
networks:
|
||||
- traefik_proxy
|
||||
environment:
|
||||
- WHOAMI_NAME=API Service
|
||||
deploy:
|
||||
replicas: 2
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
# Path-based routing
|
||||
- "traefik.http.routers.whoami-api.rule=Host(`whoami.swarm.localhost`) && PathPrefix(`/api`)"
|
||||
- "traefik.http.routers.whoami-api.entrypoints=web,websecure"
|
||||
- "traefik.http.routers.whoami-api.service=whoami-api-svc"
|
||||
- "traefik.http.services.whoami-api-svc.loadbalancer.server.port=80"
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
Apply the changes:
|
||||
|
||||
```bash
|
||||
docker stack deploy -c docker-compose.yml traefik
|
||||
```
|
||||
|
||||
### Test the Path-Based Routing
|
||||
|
||||
Verify that different paths route to different services:
|
||||
|
||||
```bash
|
||||
# Root path should go to the main whoami service
|
||||
curl -H "Host: whoami.swarm.localhost" http://localhost/
|
||||
|
||||
# /api path should go to the whoami-api service
|
||||
curl -H "Host: whoami.swarm.localhost" http://localhost/api
|
||||
```
|
||||
|
||||
For the `/api` requests, you should see the response showing "API Service" in the environment variables section, confirming that your path-based routing is working correctly.
|
||||
|
||||
## Enable TLS
|
||||
|
||||
Let's secure our service with HTTPS by adding TLS. We'll start with a self-signed certificate for local development.
|
||||
|
||||
### Create a Self-Signed Certificate
|
||||
|
||||
Generate a self-signed certificate and dynamic config file to tell Traefik where the cert lives:
|
||||
|
||||
```bash
|
||||
mkdir -p certs
|
||||
|
||||
# key + cert (valid for one year)
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
-keyout certs/local.key -out certs/local.crt \
|
||||
-subj "/CN=*.swarm.localhost"
|
||||
|
||||
# dynamic config that tells Traefik where the cert lives
|
||||
cat > certs/tls.yml <<'EOF'
|
||||
tls:
|
||||
certificates:
|
||||
- certFile: /certificates/local.crt
|
||||
keyFile: /certificates/local.key
|
||||
EOF
|
||||
```
|
||||
|
||||
Create a Docker config for the certificate files:
|
||||
|
||||
```bash
|
||||
docker config create swarm-cert.crt certs/local.crt
|
||||
docker config create swarm-cert.key certs/local.key
|
||||
docker config create swarm-tls.yml certs/tls.yml
|
||||
```
|
||||
|
||||
Update your `docker-compose.yml` file with the following changes:
|
||||
|
||||
```yaml
|
||||
# Add to the Traefik command section:
|
||||
command:
|
||||
# ... existing commands ...
|
||||
- "--entryPoints.websecure.address=:443"
|
||||
- "--entryPoints.websecure.http.tls=true"
|
||||
- "--providers.file.directory=/etc/traefik/dynamic"
|
||||
```
|
||||
|
||||
```yaml
|
||||
# Add to the root of your docker-compose.yml file:
|
||||
configs:
|
||||
swarm-cert.crt:
|
||||
file: ./certs/local.crt
|
||||
swarm-cert.key:
|
||||
file: ./certs/local.key
|
||||
swarm-tls.yml:
|
||||
file: ./certs/tls.yml
|
||||
```
|
||||
|
||||
Deploy the stack:
|
||||
|
||||
```bash
|
||||
docker stack deploy -c docker-compose.yml traefik
|
||||
```
|
||||
|
||||
Your browser can access https://whoami.swarm.localhost/ for the service. You'll need to accept the security warning for the self-signed certificate.
|
||||
|
||||
## Add Middlewares
|
||||
|
||||
Middlewares allow you to modify requests or responses as they pass through Traefik. Let's add two useful middlewares: [Headers](../reference/routing-configuration/http/middlewares/headers.md) for security and [IP allowlisting](../reference/routing-configuration/http/middlewares/ipallowlist.md) for access control.
|
||||
|
||||
Add the following labels to your whoami service deployment section in `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
deploy:
|
||||
# ... existing configuration ...
|
||||
labels:
|
||||
# ... existing labels ...
|
||||
|
||||
# Secure Headers Middleware
|
||||
- "traefik.http.middlewares.secure-headers.headers.frameDeny=true"
|
||||
- "traefik.http.middlewares.secure-headers.headers.sslRedirect=true"
|
||||
- "traefik.http.middlewares.secure-headers.headers.browserXssFilter=true"
|
||||
- "traefik.http.middlewares.secure-headers.headers.contentTypeNosniff=true"
|
||||
- "traefik.http.middlewares.secure-headers.headers.stsIncludeSubdomains=true"
|
||||
- "traefik.http.middlewares.secure-headers.headers.stsPreload=true"
|
||||
- "traefik.http.middlewares.secure-headers.headers.stsSeconds=31536000"
|
||||
|
||||
# IP Allowlist Middleware
|
||||
- "traefik.http.middlewares.ip-allowlist.ipallowlist.sourceRange=127.0.0.1/32,192.168.0.0/16,10.0.0.0/8"
|
||||
|
||||
# Apply the middlewares
|
||||
- "traefik.http.routers.whoami.middlewares=secure-headers,ip-allowlist"
|
||||
```
|
||||
|
||||
Add the same middleware to your whoami-api service:
|
||||
|
||||
```yaml
|
||||
deploy:
|
||||
# ... existing configuration ...
|
||||
labels:
|
||||
# ... existing labels ...
|
||||
- "traefik.http.routers.whoami-api.middlewares=secure-headers,ip-allowlist"
|
||||
```
|
||||
|
||||
Apply the changes:
|
||||
|
||||
```bash
|
||||
docker stack deploy -c docker-compose.yml traefik
|
||||
```
|
||||
|
||||
### Test the Middlewares
|
||||
|
||||
Now let's verify that our middlewares are working correctly:
|
||||
|
||||
Test the Secure Headers middleware:
|
||||
|
||||
```bash
|
||||
curl -k -I -H "Host: whoami.swarm.localhost" https://localhost/
|
||||
```
|
||||
|
||||
In the response headers, you should see security headers set by the middleware:
|
||||
|
||||
- `X-Frame-Options: DENY`
|
||||
- `X-Content-Type-Options: nosniff`
|
||||
- `X-XSS-Protection: 1; mode=block`
|
||||
- `Strict-Transport-Security` with the appropriate settings
|
||||
|
||||
Test the IP Allowlist middleware:
|
||||
|
||||
If your request comes from an IP that's in the allow list (e.g., 127.0.0.1), it should succeed:
|
||||
|
||||
```bash
|
||||
curl -k -I -H "Host: whoami.swarm.localhost" https://localhost/
|
||||
```
|
||||
|
||||
If you try to access from an IP not in the allow list, the request will be rejected with a `403` Forbidden response. To simulate this in a local environment, you can modify the middleware configuration temporarily to exclude your IP address, then test again.
|
||||
|
||||
## Generate Certificates with Let's Encrypt
|
||||
|
||||
Let's Encrypt provides free, automated TLS certificates. Let's configure Traefik to automatically obtain and renew certificates for our services.
|
||||
|
||||
Instead of using self-signed certificates, update your existing `docker-compose.yml` file with the following changes:
|
||||
|
||||
Add the Let's Encrypt certificate resolver to the Traefik service command section:
|
||||
|
||||
```yaml
|
||||
command:
|
||||
# ... existing commands ...
|
||||
# Let's Encrypt configuration
|
||||
- "--certificatesresolvers.le.acme.email=your-email@example.com" # replace with your actual email
|
||||
- "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
|
||||
- "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
|
||||
```
|
||||
|
||||
Add a volume for Let's Encrypt certificates:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
# ...Existing volumes...
|
||||
- letsencrypt:/letsencrypt
|
||||
```
|
||||
|
||||
Update your service labels to use the certificate resolver:
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
# ... existing labels ...
|
||||
- "traefik.http.routers.whoami.tls.certresolver=le"
|
||||
```
|
||||
|
||||
Do the same for any other services you want to secure:
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
# ... existing labels ...
|
||||
- "traefik.http.routers.whoami-api.tls.certresolver=le"
|
||||
```
|
||||
|
||||
Create a named volume for storing Let's Encrypt certificates by adding to the volumes section:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
# ... existing volumes ...
|
||||
letsencrypt:
|
||||
driver: local
|
||||
```
|
||||
|
||||
Apply the changes:
|
||||
|
||||
```bash
|
||||
docker stack deploy -c docker-compose.yml traefik
|
||||
```
|
||||
|
||||
!!! important "Public DNS Required"
|
||||
Let's Encrypt may require a publicly accessible domain to validate domain ownership. For testing with local domains like `whoami.swarm.localhost`, the certificate will remain self-signed. In production, replace it with a real domain that has a publicly accessible DNS record pointing to your Traefik instance.
|
||||
|
||||
Once the certificate is issued, you can verify it:
|
||||
|
||||
```bash
|
||||
# Verify the certificate chain
|
||||
curl -v https://whoami.swarm.localhost/ 2>&1 | grep -i "server certificate"
|
||||
```
|
||||
|
||||
You should see that your certificate is issued by Let's Encrypt.
|
||||
|
||||
## Configure Sticky Sessions
|
||||
|
||||
Sticky sessions ensure that a user's requests always go to the same backend server, which is essential for applications that maintain session state. Let's implement sticky sessions for our whoami service.
|
||||
|
||||
Docker Swarm already has multiple replicas running; we'll now add sticky session configuration. Update your whoami service in the `docker-compose.yml` file:
|
||||
|
||||
### Add Sticky Session Configuration
|
||||
|
||||
Add the following labels to your whoami service in the `docker-compose.yml` file:
|
||||
|
||||
```yaml
|
||||
deploy:
|
||||
# ... existing configuration ...
|
||||
labels:
|
||||
# ... existing labels ...
|
||||
|
||||
# Sticky Sessions Configuration
|
||||
- "traefik.http.services.whoami.loadbalancer.sticky.cookie=true"
|
||||
- "traefik.http.services.whoami.loadbalancer.sticky.cookie.name=sticky_cookie"
|
||||
- "traefik.http.services.whoami.loadbalancer.sticky.cookie.secure=true"
|
||||
- "traefik.http.services.whoami.loadbalancer.sticky.cookie.httpOnly=true"
|
||||
```
|
||||
|
||||
Apply the changes:
|
||||
|
||||
```bash
|
||||
docker stack deploy -c docker-compose.yml traefik
|
||||
```
|
||||
|
||||
### Test Sticky Sessions
|
||||
|
||||
You can test the sticky sessions by making multiple requests and observing that they all go to the same backend container:
|
||||
|
||||
```bash
|
||||
# First request - save cookies to a file
|
||||
curl -k -c cookies.txt -H "Host: whoami.swarm.localhost" https://localhost/
|
||||
|
||||
# Subsequent requests - use the cookies
|
||||
curl -k -b cookies.txt -H "Host: whoami.swarm.localhost" https://localhost/
|
||||
curl -k -b cookies.txt -H "Host: whoami.swarm.localhost" https://localhost/
|
||||
```
|
||||
|
||||
Pay attention to the `Hostname` field in each response - it should remain the same across all requests when using the cookie file, confirming that sticky sessions are working.
|
||||
|
||||
For comparison, try making requests without the cookie:
|
||||
|
||||
```bash
|
||||
# Requests without cookies should be load-balanced across different containers
|
||||
curl -k -H "Host: whoami.swarm.localhost" https://localhost/
|
||||
curl -k -H "Host: whoami.swarm.localhost" https://localhost/
|
||||
```
|
||||
|
||||
You should see different `Hostname` values in these responses, as each request is load-balanced to a different container.
|
||||
|
||||
!!! important "Browser Testing"
|
||||
When testing in browsers, you need to use the same browser session to maintain the cookie. The cookie is set with `httpOnly` and `secure` flags for security, so it will only be sent over HTTPS connections and won't be accessible via JavaScript.
|
||||
|
||||
For more advanced configuration options, see the [reference documentation](../reference/routing-configuration/http/load-balancing/service.md).
|
||||
|
||||
## Conclusion
|
||||
|
||||
In this guide, you've learned how to:
|
||||
|
||||
- Expose HTTP services through Traefik in Docker Swarm
|
||||
- Set up path-based routing to direct traffic to different backend services
|
||||
- Secure your services with TLS using self-signed certificates
|
||||
- Add security with middlewares like secure headers and IP allow listing
|
||||
- Automate certificate management with Let's Encrypt
|
||||
- Implement sticky sessions for stateful applications
|
||||
|
||||
These fundamental capabilities provide a solid foundation for exposing any application through Traefik Proxy in Docker Swarm. Each of these can be further customized to meet your specific requirements.
|
||||
|
||||
### Next Steps
|
||||
|
||||
Now that you understand the basics of exposing services with Traefik Proxy, you might want to explore:
|
||||
|
||||
- [Advanced routing options](../reference/routing-configuration/http/router/rules-and-priority.md) like query parameter matching, header-based routing, and more
|
||||
- [Additional middlewares](../reference/routing-configuration/http/middlewares/overview.md) for authentication, rate limiting, and request modifications
|
||||
- [Observability features](../reference/install-configuration/observability/metrics.md) for monitoring and debugging your Traefik deployment
|
||||
- [TCP services](../reference/routing-configuration/tcp/service.md) for exposing TCP services
|
||||
- [UDP services](../reference/routing-configuration/udp/service.md) for exposing UDP services
|
||||
- [Docker provider documentation](../reference/install-configuration/providers/docker.md) for more details about the Docker integration
|
||||
@@ -0,0 +1,474 @@
|
||||
# Exposing Services with Traefik on Docker Swarm - Advanced
|
||||
|
||||
This guide builds on the concepts and setup from the [Basic Guide](basic.md). Make sure you've completed the basic guide and have a working Traefik setup with Docker Swarm before proceeding.
|
||||
|
||||
In this advanced guide, you'll learn how to enhance your Traefik deployment with:
|
||||
|
||||
- **Middlewares** for security headers and access control
|
||||
- **Let's Encrypt** for automated certificate management
|
||||
- **Sticky sessions** for stateful applications
|
||||
- **Multi-layer routing** for complex authentication scenarios
|
||||
- **Service middlewares** for applying middleware at the service level
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Completed the [Basic Guide](basic.md)
|
||||
- Docker Swarm cluster initialized
|
||||
- Working Traefik setup from the basic guide
|
||||
|
||||
## Add Middlewares
|
||||
|
||||
Middlewares allow you to modify requests or responses as they pass through Traefik. Let's add two useful middlewares: [Headers](../../reference/routing-configuration/http/middlewares/headers.md) for security and [IP allowlisting](../../reference/routing-configuration/http/middlewares/ipallowlist.md) for access control.
|
||||
|
||||
Add the following labels to your whoami service deployment section in `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
deploy:
|
||||
# ... existing configuration ...
|
||||
labels:
|
||||
# ... existing labels ...
|
||||
|
||||
# Secure Headers Middleware
|
||||
- "traefik.http.middlewares.secure-headers.headers.frameDeny=true"
|
||||
- "traefik.http.middlewares.secure-headers.headers.sslRedirect=true"
|
||||
- "traefik.http.middlewares.secure-headers.headers.browserXssFilter=true"
|
||||
- "traefik.http.middlewares.secure-headers.headers.contentTypeNosniff=true"
|
||||
- "traefik.http.middlewares.secure-headers.headers.stsIncludeSubdomains=true"
|
||||
- "traefik.http.middlewares.secure-headers.headers.stsPreload=true"
|
||||
- "traefik.http.middlewares.secure-headers.headers.stsSeconds=31536000"
|
||||
|
||||
# IP Allowlist Middleware
|
||||
- "traefik.http.middlewares.ip-allowlist.ipallowlist.sourceRange=127.0.0.1/32,192.168.0.0/16,10.0.0.0/8"
|
||||
|
||||
# Apply the middlewares
|
||||
- "traefik.http.routers.whoami.middlewares=secure-headers,ip-allowlist"
|
||||
```
|
||||
|
||||
Add the same middleware to your whoami-api service:
|
||||
|
||||
```yaml
|
||||
deploy:
|
||||
# ... existing configuration ...
|
||||
labels:
|
||||
# ... existing labels ...
|
||||
- "traefik.http.routers.whoami-api.middlewares=secure-headers,ip-allowlist"
|
||||
```
|
||||
|
||||
Apply the changes:
|
||||
|
||||
```bash
|
||||
docker stack deploy -c docker-compose.yml traefik
|
||||
```
|
||||
|
||||
### Test the Middlewares
|
||||
|
||||
Now let's verify that our middlewares are working correctly:
|
||||
|
||||
Test the Secure Headers middleware:
|
||||
|
||||
```bash
|
||||
curl -k -I -H "Host: whoami.swarm.localhost" https://localhost/
|
||||
```
|
||||
|
||||
In the response headers, you should see security headers set by the middleware:
|
||||
|
||||
- `X-Frame-Options: DENY`
|
||||
- `X-Content-Type-Options: nosniff`
|
||||
- `X-XSS-Protection: 1; mode=block`
|
||||
- `Strict-Transport-Security` with the appropriate settings
|
||||
|
||||
Test the IP Allowlist middleware:
|
||||
|
||||
If your request comes from an IP that's in the allow list (e.g., 127.0.0.1), it should succeed:
|
||||
|
||||
```bash
|
||||
curl -k -I -H "Host: whoami.swarm.localhost" https://localhost/
|
||||
```
|
||||
|
||||
If you try to access from an IP not in the allow list, the request will be rejected with a `403` Forbidden response. To simulate this in a local environment, you can modify the middleware configuration temporarily to exclude your IP address, then test again.
|
||||
|
||||
## Generate Certificates with Let's Encrypt
|
||||
|
||||
Let's Encrypt provides free, automated TLS certificates. Let's configure Traefik to automatically obtain and renew certificates for our services.
|
||||
|
||||
Instead of using self-signed certificates, update your existing `docker-compose.yml` file with the following changes:
|
||||
|
||||
Add the Let's Encrypt certificate resolver to the Traefik service command section:
|
||||
|
||||
```yaml
|
||||
command:
|
||||
# ... existing commands ...
|
||||
# Let's Encrypt configuration
|
||||
- "--certificatesresolvers.le.acme.email=your-email@example.com" # replace with your actual email
|
||||
- "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
|
||||
- "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
|
||||
```
|
||||
|
||||
Add a volume for Let's Encrypt certificates:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
# ...Existing volumes...
|
||||
- letsencrypt:/letsencrypt
|
||||
```
|
||||
|
||||
Update your service labels to use the certificate resolver:
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
# ... existing labels ...
|
||||
- "traefik.http.routers.whoami.tls.certresolver=le"
|
||||
```
|
||||
|
||||
Do the same for any other services you want to secure:
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
# ... existing labels ...
|
||||
- "traefik.http.routers.whoami-api.tls.certresolver=le"
|
||||
```
|
||||
|
||||
Create a named volume for storing Let's Encrypt certificates by adding to the volumes section:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
# ... existing volumes ...
|
||||
letsencrypt:
|
||||
driver: local
|
||||
```
|
||||
|
||||
Apply the changes:
|
||||
|
||||
```bash
|
||||
docker stack deploy -c docker-compose.yml traefik
|
||||
```
|
||||
|
||||
!!! important "Public DNS Required"
|
||||
Let's Encrypt may require a publicly accessible domain to validate domain ownership. For testing with local domains like `whoami.swarm.localhost`, the certificate will remain self-signed. In production, replace it with a real domain that has a publicly accessible DNS record pointing to your Traefik instance.
|
||||
|
||||
Once the certificate is issued, you can verify it:
|
||||
|
||||
```bash
|
||||
# Verify the certificate chain
|
||||
curl -v https://whoami.swarm.localhost/ 2>&1 | grep -i "server certificate"
|
||||
```
|
||||
|
||||
You should see that your certificate is issued by Let's Encrypt.
|
||||
|
||||
## Configure Sticky Sessions
|
||||
|
||||
Sticky sessions ensure that a user's requests always go to the same backend server, which is essential for applications that maintain session state. Let's implement sticky sessions for our whoami service.
|
||||
|
||||
Docker Swarm already has multiple replicas running; we'll now add sticky session configuration. Update your whoami service in the `docker-compose.yml` file:
|
||||
|
||||
### Add Sticky Session Configuration
|
||||
|
||||
Add the following labels to your whoami service in the `docker-compose.yml` file:
|
||||
|
||||
```yaml
|
||||
deploy:
|
||||
# ... existing configuration ...
|
||||
labels:
|
||||
# ... existing labels ...
|
||||
|
||||
# Sticky Sessions Configuration
|
||||
- "traefik.http.services.whoami.loadbalancer.sticky.cookie=true"
|
||||
- "traefik.http.services.whoami.loadbalancer.sticky.cookie.name=sticky_cookie"
|
||||
- "traefik.http.services.whoami.loadbalancer.sticky.cookie.secure=true"
|
||||
- "traefik.http.services.whoami.loadbalancer.sticky.cookie.httpOnly=true"
|
||||
```
|
||||
|
||||
Apply the changes:
|
||||
|
||||
```bash
|
||||
docker stack deploy -c docker-compose.yml traefik
|
||||
```
|
||||
|
||||
### Test Sticky Sessions
|
||||
|
||||
You can test the sticky sessions by making multiple requests and observing that they all go to the same backend container:
|
||||
|
||||
```bash
|
||||
# First request - save cookies to a file
|
||||
curl -k -c cookies.txt -H "Host: whoami.swarm.localhost" https://localhost/
|
||||
|
||||
# Subsequent requests - use the cookies
|
||||
curl -k -b cookies.txt -H "Host: whoami.swarm.localhost" https://localhost/
|
||||
curl -k -b cookies.txt -H "Host: whoami.swarm.localhost" https://localhost/
|
||||
```
|
||||
|
||||
Pay attention to the `Hostname` field in each response - it should remain the same across all requests when using the cookie file, confirming that sticky sessions are working.
|
||||
|
||||
For comparison, try making requests without the cookie:
|
||||
|
||||
```bash
|
||||
# Requests without cookies should be load-balanced across different containers
|
||||
curl -k -H "Host: whoami.swarm.localhost" https://localhost/
|
||||
curl -k -H "Host: whoami.swarm.localhost" https://localhost/
|
||||
```
|
||||
|
||||
You should see different `Hostname` values in these responses, as each request is load-balanced to a different container.
|
||||
|
||||
!!! important "Browser Testing"
|
||||
When testing in browsers, you need to use the same browser session to maintain the cookie. The cookie is set with `httpOnly` and `secure` flags for security, so it will only be sent over HTTPS connections and won't be accessible via JavaScript.
|
||||
|
||||
For more advanced configuration options, see the [reference documentation](../../reference/routing-configuration/http/load-balancing/service.md).
|
||||
|
||||
## Multi-Layer Routing
|
||||
|
||||
Multi-layer routing enables hierarchical relationships between routers, where parent routers can process requests through middleware before child routers make final routing decisions. This is particularly useful for authentication-based routing or staged middleware application.
|
||||
|
||||
!!! info "Provider Requirement"
|
||||
Multi-layer routing requires the File provider, as Docker Swarm labels do not support the `parentRefs` field. However, you can use **both Docker Swarm and File providers together** - Swarm labels for service discovery and File configuration for multi-layer routing.
|
||||
|
||||
### Setup Multi-Layer Routing with Docker Swarm
|
||||
|
||||
To use multi-layer routing with Docker Swarm, you need to enable the File provider alongside the Docker provider.
|
||||
|
||||
Update your Traefik service in `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v3.4
|
||||
command:
|
||||
- "--api.dashboard=true"
|
||||
- "--providers.docker.swarmMode=true"
|
||||
- "--providers.docker.exposedbydefault=false"
|
||||
- "--providers.docker.network=traefik_proxy"
|
||||
- "--providers.file.directory=/etc/traefik/dynamic" # Enable File provider
|
||||
- "--entrypoints.web.address=:80"
|
||||
- "--entrypoints.websecure.address=:443"
|
||||
- "--entrypoints.websecure.http.tls=true"
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
configs:
|
||||
- source: mlr-config
|
||||
target: /etc/traefik/dynamic/mlr.yml
|
||||
networks:
|
||||
- traefik_proxy
|
||||
deploy:
|
||||
placement:
|
||||
constraints:
|
||||
- node.role == manager
|
||||
|
||||
configs:
|
||||
mlr-config:
|
||||
file: ./dynamic/mlr.yml
|
||||
|
||||
networks:
|
||||
traefik_proxy:
|
||||
external: true
|
||||
```
|
||||
|
||||
### Authentication-Based Routing Example
|
||||
|
||||
Let's create a multi-layer routing setup where a parent router authenticates requests, and child routers direct traffic based on user roles.
|
||||
|
||||
First, keep your Docker Swarm services defined with labels as usual:
|
||||
|
||||
```yaml
|
||||
# In docker-compose.yml
|
||||
services:
|
||||
# ... traefik service from above ...
|
||||
|
||||
# Admin backend service
|
||||
admin-backend:
|
||||
image: traefik/whoami
|
||||
networks:
|
||||
- traefik_proxy
|
||||
environment:
|
||||
- WHOAMI_NAME=Admin Backend
|
||||
deploy:
|
||||
replicas: 2
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.admin-backend.loadbalancer.server.port=80"
|
||||
|
||||
# User backend service
|
||||
user-backend:
|
||||
image: traefik/whoami
|
||||
networks:
|
||||
- traefik_proxy
|
||||
environment:
|
||||
- WHOAMI_NAME=User Backend
|
||||
deploy:
|
||||
replicas: 2
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.services.user-backend.loadbalancer.server.port=80"
|
||||
```
|
||||
|
||||
Now create the multi-layer routing configuration in a file. Create `dynamic/mlr.yml`:
|
||||
|
||||
```yaml
|
||||
http:
|
||||
routers:
|
||||
# Parent router with authentication middleware
|
||||
api-parent:
|
||||
rule: "Host(`api.swarm.localhost`) && PathPrefix(`/api`)"
|
||||
middlewares:
|
||||
- auth-middleware
|
||||
entryPoints:
|
||||
- websecure
|
||||
# Note: No service and no TLS config - this is a parent router
|
||||
|
||||
# Child router for admin users
|
||||
api-admin:
|
||||
rule: "HeadersRegexp(`X-Auth-User`, `admin`)"
|
||||
service: admin-backend@swarm # Reference Swarm service
|
||||
parentRefs:
|
||||
- api-parent@file # Explicit reference to parent in file provider
|
||||
|
||||
# Child router for regular users
|
||||
api-user:
|
||||
rule: "HeadersRegexp(`X-Auth-User`, `user`)"
|
||||
service: user-backend@swarm # Reference Swarm service
|
||||
parentRefs:
|
||||
- api-parent@file # Explicit reference to parent in file provider
|
||||
|
||||
middlewares:
|
||||
auth-middleware:
|
||||
basicAuth:
|
||||
users:
|
||||
- "admin:$apr1$DmXR3Add$wfdbGw6RWIhFb0ffXMM4d0"
|
||||
- "user:$apr1$GJtcIY1o$mSLdsWYeXpPHVsxGDqadI."
|
||||
headerField: X-Auth-User
|
||||
```
|
||||
|
||||
!!! note "Generating Password Hashes"
|
||||
The password hashes above are generated using `htpasswd`. To create your own user credentials:
|
||||
|
||||
```bash
|
||||
# Using htpasswd (Apache utils)
|
||||
htpasswd -nb admin yourpassword
|
||||
```
|
||||
|
||||
!!! important "Cross-Provider References"
|
||||
Notice the `@swarm` suffix on service names and the `@file` suffix in `parentRefs`. When using the File provider to orchestrate multi-layer routing with Swarm services:
|
||||
|
||||
- Use `service-name@swarm` to reference Swarm services
|
||||
- Use `parent-name@file` in `parentRefs` to reference the parent router in the File provider
|
||||
|
||||
The `@provider` suffix tells Traefik which provider namespace to look in for the resource.
|
||||
|
||||
Deploy the stack:
|
||||
|
||||
```bash
|
||||
docker stack deploy -c docker-compose.yml traefik
|
||||
```
|
||||
|
||||
### Test Multi-Layer Routing
|
||||
|
||||
Test the routing behavior:
|
||||
|
||||
```bash
|
||||
# Request goes through parent router → auth middleware → admin child router
|
||||
curl -k -u admin:test -H "Host: api.swarm.localhost" https://localhost/api
|
||||
```
|
||||
|
||||
You should see the response from the admin-backend service when authenticating as `admin`. Try with `user:test` credentials to reach the user-backend service instead.
|
||||
|
||||
### How It Works
|
||||
|
||||
1. **Request arrives** at `api.swarm.localhost/api`
|
||||
2. **Parent router** (`api-parent`) matches based on host and path
|
||||
3. **BasicAuth middleware** authenticates the user and sets the `X-Auth-User` header with the username
|
||||
4. **Child router** (`api-admin` or `api-user`) matches based on the header value
|
||||
5. **Request forwarded** to the appropriate Swarm service
|
||||
|
||||
For more details about multi-layer routing, see the [Multi-Layer Routing documentation](../../reference/routing-configuration/http/routing/multi-layer-routing.md).
|
||||
|
||||
## Service Middlewares
|
||||
|
||||
Service middlewares allow you to apply middleware to a service rather than to individual routers. This means the middleware takes effect for all requests handled by the service, regardless of which router forwards the request.
|
||||
|
||||
This is useful when you want to apply the same middleware (like headers, rate limiting, or authentication) to all traffic reaching a service without having to configure it on each router.
|
||||
|
||||
### When to Use Service Middlewares
|
||||
|
||||
Use service middlewares when:
|
||||
|
||||
- Multiple routers forward traffic to the same service, and all should have the same middleware applied
|
||||
- You want to ensure a middleware is always applied to a service regardless of how traffic reaches it
|
||||
- You're centralizing middleware configuration at the service level for easier management
|
||||
|
||||
### Add Service Middleware Labels
|
||||
|
||||
Add the following labels to your whoami service deployment section in `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
whoami:
|
||||
image: traefik/whoami
|
||||
networks:
|
||||
- traefik_proxy
|
||||
deploy:
|
||||
replicas: 2
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.whoami.rule=Host(`whoami.swarm.localhost`)"
|
||||
- "traefik.http.routers.whoami.entrypoints=websecure"
|
||||
- "traefik.http.routers.whoami.tls=true"
|
||||
# Define the middleware
|
||||
- "traefik.http.middlewares.service-headers.headers.customRequestHeaders.X-Service-Middleware=applied"
|
||||
# Attach middleware at the SERVICE level (not the router level)
|
||||
- "traefik.http.services.whoami.middlewares=service-headers"
|
||||
- "traefik.http.services.whoami.loadbalancer.server.port=80"
|
||||
```
|
||||
|
||||
!!! info "Service-Level vs Router-Level Middlewares"
|
||||
|
||||
- **Router-level middleware** (`traefik.http.routers.<name>.middlewares`): Applied only when traffic matches that specific router's rule
|
||||
- **Service-level middleware** (`traefik.http.services.<name>.middlewares`): Applied to all traffic reaching the service, regardless of which router forwarded it
|
||||
|
||||
When both are configured, router middlewares execute first, followed by service middlewares.
|
||||
|
||||
Deploy the stack:
|
||||
|
||||
```bash
|
||||
docker stack deploy -c docker-compose.yml traefik
|
||||
```
|
||||
|
||||
### Test Service Middleware
|
||||
|
||||
Verify the service middleware is working:
|
||||
|
||||
```bash
|
||||
curl -k -H "Host: whoami.swarm.localhost" https://localhost/
|
||||
```
|
||||
|
||||
In the response from whoami, you should see the custom header that was added by the service middleware:
|
||||
|
||||
```text
|
||||
X-Service-Middleware: applied
|
||||
```
|
||||
|
||||
For more details on service middlewares, see the [reference documentation](../../reference/routing-configuration/http/load-balancing/service.md#middlewares).
|
||||
|
||||
## Conclusion
|
||||
|
||||
In this advanced guide, you've learned how to:
|
||||
|
||||
- Add security with middlewares like secure headers and IP allow listing
|
||||
- Automate certificate management with Let's Encrypt
|
||||
- Implement sticky sessions for stateful applications
|
||||
- Setup multi-layer routing for authentication-based routing
|
||||
- Apply middlewares at the service level for centralized middleware management
|
||||
|
||||
These advanced capabilities allow you to build production-ready Traefik deployments with Docker Swarm. Each of these can be further customized to meet your specific requirements.
|
||||
|
||||
### Next Steps
|
||||
|
||||
Now that you've mastered both basic and advanced Traefik features with Docker Swarm, you might want to explore:
|
||||
|
||||
- [Advanced routing options](../../reference/routing-configuration/http/routing/rules-and-priority.md) like query parameter matching, header-based routing, and more
|
||||
- [Additional middlewares](../../reference/routing-configuration/http/middlewares/overview.md) for authentication, rate limiting, and request modifications
|
||||
- [Observability features](../../reference/install-configuration/observability/metrics.md) for monitoring and debugging your Traefik deployment
|
||||
- [TCP services](../../reference/routing-configuration/tcp/service.md) for exposing TCP services
|
||||
- [UDP services](../../reference/routing-configuration/udp/service.md) for exposing UDP services
|
||||
- [Docker Swarm provider documentation](../../reference/install-configuration/providers/swarm.md) for more details about the Docker Swarm integration
|
||||
@@ -0,0 +1,191 @@
|
||||
# Exposing Services with Traefik on Docker Swarm - Basic
|
||||
|
||||
This guide will help you get started with exposing your services through Traefik Proxy using Docker Swarm. You'll learn the fundamentals of routing HTTP traffic, setting up path-based routing, and securing your services with TLS.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Docker Swarm cluster initialized
|
||||
- Basic understanding of Docker Swarm concepts
|
||||
- Traefik deployed using the [Traefik Docker Swarm Setup guide](../../setup/swarm.md)
|
||||
|
||||
|
||||
## Expose Your First HTTP Service
|
||||
|
||||
Let's expose a simple HTTP service using the [whoami](https://hub.docker.com/r/traefik/whoami) application. This will demonstrate basic routing to a backend service.
|
||||
|
||||
First, update your existing `docker-compose.yml` file if you haven't already:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
whoami:
|
||||
image: traefik/whoami
|
||||
networks:
|
||||
- traefik_proxy
|
||||
deploy:
|
||||
replicas: 3
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.whoami.rule=Host(`whoami.swarm.localhost`)"
|
||||
- "traefik.http.routers.whoami.entrypoints=web,websecure"
|
||||
```
|
||||
|
||||
Save this as `docker-compose.yml` and deploy the stack:
|
||||
|
||||
```bash
|
||||
docker stack deploy -c docker-compose.yml traefik
|
||||
```
|
||||
|
||||
### Verify Your Service
|
||||
|
||||
Your service is now available at http://whoami.swarm.localhost/. Test that it works:
|
||||
|
||||
```bash
|
||||
curl -H "Host: whoami.swarm.localhost" http://localhost/
|
||||
```
|
||||
|
||||
You should see output similar to:
|
||||
|
||||
```bash
|
||||
Hostname: whoami.1.7c8f7tr56q3p949rscxrkp80e
|
||||
IP: 127.0.0.1
|
||||
IP: ::1
|
||||
IP: 10.0.1.8
|
||||
IP: fe80::215:5dff:fe00:c9e
|
||||
RemoteAddr: 10.0.1.2:45098
|
||||
GET / HTTP/1.1
|
||||
Host: whoami.swarm.localhost
|
||||
User-Agent: curl/7.68.0
|
||||
Accept: */*
|
||||
Accept-Encoding: gzip
|
||||
X-Forwarded-For: 10.0.1.1
|
||||
X-Forwarded-Host: whoami.swarm.localhost
|
||||
X-Forwarded-Port: 80
|
||||
X-Forwarded-Proto: http
|
||||
X-Forwarded-Server: 5789f594e7d5
|
||||
X-Real-Ip: 10.0.1.1
|
||||
```
|
||||
|
||||
This confirms that Traefik is successfully routing requests to your whoami application.
|
||||
|
||||
## Add Routing Rules
|
||||
|
||||
Now we'll enhance our routing by directing traffic to different services based on [URL paths](../../reference/routing-configuration/http/routing/rules-and-priority.md#path-pathprefix-and-pathregexp). This is useful for API versioning, frontend/backend separation, or organizing microservices.
|
||||
|
||||
Update your `docker-compose.yml` to add another service:
|
||||
|
||||
```yaml
|
||||
# ...
|
||||
|
||||
# New service
|
||||
whoami-api:
|
||||
image: traefik/whoami
|
||||
networks:
|
||||
- traefik_proxy
|
||||
environment:
|
||||
- WHOAMI_NAME=API Service
|
||||
deploy:
|
||||
replicas: 2
|
||||
labels:
|
||||
- "traefik.enable=true"
|
||||
# Path-based routing
|
||||
- "traefik.http.routers.whoami-api.rule=Host(`whoami.swarm.localhost`) && PathPrefix(`/api`)"
|
||||
- "traefik.http.routers.whoami-api.entrypoints=web,websecure"
|
||||
- "traefik.http.routers.whoami-api.service=whoami-api-svc"
|
||||
- "traefik.http.services.whoami-api-svc.loadbalancer.server.port=80"
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
Apply the changes:
|
||||
|
||||
```bash
|
||||
docker stack deploy -c docker-compose.yml traefik
|
||||
```
|
||||
|
||||
### Test the Path-Based Routing
|
||||
|
||||
Verify that different paths route to different services:
|
||||
|
||||
```bash
|
||||
# Root path should go to the main whoami service
|
||||
curl -H "Host: whoami.swarm.localhost" http://localhost/
|
||||
|
||||
# /api path should go to the whoami-api service
|
||||
curl -H "Host: whoami.swarm.localhost" http://localhost/api
|
||||
```
|
||||
|
||||
For the `/api` requests, you should see the response showing "API Service" in the environment variables section, confirming that your path-based routing is working correctly.
|
||||
|
||||
## Enable TLS
|
||||
|
||||
Let's secure our service with HTTPS by adding TLS. We'll start with a self-signed certificate for local development.
|
||||
|
||||
### Create a Self-Signed Certificate
|
||||
|
||||
Generate a self-signed certificate and dynamic config file to tell Traefik where the cert lives:
|
||||
|
||||
```bash
|
||||
mkdir -p certs
|
||||
|
||||
# key + cert (valid for one year)
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
-keyout certs/local.key -out certs/local.crt \
|
||||
-subj "/CN=*.swarm.localhost"
|
||||
|
||||
# dynamic config that tells Traefik where the cert lives
|
||||
cat > certs/tls.yml <<'EOF'
|
||||
tls:
|
||||
certificates:
|
||||
- certFile: /certificates/local.crt
|
||||
keyFile: /certificates/local.key
|
||||
EOF
|
||||
```
|
||||
|
||||
Create a Docker config for the certificate files:
|
||||
|
||||
```bash
|
||||
docker config create swarm-cert.crt certs/local.crt
|
||||
docker config create swarm-cert.key certs/local.key
|
||||
docker config create swarm-tls.yml certs/tls.yml
|
||||
```
|
||||
|
||||
Update your `docker-compose.yml` file with the following changes:
|
||||
|
||||
```yaml
|
||||
# Add to the Traefik command section:
|
||||
command:
|
||||
# ... existing commands ...
|
||||
- "--entryPoints.websecure.address=:443"
|
||||
- "--entryPoints.websecure.http.tls=true"
|
||||
- "--providers.file.directory=/etc/traefik/dynamic"
|
||||
```
|
||||
|
||||
```yaml
|
||||
# Add to the root of your docker-compose.yml file:
|
||||
configs:
|
||||
swarm-cert.crt:
|
||||
file: ./certs/local.crt
|
||||
swarm-cert.key:
|
||||
file: ./certs/local.key
|
||||
swarm-tls.yml:
|
||||
file: ./certs/tls.yml
|
||||
```
|
||||
|
||||
Deploy the stack:
|
||||
|
||||
```bash
|
||||
docker stack deploy -c docker-compose.yml traefik
|
||||
```
|
||||
|
||||
Your browser can access https://whoami.swarm.localhost/ for the service. You'll need to accept the security warning for the self-signed certificate.
|
||||
|
||||
## Next Steps
|
||||
|
||||
Now that you've mastered the basics of exposing services with Traefik on Docker Swarm, you're ready to explore more advanced features like middlewares, Let's Encrypt certificates, sticky sessions, and multi-layer routing.
|
||||
|
||||
Continue to the [Advanced Guide](advanced.md) to learn about:
|
||||
|
||||
- Adding middlewares for security and access control
|
||||
- Generating certificates with Let's Encrypt
|
||||
- Configuring sticky sessions for stateful applications
|
||||
- Setting up multi-layer routing for authentication-based routing
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
title: Extend Traefik
|
||||
description: Extend Traefik with custom plugins using Yaegi and WebAssembly.
|
||||
---
|
||||
|
||||
# Extend Traefik
|
||||
|
||||
Plugins are a powerful feature for extending Traefik with custom features and behaviors. The [Plugin Catalog](https://plugins.traefik.io/) is a software-as-a-service (SaaS) platform that provides an exhaustive list of the existing plugins.
|
||||
|
||||
??? note "Plugin Catalog Access"
|
||||
You can reach the [Plugin Catalog](https://plugins.traefik.io/) from the Traefik Dashboard using the `Plugins` menu entry.
|
||||
|
||||
## Add a new plugin to a Traefik instance
|
||||
|
||||
To add a new plugin to a Traefik instance, you must change that instance's install (static) configuration. Each plugin's **Install** section provides an install (static) configuration example. Many plugins have their own section in the Traefik routing (dynamic) configuration.
|
||||
|
||||
!!! danger "Experimental Features"
|
||||
Plugins can change the behavior of Traefik in unforeseen ways. Exercise caution when adding new plugins to production Traefik instances.
|
||||
|
||||
To learn more about how to add a new plugin to a Traefik instance, please refer to the [developer documentation](https://plugins.traefik.io/install).
|
||||
|
||||
## Plugin Systems
|
||||
|
||||
Traefik supports two different plugin systems, each designed for different use cases and developer preferences.
|
||||
|
||||
### Yaegi Plugin System
|
||||
|
||||
Traefik [Yaegi](https://github.com/traefik/yaegi) plugins are developed using the Go language. It is essentially a Go package. Unlike pre-compiled plugins, Yaegi plugins are executed on the fly by Yaegi, a Go interpreter embedded in Traefik.
|
||||
|
||||
This approach eliminates the need for compilation and a complex toolchain, making plugin development as straightforward as creating web browser extensions. Yaegi plugins support both middleware and provider functionality.
|
||||
|
||||
#### Key characteristics
|
||||
|
||||
- Written in Go language
|
||||
- No compilation required
|
||||
- Executed by embedded interpreter
|
||||
- Supports full Go feature set
|
||||
- Hot-reloadable during development
|
||||
|
||||
### WebAssembly (WASM) Plugin System
|
||||
|
||||
Traefik WASM plugins can be developed using any language that compiles to WebAssembly (WASM). This method is based on [http-wasm](https://http-wasm.io/).
|
||||
|
||||
WASM plugins compile to portable binary modules that execute with near-native performance while maintaining security isolation.
|
||||
|
||||
#### Key characteristics
|
||||
|
||||
- Multi-language support (Go, Rust, C++, etc.)
|
||||
- Compiled to WebAssembly binary
|
||||
- Near-native performance
|
||||
- Strong security isolation
|
||||
- Currently supports middleware only
|
||||
|
||||
## Build Your Own Plugins
|
||||
|
||||
Traefik users can create their own plugins and share them with the community using the [Plugin Catalog](https://plugins.traefik.io/). To learn more about Traefik plugin creation, please refer to the [developer documentation](https://plugins.traefik.io/create).
|
||||
@@ -0,0 +1,148 @@
|
||||
---
|
||||
title: "Traefik Product Features Comparison"
|
||||
description: "Compare features across Traefik Proxy, Traefik Hub API Gateway (including AI Gateway capabilities), and Traefik Hub API Management to choose the right solution for your needs."
|
||||
---
|
||||
|
||||
# Traefik Product Features Comparison
|
||||
|
||||
The Traefik ecosystem offers multiple products designed to meet different requirements, from basic reverse proxy functionality to comprehensive API management and AI gateway capabilities. This comparison matrix helps you understand the features available in each product and choose the right solution for your use case.
|
||||
|
||||
## Product Overview
|
||||
|
||||
- **Traefik Proxy** is the open-source application proxy that serves as the foundation for all Traefik products. It provides essential reverse proxy, load balancing, and service discovery capabilities.
|
||||
|
||||
- **[Traefik Hub API Gateway](https://traefik.io/solutions/api-gateway/)** builds on Traefik Proxy with enterprise-grade security, distributed features, and advanced access control for cloud-native API gateway scenarios. It includes **AI Gateway capabilities** that transform any AI endpoint into a managed API.
|
||||
|
||||
- **[Traefik Hub API Management](https://traefik.io/solutions/api-management/)** adds comprehensive API lifecycle management, developer portals, and organizational features for teams managing multiple APIs across environments.
|
||||
|
||||
- **[Traefik AI Gateway](https://traefik.io/solutions/ai-gateway/)** transforms any AI endpoint into a managed API with unified access to multiple LLMs, centralized credential management, semantic caching, local inferencing, and comprehensive AI governance features.
|
||||
|
||||
- **[Traefik MCP Gateway](https://traefik.io/solutions/mcp-gateway/)** provides secure, governed access to Model Context Protocol (MCP) servers for AI agents with task-based access control (TBAC), session-smart routing, and comprehensive audit capabilities for enterprise AI workflows.
|
||||
|
||||
## Features Matrix
|
||||
|
||||
| Feature | Traefik Proxy | Traefik Hub API Gateway | Traefik Hub API Management |
|
||||
|---------|---------------|------------------------|---------------------------|
|
||||
| **Core Networking** | | | |
|
||||
| Services Auto-Discovery | ✓ | ✓ | ✓ |
|
||||
| Graceful Configuration Reload | ✓ | ✓ | ✓ |
|
||||
| Websockets, HTTP/2, HTTP/3, TCP, UDP, GRPC | ✓ | ✓ | ✓ |
|
||||
| Real-time Logs, Access Logs, Metrics & Distributed Tracing | ✓ | ✓ | ✓ |
|
||||
| Canary Deployments | ✓ | ✓ | ✓ |
|
||||
| Let's Encrypt | ✓ | ✓ | ✓ |
|
||||
| **Plugin Ecosystem** | | | |
|
||||
| [Plugin Support](https://plugins.traefik.io/plugins) ([Go](https://github.com/traefik/yaegi), [WASM](https://webassembly.org/)) | ✓ | ✓ | ✓ |
|
||||
| **Deployment & Operations** | | | |
|
||||
| Hybrid cloud, multi-cloud & on-prem compatible | ✓ | ✓ | ✓ |
|
||||
| Per-cluster dashboard | ✓ | ✓ | ✓ |
|
||||
| GitOps-native declarative configuration | ✓ | ✓ | ✓ |
|
||||
| **Authentication & Authorization** | | | |
|
||||
| JWT Authentication | ✗ | ✓ | ✓ |
|
||||
| OAuth 2.0 Token Introspection Authentication | ✗ | ✓ | ✓ |
|
||||
| OAuth 2.0 Client Credentials Authentication | ✗ | ✓ | ✓ |
|
||||
| OpenID Connect Authentication | ✗ | ✓ | ✓ |
|
||||
| Lightweight Directory Access Protocol (LDAP) | ✗ | ✓ | ✓ |
|
||||
| API Key Authentication | ✗ | ✓ | ✓ |
|
||||
| **Security & Policy** | | | |
|
||||
| Open Policy Agent | ✗ | ✓ | ✓ |
|
||||
| Native Coraza Web Application Firewall (WAF) | ✗ | ✓ | ✓ |
|
||||
| HashiCorp Vault Integration | ✗ | ✓ | ✓ |
|
||||
| **Distributed Features** | | | |
|
||||
| Distributed Let's Encrypt | ✗ | ✓ | ✓ |
|
||||
| Distributed Rate Limit | ✗ | ✓ | ✓ |
|
||||
| HTTP Caching | ✗ | ✓ | ✓ |
|
||||
| **Compliance** | | | |
|
||||
| FIPS 140-2 Compliance (Linux & Windows) | ✗ | ✓ | ✓ |
|
||||
| **AI Gateway Capabilities** | | | |
|
||||
| Unified Multi-LLM API Access | ✗ | ✓ | ✓ |
|
||||
| Centralized AI Credential Management | ✗ | ✓ | ✓ |
|
||||
| AI Provider Flexibility (OpenAI, Anthropic, Azure OpenAI, AWS Bedrock, etc.) | ✗ | ✓ | ✓ |
|
||||
| Semantic Caching for AI Responses | ✗ | ✓ | ✓ |
|
||||
| Content Guard & PII Protection | ✗ | ✓ | ✓ |
|
||||
| AI-specific Observability & OpenTelemetry Integration | ✗ | ✓ | ✓ |
|
||||
| Support for Local/Self-hosted LLMs & Inference (Ollama, Mistral, etc.) | ✗ | ✓ | ✓ |
|
||||
| **MCP Gateway Capabilities** | | | |
|
||||
| Task-Based Access Control (TBAC) for AI Agents | ✗ | ✓ | ✓ |
|
||||
| MCP Servers Governance | ✗ | ✓ | ✓ |
|
||||
| Session-Smart Load Balancing for Agent Workflows | ✗ | ✓ | ✓ |
|
||||
| OAuth 2.1 / 2.0 Resource Server for MCP | ✗ | ✓ | ✓ |
|
||||
| Fine-grained Policy Enforcement for AI Tools | ✗ | ✓ | ✓ |
|
||||
| Audit-ready Observability for Agent Interactions | ✗ | ✓ | ✓ |
|
||||
| **API Management** | | | |
|
||||
| Flexible API grouping and versioning | ✗ | ✗ | ✓ |
|
||||
| API Developer Portal | ✗ | ✗ | ✓ |
|
||||
| OpenAPI Specifications Support | ✗ | ✗ | ✓ |
|
||||
| Multi-cluster dashboard | ✗ | ✗ | ✓ |
|
||||
| Built-in identity provider (or use your own) | ✗ | ✗ | ✓ |
|
||||
| Configuration linter & change impact analysis | ✗ | ✗ | ✓ |
|
||||
| Pre-built Grafana dashboards | ✗ | ✗ | ✓ |
|
||||
| Event correlation for quick incident mitigation | ✗ | ✗ | ✓ |
|
||||
| Traffic debugger | ✗ | ✓ | ✓ |
|
||||
| **Support** | | | |
|
||||
| Built-In Commercial Support | Add-on | ✓ | ✓ |
|
||||
|
||||
## Choosing the Right Product
|
||||
|
||||
### Start with Traefik Proxy
|
||||
|
||||
Traefik Proxy is the ideal starting point for organizations looking for a reliable, open-source application proxy with essential networking capabilities. Deploy it as your default ingress tier if you need:
|
||||
|
||||
- Basic reverse proxy and load balancing
|
||||
- Service discovery for containerized applications
|
||||
- Simple TLS termination and Let's Encrypt integration
|
||||
- Cost-effective solution with community support (can upgrade to Traefik Hub for more features)
|
||||
|
||||
### Upgrade to Traefik Hub API Gateway
|
||||
|
||||
Traefik Hub API Gateway layers enterprise security, distributed coordination, and AI Gateway capabilities on top of Traefik Proxy. Upgrade to it when you need:
|
||||
|
||||
- Enterprise security requirements (JWT, OIDC, LDAP)
|
||||
- Distributed deployments across multiple clusters
|
||||
- Advanced rate limiting and caching
|
||||
- WAF and policy enforcement
|
||||
- AI Gateway capabilities
|
||||
- Commercial support
|
||||
|
||||
### Consider Traefik AI Gateway
|
||||
|
||||
Traefik AI Gateway unifies hosted and self-hosted LLM access under centralized control and observability. Consider it if you have:
|
||||
|
||||
- Multi-LLM applications requiring unified API access
|
||||
- Organizations using multiple AI providers (OpenAI, Anthropic, Azure OpenAI, AWS Bedrock, etc.)
|
||||
- Local/self-hosted LLM deployments (Ollama, Mistral)
|
||||
- Centralized AI credential and security management
|
||||
- Cost optimization through semantic caching
|
||||
- PII protection and content filtering for AI interactions
|
||||
- Comprehensive AI observability and compliance requirements
|
||||
|
||||
### Choose Traefik MCP Gateway
|
||||
|
||||
Traefik MCP Gateway governs how AI agents interact with Model Context Protocol servers through task-aware policies and session-smart routing. Choose it if you need:
|
||||
|
||||
- AI agent deployments requiring secure access to MCP servers
|
||||
- Task-based access control (TBAC) for AI workflows
|
||||
- Governance of Model Context Protocol interactions
|
||||
- Session-smart routing for long-running agent conversations
|
||||
- OAuth 2.1 / 2.0 compliant MCP server protection
|
||||
- Audit-ready observability for AI agent activities
|
||||
- Fine-grained policy enforcement for AI tools and resources
|
||||
|
||||
### Choose Traefik Hub API Management
|
||||
|
||||
Traefik Hub API Management extends the gateway foundation with API lifecycle tooling, developer experience features, and governance workflows. Choose it when you have:
|
||||
|
||||
- Multiple APIs requiring centralized management
|
||||
- Developer teams needing self-service portals
|
||||
- Complex API versioning and lifecycle requirements
|
||||
- Multi-cluster environments requiring unified dashboards
|
||||
- Compliance and governance needs
|
||||
|
||||
## Migration Path
|
||||
|
||||
The Traefik ecosystem is designed for seamless upgrades. You can start with Traefik Proxy and add capabilities as your requirements grow:
|
||||
|
||||
1. **Traefik Proxy** → **Hub API Gateway**: Add enterprise security, distributed features, and AI Gateway capabilities
|
||||
2. **Hub API Gateway** → **Hub API Management**: Add comprehensive API management and governance features
|
||||
3. **MCP Gateway**: Specialized solution for AI agent governance and Model Context Protocol management
|
||||
|
||||
All products share the same core configuration concepts, making migration straightforward while preserving your existing configurations and operational knowledge.
|
||||
@@ -1,60 +0,0 @@
|
||||
---
|
||||
title: Concepts
|
||||
description: Traefik - base concepts and main features
|
||||
---
|
||||
|
||||
# Concepts
|
||||
|
||||
This page explains the base concepts of Traefik.
|
||||
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
Traefik is based on the concept of EntryPoints, Routers, Middlewares and Services.
|
||||
|
||||
The main features include dynamic configuration, automatic service discovery, and support for multiple backends and protocols.
|
||||
|
||||
1. [EntryPoints](../routing/entrypoints.md "Link to docs about EntryPoints"): EntryPoints are the network entry points into Traefik. They define the port which will receive the packets, and whether to listen for TCP or UDP.
|
||||
|
||||
2. [Routers](../routing/routers/index.md "Link to docs about routers"): A router is in charge of connecting incoming requests to the services that can handle them.
|
||||
|
||||
3. [Middlewares](../middlewares/overview.md "Link to docs about middlewares"): Attached to the routers, middlewares can modify the requests or responses before they are sent to your service
|
||||
|
||||
4. [Services](../routing/services/index.md "Link to docs about services"): Services are responsible for configuring how to reach the actual services that will eventually handle the incoming requests.
|
||||
|
||||
## Edge Router
|
||||
|
||||
Traefik is an *Edge Router*; this means that it's the door to your platform, and that it intercepts and routes every incoming request:
|
||||
it knows all the logic and every [rule](../routing/routers/index.md#rule "Link to docs about routing rules") that determine which services handle which requests (based on the *path*, the *host*, *headers*, etc.).
|
||||
|
||||

|
||||
|
||||
## Auto Service Discovery
|
||||
|
||||
Where traditionally edge routers (or reverse proxies) need a configuration file that contains every possible route to your services, Traefik gets them from the services themselves.
|
||||
|
||||
Deploying your services, you attach information that tells Traefik the characteristics of the requests the services can handle.
|
||||
|
||||

|
||||
|
||||
This means that when a service is deployed, Traefik detects it immediately and updates the routing rules in real time.
|
||||
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.
|
||||
|
||||
!!! info "Many different rules"
|
||||
|
||||
In the example above, we used the request [path rule](../routing/routers/index.md#rule "Link to docs about routing rules") to determine which service was in charge.
|
||||
Certainly, you can use many other different [rules](../routing/routers/index.md#rule "Link to docs about routing rules").
|
||||
|
||||
!!! info "Updating the requests"
|
||||
|
||||
In the [middleware](../middlewares/overview.md "Link to middleware documentation") section, you can learn about how to update the requests before forwarding them to 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 "Link to overview about Traefik providers") because they *provide* the configuration to Traefik.
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
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."
|
||||
description: "Get started with Traefik Proxy. This page will introduce you to the routing and install configurations. Read the technical documentation."
|
||||
---
|
||||
|
||||
# Configuration Introduction
|
||||
@@ -8,39 +8,37 @@ description: "Get started with Traefik Proxy. This page will introduce you to th
|
||||
How the Magic Happens
|
||||
{: .subtitle }
|
||||
|
||||

|
||||
|
||||
Configuration in Traefik can refer to two different things:
|
||||
|
||||
- The fully dynamic routing configuration (referred to as the _dynamic configuration_)
|
||||
- The startup configuration (referred to as the _static configuration_)
|
||||
- The install (startup) configuration (formerly known as the _static configuration_)
|
||||
- The routing configuration (formerly known as the _dynamic configuration_)
|
||||
|
||||
Elements in the _static configuration_ set up connections to [providers](../providers/overview.md) and define the [entrypoints](../routing/entrypoints.md) Traefik will listen to (these elements don't change often).
|
||||
Elements in the _install configuration_ set up connections to [providers](../reference/install-configuration/providers/overview.md) and define the [entrypoints](../reference/install-configuration/entrypoints.md) Traefik will listen to (these elements don't change often).
|
||||
|
||||
The _dynamic configuration_ contains everything that defines how the requests are handled by your system.
|
||||
The _routing configuration_ contains everything that defines how the requests are handled by your system.
|
||||
This configuration can change and is seamlessly hot-reloaded, without any request interruption or connection loss.
|
||||
|
||||
!!! warning "Incompatible Configuration"
|
||||
Please be aware that the old configurations for Traefik v1.x are NOT compatible with the v2.x config as of now.
|
||||
If you are running v2, please ensure you are using a v2 configuration.
|
||||
|
||||
## The Dynamic Configuration
|
||||
## The Routing Configuration
|
||||
|
||||
Traefik gets its _dynamic configuration_ from [providers](../providers/overview.md): whether an orchestrator, a service registry, or a plain old configuration file.
|
||||
Traefik gets its _routing configuration_ from [providers](../reference/install-configuration/providers/overview.md): whether an orchestrator, a service registry, or a plain old configuration file.
|
||||
|
||||
Since this configuration is specific to your infrastructure choices, we invite you to refer to the [dedicated section of this documentation](../routing/overview.md).
|
||||
Since this configuration is specific to your infrastructure choices, we invite you to refer to the [dedicated section of this documentation](../reference/routing-configuration/dynamic-configuration-methods.md).
|
||||
|
||||
!!! info ""
|
||||
|
||||
In the [Quick Start example](../getting-started/quick-start.md), the dynamic configuration comes from docker in the form of labels attached to your containers.
|
||||
In the [Quick Start example](./docker.md), the whoami application routing configuration comes from docker in the form of a label attached to the whoami container.
|
||||
|
||||
!!! info "HTTPS Certificates also belong to the dynamic configuration."
|
||||
!!! info "HTTPS Certificates also belong to the routing configuration."
|
||||
|
||||
You can add / update / remove them without restarting your Traefik instance.
|
||||
|
||||
## The Static Configuration
|
||||
## The Install Configuration
|
||||
|
||||
There are three different, **mutually exclusive** (i.e. you can use only one at the same time), ways to define static configuration options in Traefik:
|
||||
There are three different, **mutually exclusive** (i.e. you can use only one at the same time), ways to define install configuration options in Traefik:
|
||||
|
||||
1. In a configuration file
|
||||
1. In the command-line arguments
|
||||
@@ -56,7 +54,7 @@ Once positioned, this option sets (and resets) all the default values of the sub
|
||||
|
||||
### Configuration File
|
||||
|
||||
At startup, Traefik searches for static configuration in a file named `traefik.yml` (or `traefik.yaml` or `traefik.toml`) in:
|
||||
At startup, Traefik searches for install configuration in a file named `traefik.yml` (or `traefik.yaml` or `traefik.toml`) in:
|
||||
|
||||
- `/etc/traefik/`
|
||||
- `$XDG_CONFIG_HOME/`
|
||||
@@ -79,19 +77,19 @@ traefik --help
|
||||
# or
|
||||
|
||||
docker run traefik[:version] --help
|
||||
# ex: docker run traefik:v3.4 --help
|
||||
# ex: docker run traefik:v3.7 --help
|
||||
```
|
||||
|
||||
Check the [CLI reference](../reference/static-configuration/cli.md "Link to CLI reference overview") for an overview about all available arguments.
|
||||
Check the [CLI reference](../reference/install-configuration/configuration-options.md "Link to CLI reference overview") for an overview about all available arguments.
|
||||
|
||||
### Environment Variables
|
||||
|
||||
All available environment variables can be found in the [static configuration environment overview](../reference/static-configuration/env.md).
|
||||
All available environment variables can be found in the [install configuration environment overview](../reference/install-configuration/configuration-options.md).
|
||||
|
||||
## Available Configuration Options
|
||||
|
||||
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](../reference/install-configuration/providers/overview.md), or the [routing section](../reference/routing-configuration/dynamic-configuration-methods.md) to see them in action.
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
{% include-markdown "includes/traefik-for-business-applications.md" %}
|
||||
|
||||
@@ -36,7 +36,7 @@ This configuration:
|
||||
# docker-compose.yml
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v3.4
|
||||
image: traefik:v3.7
|
||||
command:
|
||||
- "--api.insecure=true"
|
||||
- "--providers.docker=true"
|
||||
@@ -84,7 +84,7 @@ docker run -d \
|
||||
-p 8080:8080 \
|
||||
-v $PWD/traefik.yml:/etc/traefik/traefik.yml \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
traefik:v3.4
|
||||
traefik:v3.7
|
||||
```
|
||||
|
||||
## Expose the Dashboard
|
||||
@@ -159,4 +159,4 @@ That's it! You've successfully deployed Traefik and configured routing in Docker
|
||||
- [Enable Metrics](../reference/install-configuration/observability/metrics.md)
|
||||
- [Learn more about Docker provider](../reference/install-configuration/providers/docker.md)
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
{% include-markdown "includes/traefik-for-business-applications.md" %}
|
||||
|
||||
@@ -12,10 +12,10 @@ and while the documentation often demonstrates configuration options through fil
|
||||
the core feature of Traefik is its dynamic configurability,
|
||||
directly reacting to changes from providers over time.
|
||||
|
||||
Notably, a part of the configuration is [static](../configuration-overview/#the-static-configuration),
|
||||
Notably, the [install configuration](./configuration-overview.md#the-install-configuration) is static,
|
||||
and can be provided by a file on startup, whereas various providers,
|
||||
such as the file provider,
|
||||
contribute dynamically all along the traefik instance lifetime to its [dynamic configuration](../configuration-overview/#the-dynamic-configuration) changes.
|
||||
contribute dynamically all along the traefik instance lifetime to its [routing configuration](./configuration-overview.md#the-routing-configuration) changes.
|
||||
|
||||
In addition, the configuration englobes concepts such as the EntryPoint which can be seen as a listener on the Transport Layer (TCP),
|
||||
as apposed to the Router which is more about the Presentation (TLS) and Application layers (HTTP).
|
||||
@@ -132,8 +132,8 @@ http:
|
||||
|
||||
## Why Is My TLS Certificate Not Reloaded When Its Contents Change?
|
||||
|
||||
With the file provider,
|
||||
a configuration update is only triggered when one of the [watched](../providers/file.md#provider-configuration) configuration files is modified.
|
||||
With the [file provider](../reference/install-configuration/providers/others/file.md),
|
||||
a configuration update is only triggered when one of the watched configuration files is modified.
|
||||
|
||||
Which is why, when a certificate is defined by path,
|
||||
and the actual contents of this certificate change,
|
||||
@@ -147,23 +147,23 @@ for example, by using the `touch` command on the configuration file.
|
||||
|
||||
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 |
|
||||
| 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.
|
||||
please check out the [forwarded header](../reference/install-configuration/entrypoints.md#opt-forwardedHeaders-connection) documentation.
|
||||
|
||||
## How Traefik is Storing and Serving TLS Certificates?
|
||||
|
||||
### Storing TLS Certificates
|
||||
|
||||
[TLS](../https/tls.md "Link to Traefik TLS docs") certificates are either provided directly by the [dynamic configuration](./configuration-overview.md#the-dynamic-configuration "Link to dynamic configuration overview") from [providers](../https/tls.md#user-defined "Link to the TLS configuration"),
|
||||
or by [ACME resolvers](../https/acme.md#providers "Link to ACME resolvers"), which act themselves as providers internally.
|
||||
TLS certificates are either provided directly by the [routing configuration](../reference/routing-configuration/dynamic-configuration-methods.md "Link to routing configuration overview"),
|
||||
or by [Certificate resolvers](../reference/install-configuration/tls/certificate-resolvers/overview.md "Link to certificates resolvers").
|
||||
|
||||
For each TLS certificate, Traefik produces an identifier used as a key to store it.
|
||||
This identifier is constructed as the alphabetically ordered concatenation of the SANs `DNSNames` and `IPAddresses` of the TLScertificate.
|
||||
@@ -252,4 +252,4 @@ In which case, you should make sure your infrastructure is properly set up for a
|
||||
LEGO_DISABLE_CNAME_SUPPORT=true
|
||||
```
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
{% include-markdown "includes/traefik-for-business-applications.md" %}
|
||||
|
||||
@@ -1,147 +0,0 @@
|
||||
---
|
||||
title: "Traefik Installation Documentation"
|
||||
description: "There are several flavors to choose from when installing Traefik Proxy. Get started with Traefik Proxy, and read the technical documentation."
|
||||
---
|
||||
|
||||
# Install Traefik
|
||||
|
||||
You can install Traefik with the following flavors:
|
||||
|
||||
* [Use the official Docker image](./#use-the-official-docker-image)
|
||||
* [Use the Helm Chart](./#use-the-helm-chart)
|
||||
* [Use the binary distribution](./#use-the-binary-distribution)
|
||||
* [Compile your binary from the sources](./#compile-your-binary-from-the-sources)
|
||||
|
||||
## Use the Official Docker Image
|
||||
|
||||
Choose one of the [official Docker images](https://hub.docker.com/_/traefik) and run it with one sample configuration file:
|
||||
|
||||
* [YAML](https://raw.githubusercontent.com/traefik/traefik/v3.4/traefik.sample.yml)
|
||||
* [TOML](https://raw.githubusercontent.com/traefik/traefik/v3.4/traefik.sample.toml)
|
||||
|
||||
```shell
|
||||
docker run -d -p 8080:8080 -p 80:80 \
|
||||
-v $PWD/traefik.yml:/etc/traefik/traefik.yml traefik:v3.4
|
||||
```
|
||||
|
||||
For more details, go to the [Docker provider documentation](../providers/docker.md)
|
||||
|
||||
!!! tip
|
||||
|
||||
* Prefer a fixed version than the latest that could be an unexpected version.
|
||||
ex: `traefik:v3.4`
|
||||
* Docker images are based from the [Alpine Linux Official image](https://hub.docker.com/_/alpine).
|
||||
* Any orchestrator using docker images can fetch the official Traefik docker image.
|
||||
|
||||
## Use the Helm Chart
|
||||
|
||||
Traefik can be installed in Kubernetes using the Helm chart from <https://github.com/traefik/traefik-helm-chart>.
|
||||
|
||||
Ensure that the following requirements are met:
|
||||
|
||||
* Kubernetes 1.22+
|
||||
* Helm version 3.9+ is [installed](https://helm.sh/docs/intro/install/)
|
||||
|
||||
Add Traefik Labs chart repository to Helm:
|
||||
|
||||
```bash
|
||||
helm repo add traefik https://traefik.github.io/charts
|
||||
```
|
||||
|
||||
You can update the chart repository by running:
|
||||
|
||||
```bash
|
||||
helm repo update
|
||||
```
|
||||
|
||||
And install it with the Helm command line:
|
||||
|
||||
```bash
|
||||
helm install traefik traefik/traefik
|
||||
```
|
||||
|
||||
!!! tip "Helm Features"
|
||||
|
||||
All [Helm features](https://helm.sh/docs/intro/using_helm/) are supported.
|
||||
|
||||
Examples are provided [here](https://github.com/traefik/traefik-helm-chart/blob/master/EXAMPLES.md).
|
||||
|
||||
For instance, installing the chart in a dedicated namespace:
|
||||
|
||||
```bash tab="Install in a Dedicated Namespace"
|
||||
kubectl create ns traefik-v2
|
||||
# Install in the namespace "traefik-v2"
|
||||
helm install --namespace=traefik-v2 \
|
||||
traefik traefik/traefik
|
||||
```
|
||||
|
||||
??? example "Installing with Custom Values"
|
||||
|
||||
You can customize the installation by specifying custom values,
|
||||
as with [any helm chart](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing).
|
||||
{: #helm-custom-values }
|
||||
|
||||
All parameters are documented in the default [`values.yaml`](https://github.com/traefik/traefik-helm-chart/blob/master/traefik/values.yaml).
|
||||
|
||||
You can also set Traefik command line flags using `additionalArguments`.
|
||||
Example of installation with logging set to `DEBUG`:
|
||||
|
||||
```bash tab="Using Helm CLI"
|
||||
helm install --namespace=traefik-v2 \
|
||||
--set="additionalArguments={--log.level=DEBUG}" \
|
||||
traefik traefik/traefik
|
||||
```
|
||||
|
||||
```yml tab="With a custom values file"
|
||||
# File custom-values.yml
|
||||
## Install with "helm install --values=./custom-values.yml traefik traefik/traefik
|
||||
additionalArguments:
|
||||
- "--log.level=DEBUG"
|
||||
```
|
||||
|
||||
## Use the Binary Distribution
|
||||
|
||||
Grab the latest binary from the [releases](https://github.com/traefik/traefik/releases) page.
|
||||
|
||||
??? info "Check the integrity of the downloaded file"
|
||||
|
||||
```bash tab="Linux"
|
||||
# Compare this value to the one found in traefik-${traefik_version}_checksums.txt
|
||||
sha256sum ./traefik_${traefik_version}_linux_${arch}.tar.gz
|
||||
```
|
||||
|
||||
```bash tab="macOS"
|
||||
# Compare this value to the one found in traefik-${traefik_version}_checksums.txt
|
||||
shasum -a256 ./traefik_${traefik_version}_darwin_amd64.tar.gz
|
||||
```
|
||||
|
||||
```powershell tab="Windows PowerShell"
|
||||
# Compare this value to the one found in traefik-${traefik_version}_checksums.txt
|
||||
Get-FileHash ./traefik_${traefik_version}_windows_${arch}.zip -Algorithm SHA256
|
||||
```
|
||||
|
||||
??? info "Extract the downloaded archive"
|
||||
|
||||
```bash tab="Linux"
|
||||
tar -zxvf traefik_${traefik_version}_linux_${arch}.tar.gz
|
||||
```
|
||||
|
||||
```bash tab="macOS"
|
||||
tar -zxvf ./traefik_${traefik_version}_darwin_amd64.tar.gz
|
||||
```
|
||||
|
||||
```powershell tab="Windows PowerShell"
|
||||
Expand-Archive traefik_${traefik_version}_windows_${arch}.zip
|
||||
```
|
||||
|
||||
And run it:
|
||||
|
||||
```bash
|
||||
./traefik --help
|
||||
```
|
||||
|
||||
## Compile your Binary from the Sources
|
||||
|
||||
All the details are available in the [Contributing Guide](../contributing/building-testing.md)
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
@@ -79,7 +79,10 @@ providers:
|
||||
kubernetesGateway:
|
||||
enabled: true
|
||||
gateway:
|
||||
namespacePolicy: All
|
||||
listeners:
|
||||
web:
|
||||
namespacePolicy:
|
||||
from: All
|
||||
```
|
||||
|
||||
!!! info
|
||||
@@ -106,7 +109,7 @@ helm install traefik traefik/traefik --wait \
|
||||
--set ingressRoute.dashboard.matchRule='Host(`dashboard.localhost`)' \
|
||||
--set ingressRoute.dashboard.entryPoints={web} \
|
||||
--set providers.kubernetesGateway.enabled=true \
|
||||
--set gateway.namespacePolicy=All
|
||||
--set gateway.listeners.web.namespacePolicy.from=All
|
||||
```
|
||||
|
||||
!!! info
|
||||
@@ -247,7 +250,7 @@ To use the Gateway API:
|
||||
Install the Gateway API CRDs in your cluster:
|
||||
|
||||
```bash
|
||||
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.1/standard-install.yaml
|
||||
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.5.1/standard-install.yaml
|
||||
```
|
||||
|
||||
Create an HTTPRoute. This configuration:
|
||||
@@ -328,4 +331,4 @@ That's it! You've successfully deployed Traefik and configured routing in a Kube
|
||||
- [Learn more about Kubernetes CRD provider](../reference/install-configuration/providers/kubernetes/kubernetes-crd.md)
|
||||
- [Learn more about Kubernetes Gateway API provider](../reference/install-configuration/providers/kubernetes/kubernetes-gateway.md)
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
{% include-markdown "includes/traefik-for-business-applications.md" %}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
title: "Traefik Getting Started With Kubernetes"
|
||||
description: "Get started with Traefik Proxy and Kubernetes."
|
||||
---
|
||||
|
||||
--8<-- "content/getting-started/kubernetes.md"
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
title: "Traefik Getting Started Quickly"
|
||||
description: "Get started with Traefik Proxy and Docker."
|
||||
---
|
||||
|
||||
--8<-- "content/getting-started/docker.md"
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
title: "Govern Your APIs with Traefik Hub"
|
||||
description: "Learn how Traefik Hub provides comprehensive API Gateway and API Management capabilities to govern, secure, and manage your APIs at scale."
|
||||
---
|
||||
|
||||
# Govern Your APIs with Traefik Hub
|
||||
|
||||
Traefik Hub transforms your API infrastructure by providing enterprise-grade API Gateway and comprehensive API Management capabilities. Built on the foundation of Traefik Proxy, Hub enables organizations to govern, secure, and manage APIs at scale across cloud-native environments.
|
||||
|
||||
## API Gateway and API Management Capabilities
|
||||
|
||||
Traefik Hub offers two complementary approaches to API governance:
|
||||
|
||||
- [Traefik Hub API Gateway](https://traefik.io/solutions/api-gateway/): Enterprise-grade security, distributed features, and advanced access control for cloud-native API gateway scenarios. It includes AI Gateway capabilities for modern machine learning workloads.
|
||||
|
||||
- [Traefik Hub API Management](https://traefik.io/solutions/api-management/): Comprehensive API lifecycle management, developer portals, and organizational features for teams managing multiple APIs across environments.
|
||||
|
||||
## API Management Features
|
||||
|
||||
Traefik Hub API Management provides a complete suite of features designed to streamline API governance and accelerate developer productivity:
|
||||
|
||||
### Comprehensive API Lifecycle Management
|
||||
|
||||
- **Centralized API Catalog**: Comprehensive API inventory providing real-time visibility and control
|
||||
- **Advanced Versioning**: Utilize the most flexible API versioning and Kubernetes-native labels to logically organize APIs, track their evolution over time, and avoid breaking changes for client applications
|
||||
- **Quality Control**: Perform error checks and change impact analysis with Traefik Hub's static analyzer to ensure compliance and prevent misconfiguration by catching errors early
|
||||
- **Resource Management**: Centralize rate limits, quotas, and policy enforcement across groups of APIs through Plans & Bundles, ensuring consistent and efficient resource management
|
||||
- **Subscription Management**: Create managed and self-serve subscriptions for enhanced ease-of-use, security, and auditability
|
||||
- **Standards Compliance**: Import, validate, and manage APIs using industry-standard OpenAPI specifications
|
||||
|
||||
### Developer Experience & Self-Service
|
||||
|
||||
- **Customizable Portals**: Easily create one or more dedicated API Portals for developers to discover, understand, and interact with your APIs
|
||||
- **Interactive API Testing**: Allow developers to test APIs directly within the portal for immediate feedback
|
||||
- **Branded Experience**: Customize the API portal to house API documentation, clear integration instructions, and personalized content to foster adoption and streamline development
|
||||
- **Credential Management**: Generate API access credentials, such as API keys, directly from the portal for simplified access and secure integration
|
||||
- **Auto-generated Documentation**: Automatically generated, interactive API documentation from OpenAPI specifications in the portal
|
||||
|
||||
### Enterprise-Grade Security
|
||||
|
||||
- **Access Control**: Implement fine-grained permissions through detailed API policies and JWT inspection to secure access to API resources
|
||||
- **Identity Integration**: Leverage existing identity and access management (IAM) solutions, such as Keycloak, Okta, Auth0, etc. through industry-standard authentication protocols, including native OIDC support
|
||||
- **Data Protection**: Advanced TLS management, supporting Let's Encrypt (ACME) and SPIFFE for robust data protection
|
||||
- **Threat Prevention**: Native Web Application Firewall (WAF) powered by OWASP Coraza to defend against known vulnerabilities
|
||||
|
||||
### Observability
|
||||
|
||||
- **Open Standards Monitoring**: Gain deep insights into API health and performance with OpenTelemetry integration to provide metrics and tracing capabilities without vendor lock-in
|
||||
- **Third-party Integrations**: Turnkey integration with Treblle directly on the Traefik Hub Dashboard for real-time, out-of-the-box observability without maintaining a complex monitoring infrastructure
|
||||
- **Pre-built Grafana Dashboards**: Ready-to-use monitoring dashboards with key API metrics and performance indicators
|
||||
|
||||
## Getting Started with API Governance
|
||||
|
||||
Transform your API infrastructure with Traefik Hub's governance capabilities:
|
||||
|
||||
1. **Start with API Gateway**: Implement enterprise security, authentication, distributed features, and AI Gateway capabilities
|
||||
2. **Scale with API Management**: Add comprehensive lifecycle management, developer portals, and governance features
|
||||
|
||||
Ready to govern your APIs at scale? Explore [Traefik Hub API Management](https://traefik.io/solutions/api-management/) and discover how it can transform your API strategy.
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
title: "Traefik Proxy HTTPS & TLS Overview |Traefik Docs"
|
||||
description: "Traefik supports HTTPS & TLS, which concerns roughly two parts of the configuration: routers, and the TLS connection. Read the documentation to learn more."
|
||||
---
|
||||
|
||||
# HTTPS & TLS
|
||||
|
||||
Overview
|
||||
{: .subtitle }
|
||||
|
||||
Traefik supports HTTPS & TLS, which concerns roughly two parts of the configuration:
|
||||
routers, and the TLS connection (and its underlying certificates).
|
||||
|
||||
When a router has to handle HTTPS traffic,
|
||||
it should be specified with a `tls` field of the router definition.
|
||||
See the TLS section of the [routers documentation](../routing/routers/index.md#tls).
|
||||
|
||||
The next sections of this documentation explain how to configure the TLS connection itself.
|
||||
That is to say, how to obtain [TLS certificates](./tls.md#certificates-definition):
|
||||
either through a definition in the dynamic configuration, or through [Let's Encrypt](./acme.md) (ACME).
|
||||
And how to configure [TLS options](./tls.md#tls-options), and [certificates stores](./tls.md#certificates-stores).
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
@@ -30,6 +30,20 @@
|
||||
#
|
||||
# certificatesDuration=2160
|
||||
|
||||
# Timeout for a complete HTTP transaction with the ACME server.
|
||||
#
|
||||
# Optional
|
||||
# Default: 2m
|
||||
#
|
||||
# clientTimeout="2m"
|
||||
|
||||
# Timeout for receiving the response headers when communicating with the ACME server.
|
||||
#
|
||||
# Optional
|
||||
# Default: 30s
|
||||
#
|
||||
# clientResponseHeaderTimeout="30s"
|
||||
|
||||
# Preferred chain to use.
|
||||
#
|
||||
# If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name.
|
||||
|
||||
@@ -29,6 +29,20 @@
|
||||
#
|
||||
--certificatesresolvers.myresolver.acme.certificatesDuration=2160
|
||||
|
||||
# Timeout for a complete HTTP transaction with the ACME server.
|
||||
#
|
||||
# Optional
|
||||
# Default: 2m
|
||||
#
|
||||
--certificatesresolvers.myresolver.acme.clientTimeout=2m
|
||||
|
||||
# Timeout for receiving the response headers when communicating with the ACME server.
|
||||
#
|
||||
# Optional
|
||||
# Default: 30s
|
||||
#
|
||||
--certificatesresolvers.myresolver.acme.clientResponseHeaderTimeout=30s
|
||||
|
||||
# Preferred chain to use.
|
||||
#
|
||||
# If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name.
|
||||
|
||||
@@ -32,6 +32,20 @@ certificatesResolvers:
|
||||
#
|
||||
# certificatesDuration: 2160
|
||||
|
||||
# Timeout for a complete HTTP transaction with the ACME server.
|
||||
#
|
||||
# Optional
|
||||
# Default: 2m
|
||||
#
|
||||
# clientTimeout: "2m"
|
||||
|
||||
# Timeout for receiving the response headers when communicating with the ACME server.
|
||||
#
|
||||
# Optional
|
||||
# Default: 30s
|
||||
#
|
||||
# clientResponseHeaderTimeout: "30s"
|
||||
|
||||
# Preferred chain to use.
|
||||
#
|
||||
# If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name.
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
---
|
||||
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) or [TCPServersTransport](../routing/services/index.md#serverstransport_2),
|
||||
that is meant to be secured with SPIFFE,
|
||||
must explicitly enable it (see [SPIFFE with ServersTransport](../routing/services/index.md#spiffe) or [SPIFFE with TCPServersTransport](../routing/services/index.md#spiffe_1)).
|
||||
|
||||
!!! 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
|
||||
```
|
||||
@@ -1,207 +0,0 @@
|
||||
---
|
||||
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 & Swarm"
|
||||
## 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.io/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
|
||||
```
|
||||
|
||||
```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 & Swarm"
|
||||
## 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.io/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
|
||||
```
|
||||
|
||||
```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,590 +0,0 @@
|
||||
---
|
||||
title: "Traefik TLS Documentation"
|
||||
description: "Learn how to configure the transport layer security (TLS) connection in Traefik Proxy. Read the technical documentation."
|
||||
---
|
||||
|
||||
# TLS
|
||||
|
||||
Transport Layer Security
|
||||
{: .subtitle }
|
||||
|
||||
## Certificates Definition
|
||||
|
||||
### Automated
|
||||
|
||||
See the [Let's Encrypt](./acme.md) page.
|
||||
|
||||
### User defined
|
||||
|
||||
To add / remove TLS certificates, even when Traefik is already running, their definition can be added to the [dynamic configuration](../getting-started/configuration-overview.md), in the `[[tls.certificates]]` section:
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic configuration
|
||||
|
||||
tls:
|
||||
certificates:
|
||||
- certFile: /path/to/domain.cert
|
||||
keyFile: /path/to/domain.key
|
||||
- certFile: /path/to/other-domain.cert
|
||||
keyFile: /path/to/other-domain.key
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic configuration
|
||||
|
||||
[[tls.certificates]]
|
||||
certFile = "/path/to/domain.cert"
|
||||
keyFile = "/path/to/domain.key"
|
||||
|
||||
[[tls.certificates]]
|
||||
certFile = "/path/to/other-domain.cert"
|
||||
keyFile = "/path/to/other-domain.key"
|
||||
```
|
||||
|
||||
!!! important "Restriction"
|
||||
|
||||
In the above example, we've used the [file provider](../providers/file.md) to handle these definitions.
|
||||
It is the only available method to configure the certificates (as well as the options and the stores).
|
||||
However, in [Kubernetes](../providers/kubernetes-crd.md), the certificates can and must be provided by [secrets](https://kubernetes.io/docs/concepts/configuration/secret/).
|
||||
|
||||
## Certificates Stores
|
||||
|
||||
In Traefik, certificates are grouped together in certificates stores, which are defined as such:
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic configuration
|
||||
|
||||
tls:
|
||||
stores:
|
||||
default: {}
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic configuration
|
||||
|
||||
[tls.stores]
|
||||
[tls.stores.default]
|
||||
```
|
||||
|
||||
!!! important "Restriction"
|
||||
|
||||
Any store definition other than the default one (named `default`) will be ignored,
|
||||
and there is therefore only one globally available TLS store.
|
||||
|
||||
In the `tls.certificates` section, a list of stores can then be specified to indicate where the certificates should be stored:
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic configuration
|
||||
|
||||
tls:
|
||||
certificates:
|
||||
- certFile: /path/to/domain.cert
|
||||
keyFile: /path/to/domain.key
|
||||
stores:
|
||||
- default
|
||||
# Note that since no store is defined,
|
||||
# the certificate below will be stored in the `default` store.
|
||||
- certFile: /path/to/other-domain.cert
|
||||
keyFile: /path/to/other-domain.key
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic configuration
|
||||
|
||||
[[tls.certificates]]
|
||||
certFile = "/path/to/domain.cert"
|
||||
keyFile = "/path/to/domain.key"
|
||||
stores = ["default"]
|
||||
|
||||
[[tls.certificates]]
|
||||
# Note that since no store is defined,
|
||||
# the certificate below will be stored in the `default` store.
|
||||
certFile = "/path/to/other-domain.cert"
|
||||
keyFile = "/path/to/other-domain.key"
|
||||
```
|
||||
|
||||
!!! important "Restriction"
|
||||
|
||||
The `stores` list will actually be ignored and automatically set to `["default"]`.
|
||||
|
||||
### Default Certificate
|
||||
|
||||
Traefik can use a default certificate for connections without a SNI, or without a matching domain.
|
||||
This default certificate should be defined in a TLS store:
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic configuration
|
||||
|
||||
tls:
|
||||
stores:
|
||||
default:
|
||||
defaultCertificate:
|
||||
certFile: path/to/cert.crt
|
||||
keyFile: path/to/cert.key
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic configuration
|
||||
|
||||
[tls.stores]
|
||||
[tls.stores.default]
|
||||
[tls.stores.default.defaultCertificate]
|
||||
certFile = "path/to/cert.crt"
|
||||
keyFile = "path/to/cert.key"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/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.io/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 & Swarm"
|
||||
## 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"
|
||||
```
|
||||
|
||||
## TLS Options
|
||||
|
||||
The TLS options allow one to configure some parameters of the TLS connection.
|
||||
|
||||
!!! important "'default' TLS Option"
|
||||
|
||||
The `default` option is special.
|
||||
When no tls options are specified in a tls router, the `default` option is used.
|
||||
When specifying the `default` option explicitly, make sure not to specify provider namespace as the `default` option does not have one.
|
||||
Conversely, for cross-provider references, for example, when referencing the file provider from a docker label,
|
||||
you must specify the provider namespace, for example:
|
||||
`traefik.http.routers.myrouter.tls.options=myoptions@file`
|
||||
|
||||
!!! important "TLSOption in Kubernetes"
|
||||
|
||||
When using the [TLSOption resource](../../routing/providers/kubernetes-crd#kind-tlsoption) in Kubernetes, one might setup a default set of options that,
|
||||
if not explicitly overwritten, should apply to all ingresses.
|
||||
To achieve that, you'll have to create a TLSOption resource with the name `default`.
|
||||
There may exist only one TLSOption with the name `default` (across all namespaces) - otherwise they will be dropped.
|
||||
To explicitly use a different TLSOption (and using the Kubernetes Ingress resources)
|
||||
you'll have to add an annotation to the Ingress in the following form:
|
||||
`traefik.ingress.kubernetes.io/router.tls.options: <resource-namespace>-<resource-name>@kubernetescrd`
|
||||
|
||||
### Minimum TLS Version
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic configuration
|
||||
|
||||
tls:
|
||||
options:
|
||||
default:
|
||||
minVersion: VersionTLS12
|
||||
|
||||
mintls13:
|
||||
minVersion: VersionTLS13
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic configuration
|
||||
|
||||
[tls.options]
|
||||
|
||||
[tls.options.default]
|
||||
minVersion = "VersionTLS12"
|
||||
|
||||
[tls.options.mintls13]
|
||||
minVersion = "VersionTLS13"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: TLSOption
|
||||
metadata:
|
||||
name: default
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
minVersion: VersionTLS12
|
||||
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: TLSOption
|
||||
metadata:
|
||||
name: mintls13
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
minVersion: VersionTLS13
|
||||
```
|
||||
|
||||
### Maximum TLS Version
|
||||
|
||||
We discourage the use of this setting to disable TLS1.3.
|
||||
|
||||
The recommended approach is to update the clients to support TLS1.3.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic configuration
|
||||
|
||||
tls:
|
||||
options:
|
||||
default:
|
||||
maxVersion: VersionTLS13
|
||||
|
||||
maxtls12:
|
||||
maxVersion: VersionTLS12
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic configuration
|
||||
|
||||
[tls.options]
|
||||
|
||||
[tls.options.default]
|
||||
maxVersion = "VersionTLS13"
|
||||
|
||||
[tls.options.maxtls12]
|
||||
maxVersion = "VersionTLS12"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: TLSOption
|
||||
metadata:
|
||||
name: default
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
maxVersion: VersionTLS13
|
||||
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: TLSOption
|
||||
metadata:
|
||||
name: maxtls12
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
maxVersion: VersionTLS12
|
||||
```
|
||||
|
||||
### Cipher Suites
|
||||
|
||||
See [cipherSuites](https://godoc.org/crypto/tls#pkg-constants) for more information.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic configuration
|
||||
|
||||
tls:
|
||||
options:
|
||||
default:
|
||||
cipherSuites:
|
||||
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic configuration
|
||||
|
||||
[tls.options]
|
||||
[tls.options.default]
|
||||
cipherSuites = [
|
||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
|
||||
]
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: TLSOption
|
||||
metadata:
|
||||
name: default
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
cipherSuites:
|
||||
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|
||||
```
|
||||
|
||||
!!! important "TLS 1.3"
|
||||
|
||||
Cipher suites defined for TLS 1.2 and below cannot be used in TLS 1.3, and vice versa. (<https://tools.ietf.org/html/rfc8446>)
|
||||
With TLS 1.3, the cipher suites are not configurable (all supported cipher suites are safe in this case).
|
||||
<https://golang.org/doc/go1.12#tls_1_3>
|
||||
|
||||
### Curve Preferences
|
||||
|
||||
This option allows to set the enabled elliptic curves for key exchange.
|
||||
|
||||
The names of the curves defined by [`crypto`](https://godoc.org/crypto/tls#CurveID) (e.g. `CurveP521`) and the [RFC defined names](https://tools.ietf.org/html/rfc8446#section-4.2.7) (e. g. `secp521r1`) can be used.
|
||||
|
||||
See [CurvePreferences](https://godoc.org/crypto/tls#Config.CurvePreferences) and [CurveID](https://godoc.org/crypto/tls#CurveID) for more information.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic configuration
|
||||
|
||||
tls:
|
||||
options:
|
||||
default:
|
||||
curvePreferences:
|
||||
- CurveP521
|
||||
- CurveP384
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic configuration
|
||||
|
||||
[tls.options]
|
||||
[tls.options.default]
|
||||
curvePreferences = ["CurveP521", "CurveP384"]
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: TLSOption
|
||||
metadata:
|
||||
name: default
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
curvePreferences:
|
||||
- CurveP521
|
||||
- CurveP384
|
||||
```
|
||||
|
||||
### Strict SNI Checking
|
||||
|
||||
With strict SNI checking enabled, Traefik won't allow connections from clients that do not specify a server_name extension
|
||||
or don't match any of the configured certificates.
|
||||
The default certificate is irrelevant on that matter.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic configuration
|
||||
|
||||
tls:
|
||||
options:
|
||||
default:
|
||||
sniStrict: true
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic configuration
|
||||
|
||||
[tls.options]
|
||||
[tls.options.default]
|
||||
sniStrict = true
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: TLSOption
|
||||
metadata:
|
||||
name: default
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
sniStrict: true
|
||||
```
|
||||
|
||||
### ALPN Protocols
|
||||
|
||||
_Optional, Default="h2, http/1.1, acme-tls/1"_
|
||||
|
||||
This option allows to specify the list of supported application level protocols for the TLS handshake,
|
||||
in order of preference.
|
||||
If the client supports ALPN, the selected protocol will be one from this list,
|
||||
and the connection will fail if there is no mutually supported protocol.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic configuration
|
||||
|
||||
tls:
|
||||
options:
|
||||
default:
|
||||
alpnProtocols:
|
||||
- http/1.1
|
||||
- h2
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic configuration
|
||||
|
||||
[tls.options]
|
||||
[tls.options.default]
|
||||
alpnProtocols = ["http/1.1", "h2"]
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: TLSOption
|
||||
metadata:
|
||||
name: default
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
alpnProtocols:
|
||||
- http/1.1
|
||||
- h2
|
||||
```
|
||||
|
||||
### Client Authentication (mTLS)
|
||||
|
||||
Traefik supports mutual authentication, through the `clientAuth` section.
|
||||
|
||||
For authentication policies that require verification of the client certificate, the certificate authority for the certificates should be set in `clientAuth.caFiles`.
|
||||
|
||||
In Kubernetes environment, CA certificate can be set in `clientAuth.secretNames`. See [TLSOption resource](../../routing/providers/kubernetes-crd#kind-tlsoption) for more details.
|
||||
|
||||
The `clientAuth.clientAuthType` option governs the behaviour as follows:
|
||||
|
||||
- `NoClientCert`: disregards any client certificate.
|
||||
- `RequestClientCert`: asks for a certificate but proceeds anyway if none is provided.
|
||||
- `RequireAnyClientCert`: requires a certificate but does not verify if it is signed by a CA listed in `clientAuth.caFiles` or in `clientAuth.secretNames`.
|
||||
- `VerifyClientCertIfGiven`: if a certificate is provided, verifies if it is signed by a CA listed in `clientAuth.caFiles` or in `clientAuth.secretNames`. Otherwise proceeds without any certificate.
|
||||
- `RequireAndVerifyClientCert`: requires a certificate, which must be signed by a CA listed in `clientAuth.caFiles` or in `clientAuth.secretNames`.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic configuration
|
||||
|
||||
tls:
|
||||
options:
|
||||
default:
|
||||
clientAuth:
|
||||
# in PEM format. each file can contain multiple CAs.
|
||||
caFiles:
|
||||
- tests/clientca1.crt
|
||||
- tests/clientca2.crt
|
||||
clientAuthType: RequireAndVerifyClientCert
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic configuration
|
||||
|
||||
[tls.options]
|
||||
[tls.options.default]
|
||||
[tls.options.default.clientAuth]
|
||||
# in PEM format. each file can contain multiple CAs.
|
||||
caFiles = ["tests/clientca1.crt", "tests/clientca2.crt"]
|
||||
clientAuthType = "RequireAndVerifyClientCert"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: TLSOption
|
||||
metadata:
|
||||
name: default
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
clientAuth:
|
||||
# the CA certificate is extracted from key `tls.ca` or `ca.crt` of the given secrets.
|
||||
secretNames:
|
||||
- secretCA
|
||||
clientAuthType: RequireAndVerifyClientCert
|
||||
```
|
||||
|
||||
### Disable Session Tickets
|
||||
|
||||
_Optional, Default="false"_
|
||||
|
||||
When set to true, Traefik disables the use of session tickets, forcing every client to perform a full TLS handshake instead of resuming sessions.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic configuration
|
||||
|
||||
tls:
|
||||
options:
|
||||
default:
|
||||
disableSessionTickets: true
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic configuration
|
||||
|
||||
[tls.options]
|
||||
[tls.options.default]
|
||||
disableSessionTickets = true
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: TLSOption
|
||||
metadata:
|
||||
name: default
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
disableSessionTickets: true
|
||||
```
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
@@ -11,7 +11,9 @@ Traefik is an [open-source](https://github.com/traefik/traefik) Application Prox
|
||||
|
||||
If you start with Traefik for service discovery and routing, you can seamlessly add [API management](https://traefik.io/solutions/api-management/), [API gateway](https://traefik.io/solutions/api-gateway/), [AI gateway](https://traefik.io/solutions/ai-gateway/), and [API mocking](https://traefik.io/solutions/api-mocking/) capabilities as needed.
|
||||
|
||||
With 3.3 billion downloads and over 55k stars on GitHub, Traefik is used globally across hybrid cloud, multi-cloud, on prem, and bare metal environments running Kuberentes, Docker Swarm, AWS, [the list goes on](https://doc.traefik.io/traefik/reference/install-configuration/providers/overview/).
|
||||
For a detailed comparison of all Traefik products and their capabilities, see our [Product Features Comparison](./features/index.md).
|
||||
|
||||
With 3.3 billion downloads and over 55k stars on GitHub, Traefik is used globally across hybrid cloud, multi-cloud, on prem, and bare metal environments running Kubernetes, Docker Swarm, AWS, [the list goes on](https://doc.traefik.io/traefik/reference/install-configuration/providers/overview/).
|
||||
|
||||
Here’s how it works—Traefik receives requests on behalf of your system, identifies which components are responsible for handling them, and routes them securely. It automatically discovers the right configuration for your services by inspecting your infrastructure to identify relevant information and which service serves which request.
|
||||
|
||||
@@ -33,7 +35,7 @@ Traefik supports different needs depending on your background. We keep three use
|
||||
Traefik’s main concepts help you understand how requests flow to your services:
|
||||
|
||||
- [Entrypoints](./reference/install-configuration/entrypoints.md) are the network entry points into Traefik. They define the port that will receive the packets and whether to listen for TCP or UDP.
|
||||
- [Routers](./reference/routing-configuration/http/router/rules-and-priority.md) are in charge of connecting incoming requests to the services that can handle them. In the process, routers may use pieces of [middleware](./reference/routing-configuration/http/middlewares/overview.md) to update the request or act before forwarding the request to the service.
|
||||
- [Routers](./reference/routing-configuration/http/routing/rules-and-priority.md) are in charge of connecting incoming requests to the services that can handle them. In the process, routers may use pieces of [middleware](./reference/routing-configuration/http/middlewares/overview.md) to update the request or act before forwarding the request to the service.
|
||||
- [Services](./reference/routing-configuration/http/load-balancing/service.md) are responsible for configuring how to reach the actual services that will eventually handle the incoming requests.
|
||||
- [Providers](./reference/install-configuration/providers/overview.md) are infrastructure components, whether orchestrators, container engines, cloud providers, or key-value stores. The idea is that Traefik queries the provider APIs in order to find relevant information about routing, and when Traefik detects a change, it dynamically updates the routes.
|
||||
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
---
|
||||
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
|
||||
|
||||
Prefixing the Path
|
||||
{: .subtitle }
|
||||
|
||||

|
||||
|
||||
The AddPrefix middleware updates the path of a request before forwarding it.
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
```yaml tab="Docker & Swarm"
|
||||
# Prefixing with /foo
|
||||
labels:
|
||||
- "traefik.http.middlewares.add-foo.addprefix.prefix=/foo"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
# Prefixing with /foo
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: add-foo
|
||||
spec:
|
||||
addPrefix:
|
||||
prefix: /foo
|
||||
```
|
||||
|
||||
```yaml tab="Consul Catalog"
|
||||
# Prefixing with /foo
|
||||
- "traefik.http.middlewares.add-foo.addprefix.prefix=/foo"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Prefixing with /foo
|
||||
http:
|
||||
middlewares:
|
||||
add-foo:
|
||||
addPrefix:
|
||||
prefix: "/foo"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Prefixing with /foo
|
||||
[http.middlewares]
|
||||
[http.middlewares.add-foo.addPrefix]
|
||||
prefix = "/foo"
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### `prefix`
|
||||
|
||||
`prefix` is the string to add before the current path in the requested URL.
|
||||
It should include a leading slash (`/`).
|
||||
@@ -1,345 +0,0 @@
|
||||
---
|
||||
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
|
||||
|
||||
Adding Basic Authentication
|
||||
{: .subtitle }
|
||||
|
||||

|
||||
|
||||
The BasicAuth middleware grants access to services to authorized users only.
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
```yaml tab="Docker & Swarm"
|
||||
# Declaring the user list
|
||||
#
|
||||
# Note: when used in docker-compose.yml all dollar signs in the hash need to be doubled for escaping.
|
||||
# To create user:password pair, it's possible to use this command:
|
||||
# echo $(htpasswd -nB user) | sed -e s/\\$/\\$\\$/g
|
||||
#
|
||||
# Also note that dollar signs should NOT be doubled when they are not being evaluated (e.g. Ansible docker_container module).
|
||||
labels:
|
||||
- "traefik.http.middlewares.test-auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
# Declaring the user list
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: test-auth
|
||||
spec:
|
||||
basicAuth:
|
||||
secret: secretName
|
||||
```
|
||||
|
||||
```yaml tab="Consul Catalog"
|
||||
- "traefik.http.middlewares.test-auth.basicauth.users=test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Declaring the user list
|
||||
http:
|
||||
middlewares:
|
||||
test-auth:
|
||||
basicAuth:
|
||||
users:
|
||||
- "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
|
||||
- "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Declaring the user list
|
||||
[http.middlewares]
|
||||
[http.middlewares.test-auth.basicAuth]
|
||||
users = [
|
||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
]
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### General
|
||||
|
||||
Passwords must be hashed using MD5, SHA1, or BCrypt.
|
||||
|
||||
!!! tip
|
||||
|
||||
Use `htpasswd` to generate the passwords.
|
||||
|
||||
### `users`
|
||||
|
||||
The `users` option is an array of authorized users. Each user must be declared using the `name:hashed-password` format.
|
||||
|
||||
!!! note ""
|
||||
|
||||
- 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.
|
||||
|
||||
!!! 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 & Swarm"
|
||||
# Declaring the user list
|
||||
#
|
||||
# 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:
|
||||
# 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:
|
||||
- "traefik.http.middlewares.test-auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
# Declaring the user list
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: test-auth
|
||||
spec:
|
||||
basicAuth:
|
||||
secret: authsecret
|
||||
|
||||
---
|
||||
# Note: in a kubernetes secret the string (e.g. generated by htpasswd) must be base64-encoded first.
|
||||
# To create an encoded user:password pair, the following command can be used:
|
||||
# htpasswd -nb user password | openssl base64
|
||||
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: authsecret
|
||||
namespace: default
|
||||
data:
|
||||
users: |2
|
||||
dGVzdDokYXByMSRINnVza2trVyRJZ1hMUDZld1RyU3VCa1RycUU4d2ovCnRlc3QyOiRhcHIxJGQ5
|
||||
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"
|
||||
# Declaring the user list
|
||||
- "traefik.http.middlewares.test-auth.basicauth.users=test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Declaring the user list
|
||||
http:
|
||||
middlewares:
|
||||
test-auth:
|
||||
basicAuth:
|
||||
users:
|
||||
- "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
|
||||
- "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Declaring the user list
|
||||
[http.middlewares]
|
||||
[http.middlewares.test-auth.basicAuth]
|
||||
users = [
|
||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
]
|
||||
```
|
||||
|
||||
### `usersFile`
|
||||
|
||||
The `usersFile` option is the path to an external file that contains the authorized users for the middleware.
|
||||
|
||||
The file content is a list of `name:hashed-password`.
|
||||
|
||||
!!! note ""
|
||||
|
||||
- If both `users` and `usersFile` are provided, the two are merged. The contents of `usersFile` have precedence over the values in `users`.
|
||||
- Because it does not make much sense to refer to a file path on Kubernetes, the `usersFile` field doesn't exist for Kubernetes IngressRoute, and one should use the `secret` field instead.
|
||||
|
||||
```yaml tab="Docker & Swarm"
|
||||
labels:
|
||||
- "traefik.http.middlewares.test-auth.basicauth.usersfile=/path/to/my/usersfile"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: test-auth
|
||||
spec:
|
||||
basicAuth:
|
||||
secret: authsecret
|
||||
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: authsecret
|
||||
namespace: default
|
||||
|
||||
data:
|
||||
users: |2
|
||||
dGVzdDokYXByMSRINnVza2trVyRJZ1hMUDZld1RyU3VCa1RycUU4d2ovCnRlc3QyOiRhcHIxJGQ5
|
||||
aHI5SEJCJDRIeHdnVWlyM0hQNEVzZ2dQL1FObzAK
|
||||
```
|
||||
|
||||
```yaml tab="Consul Catalog"
|
||||
- "traefik.http.middlewares.test-auth.basicauth.usersfile=/path/to/my/usersfile"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
http:
|
||||
middlewares:
|
||||
test-auth:
|
||||
basicAuth:
|
||||
usersFile: "/path/to/my/usersfile"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[http.middlewares]
|
||||
[http.middlewares.test-auth.basicAuth]
|
||||
usersFile = "/path/to/my/usersfile"
|
||||
```
|
||||
|
||||
??? example "A file containing test/test and test2/test2"
|
||||
|
||||
```txt
|
||||
test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/
|
||||
test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0
|
||||
```
|
||||
|
||||
### `realm`
|
||||
|
||||
You can customize the realm for the authentication with the `realm` option. The default value is `traefik`.
|
||||
|
||||
```yaml tab="Docker & Swarm"
|
||||
labels:
|
||||
- "traefik.http.middlewares.test-auth.basicauth.realm=MyRealm"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: test-auth
|
||||
spec:
|
||||
basicAuth:
|
||||
realm: MyRealm
|
||||
```
|
||||
|
||||
```json tab="Consul Catalog"
|
||||
- "traefik.http.middlewares.test-auth.basicauth.realm=MyRealm"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
http:
|
||||
middlewares:
|
||||
test-auth:
|
||||
basicAuth:
|
||||
realm: "MyRealm"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[http.middlewares]
|
||||
[http.middlewares.test-auth.basicAuth]
|
||||
realm = "MyRealm"
|
||||
```
|
||||
|
||||
### `headerField`
|
||||
|
||||
You can define a header field to store the authenticated user using the `headerField`option.
|
||||
|
||||
```yaml tab="Docker & Swarm"
|
||||
labels:
|
||||
- "traefik.http.middlewares.my-auth.basicauth.headerField=X-WebAuth-User"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: my-auth
|
||||
spec:
|
||||
basicAuth:
|
||||
# ...
|
||||
headerField: X-WebAuth-User
|
||||
```
|
||||
|
||||
```json tab="Consul Catalog"
|
||||
- "traefik.http.middlewares.my-auth.basicauth.headerField=X-WebAuth-User"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
http:
|
||||
middlewares:
|
||||
my-auth:
|
||||
basicAuth:
|
||||
# ...
|
||||
headerField: "X-WebAuth-User"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[http.middlewares.my-auth.basicAuth]
|
||||
# ...
|
||||
headerField = "X-WebAuth-User"
|
||||
```
|
||||
|
||||
### `removeHeader`
|
||||
|
||||
Set the `removeHeader` option to `true` to remove the authorization header before forwarding the request to your service. (Default value is `false`.)
|
||||
|
||||
```yaml tab="Docker & Swarm"
|
||||
labels:
|
||||
- "traefik.http.middlewares.test-auth.basicauth.removeheader=true"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: test-auth
|
||||
spec:
|
||||
basicAuth:
|
||||
removeHeader: true
|
||||
```
|
||||
|
||||
```json tab="Consul Catalog"
|
||||
- "traefik.http.middlewares.test-auth.basicauth.removeheader=true"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
http:
|
||||
middlewares:
|
||||
test-auth:
|
||||
basicAuth:
|
||||
removeHeader: true
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[http.middlewares]
|
||||
[http.middlewares.test-auth.basicAuth]
|
||||
removeHeader = true
|
||||
```
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
@@ -1,270 +0,0 @@
|
||||
---
|
||||
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
|
||||
|
||||
How to Read the Request before Forwarding It
|
||||
{: .subtitle }
|
||||
|
||||

|
||||
|
||||
The Buffering middleware limits the size of requests that can be forwarded to services.
|
||||
|
||||
With Buffering, Traefik reads the entire request into memory (possibly buffering large requests into disk), and rejects requests that are over a specified size limit.
|
||||
|
||||
This can help services avoid large amounts of data (`multipart/form-data` for example), and can minimize the time spent sending data to a service.
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
```yaml tab="Docker & Swarm"
|
||||
# Sets the maximum request body to 2MB
|
||||
labels:
|
||||
- "traefik.http.middlewares.limit.buffering.maxRequestBodyBytes=2000000"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
# Sets the maximum request body to 2MB
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: limit
|
||||
spec:
|
||||
buffering:
|
||||
maxRequestBodyBytes: 2000000
|
||||
```
|
||||
|
||||
```yaml tab="Consul Catalog"
|
||||
# Sets the maximum request body to 2MB
|
||||
- "traefik.http.middlewares.limit.buffering.maxRequestBodyBytes=2000000"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Sets the maximum request body to 2MB
|
||||
http:
|
||||
middlewares:
|
||||
limit:
|
||||
buffering:
|
||||
maxRequestBodyBytes: 2000000
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Sets the maximum request body to 2MB
|
||||
[http.middlewares]
|
||||
[http.middlewares.limit.buffering]
|
||||
maxRequestBodyBytes = 2000000
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### `maxRequestBodyBytes`
|
||||
|
||||
_Optional, Default=0_
|
||||
|
||||
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.
|
||||
|
||||
```yaml tab="Docker & Swarm"
|
||||
labels:
|
||||
- "traefik.http.middlewares.limit.buffering.maxRequestBodyBytes=2000000"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: limit
|
||||
spec:
|
||||
buffering:
|
||||
maxRequestBodyBytes: 2000000
|
||||
```
|
||||
|
||||
```yaml tab="Consul Catalog"
|
||||
- "traefik.http.middlewares.limit.buffering.maxRequestBodyBytes=2000000"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
http:
|
||||
middlewares:
|
||||
limit:
|
||||
buffering:
|
||||
maxRequestBodyBytes: 2000000
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[http.middlewares]
|
||||
[http.middlewares.limit.buffering]
|
||||
maxRequestBodyBytes = 2000000
|
||||
```
|
||||
|
||||
### `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.
|
||||
|
||||
```yaml tab="Docker & Swarm"
|
||||
labels:
|
||||
- "traefik.http.middlewares.limit.buffering.memRequestBodyBytes=2000000"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: limit
|
||||
spec:
|
||||
buffering:
|
||||
memRequestBodyBytes: 2000000
|
||||
```
|
||||
|
||||
```yaml tab="Consul Catalog"
|
||||
- "traefik.http.middlewares.limit.buffering.memRequestBodyBytes=2000000"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
http:
|
||||
middlewares:
|
||||
limit:
|
||||
buffering:
|
||||
memRequestBodyBytes: 2000000
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[http.middlewares]
|
||||
[http.middlewares.limit.buffering]
|
||||
memRequestBodyBytes = 2000000
|
||||
```
|
||||
|
||||
### `maxResponseBodyBytes`
|
||||
|
||||
_Optional, Default=0_
|
||||
|
||||
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 `500` (Internal Server Error) response instead.
|
||||
|
||||
```yaml tab="Docker & Swarm"
|
||||
labels:
|
||||
- "traefik.http.middlewares.limit.buffering.maxResponseBodyBytes=2000000"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: limit
|
||||
spec:
|
||||
buffering:
|
||||
maxResponseBodyBytes: 2000000
|
||||
```
|
||||
|
||||
```yaml tab="Consul Catalog"
|
||||
- "traefik.http.middlewares.limit.buffering.maxResponseBodyBytes=2000000"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
http:
|
||||
middlewares:
|
||||
limit:
|
||||
buffering:
|
||||
maxResponseBodyBytes: 2000000
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[http.middlewares]
|
||||
[http.middlewares.limit.buffering]
|
||||
maxResponseBodyBytes = 2000000
|
||||
```
|
||||
|
||||
### `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.
|
||||
|
||||
```yaml tab="Docker & Swarm"
|
||||
labels:
|
||||
- "traefik.http.middlewares.limit.buffering.memResponseBodyBytes=2000000"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: limit
|
||||
spec:
|
||||
buffering:
|
||||
memResponseBodyBytes: 2000000
|
||||
```
|
||||
|
||||
```yaml tab="Consul Catalog"
|
||||
- "traefik.http.middlewares.limit.buffering.memResponseBodyBytes=2000000"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
http:
|
||||
middlewares:
|
||||
limit:
|
||||
buffering:
|
||||
memResponseBodyBytes: 2000000
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[http.middlewares]
|
||||
[http.middlewares.limit.buffering]
|
||||
memResponseBodyBytes = 2000000
|
||||
```
|
||||
|
||||
### `retryExpression`
|
||||
|
||||
_Optional, Default=""_
|
||||
|
||||
You can have the Buffering middleware replay the request using `retryExpression`.
|
||||
|
||||
??? example "Retries once in the case of a network error"
|
||||
|
||||
```yaml tab="Docker & Swarm"
|
||||
labels:
|
||||
- "traefik.http.middlewares.limit.buffering.retryExpression=IsNetworkError() && Attempts() < 2"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: limit
|
||||
spec:
|
||||
buffering:
|
||||
retryExpression: "IsNetworkError() && Attempts() < 2"
|
||||
```
|
||||
|
||||
```yaml tab="Consul Catalog"
|
||||
- "traefik.http.middlewares.limit.buffering.retryExpression=IsNetworkError() && Attempts() < 2"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
http:
|
||||
middlewares:
|
||||
limit:
|
||||
buffering:
|
||||
retryExpression: "IsNetworkError() && Attempts() < 2"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[http.middlewares]
|
||||
[http.middlewares.limit.buffering]
|
||||
retryExpression = "IsNetworkError() && Attempts() < 2"
|
||||
```
|
||||
|
||||
The retry expression is defined as a logical combination of the functions below with the operators AND (`&&`) and OR (`||`). At least one function is required:
|
||||
|
||||
- `Attempts()` number of attempts (the first one counts)
|
||||
- `ResponseCode()` response code of the service
|
||||
- `IsNetworkError()` whether the response code is related to networking error
|
||||
|
||||
### Content-Length
|
||||
|
||||
See [Best Practices: Content‑Length](../../security/content-length.md)
|
||||
@@ -1,166 +0,0 @@
|
||||
---
|
||||
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
|
||||
|
||||
When One Isn't Enough
|
||||
{: .subtitle }
|
||||
|
||||

|
||||
|
||||
The Chain middleware enables you to define reusable combinations of other pieces of middleware.
|
||||
It makes reusing the same groups easier.
|
||||
|
||||
## Configuration Example
|
||||
|
||||
Below is an example of a Chain containing `AllowList`, `BasicAuth`, and `RedirectScheme`.
|
||||
|
||||
```yaml tab="Docker & Swarm"
|
||||
labels:
|
||||
- "traefik.http.routers.router1.service=service1"
|
||||
- "traefik.http.routers.router1.middlewares=secured"
|
||||
- "traefik.http.routers.router1.rule=Host(`mydomain`)"
|
||||
- "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.https-only.redirectscheme.scheme=https"
|
||||
- "traefik.http.middlewares.known-ips.ipallowlist.sourceRange=192.168.1.7,127.0.0.1/32"
|
||||
- "traefik.http.services.service1.loadbalancer.server.port=80"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: test
|
||||
namespace: default
|
||||
spec:
|
||||
entryPoints:
|
||||
- web
|
||||
routes:
|
||||
- match: Host(`mydomain`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: whoami
|
||||
port: 80
|
||||
middlewares:
|
||||
- name: secured
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: secured
|
||||
spec:
|
||||
chain:
|
||||
middlewares:
|
||||
- name: https-only
|
||||
- name: known-ips
|
||||
- name: auth-users
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: auth-users
|
||||
spec:
|
||||
basicAuth:
|
||||
users:
|
||||
- test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: https-only
|
||||
spec:
|
||||
redirectScheme:
|
||||
scheme: https
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: known-ips
|
||||
spec:
|
||||
ipAllowList:
|
||||
sourceRange:
|
||||
- 192.168.1.7
|
||||
- 127.0.0.1/32
|
||||
```
|
||||
|
||||
```yaml tab="Consul Catalog"
|
||||
- "traefik.http.routers.router1.service=service1"
|
||||
- "traefik.http.routers.router1.middlewares=secured"
|
||||
- "traefik.http.routers.router1.rule=Host(`mydomain`)"
|
||||
- "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.https-only.redirectscheme.scheme=https"
|
||||
- "traefik.http.middlewares.known-ips.ipallowlist.sourceRange=192.168.1.7,127.0.0.1/32"
|
||||
- "traefik.http.services.service1.loadbalancer.server.port=80"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# ...
|
||||
http:
|
||||
routers:
|
||||
router1:
|
||||
service: service1
|
||||
middlewares:
|
||||
- secured
|
||||
rule: "Host(`mydomain`)"
|
||||
|
||||
middlewares:
|
||||
secured:
|
||||
chain:
|
||||
middlewares:
|
||||
- https-only
|
||||
- known-ips
|
||||
- auth-users
|
||||
|
||||
auth-users:
|
||||
basicAuth:
|
||||
users:
|
||||
- "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
|
||||
|
||||
https-only:
|
||||
redirectScheme:
|
||||
scheme: https
|
||||
|
||||
known-ips:
|
||||
ipAllowList:
|
||||
sourceRange:
|
||||
- "192.168.1.7"
|
||||
- "127.0.0.1/32"
|
||||
|
||||
services:
|
||||
service1:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://127.0.0.1:80"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# ...
|
||||
[http.routers]
|
||||
[http.routers.router1]
|
||||
service = "service1"
|
||||
middlewares = ["secured"]
|
||||
rule = "Host(`mydomain`)"
|
||||
|
||||
[http.middlewares]
|
||||
[http.middlewares.secured.chain]
|
||||
middlewares = ["https-only", "known-ips", "auth-users"]
|
||||
|
||||
[http.middlewares.auth-users.basicAuth]
|
||||
users = ["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"]
|
||||
|
||||
[http.middlewares.https-only.redirectScheme]
|
||||
scheme = "https"
|
||||
|
||||
[http.middlewares.known-ips.ipAllowList]
|
||||
sourceRange = ["192.168.1.7", "127.0.0.1/32"]
|
||||
|
||||
[http.services]
|
||||
[http.services.service1]
|
||||
[http.services.service1.loadBalancer]
|
||||
[[http.services.service1.loadBalancer.servers]]
|
||||
url = "http://127.0.0.1:80"
|
||||
```
|
||||
@@ -1,188 +0,0 @@
|
||||
---
|
||||
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
|
||||
|
||||
Don't Waste Time Calling Unhealthy Services
|
||||
{: .subtitle }
|
||||
|
||||

|
||||
|
||||
The circuit breaker protects your system from stacking requests to unhealthy services, resulting in cascading failures.
|
||||
|
||||
When your system is healthy, the circuit is closed (normal operations).
|
||||
When your system becomes unhealthy, the circuit opens, and the requests are no longer forwarded, but instead are handled by a fallback mechanism.
|
||||
|
||||
To assess if your system is healthy, the circuit breaker constantly monitors the services.
|
||||
|
||||
!!! note ""
|
||||
|
||||
The CircuitBreaker only analyzes what happens _after_ its position within the middleware chain. What happens _before_ has no impact on its state.
|
||||
|
||||
!!! important
|
||||
|
||||
Each router gets its own instance of a given circuit breaker.
|
||||
One circuit breaker instance can be open while the other remains closed: their state is not shared.
|
||||
|
||||
This is the expected behavior, we want you to be able to define what makes a service healthy without having to declare a circuit breaker for each route.
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
```yaml tab="Docker & Swarm"
|
||||
# Latency Check
|
||||
labels:
|
||||
- "traefik.http.middlewares.latency-check.circuitbreaker.expression=LatencyAtQuantileMS(50.0) > 100"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
# Latency Check
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: latency-check
|
||||
spec:
|
||||
circuitBreaker:
|
||||
expression: LatencyAtQuantileMS(50.0) > 100
|
||||
```
|
||||
|
||||
```yaml tab="Consul Catalog"
|
||||
# Latency Check
|
||||
- "traefik.http.middlewares.latency-check.circuitbreaker.expression=LatencyAtQuantileMS(50.0) > 100"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Latency Check
|
||||
http:
|
||||
middlewares:
|
||||
latency-check:
|
||||
circuitBreaker:
|
||||
expression: "LatencyAtQuantileMS(50.0) > 100"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Latency Check
|
||||
[http.middlewares]
|
||||
[http.middlewares.latency-check.circuitBreaker]
|
||||
expression = "LatencyAtQuantileMS(50.0) > 100"
|
||||
```
|
||||
|
||||
## Possible States
|
||||
|
||||
There are three possible states for your circuit breaker:
|
||||
|
||||
- Closed (your service operates normally)
|
||||
- Open (the fallback mechanism takes over your service)
|
||||
- Recovering (the circuit breaker tries to resume normal operations by progressively sending requests to your service)
|
||||
|
||||
### Closed
|
||||
|
||||
While the circuit is closed, the circuit breaker only collects metrics to analyze the behavior of the requests.
|
||||
|
||||
At specified intervals (`checkPeriod`), the circuit breaker evaluates `expression` to decide if its state must change.
|
||||
|
||||
### Open
|
||||
|
||||
While open, the fallback mechanism takes over the normal service calls for a duration of `FallbackDuration`.
|
||||
The fallback mechanism returns a `HTTP 503` (or `ResponseCode`) to the client.
|
||||
After this duration, it enters the recovering state.
|
||||
|
||||
### Recovering
|
||||
|
||||
While recovering, the circuit breaker sends linearly increasing amounts of requests to your service (for `RecoveryDuration`).
|
||||
If your service fails during recovery, the circuit breaker opens again.
|
||||
If the service operates normally during the entire recovery duration, then the circuit breaker closes.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### Configuring the Trigger
|
||||
|
||||
You can specify an `expression` that, once matched, opens the circuit breaker and applies the fallback mechanism instead of calling your services.
|
||||
|
||||
The `expression` option can check three different metrics:
|
||||
|
||||
- The network error ratio (`NetworkErrorRatio`)
|
||||
- The status code ratio (`ResponseCodeRatio`)
|
||||
- The latency at a quantile in milliseconds (`LatencyAtQuantileMS`)
|
||||
|
||||
#### `NetworkErrorRatio`
|
||||
|
||||
If you want the circuit breaker to open at a 30% ratio of network errors, the `expression` is `NetworkErrorRatio() > 0.30`
|
||||
|
||||
#### `ResponseCodeRatio`
|
||||
|
||||
You can configure the circuit breaker to open based on the ratio of a given range of status codes.
|
||||
|
||||
The `ResponseCodeRatio` accepts four parameters, `from`, `to`, `dividedByFrom`, `dividedByTo`.
|
||||
|
||||
The operation that will be computed is sum(`to` -> `from`) / sum (`dividedByFrom` -> `dividedByTo`).
|
||||
|
||||
!!! note ""
|
||||
|
||||
If sum (`dividedByFrom` -> `dividedByTo`) equals 0, then `ResponseCodeRatio` returns 0.
|
||||
|
||||
`from`is inclusive, `to` is exclusive.
|
||||
|
||||
For example, the expression `ResponseCodeRatio(500, 600, 0, 600) > 0.25` will trigger the circuit breaker if 25% of the requests returned a 5XX status (amongst the request that returned a status code from 0 to 5XX).
|
||||
|
||||
#### `LatencyAtQuantileMS`
|
||||
|
||||
You can configure the circuit breaker to open when a given proportion of your requests become too slow.
|
||||
|
||||
For example, the expression `LatencyAtQuantileMS(50.0) > 100` opens the circuit breaker when the median latency (quantile 50) reaches 100ms.
|
||||
|
||||
!!! note ""
|
||||
|
||||
You must provide a floating point number (with the trailing .0) for the quantile value
|
||||
|
||||
#### Using Multiple Metrics
|
||||
|
||||
You can combine multiple metrics using operators in your `expression`.
|
||||
|
||||
Supported operators are:
|
||||
|
||||
- AND (`&&`)
|
||||
- OR (`||`)
|
||||
|
||||
For example, `ResponseCodeRatio(500, 600, 0, 600) > 0.30 || NetworkErrorRatio() > 0.10` triggers the circuit breaker when 30% of the requests return a 5XX status code, or when the ratio of network errors reaches 10%.
|
||||
|
||||
#### Operators
|
||||
|
||||
Here is the list of supported operators:
|
||||
|
||||
- Greater than (`>`)
|
||||
- Greater or equal than (`>=`)
|
||||
- Lesser than (`<`)
|
||||
- Lesser or equal than (`<=`)
|
||||
- Equal (`==`)
|
||||
- Not Equal (`!=`)
|
||||
|
||||
### Fallback mechanism
|
||||
|
||||
By default the fallback mechanism returns a `HTTP 503 Service Unavailable` to the client instead of calling the target service.
|
||||
The response code can be configured.
|
||||
|
||||
### `CheckPeriod`
|
||||
|
||||
_Optional, Default="100ms"_
|
||||
|
||||
The interval between successive checks of the circuit breaker condition (when in standby state).
|
||||
|
||||
### `FallbackDuration`
|
||||
|
||||
_Optional, Default="10s"_
|
||||
|
||||
The duration for which the circuit breaker will wait before trying to recover (from a tripped state).
|
||||
|
||||
### `RecoveryDuration`
|
||||
|
||||
_Optional, Default="10s"_
|
||||
|
||||
The duration for which the circuit breaker will try to recover (as soon as it is in recovering state).
|
||||
|
||||
### `ResponseCode`
|
||||
|
||||
_Optional, Default="503"_
|
||||
|
||||
The status code that the circuit breaker will return while it is in the open state.
|
||||