Compare commits
1279 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2f215ab9a1 | |||
| f6ce751a06 | |||
| a28da8a226 | |||
| 7747b40310 | |||
| 31e566e9f1 | |||
| 72e2454e42 | |||
| 256fcbed5e | |||
| 0beed101ec | |||
| 4b3c971ea3 | |||
| d337748873 | |||
| ef546944b1 | |||
| 29d1c751c1 | |||
| f1e850ae0b | |||
| 6b2bb3cb24 | |||
| f2b68f29a1 | |||
| c320bb4adb | |||
| 75ac25bb31 | |||
| b3528b4f4d | |||
| 7d13368150 | |||
| f32d58c577 | |||
| 72ba481bbc | |||
| 3678d2332f | |||
| 121dfa6060 | |||
| 5c97bfbbbc | |||
| ed990f279a | |||
| fb54793b93 | |||
| 7e5ee8988e | |||
| d4d61dbd80 | |||
| 91381590d3 | |||
| 85cd5485b7 | |||
| 9ac5d3ac1c | |||
| d675b163b3 | |||
| 139b59def9 | |||
| ef5d040fd6 | |||
| 7cb25da31c | |||
| 63820e1d78 | |||
| bb35197d5a | |||
| 6cd85caa99 | |||
| 3315a9fbec | |||
| 27b27e9b1f | |||
| 34d8491ac2 | |||
| 51343bc15f | |||
| 105bf1cfd6 | |||
| 1728364341 | |||
| 8479d66d18 | |||
| 9e5d4ba5a1 | |||
| adf47fba31 | |||
| 794916a183 | |||
| 2e6dfbae57 | |||
| ee265a8509 | |||
| 5a9f3e6999 | |||
| fc67185987 | |||
| d054299ed0 | |||
| dc04dc1940 | |||
| 26f4a669b8 | |||
| e8067f4e01 | |||
| 1881434ac6 | |||
| 862488569d | |||
| 2b710f05b3 | |||
| c7487c4a69 | |||
| e9f3089e90 | |||
| 7e703742cb | |||
| 4de6d6b902 | |||
| 1778ff3bac | |||
| 7e1654ae27 | |||
| 5d00096f82 | |||
| be27044099 | |||
| dbebe5fa3e | |||
| 413b7c8cca | |||
| 3b6949c18c | |||
| 66bf4632e2 | |||
| 1de72c715d | |||
| cabcf19303 | |||
| 47d7094dfb | |||
| c2b21d3719 | |||
| 5b17f0116e | |||
| d1765c7768 | |||
| 23788e90cb | |||
| 90ce858347 | |||
| 8e6ce08f33 | |||
| 97f94349d5 | |||
| 278d903bb4 | |||
| 1d445d5c9d | |||
| f726a50038 | |||
| 3ef6c06b51 | |||
| e6b9f14022 | |||
| 023adeff12 | |||
| e40d8c3d11 | |||
| 8ebab1b243 | |||
| 5fe10e2098 | |||
| fd36de5a0a | |||
| 50e5720464 | |||
| 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 | |||
| b9c2e757c7 | |||
| e15c11961f | |||
| d7406ccb4b | |||
| f9b943a7ee | |||
| 042feacf3e | |||
| 9638c0b8b0 | |||
| 759e82c3c8 | |||
| 2d7262515d | |||
| ae86cb7d11 | |||
| 155b29edbe | |||
| 4ed8f08757 | |||
| 44219f9a1c | |||
| f3e199cd47 | |||
| 9232535cf6 | |||
| 00b64f0ca6 | |||
| f6ea1f473a | |||
| e0d9b060cf | |||
| ade2306a60 | |||
| 67fef270af | |||
| 79bb320f4c | |||
| 9722e2bd6a | |||
| dd67dac537 | |||
| baba5da88b | |||
| 058b194604 | |||
| d271750062 | |||
| a01c73d506 | |||
| 77b1282570 | |||
| f45317c9c9 | |||
| 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 | |||
| ba0f7364f1 | |||
| 40bdea4db8 | |||
| 31db97cbe4 | |||
| 5d85e6d088 | |||
| c0edcc09bb | |||
| 24cede62ee | |||
| 2cbd96e64c | |||
| 4576155005 | |||
| 2dcc1c16b7 | |||
| 43162507e3 | |||
| 2ed2123fc0 | |||
| 9bf14b6764 | |||
| 16d43aefd7 | |||
| c6daab54e3 | |||
| a59bcb29b5 | |||
| 117e0b4471 | |||
| 028e8ca0b0 | |||
| 6486cf95d8 | |||
| 50931813f2 | |||
| 96386b1d78 | |||
| 5ef853a0c5 | |||
| b2b4b66b08 | |||
| 78cc85283c | |||
| 27326e6569 | |||
| 7b78128d4e | |||
| 8c23eb6833 | |||
| f2b7d7f6e1 | |||
| 5c94bbf122 | |||
| 77ef7fe490 | |||
| ff992fb7f9 | |||
| 95434e870b | |||
| aa1c0d8686 | |||
| 7ca90a4b18 | |||
| a3685ee9fa | |||
| ba595bfa98 | |||
| 9a46d35169 | |||
| 955f484d33 | |||
| cdacf0bca8 | |||
| 91331415ce | |||
| d674b393a8 | |||
| 137efedba7 | |||
| c69a8b5cdb | |||
| 9862cd6780 | |||
| 4af188242c | |||
| b0e246cea9 | |||
| c9c8cd6b50 | |||
| c02ec6857f | |||
| a519180665 | |||
| e223116225 | |||
| 8ae0379171 | |||
| 2d98795cc5 | |||
| 4daf13b866 | |||
| cbfecc5d49 | |||
| c2c488ffc5 | |||
| 56a95d6c16 | |||
| 9bd5c61782 | |||
| 107efb8a5a | |||
| 0f862f4792 | |||
| 74eafcd044 | |||
| b0d8e08e2b | |||
| b39ee8ede5 | |||
| 917771739e | |||
| 2949995abc | |||
| ae79d4e5f0 | |||
| aac8bc69ad | |||
| bfcef58a4f | |||
| bf72b9768c | |||
| f7a6f32784 | |||
| fd5796ac39 | |||
| ce1b13f228 | |||
| 289d6e5dca | |||
| fe5c7fdc65 | |||
| 92f798dfcd | |||
| bd4bfd8919 | |||
| f174014d96 | |||
| 2fdee25bb3 | |||
| cd16321dd9 | |||
| 0b4058dde0 | |||
| 6a54f1f66c | |||
| f16fff577a | |||
| 8b495b45a5 | |||
| 4b68e674eb | |||
| 5f35c88805 | |||
| 859f4e8868 | |||
| de1802d849 | |||
| a3745d1eb2 | |||
| 23c7c78a1a | |||
| fa18c35a9a | |||
| 55e6d327bc | |||
| be0b54bade | |||
| ab3234e458 | |||
| 08d5dfee01 | |||
| b669981018 | |||
| 76153acac6 | |||
| 06b02bcd95 | |||
| 4790e4910f | |||
| 3deea566ac | |||
| 79fde2b6dd | |||
| aa5f2b92d4 | |||
| 8c6ed23c5f | |||
| 9ee0e43eac | |||
| 6b9738e675 | |||
| b82290ac5b | |||
| 3ee316c5bb | |||
| c5f7381c80 | |||
| 0dc5b7d013 | |||
| 49b598d087 | |||
| 448785d830 | |||
| ce42e8501e | |||
| bf399f3075 | |||
| 74bc93308b | |||
| dddb68cd5f | |||
| 87b57406ff | |||
| 9bc71b0010 | |||
| 8f37c8f0c5 | |||
| a092c4f535 | |||
| 9d0e76baa8 | |||
| 9c1902c62e | |||
| b05ec75f98 | |||
| 2d617b3a65 | |||
| ec6deb40ab | |||
| 160edff257 | |||
| 8816cb86a4 | |||
| 316be0782c | |||
| 30d836f963 | |||
| 14da838a21 | |||
| f6fb240eb6 | |||
| a75b2384ea | |||
| 8bdca45861 | |||
| 7442162e3f | |||
| dd5cb68cb1 | |||
| 299a16f0a4 | |||
| 545f2feacc | |||
| e3caaf0791 | |||
| 746cc80d0f | |||
| fd0fd39642 | |||
| f794f8a294 | |||
| 8cf22207b5 | |||
| 5e44a138a8 | |||
| 0664367c53 | |||
| d7d0017545 | |||
| bb8dfa568a | |||
| 88c5e6a3fd | |||
| 2965aa42cc | |||
| 6c3b099c25 | |||
| 405be420c9 | |||
| ec38a0675f | |||
| bd4ff81818 | |||
| e817d822d7 | |||
| b7be71c02a | |||
| 6e9d713668 | |||
| ddb32ef86f | |||
| 496f00c7c2 | |||
| f0cd6f210b | |||
| c910ceeb00 | |||
| 2087e11f55 | |||
| 42778d2ba6 | |||
| a5d46fc6ef | |||
| 84742275a4 | |||
| 54a2d657f3 | |||
| 08b90ade94 | |||
| bb7ef7b48a | |||
| 8ba99adc50 | |||
| 50b0d772e5 | |||
| b02946147d | |||
| 137c632793 | |||
| e76b65f44d | |||
| 55ebaee4a7 | |||
| 5953331c73 | |||
| ae4a00b4bc | |||
| 4ff76e13c4 | |||
| 30fe11eccf | |||
| 05eb438ae1 | |||
| b7170df2c3 | |||
| 9e029a84c4 | |||
| 14e400bcd0 | |||
| 550d96ea67 | |||
| 3c99135bf9 | |||
| 474ab23fe9 | |||
| c166a41c99 | |||
| 740b4cfd25 | |||
| 7cfd10db62 | |||
| fa76ed57d3 | |||
| 9d8a42111f | |||
| 0dfd12ee61 | |||
| 07e6491ace | |||
| 32ea014d07 | |||
| a3fd484728 | |||
| 9b0348577a | |||
| efe03bc9da | |||
| cce935493a | |||
| f196de90e1 | |||
| c2a294c872 | |||
| 8e5d4c6ae9 | |||
| f0849e8ee6 | |||
| 1ccbf743cb | |||
| 1cfcf0d318 | |||
| eb07a5ca1a | |||
| 56ea028e81 | |||
| 05c547f211 | |||
| dcd9f2ea96 | |||
| 84e20aa9c3 | |||
| b5a5e259ed | |||
| 8488214e93 | |||
| b74767bfa4 | |||
| 786d9f3272 | |||
| da2278b29a | |||
| cfebed7328 | |||
| 4e441d09ed | |||
| 8f5dd7bd9d | |||
| d04e2d717c | |||
| cdd24e91b4 | |||
| 4fd6b10b7d | |||
| 86315e0f18 | |||
| c20af070e3 | |||
| 8593581cbf | |||
| 857fbb933e | |||
| 8103992977 | |||
| c5b92b5260 | |||
| 2afa03b55c | |||
| 2b6a04bc1d | |||
| fb527dac1c | |||
| ef887332c2 | |||
| c19cf125e8 | |||
| 261e4395f3 | |||
| 435d28c790 | |||
| 4ce4bd7121 | |||
| 020ab5f347 | |||
| ad7fb8e82b | |||
| 0528c054a6 | |||
| ad99c5bbea | |||
| 8272be0eda | |||
| 0a6ff446c7 | |||
| 9a9644bafe | |||
| 95dd17e020 | |||
| b0a72960bc | |||
| a57e118a1a | |||
| d2414feaff | |||
| 6aa56788ea | |||
| 1aa450c028 | |||
| f9ff6049d3 | |||
| d4d61151e1 | |||
| 456188fa0d | |||
| 03c170f264 | |||
| 7cb46626a1 | |||
| 5b53bae42d | |||
| caf56e6aed | |||
| 69c8ecfa99 | |||
| 7db2bbb4a3 | |||
| ee8305549a | |||
| a31b026364 | |||
| 20d496268c | |||
| 6d3a685d5a | |||
| 845d0b5ac7 | |||
| 34aa3b75b8 | |||
| f62fc67418 | |||
| 5f3c30e37b | |||
| 139f929ec8 | |||
| e20409676a | |||
| d152f7fafc | |||
| ee449db656 | |||
| 38ac1e75a2 | |||
| 109a8712cc | |||
| 278e739242 | |||
| db31a4c961 | |||
| a1099bf8d0 | |||
| 596aadfe68 | |||
| 35ce6baaae | |||
| 95f20fc753 | |||
| d9f58f94a2 | |||
| a29628fa2e | |||
| e280716645 | |||
| f1c4ba2f26 | |||
| a870c2af9b | |||
| aa8eb1af6e | |||
| 189db8d990 | |||
| 68a8650297 | |||
| 1a5ea1c597 | |||
| 8983e45fcf | |||
| ec214fa825 | |||
| 1c0094048b | |||
| 3a3ffab689 | |||
| 2302debac2 | |||
| 4974d9e4d7 | |||
| 33cf06b36a | |||
| 590ddfc990 | |||
| 39d7b77609 | |||
| e85d02c530 | |||
| 74e0abf8bf | |||
| d953ee69b4 | |||
| 26738cbf93 | |||
| b1934231ca | |||
| 9588e51146 | |||
| e87da0f390 | |||
| 8eb12795d7 | |||
| cc14c165c0 | |||
| f2ba4353b2 | |||
| 514914639a | |||
| a4c0b1649d | |||
| f547f1b22b | |||
| 42df9afeaf | |||
| c8b0285c91 | |||
| 2df655cefe | |||
| 826a2b74aa | |||
| 47b4df71bf | |||
| 2b35c7e205 | |||
| 33c1d700c0 | |||
| 536e11d949 | |||
| 0ec12c7aa7 | |||
| c120b70483 | |||
| ab0713d587 | |||
| 090db6d4b0 | |||
| 5cfc11fe68 | |||
| 8a0c1e614f | |||
| 394f97bc48 | |||
| 8eadfbb990 | |||
| ca5b70e196 | |||
| cc80568d9e | |||
| 8ffd1854db | |||
| 6baa110adb | |||
| 5658c8ac06 | |||
| 1c80f12bc2 | |||
| ef5f1b1508 | |||
| fdce8c604a | |||
| 8c19652361 | |||
| b7b4dd9554 | |||
| e5c80637fc | |||
| f437fb4230 | |||
| 9c50129520 | |||
| 00a5f4c401 | |||
| a79cdd1dfa | |||
| 2096fd7081 | |||
| ec00c4aa42 | |||
| 552bd8f180 | |||
| f70949e3fa | |||
| 97caf758ef | |||
| 7f4ff359a2 | |||
| 47466a456e | |||
| 6f18344c56 | |||
| e8ff825ed2 | |||
| 8527369797 | |||
| 7004f0e750 | |||
| 25caa72c09 | |||
| 8beba9f278 | |||
| e90f4a7cb4 | |||
| 20cdbdbf31 | |||
| 08fe27ce5f | |||
| 0dc36379cf | |||
| 27948493aa | |||
| e3ed52ba7c | |||
| b22e081c7c | |||
| 62fa5f1a8e | |||
| edc0a52b5a | |||
| 3d2336bc83 | |||
| 0605f8bf09 | |||
| f18fcf3688 | |||
| eeb99c3536 | |||
| 83871f27dd | |||
| 6e1f5dc071 | |||
| ef5aa129c7 | |||
| f54f28921b | |||
| ef168b801c | |||
| 06e64af9e9 | |||
| be156f6071 | |||
| 6f469ee1ec | |||
| b46665c620 | |||
| be13b5b55d | |||
| e9d677f8cb | |||
| 7edb9a2101 | |||
| 4613ddd757 | |||
| c441d04788 | |||
| 5d5dd9dd30 | |||
| 1508a2c221 | |||
| 934ca5fd22 | |||
| f16d14cfa6 | |||
| 4625bdf5cb | |||
| 7b477f762a | |||
| 157cf75e38 | |||
| ab35b3266a | |||
| d339bfc8d2 | |||
| 7b08ecfa5e | |||
| 0a6b8780f0 | |||
| 45292148e7 | |||
| fc563d3f6e | |||
| a762cce430 | |||
| 306d3f277d | |||
| 6f7649fccc | |||
| e8ab3af74d | |||
| a7502c8700 | |||
| 54c3afd760 | |||
| a2ab3e534d | |||
| 8cfa68a8e1 | |||
| 518caa79f9 | |||
| 373095f1a8 | |||
| b641d5cf2a | |||
| 4d6cb6af03 | |||
| 9eb804a689 | |||
| c02b72ca51 | |||
| 2bb712135d | |||
| 14e5d4b4b3 | |||
| e485edbe9f | |||
| d317cd90fc | |||
| eccfcc0924 | |||
| 61bb3ab991 | |||
| e62f8af23b | |||
| a42d396ed2 | |||
| 7bb181dfa0 | |||
| fbf6757ce9 | |||
| f8a78b3b25 | |||
| a6db1cac37 | |||
| 312ebb17ab | |||
| a398536688 | |||
| 0be01cc067 | |||
| f3eba8d3a2 | |||
| 7e75dc0819 | |||
| b00f640d72 | |||
| ac42dd8f83 | |||
| 4b5968e0cc | |||
| 42e1f2c9b1 | |||
| bbeceba580 | |||
| 1ebd12ff82 | |||
| 89f3b272c3 | |||
| 093989fc14 | |||
| 06d7fab820 | |||
| f90f9df1db | |||
| 9750bbc353 | |||
| 8c977b8f8c | |||
| 5841441005 | |||
| 0cf2032c15 | |||
| d547b943df | |||
| 71d4b3b13c | |||
| ac1dad3d14 | |||
| be5c429825 | |||
| e222d5cb2f | |||
| 9dc2155e63 | |||
| c2cb4fac10 | |||
| e8335a94a4 | |||
| 3d92f1645f | |||
| 3f74993f4a | |||
| 533c102d4f | |||
| 3eb7ecce19 | |||
| 0b34e0cdcb | |||
| cf2869407d | |||
| 8ca27b4a1d | |||
| 6009aaed87 | |||
| eb99c8c785 | |||
| bf71560515 | |||
| 51f7f610c9 | |||
| 5ed972ccd8 | |||
| 2714831a4e | |||
| 6b3167d03e | |||
| 1417da4a21 | |||
| 3040f2659a | |||
| 6b1a584c2b | |||
| 3a80aa172c | |||
| 8dc9607db7 | |||
| 85f4fd0979 | |||
| e56ae1a766 | |||
| d2030a5835 | |||
| 58bbc0cf0f | |||
| 7056eeff6a | |||
| ad613e58cd | |||
| e7dc097901 | |||
| 12a37346a4 | |||
| 78079377e8 | |||
| 75881359ab | |||
| 0eb0a15aa1 | |||
| 8d9ff0c441 | |||
| b611f967b7 | |||
| 4c4780f886 | |||
| 926a8e88e9 | |||
| 6b1adabeb5 | |||
| 4eedcabbb3 | |||
| 5bf4b536e2 | |||
| 5380e48747 | |||
| ccc11a69f1 | |||
| 0f57f108ae | |||
| c0b704e1b0 | |||
| a50345bf8d | |||
| bd93e224de | |||
| ea019be133 | |||
| 02de683b94 | |||
| 930f84850b | |||
| 8970ae9199 | |||
| de732ba53c | |||
| 0f7af2b4e7 | |||
| e8324132f9 | |||
| f52a36ba12 | |||
| 2ffa6c6feb | |||
| 210400905f | |||
| ba6b4cbcc3 | |||
| 7dbd3f88f6 | |||
| 898eab20ac | |||
| 957a5f5e73 | |||
| 5a70910dce | |||
| 386c2ffb20 | |||
| 266a2d8b91 | |||
| 3ba53df005 | |||
| 5142733858 | |||
| ecdfb10653 | |||
| 0f4e72d522 | |||
| 70dd7cdc71 | |||
| c3e943658a | |||
| 4720caed04 | |||
| c5a6b49330 | |||
| a5df24a21d | |||
| f5a811d8fa | |||
| 87db3300d3 | |||
| 4b4eaa49b5 | |||
| fc174062b6 | |||
| d700e95c21 | |||
| aa760b5a71 | |||
| a52c81fd91 | |||
| 127c0a7542 | |||
| 58dcbb43f9 | |||
| f32884d9b8 | |||
| 173a18fdc1 | |||
| 876899be4b | |||
| 89108972b6 | |||
| d42e75bb2e | |||
| 8d016f5e16 | |||
| 927f0bc01a | |||
| 900784a95a | |||
| 98c624bf1a | |||
| f3479f532b | |||
| 8946dd1898 | |||
| 2a0cfda90b | |||
| 12fae2ebb8 | |||
| 9758b1ce36 | |||
| fe4cca6e9c | |||
| b1b4e6b918 | |||
| 8cb1829698 | |||
| 2f9905061e | |||
| 0a7a6afd59 | |||
| b577b3a6ba | |||
| 230019eccf | |||
| 2090baa938 | |||
| b7de043991 | |||
| 9e0800f938 | |||
| e7d1a98c5e | |||
| 6f1bd54d86 | |||
| 983940ae60 | |||
| 6d8407893d | |||
| a8a92eb2a5 | |||
| 2798e18e18 | |||
| 61defcdd66 | |||
| ec638a741e | |||
| 097e71ad24 | |||
| eabcb3e1c0 | |||
| 53a8bd76f2 | |||
| 0e89c48e38 | |||
| 385ff5055c | |||
| b4f99ae3ac | |||
| a696f7c654 | |||
| 3ca667a3d4 | |||
| 27af1fb478 | |||
| e322184a98 | |||
| 69424a16a5 | |||
| f9f22b7b70 | |||
| b795f128d7 | |||
| 6706bb1612 | |||
| 3f48e6f8ef | |||
| 8ea339816a | |||
| 00b1d8b0bc | |||
| 21c6edcf58 | |||
| 5c48e3c96c | |||
| c23c3e0ed3 | |||
| b37aaea36d | |||
| 67f0700377 | |||
| 778dc22e14 | |||
| cdf0c8b3ec | |||
| 359477c583 | |||
| 28d40e7f3c | |||
| b368e71337 | |||
| dc752c7847 | |||
| 6155c900be | |||
| 6ca4c5da5c | |||
| 7eac92f49c | |||
| e6b1b05fdf | |||
| b452f37e08 | |||
| 8cff718c53 | |||
| bfda5e607f | |||
| 7fc56454ea | |||
| c0a2e6b4b6 | |||
| 0f0cc420e1 | |||
| 9250b5937d | |||
| e9bd2b45ac | |||
| 4406c337d4 | |||
| ed10bc5833 | |||
| e33bd6874f | |||
| 6e61fe0de1 | |||
| 05828bab07 | |||
| 0e215f9b61 | |||
| 7fdb1ff8af | |||
| 736f37cb58 | |||
| cff71ee496 | |||
| f02b223639 | |||
| d4d23dce72 | |||
| 5e4dc783c7 | |||
| 440cb11250 | |||
| 42920595ad | |||
| e68e647fd9 | |||
| 8b558646fc | |||
| f8e45a0b29 | |||
| d65de8fe6c | |||
| 5f2c00b438 | |||
| c2c1c3e09e | |||
| d8a778b5cd | |||
| d8cf90dade | |||
| 6a06560318 | |||
| a4aad5ce5c | |||
| 15973f5503 | |||
| a4150409c8 | |||
| aee515b930 | |||
| 05d2c86074 | |||
| b0d19bd466 | |||
| d99d2f95e6 | |||
| 8d2a2ff08f | |||
| 73e5dbbfe5 | |||
| ee3e7cbbec | |||
| 9d8fd24730 | |||
| f5d451d816 | |||
| f84e00e481 | |||
| fe0af1ec4b | |||
| 95312d5324 | |||
| e3729ec600 | |||
| 20d6c19c30 | |||
| 7a7b03eb01 | |||
| ea4f307fcd | |||
| a6b00608d2 | |||
| 7b649e2f0c | |||
| 52e95deee3 | |||
| 70968bc6a9 | |||
| da7bb5fc25 | |||
| 34bd611131 | |||
| b9b7527762 | |||
| 240b83b773 | |||
| 584839e00b | |||
| 099c7e9444 | |||
| 83a5c5cfbd | |||
| c1d9b9ee1f | |||
| d53f5f01a0 | |||
| 4e11bf3c38 | |||
| 1a266c661a | |||
| bda4f50eae | |||
| 19e6170fa5 | |||
| 0017471f0d | |||
| 76723b1288 | |||
| cef842245c | |||
| f69fd43122 | |||
| e5062cef42 | |||
| 998c6174cd | |||
| ac1753a614 | |||
| d3516aec31 | |||
| 2c6418e17a | |||
| 1ffbffb26a | |||
| fdf27eb644 | |||
| 945ff9b0f9 | |||
| bbd5846c6a | |||
| 9f145dbc28 | |||
| c84b510f0d | |||
| 2bc3fa7b4b | |||
| fc897f6756 | |||
| c31f5df854 | |||
| b636b21167 | |||
| 167bdb0d53 | |||
| 7f29595c0a | |||
| 3fcf265d80 | |||
| 618fb5f232 | |||
| d94e676083 | |||
| 141abce2d5 | |||
| fc875b38e0 | |||
| 39fe3869b6 | |||
| d582e01892 | |||
| 75790e0ab8 | |||
| 1391c35978 | |||
| 7bda07a422 | |||
| 9b6af61d1b | |||
| 5edac5eccd | |||
| 83e4abdb30 | |||
| 4e1e2f5ed0 | |||
| c06629459d | |||
| 05be441027 | |||
| 6c9687f410 | |||
| 5cf1b95c29 | |||
| 74daa4cbb3 | |||
| 709ff6fb09 | |||
| 4fd5fca34f | |||
| 31a93d5045 | |||
| 4cb5825d11 | |||
| 15f50553e9 | |||
| b4ca02da86 | |||
| 86be0a4e6f | |||
| 0e89a6bec7 | |||
| c5808af4d9 | |||
| c1ef742977 | |||
| 935d251b21 | |||
| 73769af0fe | |||
| 3b851a5ef2 | |||
| deab4dae8e | |||
| f7edb394f2 | |||
| baf687218c | |||
| 153765f99f | |||
| 5a2e233a15 | |||
| 453e21c7c9 | |||
| 8b759ab797 | |||
| 88a2020817 | |||
| c3545c620b | |||
| 1034646ae2 | |||
| 538f780a85 | |||
| b931c8ae9b | |||
| c5c61dbade | |||
| 1e7dbc70a0 | |||
| 6a2db4e4e9 | |||
| 1ea98d3d31 | |||
| 014fdfc4ec | |||
| b5ec787fb6 | |||
| 0c8778639a | |||
| 8f29398573 | |||
| 676de5fb68 | |||
| 063f8fae79 | |||
| efa6ca0fa1 | |||
| 4e831b920e | |||
| bc84fdd006 | |||
| d5cb9b50f4 | |||
| e11ff98608 | |||
| 9df04df334 | |||
| bb6cd581a6 | |||
| 8f9ad16f54 | |||
| 5d8b1949b7 | |||
| d7ec0cedbf | |||
| f1104ada65 | |||
| 3ba3ca6eb0 | |||
| 85039e0d54 | |||
| 9be523d772 | |||
| 8b77f0c2dd | |||
| d02be003ab | |||
| 4d539273ad | |||
| 40de310927 | |||
| 18203f57d2 | |||
| ef0e9c6f05 | |||
| d37ea3e882 | |||
| 3174c69c66 | |||
| f4f3dbe1f5 | |||
| 49f04f2772 | |||
| 03d2e35488 | |||
| aece9a1051 | |||
| 547cd81599 | |||
| b5251c6ac4 | |||
| 190b9b1afa | |||
| 9befe0dd51 | |||
| 683e2ee5c6 | |||
| 21da705ec9 | |||
| a3ac456199 | |||
| 9843757834 | |||
| f9831f5b1b | |||
| 177c4b0ed1 | |||
| bab48bed22 | |||
| 6cb2ff2af9 | |||
| 5e0855ecc7 | |||
| f57cee578f | |||
| 8da38ec0a5 | |||
| a6d462f6e8 | |||
| 6c19a9cb8f | |||
| 0eeb85d01d | |||
| 64ff214ff8 | |||
| 111f3716fa | |||
| 4e0a05406b | |||
| 39b0aa6650 | |||
| 319517adef | |||
| 7a315bb043 | |||
| 34d2a816c2 | |||
| 81ce45271d | |||
| 3a461d2f23 | |||
| 980dac4572 | |||
| ff7966f9cd | |||
| e78374aa29 | |||
| 3bbc560283 | |||
| ccf3a9995a | |||
| e522446909 | |||
| fea94a3393 | |||
| 4ddef9830b | |||
| 45bb00be04 | |||
| cd8d5b8f10 | |||
| eff294829f | |||
| a69c1ba3b7 | |||
| 9adf0fb638 | |||
| 56e2110dc5 | |||
| 5be13802dc | |||
| 7345afd8b6 | |||
| a84d5c0ef1 | |||
| 2a9471d278 | |||
| 0042562678 | |||
| 74ab88d47e | |||
| 6df9578ace | |||
| cd7d324295 | |||
| 0e92b02474 | |||
| 9662cdca64 | |||
| 3dfaa3d5fa | |||
| 60123a8f3f | |||
| 2a7b2ef772 | |||
| d51a2ce487 | |||
| 0a79643001 | |||
| e77a66c2ac | |||
| 6858dbdd07 | |||
| b2bb96390a | |||
| e29a142f6a | |||
| dae0491b61 | |||
| f4ddf25e41 | |||
| 35c704ace3 | |||
| 789046f162 | |||
| 186e3e1541 | |||
| 088fe3c270 | |||
| 553ef94047 | |||
| 12e50e20e6 | |||
| cd326654a7 | |||
| 3de29433f8 | |||
| 84516f962d | |||
| f92b03a44d | |||
| 085b70c94e | |||
| 0e66ed87f8 | |||
| d141e4a1ed | |||
| 679975beec | |||
| 8faed97e74 | |||
| 0b4c582088 | |||
| c7cd0df3b3 | |||
| 286181aa61 | |||
| 6a34f238ce | |||
| 4b2c763cf3 | |||
| d03d8d53fd | |||
| 8d0979bfd0 | |||
| e95fde5652 | |||
| ab7993428d | |||
| b966215e6c | |||
| b786f58f80 | |||
| 173154cf59 | |||
| 4acec60e72 | |||
| c3880a69ca | |||
| 4d63eb30f9 | |||
| 0ee377bc9f | |||
| dbc679dc30 | |||
| fc7f732029 | |||
| ba912e1a93 | |||
| 3216c8ab10 | |||
| 561c580701 | |||
| 3fd5c747a2 | |||
| b6b6cef3da | |||
| d651d1e7cf | |||
| 6f22b9e0a7 | |||
| f29325c679 | |||
| 57780d8004 | |||
| 46f4a8541e | |||
| 1d85515aac | |||
| 55e00be36e | |||
| d6457e6cbb | |||
| ca2b9e8e77 | |||
| d948784d38 | |||
| 1ddb0afb24 | |||
| f518676238 | |||
| db3e8a7f5a | |||
| 0bd367ebbd | |||
| f4dc298406 | |||
| 4f6c15cc14 | |||
| 3f93e9ea71 | |||
| eb585740a1 | |||
| 1709f3854c | |||
| ebde81e91c | |||
| 47faae25d7 | |||
| 7792d197e6 | |||
| deb4235028 | |||
| 7d66f439eb | |||
| 124ee3c48c | |||
| bed6069e82 | |||
| e29da5ad65 | |||
| 48de3b0230 | |||
| 00048a8351 | |||
| 2df5defd36 | |||
| aaa763b7af | |||
| 8a68ece2cc | |||
| 08b80c20f0 | |||
| d4daafa468 | |||
| 52d2d959af | |||
| 0a35fa096a | |||
| a7ef965412 | |||
| 0a861716d4 | |||
| 4fbe9b81ec | |||
| 5fd6913ee5 | |||
| 7741c68eaa | |||
| 18077ff69a | |||
| fa555d0d29 | |||
| 0e5898b2f8 | |||
| fecaec7a4a | |||
| 60bc47d00e | |||
| 606281a4a5 | |||
| cf1cbb24df | |||
| 981ad74870 | |||
| 466d7461b7 | |||
| 619045eb4b | |||
| 2cebd0a083 | |||
| fc071a5ebe | |||
| 79c5f34156 | |||
| 928db9bc42 | |||
| 8174860770 | |||
| 598caf6f78 | |||
| 8b47c5adf7 | |||
| a3bcf0f39e | |||
| 7875826bd9 | |||
| 7c2af10bbd | |||
| 598a257ae1 | |||
| 20e47d9102 | |||
| f26e250648 | |||
| 80790cba17 | |||
| 241fb5093a | |||
| 044dc6a221 | |||
| 707f84e2e4 | |||
| f94298e867 | |||
| e1abf103c0 | |||
| 8cd4923e72 | |||
| cd90b9761a | |||
| f0f5f41fb9 | |||
| 8bf68b7efd | |||
| b22aef7fff | |||
| a2016a2953 | |||
| c38d405cfd | |||
| 8c98234c07 | |||
| d046af2e91 | |||
| 943238faba | |||
| 2b67f1f66f | |||
| 2ad1fd725a | |||
| 7129f03dc9 | |||
| 748254b6c5 | |||
| a08a428787 | |||
| 3eeea2bb2b | |||
| c2dac39da1 | |||
| e54ee89330 | |||
| fdd3f2abef | |||
| 517917cd7c | |||
| 6c75052a13 | |||
| 8cf9385938 | |||
| 519ed8bde5 | |||
| 46a61ce9c8 | |||
| c57876c116 | |||
| 0d81fac3fc | |||
| db287c4d31 | |||
| 4d86668af3 | |||
| b93141992e | |||
| a3e4c85ec0 | |||
| bee86b5ac7 | |||
| 0ba51d62fa | |||
| 268d1edc8f | |||
| 580e7fa774 | |||
| 46c266661c | |||
| 61325d7b91 | |||
| 3f8aa13e68 | |||
| 08279047ae | |||
| 3dd4968c41 | |||
| ba1ca68977 | |||
| 56f7515ecd | |||
| 27c02b5a56 | |||
| 630de7481e | |||
| fadee5e87b | |||
| 67d9c8da0b | |||
| 1a1cfd1adc | |||
| 240fb871b6 | |||
| d131ef57da | |||
| 281fa25844 | |||
| bd3eaf4f5e | |||
| 7a6bfd3336 | |||
| 1b9873cae9 | |||
| e86f21ae7b | |||
| 194247caae | |||
| cd0654026a | |||
| b39ce8cc58 | |||
| 33f0aed5ea | |||
| 188ef84c4f | |||
| a5c520664a | |||
| 38d7011487 | |||
| 033fccccc7 | |||
| df99a9fb57 | |||
| d6b69e1347 | |||
| 4bd055cf97 |
@@ -1,5 +1,5 @@
|
||||
dist/
|
||||
!dist/traefik
|
||||
!dist/**/traefik
|
||||
site/
|
||||
vendor/
|
||||
.idea/
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: traefik
|
||||
@@ -1,17 +1,16 @@
|
||||
<!--
|
||||
PLEASE READ THIS MESSAGE.
|
||||
|
||||
Documentation fixes or enhancements:
|
||||
- for Traefik v2: use branch v2.10
|
||||
- for Traefik v3: use branch v3.0
|
||||
Documentation:
|
||||
- for Traefik v2: use branch v2.11 (fixes only)
|
||||
- for Traefik v3: use branch v3.6
|
||||
|
||||
Bug fixes:
|
||||
- for Traefik v2: use branch v2.10
|
||||
- for Traefik v3: use branch v3.0
|
||||
Bug:
|
||||
- for Traefik v2: use branch v2.11 (security fixes only)
|
||||
- for Traefik v3: use branch v3.6
|
||||
|
||||
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/
|
||||
|
||||
|
||||
@@ -4,76 +4,78 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '**.md'
|
||||
- 'script/gcg/**'
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.20'
|
||||
GO_VERSION: '1.25'
|
||||
CGO_ENABLED: 0
|
||||
IN_DOCKER: ""
|
||||
|
||||
jobs:
|
||||
|
||||
build-webui:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Build webui
|
||||
run: |
|
||||
make clean-webui generate-webui
|
||||
tar czvf webui.tar.gz ./webui/static/
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: webui.tar.gz
|
||||
path: webui.tar.gz
|
||||
uses: ./.github/workflows/template-webui.yaml
|
||||
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-20.04, macos-latest, windows-latest ]
|
||||
os: [ darwin, freebsd, linux, openbsd, windows ]
|
||||
arch: [ amd64, arm64 ]
|
||||
include:
|
||||
- os: freebsd
|
||||
arch: 386
|
||||
- os: linux
|
||||
arch: 386
|
||||
- os: linux
|
||||
arch: arm
|
||||
goarm: 6
|
||||
- os: linux
|
||||
arch: arm
|
||||
goarm: 7
|
||||
- os: linux
|
||||
arch: ppc64le
|
||||
- os: linux
|
||||
arch: riscv64
|
||||
- os: linux
|
||||
arch: s390x
|
||||
- os: openbsd
|
||||
arch: 386
|
||||
- os: windows
|
||||
arch: 386
|
||||
needs:
|
||||
- build-webui
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
|
||||
|
||||
steps:
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
path: go/src/github.com/traefik/traefik
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v3
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
env:
|
||||
ImageOS: ${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.goarm }}
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
~/.cache/go-build
|
||||
~/Library/Caches/go-build
|
||||
'%LocalAppData%\go-build'
|
||||
key: ${{ runner.os }}-build-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: ${{ runner.os }}-build-go-
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: webui.tar.gz
|
||||
path: ${{ github.workspace }}/go/src/github.com/traefik/traefik
|
||||
|
||||
- name: Untar webui
|
||||
run: tar xvf webui.tar.gz
|
||||
run: |
|
||||
tar xvf webui.tar.gz
|
||||
rm webui.tar.gz
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
GOOS: ${{ matrix.os }}
|
||||
GOARCH: ${{ matrix.arch }}
|
||||
GOARM: ${{ matrix.goarm }}
|
||||
run: make binary
|
||||
|
||||
@@ -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-20.04
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
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 }}
|
||||
@@ -0,0 +1,70 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- v*
|
||||
schedule:
|
||||
- cron: '11 22 * * 1'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'javascript', 'go' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Use only 'java' to analyze code written in Java, Kotlin or both
|
||||
# Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
|
||||
- name: setup go
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
if: ${{ matrix.language == 'go' }}
|
||||
with:
|
||||
go-version-file: 'go.mod'
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
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.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
|
||||
# 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@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
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
# - run: |
|
||||
# echo "Run, Build Application using script"
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
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
|
||||
@@ -14,17 +15,17 @@ jobs:
|
||||
|
||||
docs:
|
||||
name: Doc Process
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'traefik/traefik'
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
@@ -39,14 +40,14 @@ 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
|
||||
|
||||
- name: Publish documentation
|
||||
run: $HOME/bin/mixtus --dst-doc-path="./traefik" --dst-owner=traefik --dst-repo-name=doc --git-user-email="30906710+traefiker@users.noreply.github.com" --git-user-name=traefiker --src-doc-path="./site" --src-owner=containous --src-repo-name=traefik
|
||||
run: $HOME/bin/mixtus --dst-doc-path="./traefik" --dst-owner=traefik --dst-repo-name=doc --git-user-email="30906710+traefiker@users.noreply.github.com" --git-user-name=traefiker --src-doc-path="./site" --src-owner=traefik --src-repo-name=traefik
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_REPO }}
|
||||
@@ -6,32 +6,65 @@ on:
|
||||
- master
|
||||
- v*
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.25'
|
||||
CGO_ENABLED: 0
|
||||
|
||||
jobs:
|
||||
|
||||
build-webui:
|
||||
if: github.repository == 'traefik/traefik'
|
||||
uses: ./.github/workflows/template-webui.yaml
|
||||
|
||||
experimental:
|
||||
if: github.repository == 'traefik/traefik'
|
||||
name: Build experimental image on branch
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
||||
# https://github.com/marketplace/actions/checkout
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
env:
|
||||
ImageOS: ${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.goarm }}
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: Build
|
||||
run: make generate binary
|
||||
|
||||
- name: Branch name
|
||||
run: echo ${GITHUB_REF##*/}
|
||||
|
||||
- name: Build docker experimental image
|
||||
run: docker build -t traefik/traefik:experimental-${GITHUB_REF##*/} -f exp.Dockerfile .
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Push to Docker Hub
|
||||
run: docker push traefik/traefik:experimental-${GITHUB_REF##*/}
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: webui.tar.gz
|
||||
|
||||
- name: Untar webui
|
||||
run: |
|
||||
tar xvf webui.tar.gz
|
||||
rm webui.tar.gz
|
||||
|
||||
- name: Build docker experimental image
|
||||
env:
|
||||
DOCKER_BUILDX_ARGS: "--push"
|
||||
run: |
|
||||
make multi-arch-image-experimental-${GITHUB_REF##*/}
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.25'
|
||||
CGO_ENABLED: 0
|
||||
VERSION: ${{ github.ref_name }}
|
||||
TRAEFIKER_EMAIL: "traefiker@traefik.io"
|
||||
CODENAME: ramequin
|
||||
|
||||
jobs:
|
||||
|
||||
build-webui:
|
||||
if: github.ref_type == 'tag' && github.repository == 'traefik/traefik'
|
||||
uses: ./.github/workflows/template-webui.yaml
|
||||
|
||||
build:
|
||||
if: github.ref_type == 'tag' && github.repository == 'traefik/traefik'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
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@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
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 }}
|
||||
check-latest: true
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: webui.tar.gz
|
||||
|
||||
- name: Untar webui
|
||||
run: |
|
||||
tar xvf webui.tar.gz
|
||||
rm webui.tar.gz
|
||||
|
||||
- name: Go generate
|
||||
run: go generate
|
||||
|
||||
|
||||
- name: Generate goreleaser file
|
||||
run: |
|
||||
GORELEASER_CONFIG_FILE_PATH=$(go run ./internal/release "${{ matrix.os }}")
|
||||
echo "GORELEASER_CONFIG_FILE_PATH=$GORELEASER_CONFIG_FILE_PATH" >> $GITHUB_ENV
|
||||
|
||||
- name: Build with goreleaser
|
||||
uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
|
||||
with:
|
||||
distribution: goreleaser
|
||||
# 'latest', 'nightly', or a semver
|
||||
version: '~> v2'
|
||||
args: release --clean --timeout="90m" --config "${{ env.GORELEASER_CONFIG_FILE_PATH }}"
|
||||
|
||||
- name: Artifact binaries
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: ${{ matrix.os }}-binaries
|
||||
path: |
|
||||
dist/**/*_checksums.txt
|
||||
dist/**/*.tar.gz
|
||||
dist/**/*.zip
|
||||
retention-days: 1
|
||||
|
||||
release:
|
||||
if: github.ref_type == 'tag' && github.repository == 'traefik/traefik'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
needs:
|
||||
- build
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: webui.tar.gz
|
||||
|
||||
- name: Untar webui
|
||||
run: |
|
||||
tar xvf webui.tar.gz
|
||||
rm webui.tar.gz
|
||||
|
||||
- name: Retrieve the secret and decode it to a file
|
||||
env:
|
||||
TRAEFIKER_RSA: ${{ secrets.TRAEFIKER_RSA }}
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
echo "${TRAEFIKER_RSA}" | base64 --decode > ~/.ssh/traefiker_rsa
|
||||
|
||||
- name: Download All Artifacts
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
path: dist/
|
||||
pattern: "*-binaries"
|
||||
merge-multiple: true
|
||||
|
||||
- name: Publish Release
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
cat dist/**/*_checksums.txt >> "dist/traefik_${VERSION}_checksums.txt"
|
||||
rm dist/**/*_checksums.txt
|
||||
tar cfz "dist/traefik-${VERSION}.src.tar.gz" \
|
||||
--exclude-vcs \
|
||||
--exclude .idea \
|
||||
--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} --latest=false
|
||||
|
||||
./script/deploy.sh
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
name: Sync Docker Images
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 0 * * *" # Run every day
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
if: github.repository == 'traefik/traefik'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
|
||||
- uses: imjasonh/setup-crane@31b88efe9de28ae0ffa220711af4b60be9435f6e # v0.4
|
||||
|
||||
- name: Sync
|
||||
run: |
|
||||
EXCLUDED_TAGS="1.7.9-alpine v1.0.0-beta.392 v1.0.0-beta.404 v1.0.0-beta.704 v1.0.0-rc1 v1.7.9-alpine"
|
||||
EXCLUDED_REGEX=$(echo $EXCLUDED_TAGS | sed 's/ /|/g')
|
||||
diff <(crane ls traefik) <(crane ls ghcr.io/traefik/traefik) | grep '^<' | awk '{print $2}' | while read -r tag; do [[ "$tag" =~ ^($EXCLUDED_REGEX)$ ]] || (echo "Processing image: traefik:$tag"; crane cp "traefik:$tag" "ghcr.io/traefik/traefik:$tag"); done
|
||||
crane cp traefik:latest ghcr.io/traefik/traefik:latest
|
||||
@@ -0,0 +1,48 @@
|
||||
name: Build Web UI
|
||||
on:
|
||||
workflow_call: {}
|
||||
env:
|
||||
SAFE_CHAIN_MINIMUM_PACKAGE_AGE_HOURS: 360 # 15 days
|
||||
jobs:
|
||||
|
||||
build-webui:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Enable corepack
|
||||
run: corepack enable
|
||||
|
||||
- name: Setup node
|
||||
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 build
|
||||
|
||||
- name: Package webui
|
||||
run: |
|
||||
tar czvf webui.tar.gz ./webui/static/
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: webui.tar.gz
|
||||
path: webui.tar.gz
|
||||
retention-days: 1
|
||||
@@ -0,0 +1,41 @@
|
||||
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:
|
||||
GO_VERSION: '1.25'
|
||||
CGO_ENABLED: 0
|
||||
|
||||
jobs:
|
||||
|
||||
test-gateway-api-conformance:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- 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
|
||||
@@ -0,0 +1,103 @@
|
||||
name: Test Integration
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '**.md'
|
||||
- 'script/gcg/**'
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.25'
|
||||
CGO_ENABLED: 0
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: Avoid generating webui
|
||||
run: |
|
||||
touch webui/static/index.html
|
||||
|
||||
- name: Build binary
|
||||
run: make binary-linux-amd64
|
||||
|
||||
- name: Save go cache build
|
||||
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') }}
|
||||
|
||||
- name: Artifact traefik binary
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
with:
|
||||
name: traefik
|
||||
path: ./dist/linux/amd64/traefik
|
||||
retention-days: 1
|
||||
|
||||
test-integration:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
parallel: [12]
|
||||
index: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: Download traefik binary
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
with:
|
||||
name: traefik
|
||||
path: ./dist/linux/amd64/
|
||||
|
||||
- name: Make binary executable
|
||||
run: chmod +x ./dist/linux/amd64/traefik
|
||||
|
||||
- name: Restore go cache build
|
||||
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') }}
|
||||
|
||||
- name: Generate go test Slice
|
||||
id: test_split
|
||||
uses: hashicorp-forge/go-test-split-action@ddb2685fb140c29505663b405af7eb2cd953dd13 # v2.0.1
|
||||
with:
|
||||
packages: ./integration
|
||||
total: ${{ matrix.parallel }}
|
||||
index: ${{ matrix.index }}
|
||||
|
||||
- name: Run Integration tests
|
||||
run: |
|
||||
TESTS=$(echo "${{ steps.test_split.outputs.run}}" | sed 's/\$/\$\$/g')
|
||||
TESTFLAGS="-run \"${TESTS}\"" make test-integration
|
||||
@@ -0,0 +1,51 @@
|
||||
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:
|
||||
GO_VERSION: '1.25'
|
||||
CGO_ENABLED: 0
|
||||
|
||||
jobs:
|
||||
|
||||
test-knative-conformance:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- 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
|
||||
@@ -4,43 +4,90 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '**.md'
|
||||
- 'script/gcg/**'
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.20'
|
||||
IN_DOCKER: ""
|
||||
GO_VERSION: '1.25'
|
||||
|
||||
jobs:
|
||||
|
||||
test-unit:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
|
||||
|
||||
generate-packages:
|
||||
name: List Go Packages
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.set-matrix.outputs.matrix }}
|
||||
steps:
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
path: go/src/github.com/traefik/traefik
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v3
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
~/.cache/go-build
|
||||
key: ${{ runner.os }}-test-unit-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: ${{ runner.os }}-test-unit-go-
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: Avoid generating webui
|
||||
run: touch webui/static/index.html
|
||||
- name: Generate matrix
|
||||
id: set-matrix
|
||||
run: |
|
||||
matrix_output=$(go run ./internal/testsci/genmatrix.go)
|
||||
echo "$matrix_output"
|
||||
echo "$matrix_output" >> $GITHUB_OUTPUT
|
||||
|
||||
test-unit:
|
||||
runs-on: ubuntu-latest
|
||||
needs: generate-packages
|
||||
strategy:
|
||||
matrix:
|
||||
package: ${{ fromJson(needs.generate-packages.outputs.matrix) }}
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: Tests
|
||||
run: make test-unit
|
||||
run: |
|
||||
go test -v -parallel 8 ${{ matrix.package.group }}
|
||||
|
||||
test-ui-unit:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
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@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
with:
|
||||
node-version-file: webui/.nvmrc
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: webui/yarn.lock
|
||||
|
||||
- name: Setup safe-chain
|
||||
run: |
|
||||
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,84 +6,71 @@ on:
|
||||
- '*'
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.20'
|
||||
GOLANGCI_LINT_VERSION: v1.53.1
|
||||
MISSSPELL_VERSION: v0.4.0
|
||||
IN_DOCKER: ""
|
||||
GO_VERSION: '1.25'
|
||||
GOLANGCI_LINT_VERSION: v2.8.0
|
||||
MISSPELL_VERSION: v0.7.0
|
||||
|
||||
jobs:
|
||||
|
||||
validate:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
path: go/src/github.com/traefik/traefik
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v3
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
~/.cache/go-build
|
||||
key: ${{ runner.os }}-validate-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: ${{ runner.os }}-validate-go-
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: Install golangci-lint ${{ env.GOLANGCI_LINT_VERSION }}
|
||||
run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCI_LINT_VERSION}
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
|
||||
with:
|
||||
version: "${{ env.GOLANGCI_LINT_VERSION }}"
|
||||
|
||||
- name: Install missspell ${{ env.MISSSPELL_VERSION }}
|
||||
run: curl -sfL https://raw.githubusercontent.com/golangci/misspell/master/install-misspell.sh | sh -s -- -b $(go env GOPATH)/bin ${MISSSPELL_VERSION}
|
||||
validate:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
- name: Avoid generating webui
|
||||
run: touch webui/static/index.html
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
go-version: ${{ env.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: Validate
|
||||
run: make validate
|
||||
run: make validate-files
|
||||
|
||||
validate-generate:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
||||
with:
|
||||
path: go/src/github.com/traefik/traefik
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v3
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
~/.cache/go-build
|
||||
key: ${{ runner.os }}-validate-generate-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: ${{ runner.os }}-validate-generate-go-
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: go generate
|
||||
run: |
|
||||
go generate
|
||||
make generate
|
||||
git diff --exit-code
|
||||
|
||||
- name: go mod tidy
|
||||
|
||||
@@ -19,3 +19,4 @@ plugins-storage/
|
||||
plugins-local/
|
||||
traefik_changelog.md
|
||||
integration/tailscale.secret
|
||||
integration/gateway-api-conformance-reports/**/experimental-dev-default-report.yaml
|
||||
|
||||
@@ -1,271 +1,350 @@
|
||||
run:
|
||||
timeout: 10m
|
||||
skip-files: []
|
||||
skip-dirs:
|
||||
- pkg/provider/kubernetes/crd/generated/
|
||||
version: "2"
|
||||
|
||||
linters-settings:
|
||||
govet:
|
||||
enable-all: true
|
||||
disable:
|
||||
- shadow
|
||||
- fieldalignment
|
||||
gocyclo:
|
||||
min-complexity: 14
|
||||
goconst:
|
||||
min-len: 3
|
||||
min-occurrences: 4
|
||||
misspell:
|
||||
locale: US
|
||||
funlen:
|
||||
lines: -1
|
||||
statements: 120
|
||||
forbidigo:
|
||||
forbid:
|
||||
- ^print(ln)?$
|
||||
- ^spew\.Print(f|ln)?$
|
||||
- ^spew\.Dump$
|
||||
depguard:
|
||||
rules:
|
||||
main:
|
||||
deny:
|
||||
- pkg: "github.com/instana/testify"
|
||||
desc: not allowed
|
||||
- pkg: "github.com/pkg/errors"
|
||||
desc: Should be replaced by standard lib errors package
|
||||
godox:
|
||||
keywords:
|
||||
- FIXME
|
||||
importas:
|
||||
no-unaliased: true
|
||||
alias:
|
||||
- alias: composeapi
|
||||
pkg: github.com/docker/compose/v2/pkg/api
|
||||
|
||||
# Standard Kubernetes rewrites:
|
||||
- alias: corev1
|
||||
pkg: "k8s.io/api/core/v1"
|
||||
- alias: netv1
|
||||
pkg: "k8s.io/api/networking/v1"
|
||||
- alias: netv1beta1
|
||||
pkg: "k8s.io/api/networking/v1beta1"
|
||||
- alias: admv1
|
||||
pkg: "k8s.io/api/admission/v1"
|
||||
- alias: admv1beta1
|
||||
pkg: "k8s.io/api/admission/v1beta1"
|
||||
- alias: extv1beta1
|
||||
pkg: "k8s.io/api/extensions/v1beta1"
|
||||
- alias: metav1
|
||||
pkg: "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
- alias: ktypes
|
||||
pkg: "k8s.io/apimachinery/pkg/types"
|
||||
- alias: kerror
|
||||
pkg: "k8s.io/apimachinery/pkg/api/errors"
|
||||
- alias: kclientset
|
||||
pkg: "k8s.io/client-go/kubernetes"
|
||||
- alias: kinformers
|
||||
pkg: "k8s.io/client-go/informers"
|
||||
- alias: ktesting
|
||||
pkg: "k8s.io/client-go/testing"
|
||||
- alias: kschema
|
||||
pkg: "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
- alias: kscheme
|
||||
pkg: "k8s.io/client-go/kubernetes/scheme"
|
||||
- alias: kversion
|
||||
pkg: "k8s.io/apimachinery/pkg/version"
|
||||
- alias: kubefake
|
||||
pkg: "k8s.io/client-go/kubernetes/fake"
|
||||
- alias: discoveryfake
|
||||
pkg: "k8s.io/client-go/discovery/fake"
|
||||
|
||||
# Kubernetes Gateway rewrites:
|
||||
- alias: gateclientset
|
||||
pkg: "sigs.k8s.io/gateway-api/pkg/client/clientset/gateway/versioned"
|
||||
- alias: gateinformers
|
||||
pkg: "sigs.k8s.io/gateway-api/pkg/client/informers/gateway/externalversions"
|
||||
- alias: gatev1alpha2
|
||||
pkg: "sigs.k8s.io/gateway-api/apis/v1alpha2"
|
||||
|
||||
# Traefik Kubernetes rewrites:
|
||||
- alias: containousv1alpha1
|
||||
pkg: "github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/traefikcontainous/v1alpha1"
|
||||
- alias: traefikv1alpha1
|
||||
pkg: "github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/traefikio/v1alpha1"
|
||||
- alias: traefikclientset
|
||||
pkg: "github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/generated/clientset/versioned"
|
||||
- alias: traefikinformers
|
||||
pkg: "github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/generated/informers/externalversions"
|
||||
- alias: traefikscheme
|
||||
pkg: "github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme"
|
||||
- alias: traefikcrdfake
|
||||
pkg: "github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake"
|
||||
tagalign:
|
||||
align: false
|
||||
sort: true
|
||||
order:
|
||||
- description
|
||||
- json
|
||||
- toml
|
||||
- yaml
|
||||
- yml
|
||||
- label
|
||||
- label-slice-as-struct
|
||||
- file
|
||||
- kv
|
||||
- export
|
||||
revive:
|
||||
rules:
|
||||
- name: struct-tag
|
||||
- name: blank-imports
|
||||
- name: context-as-argument
|
||||
- name: context-keys-type
|
||||
- name: dot-imports
|
||||
- name: error-return
|
||||
- name: error-strings
|
||||
- name: error-naming
|
||||
- name: exported
|
||||
disabled: true
|
||||
- name: if-return
|
||||
- name: increment-decrement
|
||||
- name: var-naming
|
||||
- name: var-declaration
|
||||
- name: package-comments
|
||||
disabled: true
|
||||
- name: range
|
||||
- name: receiver-naming
|
||||
- name: time-naming
|
||||
- name: unexported-return
|
||||
- name: indent-error-flow
|
||||
- name: errorf
|
||||
- name: empty-block
|
||||
- name: superfluous-else
|
||||
- name: unused-parameter
|
||||
disabled: true
|
||||
- name: unreachable-code
|
||||
- name: redefines-builtin-id
|
||||
gomoddirectives:
|
||||
replace-allow-list:
|
||||
- github.com/abbot/go-http-auth
|
||||
- github.com/go-check/check
|
||||
- github.com/gorilla/mux
|
||||
- github.com/mailgun/minheap
|
||||
- github.com/mailgun/multibuf
|
||||
- github.com/jaguilar/vt100
|
||||
formatters:
|
||||
enable:
|
||||
- gci
|
||||
- gofumpt
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- pkg/provider/kubernetes/crd/generated/
|
||||
|
||||
linters:
|
||||
enable-all: true
|
||||
default: all
|
||||
disable:
|
||||
- deadcode # deprecated
|
||||
- exhaustivestruct # deprecated
|
||||
- golint # deprecated
|
||||
- ifshort # deprecated
|
||||
- interfacer # deprecated
|
||||
- maligned # deprecated
|
||||
- nosnakecase # deprecated
|
||||
- scopelint # deprecated
|
||||
- scopelint # deprecated
|
||||
- structcheck # deprecated
|
||||
- varcheck # deprecated
|
||||
- sqlclosecheck # not relevant (SQL)
|
||||
- rowserrcheck # not relevant (SQL)
|
||||
- execinquery # not relevant (SQL)
|
||||
- bodyclose # too many false-positive
|
||||
- containedctx # too many false-positive
|
||||
- contextcheck # too many false-positive
|
||||
- cyclop # duplicate of gocyclo
|
||||
- lll # Not relevant
|
||||
- gocyclo # FIXME must be fixed
|
||||
- gocognit # Too strict
|
||||
- nestif # Too many false-positive.
|
||||
- prealloc # Too many false-positive.
|
||||
- makezero # Not relevant
|
||||
- dupl # Too strict
|
||||
- gosec # Too strict
|
||||
- gochecknoinits
|
||||
- gochecknoglobals
|
||||
- wsl # Too strict
|
||||
- nlreturn # Not relevant
|
||||
- gomnd # Too strict
|
||||
- stylecheck # skip because report issues related to some generated files.
|
||||
- testpackage # Too strict
|
||||
- tparallel # Not relevant
|
||||
- paralleltest # Not relevant
|
||||
- err113 # Too strict
|
||||
- exhaustive # Not relevant
|
||||
- exhaustruct # Not relevant
|
||||
- goerr113 # Too strict
|
||||
- wrapcheck # Too strict
|
||||
- noctx # Too strict
|
||||
- bodyclose # too many false-positive
|
||||
- forcetypeassert # Too strict
|
||||
- tagliatelle # Too strict
|
||||
- varnamelen # Not relevant
|
||||
- nilnil # Not relevant
|
||||
- ireturn # Not relevant
|
||||
- contextcheck # too many false-positive
|
||||
- containedctx # too many false-positive
|
||||
- maintidx # kind of duplicate of gocyclo
|
||||
- nonamedreturns # Too strict
|
||||
- gochecknoglobals
|
||||
- gochecknoinits
|
||||
- gocognit # Too strict
|
||||
- gocyclo # FIXME must be fixed
|
||||
- gosec # Too strict
|
||||
- gosmopolitan # not relevant
|
||||
- ireturn # Not relevant
|
||||
- lll # Not relevant
|
||||
- maintidx # kind of duplicate of gocyclo
|
||||
- makezero # Not relevant
|
||||
- mnd # Too strict
|
||||
- nestif # Too many false-positive.
|
||||
- nilnil # Not relevant
|
||||
- nlreturn # Not relevant
|
||||
- noctx # Too strict
|
||||
- noinlineerr # Too strict
|
||||
- nonamedreturns # Too strict
|
||||
- paralleltest # Not relevant
|
||||
- prealloc # Too many false-positive.
|
||||
- rowserrcheck # not relevant (SQL)
|
||||
- sqlclosecheck # not relevant (SQL)
|
||||
- tagliatelle # Too strict
|
||||
- testpackage # Too strict
|
||||
- tparallel # Not relevant
|
||||
- varnamelen # Not relevant
|
||||
- wrapcheck # Too strict
|
||||
- wsl # Too strict
|
||||
- wsl_v5 # Too strict
|
||||
|
||||
settings:
|
||||
depguard:
|
||||
rules:
|
||||
main:
|
||||
deny:
|
||||
- pkg: github.com/instana/testify
|
||||
desc: not allowed
|
||||
- pkg: github.com/pkg/errors
|
||||
desc: Should be replaced by standard lib errors package
|
||||
errcheck:
|
||||
exclude-functions:
|
||||
- fmt.Fprintln
|
||||
forbidigo:
|
||||
forbid:
|
||||
- pattern: ^print(ln)?$
|
||||
- pattern: ^spew\.Print(f|ln)?$
|
||||
- pattern: ^spew\.Dump$
|
||||
funlen:
|
||||
lines: -1
|
||||
statements: 120
|
||||
goconst:
|
||||
min-len: 3
|
||||
min-occurrences: 4
|
||||
gocyclo:
|
||||
min-complexity: 14
|
||||
godox:
|
||||
keywords:
|
||||
- FIXME
|
||||
gomoddirectives:
|
||||
toolchain-pattern: go1\.\d+\.\d+$
|
||||
tool-forbidden: true
|
||||
go-version-pattern: ^1\.\d+(\.0)?$
|
||||
replace-allow-list:
|
||||
- github.com/abbot/go-http-auth
|
||||
- github.com/gorilla/mux
|
||||
- github.com/mailgun/minheap
|
||||
- github.com/mailgun/multibuf
|
||||
- github.com/jaguilar/vt100
|
||||
- github.com/cucumber/godog
|
||||
govet:
|
||||
enable-all: true
|
||||
disable:
|
||||
- shadow
|
||||
- fieldalignment
|
||||
importas:
|
||||
no-unaliased: true
|
||||
alias:
|
||||
- pkg: github.com/docker/compose/v2/pkg/api
|
||||
alias: composeapi
|
||||
|
||||
# Standard Kubernetes rewrites:
|
||||
- pkg: k8s.io/api/core/v1
|
||||
alias: corev1
|
||||
- pkg: k8s.io/api/networking/v1
|
||||
alias: netv1
|
||||
- pkg: k8s.io/api/networking/v1beta1
|
||||
alias: netv1beta1
|
||||
- pkg: k8s.io/api/admission/v1
|
||||
alias: admv1
|
||||
- pkg: k8s.io/api/admission/v1beta1
|
||||
alias: admv1beta1
|
||||
- pkg: k8s.io/api/extensions/v1beta1
|
||||
alias: extv1beta1
|
||||
- pkg: k8s.io/apimachinery/pkg/apis/meta/v1
|
||||
alias: metav1
|
||||
- pkg: k8s.io/apimachinery/pkg/types
|
||||
alias: ktypes
|
||||
- pkg: k8s.io/apimachinery/pkg/api/errors
|
||||
alias: kerror
|
||||
- pkg: k8s.io/client-go/kubernetes
|
||||
alias: kclientset
|
||||
- pkg: k8s.io/client-go/informers
|
||||
alias: kinformers
|
||||
- pkg: k8s.io/client-go/testing
|
||||
alias: ktesting
|
||||
- pkg: k8s.io/apimachinery/pkg/runtime/schema
|
||||
alias: kschema
|
||||
- pkg: k8s.io/client-go/kubernetes/scheme
|
||||
alias: kscheme
|
||||
- pkg: k8s.io/apimachinery/pkg/version
|
||||
alias: kversion
|
||||
- pkg: k8s.io/client-go/kubernetes/fake
|
||||
alias: kubefake
|
||||
- pkg: k8s.io/client-go/discovery/fake
|
||||
alias: discoveryfake
|
||||
|
||||
# Kubernetes Gateway rewrites:
|
||||
- pkg: sigs.k8s.io/gateway-api/pkg/client/clientset/gateway/versioned
|
||||
alias: gateclientset
|
||||
- pkg: sigs.k8s.io/gateway-api/pkg/client/informers/gateway/externalversions
|
||||
alias: gateinformers
|
||||
- pkg: sigs.k8s.io/gateway-api/apis/v1alpha2
|
||||
alias: gatev1alpha2
|
||||
|
||||
# Traefik Kubernetes rewrites:
|
||||
- pkg: github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1
|
||||
alias: traefikv1alpha1
|
||||
- pkg: github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned
|
||||
alias: traefikclientset
|
||||
- pkg: github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/informers/externalversions
|
||||
alias: traefikinformers
|
||||
- pkg: github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme
|
||||
alias: traefikscheme
|
||||
- pkg: github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake
|
||||
alias: traefikcrdfake
|
||||
misspell:
|
||||
locale: US
|
||||
revive:
|
||||
rules:
|
||||
- name: struct-tag
|
||||
- name: blank-imports
|
||||
- name: context-as-argument
|
||||
- name: context-keys-type
|
||||
- name: dot-imports
|
||||
- name: error-return
|
||||
- name: error-strings
|
||||
- name: error-naming
|
||||
- name: exported
|
||||
disabled: true
|
||||
- name: if-return
|
||||
- name: increment-decrement
|
||||
- name: var-naming
|
||||
- name: var-declaration
|
||||
- name: package-comments
|
||||
disabled: true
|
||||
- name: range
|
||||
- name: receiver-naming
|
||||
- name: time-naming
|
||||
- name: unexported-return
|
||||
- name: indent-error-flow
|
||||
- name: errorf
|
||||
- name: empty-block
|
||||
- name: superfluous-else
|
||||
- name: unused-parameter
|
||||
disabled: true
|
||||
- name: unreachable-code
|
||||
- name: redefines-builtin-id
|
||||
tagalign:
|
||||
align: false
|
||||
sort: true
|
||||
order:
|
||||
- description
|
||||
- json
|
||||
- toml
|
||||
- yaml
|
||||
- yml
|
||||
- label
|
||||
- label-slice-as-struct
|
||||
- file
|
||||
- kv
|
||||
- export
|
||||
testifylint:
|
||||
disable:
|
||||
- suite-dont-use-pkg
|
||||
- require-error
|
||||
- go-require
|
||||
perfsprint:
|
||||
err-error: true
|
||||
errorf: true
|
||||
sprintf1: true
|
||||
strconcat: false
|
||||
staticcheck:
|
||||
checks:
|
||||
- all
|
||||
- '-SA1019'
|
||||
- '-ST1000'
|
||||
- '-ST1003'
|
||||
- '-ST1016'
|
||||
- '-ST1020'
|
||||
- '-ST1021'
|
||||
- '-ST1022'
|
||||
- '-QF1001'
|
||||
- '-QF1008' # TODO must be fixed
|
||||
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- std-error-handling
|
||||
rules:
|
||||
- path: (.+)_test.go
|
||||
linters:
|
||||
- canonicalheader
|
||||
- fatcontext
|
||||
- funlen
|
||||
- goconst
|
||||
- godot
|
||||
- path: (.+)_test.go
|
||||
text: ' always receives '
|
||||
linters:
|
||||
- unparam
|
||||
- path: pkg/server/service/bufferpool.go
|
||||
text: 'SA6002: argument should be pointer-like to avoid allocations'
|
||||
- path: pkg/server/middleware/middlewares.go
|
||||
text: Function 'buildConstructor' has too many statements
|
||||
linters:
|
||||
- funlen
|
||||
- path: pkg/provider/kubernetes/ingress-nginx/kubernetes.go
|
||||
text: Function 'loadConfiguration' has too many statements
|
||||
linters:
|
||||
- funlen
|
||||
- path: pkg/tracing/haystack/logger.go
|
||||
linters:
|
||||
- goprintffuncname
|
||||
- path: pkg/tracing/tracing.go
|
||||
text: printf-like formatting function 'SetErrorWithEvent' should be named 'SetErrorWithEventf'
|
||||
linters:
|
||||
- goprintffuncname
|
||||
- path: pkg/tls/tlsmanager_test.go
|
||||
text: 'SA1019: config.ClientCAs.Subjects has been deprecated since Go 1.18'
|
||||
- path: pkg/types/tls_test.go
|
||||
text: 'SA1019: tlsConfig.RootCAs.Subjects has been deprecated since Go 1.18'
|
||||
- path: pkg/provider/kubernetes/(crd|gateway)/client.go
|
||||
linters:
|
||||
- interfacebloat
|
||||
- path: pkg/observability/metrics/metrics.go
|
||||
linters:
|
||||
- interfacebloat
|
||||
- path: integration/healthcheck_test.go
|
||||
text: Duplicate words \(wsp2,\) found
|
||||
linters:
|
||||
- dupword
|
||||
- path: pkg/types/domain_test.go
|
||||
text: Duplicate words \(sub\) found
|
||||
linters:
|
||||
- dupword
|
||||
- path: pkg/provider/kubernetes/gateway/client_mock_test.go
|
||||
text: 'unusedwrite: unused write to field'
|
||||
linters:
|
||||
- govet
|
||||
- path: pkg/provider/acme/local_store.go
|
||||
linters:
|
||||
- musttag
|
||||
- path: pkg/tls/certificate.go
|
||||
text: the methods of "Certificates" use pointer receiver and non-pointer receiver.
|
||||
linters:
|
||||
- recvcheck
|
||||
- path: pkg/config/static/static_config.go
|
||||
source: 'errors.New\("Consul Catalog provider'
|
||||
text: 'ST1005: error strings should not be capitalized'
|
||||
- path: pkg/config/static/static_config.go
|
||||
source: 'errors.New\("Consul provider'
|
||||
text: 'ST1005: error strings should not be capitalized'
|
||||
- path: pkg/config/static/static_config.go
|
||||
source: 'errors.New\("Nomad provider'
|
||||
text: 'ST1005: error strings should not be capitalized'
|
||||
- path: (.+)\.go
|
||||
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'
|
||||
linters:
|
||||
- revive
|
||||
- path: (pkg/types/.+|pkg/api/.+|pkg/observability/types/.+)\.go
|
||||
text: 'var-naming: avoid meaningless package names'
|
||||
linters:
|
||||
- revive
|
||||
- path: (pkg/muxer/http/.+|pkg/provider/http/.+)\.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: pkg/provider/kubernetes/crd/kubernetes.go
|
||||
text: "Function 'loadConfigurationFromCRD' has too many statements"
|
||||
linters:
|
||||
- funlen
|
||||
- path: pkg/plugins/middlewarewasm.go
|
||||
text: 'the methods of "wasmMiddlewareBuilder" use pointer receiver and non-pointer receiver.'
|
||||
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
|
||||
paths:
|
||||
- pkg/provider/kubernetes/crd/generated/
|
||||
|
||||
issues:
|
||||
exclude-use-default: false
|
||||
max-per-linter: 0
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
exclude:
|
||||
- 'Error return value of .((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked'
|
||||
- "should have a package comment, unless it's in another file for this package"
|
||||
- 'SA1019: http.CloseNotifier has been deprecated' # FIXME must be fixed
|
||||
- 'SA1019: cfg.SSLRedirect is deprecated'
|
||||
- 'SA1019: cfg.SSLTemporaryRedirect is deprecated'
|
||||
- 'SA1019: cfg.SSLHost is deprecated'
|
||||
- 'SA1019: cfg.SSLForceHost is deprecated'
|
||||
- 'SA1019: cfg.FeaturePolicy is deprecated'
|
||||
- 'SA1019: c.Providers.ConsulCatalog.Namespace is deprecated'
|
||||
- 'SA1019: c.Providers.Consul.Namespace is deprecated'
|
||||
- 'SA1019: c.Providers.Nomad.Namespace is deprecated'
|
||||
exclude-rules:
|
||||
- path: '(.+)_test.go'
|
||||
linters:
|
||||
- goconst
|
||||
- funlen
|
||||
- godot
|
||||
- path: '(.+)_test.go'
|
||||
text: ' always receives '
|
||||
linters:
|
||||
- unparam
|
||||
- path: '(.+)\.go'
|
||||
text: 'struct-tag: unknown option ''inline'' in JSON tag'
|
||||
linters:
|
||||
- revive
|
||||
- path: pkg/server/service/bufferpool.go
|
||||
text: 'SA6002: argument should be pointer-like to avoid allocations'
|
||||
- path: pkg/server/middleware/middlewares.go
|
||||
text: "Function 'buildConstructor' has too many statements"
|
||||
linters:
|
||||
- funlen
|
||||
- path: pkg/tracing/haystack/logger.go
|
||||
linters:
|
||||
- goprintffuncname
|
||||
- path: pkg/tracing/tracing.go
|
||||
text: "printf-like formatting function 'SetErrorWithEvent' should be named 'SetErrorWithEventf'"
|
||||
linters:
|
||||
- goprintffuncname
|
||||
- path: pkg/tls/tlsmanager_test.go
|
||||
text: 'SA1019: config.ClientCAs.Subjects has been deprecated since Go 1.18'
|
||||
- path: pkg/types/tls_test.go
|
||||
text: 'SA1019: tlsConfig.RootCAs.Subjects has been deprecated since Go 1.18'
|
||||
- path: pkg/provider/kubernetes/(crd|gateway)/client.go
|
||||
linters:
|
||||
- interfacebloat
|
||||
- path: pkg/metrics/metrics.go
|
||||
linters:
|
||||
- interfacebloat
|
||||
- path: integration/healthcheck_test.go
|
||||
text: 'Duplicate words \(wsp2,\) found'
|
||||
linters:
|
||||
- dupword
|
||||
- path: pkg/types/domain_test.go
|
||||
text: 'Duplicate words \(sub\) found'
|
||||
linters:
|
||||
- dupword
|
||||
- path: pkg/provider/kubernetes/gateway/client_mock_test.go
|
||||
text: 'unusedwrite: unused write to field'
|
||||
linters:
|
||||
- govet
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
project_name: traefik
|
||||
version: 2
|
||||
|
||||
before:
|
||||
hooks:
|
||||
- go generate
|
||||
[[if .GOARCH]]
|
||||
dist: "./dist/[[ .GOOS ]]-[[ .GOARCH ]]"
|
||||
[[else]]
|
||||
dist: "./dist/[[ .GOOS ]]"
|
||||
[[end]]
|
||||
|
||||
builds:
|
||||
- binary: traefik
|
||||
@@ -11,22 +14,23 @@ builds:
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
ldflags:
|
||||
- -s -w -X github.com/traefik/traefik/v2/pkg/version.Version={{.Version}} -X github.com/traefik/traefik/v2/pkg/version.Codename={{.Env.CODENAME}} -X github.com/traefik/traefik/v2/pkg/version.BuildDate={{.Date}}
|
||||
- -s -w -X github.com/traefik/traefik/v3/pkg/version.Version={{.Version}} -X github.com/traefik/traefik/v3/pkg/version.Codename={{.Env.CODENAME}} -X github.com/traefik/traefik/v3/pkg/version.BuildDate={{.Date}}
|
||||
flags:
|
||||
- -trimpath
|
||||
goos:
|
||||
- linux
|
||||
- darwin
|
||||
- windows
|
||||
- freebsd
|
||||
- openbsd
|
||||
- "[[ .GOOS ]]"
|
||||
goarch:
|
||||
[[if .GOARCH]]
|
||||
- "[[ .GOARCH ]]"
|
||||
[[else]]
|
||||
- amd64
|
||||
- '386'
|
||||
- arm
|
||||
- arm64
|
||||
- ppc64le
|
||||
- s390x
|
||||
- riscv64
|
||||
[[end]]
|
||||
goarm:
|
||||
- '7'
|
||||
- '6'
|
||||
@@ -45,15 +49,17 @@ builds:
|
||||
goarch: arm
|
||||
|
||||
changelog:
|
||||
skip: true
|
||||
disable: true
|
||||
|
||||
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,83 +0,0 @@
|
||||
version: v1.0
|
||||
name: Traefik
|
||||
agent:
|
||||
machine:
|
||||
type: e1-standard-4
|
||||
os_image: ubuntu2004
|
||||
|
||||
fail_fast:
|
||||
stop:
|
||||
when: "branch != 'master'"
|
||||
|
||||
auto_cancel:
|
||||
queued:
|
||||
when: "branch != 'master'"
|
||||
running:
|
||||
when: "branch != 'master'"
|
||||
|
||||
global_job_config:
|
||||
prologue:
|
||||
commands:
|
||||
- curl -sSfL https://raw.githubusercontent.com/ldez/semgo/master/godownloader.sh | sudo sh -s -- -b "/usr/local/bin"
|
||||
- sudo semgo go1.20
|
||||
- export "GOPATH=$(go env GOPATH)"
|
||||
- export "SEMAPHORE_GIT_DIR=${GOPATH}/src/github.com/traefik/${SEMAPHORE_PROJECT_NAME}"
|
||||
- export "PATH=${GOPATH}/bin:${PATH}"
|
||||
- mkdir -vp "${SEMAPHORE_GIT_DIR}" "${GOPATH}/bin"
|
||||
- export GOPROXY=https://proxy.golang.org,direct
|
||||
- curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b "${GOPATH}/bin" v1.52.2
|
||||
- curl -sSfL https://gist.githubusercontent.com/traefiker/6d7ac019c11d011e4f131bb2cca8900e/raw/goreleaser.sh | bash -s -- -b "${GOPATH}/bin"
|
||||
- checkout
|
||||
- cache restore traefik-$(checksum go.sum)
|
||||
|
||||
blocks:
|
||||
- name: Test Integration
|
||||
dependencies: []
|
||||
run:
|
||||
when: "branch =~ '.*' OR pull_request =~'.*'"
|
||||
task:
|
||||
jobs:
|
||||
- name: Test Integration
|
||||
commands:
|
||||
- make pull-images
|
||||
- touch webui/static/index.html # Avoid generating webui
|
||||
- IN_DOCKER="" make binary
|
||||
- make test-integration
|
||||
- df -h
|
||||
epilogue:
|
||||
always:
|
||||
commands:
|
||||
- cache store traefik-$(checksum go.sum) $HOME/go/pkg/mod
|
||||
|
||||
- name: Release
|
||||
dependencies: []
|
||||
run:
|
||||
when: "tag =~ '.*'"
|
||||
task:
|
||||
agent:
|
||||
machine:
|
||||
type: e1-standard-8
|
||||
os_image: ubuntu2004
|
||||
secrets:
|
||||
- name: traefik
|
||||
env_vars:
|
||||
- name: GH_VERSION
|
||||
value: 1.12.1
|
||||
- name: CODENAME
|
||||
value: "saintmarcelin"
|
||||
- name: IN_DOCKER
|
||||
value: ""
|
||||
prologue:
|
||||
commands:
|
||||
- export VERSION=${SEMAPHORE_GIT_TAG_NAME}
|
||||
- curl -sSL -o /tmp/gh_${GH_VERSION}_linux_amd64.tar.gz https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_amd64.tar.gz
|
||||
- tar -zxvf /tmp/gh_${GH_VERSION}_linux_amd64.tar.gz -C /tmp
|
||||
- sudo mv /tmp/gh_${GH_VERSION}_linux_amd64/bin/gh /usr/local/bin/gh
|
||||
- sudo rm -rf ~/.phpbrew ~/.kerl ~/.sbt ~/.nvm ~/.npm ~/.kiex /usr/lib/jvm /opt/az /opt/firefox # Remove unnecessary data.
|
||||
- sudo service docker stop && sudo umount /var/lib/docker && sudo service docker start # Unmounts the docker disk and the whole system disk is usable.
|
||||
jobs:
|
||||
- name: Release
|
||||
commands:
|
||||
- make release-packages
|
||||
- gh release create ${SEMAPHORE_GIT_TAG_NAME} ./dist/traefik*.* --repo traefik/traefik --title ${SEMAPHORE_GIT_TAG_NAME} --notes ${SEMAPHORE_GIT_TAG_NAME}
|
||||
- ./script/deploy.sh
|
||||
@@ -47,6 +47,18 @@ Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
When an inappropriate behavior is reported, maintainers will discuss on the Maintainer's Discord before marking the message as "abuse".
|
||||
This conversation beforehand avoids one-sided decisions.
|
||||
|
||||
The first message will be edited and marked as abuse.
|
||||
The second edited message and marked as abuse results in a 7-day ban.
|
||||
The third edited message and marked as abuse results in a permanent ban.
|
||||
|
||||
The content of edited messages is:
|
||||
`Dear user, we want traefik to provide a welcoming and respectful environment. Your [comment/issue/PR] has been reported and marked as abuse according to our [Code of Conduct](./CODE_OF_CONDUCT.md). Thank you.`
|
||||
|
||||
The [report must be resolved](https://docs.github.com/en/communities/moderating-comments-and-conversations/managing-reported-content-in-your-organizations-repository#resolving-a-report) accordingly.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
FROM scratch
|
||||
COPY script/ca-certificates.crt /etc/ssl/certs/
|
||||
COPY dist/traefik /
|
||||
# syntax=docker/dockerfile:1.2
|
||||
FROM alpine:3.23
|
||||
|
||||
RUN apk add --no-cache --no-progress ca-certificates tzdata
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
COPY ./dist/$TARGETPLATFORM/traefik /
|
||||
|
||||
EXPOSE 80
|
||||
VOLUME ["/tmp"]
|
||||
|
||||
ENTRYPOINT ["/traefik"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2023 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,126 +1,124 @@
|
||||
SRCS = $(shell git ls-files '*.go' | grep -v '^vendor/')
|
||||
|
||||
TAG_NAME := $(shell git tag -l --contains HEAD)
|
||||
TAG_NAME := $(shell git describe --abbrev=0 --tags --exact-match)
|
||||
SHA := $(shell git rev-parse HEAD)
|
||||
VERSION_GIT := $(if $(TAG_NAME),$(TAG_NAME),$(SHA))
|
||||
VERSION := $(if $(VERSION),$(VERSION),$(VERSION_GIT))
|
||||
|
||||
GIT_BRANCH := $(subst heads/,,$(shell git rev-parse --abbrev-ref HEAD 2>/dev/null))
|
||||
TRAEFIK_DEV_IMAGE := traefik-dev$(if $(GIT_BRANCH),:$(subst /,-,$(GIT_BRANCH)))
|
||||
BIN_NAME := traefik
|
||||
CODENAME ?= cheddar
|
||||
|
||||
REPONAME := $(shell echo $(REPO) | tr '[:upper:]' '[:lower:]')
|
||||
TRAEFIK_IMAGE := $(if $(REPONAME),$(REPONAME),"traefik/traefik")
|
||||
DATE := $(shell date -u '+%Y-%m-%d_%I:%M:%S%p')
|
||||
|
||||
INTEGRATION_OPTS := $(if $(MAKE_DOCKER_HOST),-e "DOCKER_HOST=$(MAKE_DOCKER_HOST)",-v "/var/run/docker.sock:/var/run/docker.sock")
|
||||
DOCKER_BUILD_ARGS := $(if $(DOCKER_VERSION), "--build-arg=DOCKER_VERSION=$(DOCKER_VERSION)",)
|
||||
# Default build target
|
||||
GOOS := $(shell go env GOOS)
|
||||
GOARCH := $(shell go env GOARCH)
|
||||
GOGC ?=
|
||||
|
||||
# only used when running in docker
|
||||
TRAEFIK_ENVS := \
|
||||
-e OS_ARCH_ARG \
|
||||
-e OS_PLATFORM_ARG \
|
||||
-e TESTFLAGS \
|
||||
-e VERBOSE \
|
||||
-e VERSION \
|
||||
-e CODENAME \
|
||||
-e TESTDIRS \
|
||||
-e CI \
|
||||
-e IN_DOCKER=true # Indicator for integration tests that we are running inside a container.
|
||||
LINT_EXECUTABLES = misspell shellcheck
|
||||
|
||||
TRAEFIK_MOUNT := -v "$(CURDIR)/dist:/go/src/github.com/traefik/traefik/dist"
|
||||
DOCKER_RUN_OPTS := $(TRAEFIK_ENVS) $(TRAEFIK_MOUNT) "$(TRAEFIK_DEV_IMAGE)"
|
||||
DOCKER_NON_INTERACTIVE ?= false
|
||||
DOCKER_RUN_TRAEFIK := docker run $(INTEGRATION_OPTS) $(if $(DOCKER_NON_INTERACTIVE), , -it) $(DOCKER_RUN_OPTS)
|
||||
DOCKER_RUN_TRAEFIK_TEST := docker run --add-host=host.docker.internal:127.0.0.1 --rm --name=traefik --network traefik-test-network -v $(PWD):$(PWD) -w $(PWD) $(INTEGRATION_OPTS) $(if $(DOCKER_NON_INTERACTIVE), , -it) $(DOCKER_RUN_OPTS)
|
||||
DOCKER_RUN_TRAEFIK_NOTTY := docker run $(INTEGRATION_OPTS) $(if $(DOCKER_NON_INTERACTIVE), , -i) $(DOCKER_RUN_OPTS)
|
||||
|
||||
IN_DOCKER ?= true
|
||||
DOCKER_BUILD_PLATFORMS ?= linux/amd64,linux/arm64
|
||||
|
||||
.PHONY: default
|
||||
default: binary
|
||||
#? default: Run `make generate` and `make binary`
|
||||
default: generate binary
|
||||
|
||||
## Create the "dist" directory
|
||||
#? dist: Create the "dist" directory
|
||||
dist:
|
||||
mkdir -p dist
|
||||
|
||||
## Build Dev Docker image
|
||||
.PHONY: build-dev-image
|
||||
build-dev-image: dist
|
||||
ifneq ("$(IN_DOCKER)", "")
|
||||
docker build $(DOCKER_BUILD_ARGS) -t "$(TRAEFIK_DEV_IMAGE)" --build-arg HOST_PWD="$(PWD)" -f build.Dockerfile .
|
||||
endif
|
||||
|
||||
## Build Dev Docker image without cache
|
||||
.PHONY: build-dev-image-no-cache
|
||||
build-dev-image-no-cache: dist
|
||||
ifneq ("$(IN_DOCKER)", "")
|
||||
docker build $(DOCKER_BUILD_ARGS) --no-cache -t "$(TRAEFIK_DEV_IMAGE)" --build-arg HOST_PWD="$(PWD)" -f build.Dockerfile .
|
||||
endif
|
||||
|
||||
## Build WebUI Docker image
|
||||
.PHONY: build-webui-image
|
||||
#? build-webui-image: 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
|
||||
|
||||
## Clean WebUI static generated assets
|
||||
.PHONY: clean-webui
|
||||
#? clean-webui: Clean WebUI static generated assets
|
||||
clean-webui:
|
||||
rm -r webui/static
|
||||
mkdir -p webui/static
|
||||
printf 'For more information see `webui/readme.md`' > webui/static/DONT-EDIT-FILES-IN-THIS-DIRECTORY.md
|
||||
|
||||
## Generate WebUI
|
||||
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
|
||||
generate-webui: webui/static/index.html
|
||||
|
||||
## Build the binary
|
||||
.PHONY: generate
|
||||
#? generate: Generate code (Dynamic and Static configuration documentation reference files)
|
||||
generate:
|
||||
go generate
|
||||
|
||||
.PHONY: binary
|
||||
binary: generate-webui build-dev-image
|
||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate binary
|
||||
#? 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 \
|
||||
-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)" \
|
||||
-installsuffix nocgo -o "./dist/${GOOS}/${GOARCH}/$(BIN_NAME)" ./cmd/traefik
|
||||
|
||||
## Build the linux binary locally
|
||||
.PHONY: binary-debug
|
||||
binary-debug: generate-webui
|
||||
GOOS=linux ./script/make.sh binary
|
||||
binary-linux-arm64: export GOOS := linux
|
||||
binary-linux-arm64: export GOARCH := arm64
|
||||
binary-linux-arm64:
|
||||
@$(MAKE) binary
|
||||
|
||||
binary-linux-amd64: export GOOS := linux
|
||||
binary-linux-amd64: export GOARCH := amd64
|
||||
binary-linux-amd64:
|
||||
@$(MAKE) binary
|
||||
|
||||
binary-windows-amd64: export GOOS := windows
|
||||
binary-windows-amd64: export GOARCH := amd64
|
||||
binary-windows-amd64: export BIN_NAME := traefik.exe
|
||||
binary-windows-amd64:
|
||||
@$(MAKE) binary
|
||||
|
||||
## Build the binary for the standard platforms (linux, darwin, windows)
|
||||
.PHONY: crossbinary-default
|
||||
crossbinary-default: generate-webui build-dev-image
|
||||
$(DOCKER_RUN_TRAEFIK_NOTTY) ./script/make.sh generate crossbinary-default
|
||||
#? crossbinary-default: Build the binary for the standard platforms (linux, darwin, windows)
|
||||
crossbinary-default: generate generate-webui
|
||||
$(CURDIR)/script/crossbinary-default.sh
|
||||
|
||||
## Build the binary for the standard platforms (linux, darwin, windows) in parallel
|
||||
.PHONY: crossbinary-default-parallel
|
||||
crossbinary-default-parallel:
|
||||
$(MAKE) generate-webui
|
||||
$(MAKE) build-dev-image crossbinary-default
|
||||
|
||||
## Run the unit and integration tests
|
||||
.PHONY: test
|
||||
test: build-dev-image
|
||||
-docker network create traefik-test-network --driver bridge --subnet 172.31.42.0/24
|
||||
trap 'docker network rm traefik-test-network' EXIT; \
|
||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_TEST)) ./script/make.sh generate test-unit binary test-integration
|
||||
#? test: Run the unit and integration tests
|
||||
test: test-ui-unit test-unit test-integration
|
||||
|
||||
## Run the unit tests
|
||||
.PHONY: test-unit
|
||||
test-unit: build-dev-image
|
||||
-docker network create traefik-test-network --driver bridge --subnet 172.31.42.0/24
|
||||
trap 'docker network rm traefik-test-network' EXIT; \
|
||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_TEST)) ./script/make.sh generate test-unit
|
||||
#? test-unit: Run the unit tests
|
||||
test-unit:
|
||||
GOOS=$(GOOS) GOARCH=$(GOARCH) go test -cover "-coverprofile=cover.out" -v $(TESTFLAGS) ./pkg/... ./cmd/...
|
||||
|
||||
## Run the integration tests
|
||||
.PHONY: test-integration
|
||||
test-integration: build-dev-image
|
||||
-docker network create traefik-test-network --driver bridge --subnet 172.31.42.0/24
|
||||
trap 'docker network rm traefik-test-network' EXIT; \
|
||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_TEST)) ./script/make.sh generate binary test-integration
|
||||
#? test-integration: Run the integration tests
|
||||
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 Gateway API conformance tests
|
||||
test-gateway-api-conformance: build-image-dirty
|
||||
# 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.6" $(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
|
||||
test-ui-unit:
|
||||
$(MAKE) build-webui-image
|
||||
docker run --rm -v "$(PWD)/webui/static":'/src/webui/static' traefik-webui yarn --cwd webui install
|
||||
docker run --rm -v "$(PWD)/webui/static":'/src/webui/static' traefik-webui yarn --cwd webui test:unit:ci
|
||||
|
||||
## Pull all images for integration tests
|
||||
.PHONY: pull-images
|
||||
#? pull-images: Pull all Docker images to avoid timeout during integration tests
|
||||
pull-images:
|
||||
grep --no-filename -E '^\s+image:' ./integration/resources/compose/*.yml \
|
||||
| awk '{print $$2}' \
|
||||
@@ -128,84 +126,76 @@ pull-images:
|
||||
| uniq \
|
||||
| xargs -P 6 -n 1 docker pull
|
||||
|
||||
## Validate code and docs
|
||||
.PHONY: lint
|
||||
#? lint: Run golangci-lint
|
||||
lint:
|
||||
golangci-lint run
|
||||
|
||||
.PHONY: validate-files
|
||||
validate-files: build-dev-image
|
||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate validate-lint validate-misspell
|
||||
bash $(CURDIR)/script/validate-shell-script.sh
|
||||
#? validate-files: Validate code and docs
|
||||
validate-files:
|
||||
$(foreach exec,$(LINT_EXECUTABLES),\
|
||||
$(if $(shell which $(exec)),,$(error "No $(exec) in PATH")))
|
||||
$(CURDIR)/script/validate-vendor.sh
|
||||
$(CURDIR)/script/validate-misspell.sh
|
||||
$(CURDIR)/script/validate-shell-script.sh
|
||||
|
||||
## Validate code, docs, and vendor
|
||||
.PHONY: validate
|
||||
validate: build-dev-image
|
||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate validate-lint validate-misspell validate-vendor
|
||||
bash $(CURDIR)/script/validate-shell-script.sh
|
||||
#? validate: Validate code, docs, and vendor
|
||||
validate: lint validate-files
|
||||
|
||||
# Target for building images for multiple architectures.
|
||||
.PHONY: multi-arch-image-%
|
||||
multi-arch-image-%: binary-linux-amd64 binary-linux-arm64
|
||||
docker buildx build $(DOCKER_BUILDX_ARGS) -t traefik/traefik:$* --platform=$(DOCKER_BUILD_PLATFORMS) -f Dockerfile .
|
||||
|
||||
|
||||
## Clean up static directory and build a Docker Traefik image
|
||||
.PHONY: build-image
|
||||
build-image: clean-webui binary
|
||||
docker build -t $(TRAEFIK_IMAGE) .
|
||||
#? build-image: Clean up static directory and build a Docker Traefik image
|
||||
build-image: export DOCKER_BUILDX_ARGS := --load
|
||||
build-image: export DOCKER_BUILD_PLATFORMS := linux/$(GOARCH)
|
||||
build-image: clean-webui
|
||||
@$(MAKE) multi-arch-image-latest
|
||||
|
||||
## Build a Docker Traefik image without re-building the webui
|
||||
.PHONY: build-image-dirty
|
||||
build-image-dirty: binary
|
||||
docker build -t $(TRAEFIK_IMAGE) .
|
||||
#? build-image-dirty: Build a Docker Traefik image without re-building the webui when it's already built
|
||||
build-image-dirty: export DOCKER_BUILDX_ARGS := --load
|
||||
build-image-dirty: export DOCKER_BUILD_PLATFORMS := linux/$(GOARCH)
|
||||
build-image-dirty:
|
||||
@$(MAKE) multi-arch-image-latest
|
||||
|
||||
## Locally build traefik for linux, then shove it an alpine image, with basic tools.
|
||||
.PHONY: build-image-debug
|
||||
build-image-debug: binary-debug
|
||||
docker build -t $(TRAEFIK_IMAGE) -f debug.Dockerfile .
|
||||
|
||||
## Start a shell inside the build env
|
||||
.PHONY: shell
|
||||
shell: build-dev-image
|
||||
$(DOCKER_RUN_TRAEFIK) /bin/bash
|
||||
|
||||
## Build documentation site
|
||||
.PHONY: docs
|
||||
#? docs: Build documentation site
|
||||
docs:
|
||||
make -C ./docs docs
|
||||
|
||||
## Serve the documentation site locally
|
||||
.PHONY: docs-serve
|
||||
#? docs-serve: Serve the documentation site locally
|
||||
docs-serve:
|
||||
make -C ./docs docs-serve
|
||||
|
||||
## Pull image for doc building
|
||||
.PHONY: docs-pull-images
|
||||
#? docs-pull-images: Pull image for doc building
|
||||
docs-pull-images:
|
||||
make -C ./docs docs-pull-images
|
||||
|
||||
## Generate CRD clientset and CRD manifests
|
||||
.PHONY: generate-crd
|
||||
#? generate-crd: Generate CRD clientset and CRD manifests
|
||||
generate-crd:
|
||||
@$(CURDIR)/script/code-gen.sh
|
||||
|
||||
## Generate code from dynamic configuration https://github.com/traefik/genconf
|
||||
.PHONY: generate-genconf
|
||||
#? generate-genconf: Generate code from dynamic configuration github.com/traefik/genconf
|
||||
generate-genconf:
|
||||
go run ./cmd/internal/gen/
|
||||
|
||||
## Create packages for the release
|
||||
.PHONY: release-packages
|
||||
release-packages: generate-webui build-dev-image
|
||||
rm -rf dist
|
||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) goreleaser release --skip-publish -p 2 --timeout="90m"
|
||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) tar cfz dist/traefik-${VERSION}.src.tar.gz \
|
||||
--exclude-vcs \
|
||||
--exclude .idea \
|
||||
--exclude .travis \
|
||||
--exclude .semaphoreci \
|
||||
--exclude .github \
|
||||
--exclude dist .
|
||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) chown -R $(shell id -u):$(shell id -g) dist/
|
||||
|
||||
## Format the Code
|
||||
.PHONY: fmt
|
||||
#? fmt: Format the Code
|
||||
fmt:
|
||||
gofmt -s -l -w $(SRCS)
|
||||
|
||||
.PHONY: run-dev
|
||||
run-dev:
|
||||
go generate
|
||||
GO111MODULE=on go build ./cmd/traefik
|
||||
./traefik
|
||||
.PHONY: help
|
||||
#? help: Get more info on make commands
|
||||
help: Makefile
|
||||
@echo " Choose a command run in traefik:"
|
||||
@sed -n 's/^#?//p' $< | column -t -s ':' | sort | sed -e 's/^/ /'
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
</picture>
|
||||
</p>
|
||||
|
||||
[](https://semaphoreci.com/containous/traefik)
|
||||
[](https://doc.traefik.io/traefik)
|
||||
[](https://goreportcard.com/report/traefik/traefik)
|
||||
[](https://github.com/traefik/traefik/blob/master/LICENSE.md)
|
||||
@@ -15,7 +14,7 @@
|
||||
[](https://twitter.com/intent/follow?screen_name=traefik)
|
||||
|
||||
Traefik (pronounced _traffic_) is a modern HTTP reverse proxy and load balancer that makes deploying microservices easy.
|
||||
Traefik integrates with your existing infrastructure components ([Docker](https://www.docker.com/), [Swarm mode](https://docs.docker.com/engine/swarm/), [Kubernetes](https://kubernetes.io), [Marathon](https://mesosphere.github.io/marathon/), [Consul](https://www.consul.io/), [Etcd](https://coreos.com/etcd/), [Rancher](https://rancher.com), [Amazon ECS](https://aws.amazon.com/ecs), ...) and configures itself automatically and dynamically.
|
||||
Traefik integrates with your existing infrastructure components ([Docker](https://www.docker.com/), [Swarm mode](https://docs.docker.com/engine/swarm/), [Kubernetes](https://kubernetes.io), [Consul](https://www.consul.io/), [Etcd](https://coreos.com/etcd/), [Rancher v2](https://rancher.com), [Amazon ECS](https://aws.amazon.com/ecs), ...) and configures itself automatically and dynamically.
|
||||
Pointing Traefik at your orchestrator should be the _only_ configuration step you need.
|
||||
|
||||
---
|
||||
@@ -35,7 +34,8 @@ Pointing Traefik at your orchestrator should be the _only_ configuration step yo
|
||||
|
||||
---
|
||||
|
||||
:warning: Please be aware that the old configurations for Traefik v1.x are NOT compatible with the v2.x config as of now. If you're running v2, please ensure you are using a [v2 configuration](https://doc.traefik.io/traefik/).
|
||||
: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
|
||||
|
||||
@@ -58,11 +58,11 @@ _(But if you'd rather configure some of your routes manually, Traefik supports t
|
||||
|
||||
- Continuously updates its configuration (No restarts!)
|
||||
- Supports multiple load balancing algorithms
|
||||
- Provides HTTPS to your microservices by leveraging [Let's Encrypt](https://letsencrypt.org) (wildcard certificates support)
|
||||
- Provides HTTPS to your microservices by leveraging [Let's Encrypt](https://letsencrypt.org) (wildcard certificates support)
|
||||
- Circuit breakers, retry
|
||||
- See the magic through its clean web UI
|
||||
- Websocket, HTTP/2, GRPC ready
|
||||
- Provides metrics (Rest, Prometheus, Datadog, Statsd, InfluxDB)
|
||||
- WebSocket, HTTP/2, gRPC ready
|
||||
- Provides metrics (Rest, Prometheus, Datadog, Statsd, InfluxDB 2.X)
|
||||
- Keeps access logs (JSON, CLF)
|
||||
- Fast
|
||||
- Exposes a Rest API
|
||||
@@ -72,8 +72,7 @@ _(But if you'd rather configure some of your routes manually, Traefik supports t
|
||||
|
||||
- [Docker](https://doc.traefik.io/traefik/providers/docker/) / [Swarm mode](https://doc.traefik.io/traefik/providers/docker/)
|
||||
- [Kubernetes](https://doc.traefik.io/traefik/providers/kubernetes-crd/)
|
||||
- [Marathon](https://doc.traefik.io/traefik/providers/marathon/)
|
||||
- [Rancher](https://doc.traefik.io/traefik/providers/rancher/) (Metadata)
|
||||
- [ECS](https://doc.traefik.io/traefik/providers/ecs/)
|
||||
- [File](https://doc.traefik.io/traefik/providers/file/)
|
||||
|
||||
## Quickstart
|
||||
@@ -88,9 +87,7 @@ You can access the simple HTML frontend of Traefik.
|
||||
|
||||
## Documentation
|
||||
|
||||
You can find the complete documentation of Traefik v2 at [https://doc.traefik.io/traefik/](https://doc.traefik.io/traefik/).
|
||||
|
||||
A collection of contributions around Traefik can be found at [https://awesome.traefik.io](https://awesome.traefik.io).
|
||||
You can find the complete documentation of Traefik v3 at [https://doc.traefik.io/traefik/](https://doc.traefik.io/traefik/).
|
||||
|
||||
## Support
|
||||
|
||||
@@ -127,8 +124,8 @@ You can find high level and deep dive videos on [videos.traefik.io](https://vide
|
||||
## Maintainers
|
||||
|
||||
We are strongly promoting a philosophy of openness and sharing, and firmly standing against the elitist closed approach. Being part of the core team should be accessible to anyone who is motivated and want to be part of that journey!
|
||||
This [document](docs/content/contributing/maintainers-guidelines.md) describes how to be part of the core team as well as various responsibilities and guidelines for Traefik maintainers.
|
||||
You can also find more information on our process to review pull requests and manage issues [in this document](docs/content/contributing/maintainers.md).
|
||||
This [document](docs/content/contributing/maintainers-guidelines.md) describes how to be part of the [maintainers' team](docs/content/contributing/maintainers.md) as well as various responsibilities and guidelines for Traefik maintainers.
|
||||
You can also find more information on our process to review pull requests and manage issues [in this document](https://github.com/traefik/contributors-guide/blob/master/issue_triage.md).
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -154,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 sending a mail to security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
|
||||
|
||||
Reported vulnerabilities can be found on [cve.mitre.org](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=traefik).
|
||||
|
||||
## Supported Versions
|
||||
|
||||
- We usually release 3/4 new versions (e.g. 1.1.0, 1.2.0, 1.3.0) per year.
|
||||
@@ -16,13 +11,17 @@ Each version is supported until the next one is released (e.g. 1.1.x will be sup
|
||||
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
|
||||
|
||||
We want to keep Traefik safe for everyone.
|
||||
If you've discovered a security vulnerability in Traefik, we appreciate your help in disclosing it to us in a responsible manner, using [this form](https://security.traefik.io).
|
||||
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).
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
FROM golang:1.20-alpine
|
||||
|
||||
RUN apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar ca-certificates tzdata \
|
||||
&& update-ca-certificates \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
# Which docker version to test on
|
||||
ARG DOCKER_VERSION=18.09.7
|
||||
|
||||
# Download docker
|
||||
RUN mkdir -p /usr/local/bin \
|
||||
&& curl -fL https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}.tgz \
|
||||
| tar -xzC /usr/local/bin --transform 's#^.+/##x'
|
||||
|
||||
# Download golangci-lint binary to bin folder in $GOPATH
|
||||
RUN curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash -s -- -b $GOPATH/bin v1.52.2
|
||||
|
||||
# Download misspell binary to bin folder in $GOPATH
|
||||
RUN curl -sfL https://raw.githubusercontent.com/golangci/misspell/master/install-misspell.sh | bash -s -- -b $GOPATH/bin v0.4.0
|
||||
|
||||
# Download goreleaser binary to bin folder in $GOPATH
|
||||
RUN curl -sfL https://gist.githubusercontent.com/traefiker/6d7ac019c11d011e4f131bb2cca8900e/raw/goreleaser.sh | sh
|
||||
|
||||
WORKDIR /go/src/github.com/traefik/traefik
|
||||
|
||||
# Because of CVE-2022-24765 (https://github.blog/2022-04-12-git-security-vulnerability-announced/),
|
||||
# we configure git to allow the Traefik codebase path on the Host for docker in docker usages.
|
||||
ARG HOST_PWD=""
|
||||
|
||||
RUN git config --global --add safe.directory "${HOST_PWD}"
|
||||
|
||||
# Download go modules
|
||||
COPY go.mod .
|
||||
COPY go.sum .
|
||||
RUN GO111MODULE=on GOPROXY=https://proxy.golang.org go mod download
|
||||
|
||||
COPY . /go/src/github.com/traefik/traefik
|
||||
@@ -4,12 +4,13 @@ import (
|
||||
"time"
|
||||
|
||||
ptypes "github.com/traefik/paerser/types"
|
||||
"github.com/traefik/traefik/v2/pkg/config/static"
|
||||
"github.com/traefik/traefik/v3/pkg/config/static"
|
||||
)
|
||||
|
||||
// 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"`
|
||||
}
|
||||
@@ -28,6 +29,10 @@ func NewTraefikConfiguration() *TraefikCmdConfiguration {
|
||||
ServersTransport: &static.ServersTransport{
|
||||
MaxIdleConnsPerHost: 200,
|
||||
},
|
||||
TCPServersTransport: &static.TCPServersTransport{
|
||||
DialTimeout: ptypes.Duration(30 * time.Second),
|
||||
DialKeepAlive: ptypes.Duration(15 * time.Second),
|
||||
},
|
||||
},
|
||||
ConfigFile: "",
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/traefik/paerser/cli"
|
||||
"github.com/traefik/traefik/v2/pkg/config/static"
|
||||
"github.com/traefik/traefik/v3/pkg/config/static"
|
||||
)
|
||||
|
||||
// NewCmd builds a new HealthCheck command.
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
@@ -80,7 +81,7 @@ func (c Centrifuge) Run(dest string, pkgName string) error {
|
||||
}
|
||||
|
||||
for _, p := range c.pkg.Imports() {
|
||||
if contains(c.IncludedImports, p.Path()) {
|
||||
if slices.Contains(c.IncludedImports, p.Path()) {
|
||||
fls := c.run(p.Scope(), p.Path(), p.Name())
|
||||
|
||||
err = fileWriter{baseDir: filepath.Join(dest, p.Name())}.Write(fls)
|
||||
@@ -97,7 +98,7 @@ func (c Centrifuge) run(sc *types.Scope, rootPkg string, pkgName string) map[str
|
||||
files := map[string]*File{}
|
||||
|
||||
for _, name := range sc.Names() {
|
||||
if contains(c.ExcludedTypes, name) {
|
||||
if slices.Contains(c.ExcludedTypes, name) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -107,7 +108,7 @@ func (c Centrifuge) run(sc *types.Scope, rootPkg string, pkgName string) map[str
|
||||
}
|
||||
|
||||
filename := filepath.Base(c.fileSet.File(o.Pos()).Name())
|
||||
if contains(c.ExcludedFiles, path.Join(rootPkg, filename)) {
|
||||
if slices.Contains(c.ExcludedFiles, path.Join(rootPkg, filename)) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -159,7 +160,7 @@ func (c Centrifuge) writeStruct(name string, obj *types.Struct, rootPkg string,
|
||||
b := strings.Builder{}
|
||||
b.WriteString(fmt.Sprintf("type %s struct {\n", name))
|
||||
|
||||
for i := 0; i < obj.NumFields(); i++ {
|
||||
for i := range obj.NumFields() {
|
||||
field := obj.Field(i)
|
||||
|
||||
if !field.Exported() {
|
||||
@@ -237,16 +238,6 @@ func extractPackage(t types.Type) string {
|
||||
}
|
||||
}
|
||||
|
||||
func contains(values []string, value string) bool {
|
||||
for _, val := range values {
|
||||
if val == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
type fileWriter struct {
|
||||
baseDir string
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const rootPkg = "github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
const rootPkg = "github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||
|
||||
const (
|
||||
destModuleName = "github.com/traefik/genconf"
|
||||
@@ -57,8 +57,8 @@ func run(dest string) error {
|
||||
}
|
||||
|
||||
centrifuge.IncludedImports = []string{
|
||||
"github.com/traefik/traefik/v2/pkg/tls",
|
||||
"github.com/traefik/traefik/v2/pkg/types",
|
||||
"github.com/traefik/traefik/v3/pkg/tls",
|
||||
"github.com/traefik/traefik/v3/pkg/types",
|
||||
}
|
||||
|
||||
centrifuge.ExcludedTypes = []string{
|
||||
@@ -71,8 +71,8 @@ func run(dest string) error {
|
||||
}
|
||||
|
||||
centrifuge.ExcludedFiles = []string{
|
||||
"github.com/traefik/traefik/v2/pkg/types/logs.go",
|
||||
"github.com/traefik/traefik/v2/pkg/types/metrics.go",
|
||||
"github.com/traefik/traefik/v3/pkg/types/logs.go",
|
||||
"github.com/traefik/traefik/v3/pkg/types/metrics.go",
|
||||
}
|
||||
|
||||
centrifuge.TypeCleaner = cleanType
|
||||
@@ -83,15 +83,15 @@ 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 {
|
||||
if typ.String() == "github.com/traefik/traefik/v2/pkg/tls.FileOrContent" {
|
||||
if typ.String() == "github.com/traefik/traefik/v3/pkg/types.FileOrContent" {
|
||||
return "string"
|
||||
}
|
||||
|
||||
if typ.String() == "[]github.com/traefik/traefik/v2/pkg/tls.FileOrContent" {
|
||||
if typ.String() == "[]github.com/traefik/traefik/v3/pkg/types.FileOrContent" {
|
||||
return "[]string"
|
||||
}
|
||||
|
||||
@@ -103,8 +103,8 @@ func cleanType(typ types.Type, base string) string {
|
||||
return strings.ReplaceAll(typ.String(), base+".", "")
|
||||
}
|
||||
|
||||
if strings.Contains(typ.String(), "github.com/traefik/traefik/v2/pkg/") {
|
||||
return strings.ReplaceAll(typ.String(), "github.com/traefik/traefik/v2/pkg/", "")
|
||||
if strings.Contains(typ.String(), "github.com/traefik/traefik/v3/pkg/") {
|
||||
return strings.ReplaceAll(typ.String(), "github.com/traefik/traefik/v3/pkg/", "")
|
||||
}
|
||||
|
||||
return typ.String()
|
||||
@@ -114,9 +114,9 @@ func cleanPackage(src string) string {
|
||||
switch src {
|
||||
case "github.com/traefik/paerser/types":
|
||||
return ""
|
||||
case "github.com/traefik/traefik/v2/pkg/tls":
|
||||
case "github.com/traefik/traefik/v3/pkg/tls":
|
||||
return path.Join(destModuleName, destPkg, "tls")
|
||||
case "github.com/traefik/traefik/v2/pkg/types":
|
||||
case "github.com/traefik/traefik/v3/pkg/types":
|
||||
return path.Join(destModuleName, destPkg, "types")
|
||||
default:
|
||||
return src
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
stdlog "log"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/traefik/traefik/v3/pkg/config/static"
|
||||
"github.com/traefik/traefik/v3/pkg/observability/logs"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// hide the first logs before the setup of the logger.
|
||||
zerolog.SetGlobalLevel(zerolog.ErrorLevel)
|
||||
}
|
||||
|
||||
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.
|
||||
if staticConfiguration.Log != nil && staticConfiguration.Log.OTLP != nil &&
|
||||
(staticConfiguration.Experimental == nil || !staticConfiguration.Experimental.OTLPLogs) {
|
||||
return errors.New("the experimental OTLPLogs feature must be enabled to use OTLP logging")
|
||||
}
|
||||
|
||||
// configure log format
|
||||
w := getLogWriter(staticConfiguration)
|
||||
|
||||
// configure log level
|
||||
logLevel := getLogLevel(staticConfiguration)
|
||||
zerolog.SetGlobalLevel(logLevel)
|
||||
|
||||
// create logger
|
||||
logger := zerolog.New(w).With().Timestamp()
|
||||
if logLevel <= zerolog.DebugLevel {
|
||||
logger = logger.Caller()
|
||||
}
|
||||
|
||||
log.Logger = logger.Logger().Level(logLevel)
|
||||
|
||||
if staticConfiguration.Log != nil && staticConfiguration.Log.OTLP != nil {
|
||||
var err error
|
||||
log.Logger, err = logs.SetupOTelLogger(ctx, log.Logger, staticConfiguration.Log.OTLP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting up OpenTelemetry logger: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
zerolog.DefaultContextLogger = &log.Logger
|
||||
|
||||
// Global logrus replacement (related to lib like go-rancher-metadata, docker, etc.)
|
||||
logrus.StandardLogger().Out = logs.NoLevel(log.Logger, zerolog.DebugLevel)
|
||||
|
||||
// configure default standard log.
|
||||
stdlog.SetFlags(stdlog.Lshortfile | stdlog.LstdFlags)
|
||||
stdlog.SetOutput(logs.NoLevel(log.Logger, zerolog.DebugLevel))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLogWriter(staticConfiguration *static.Configuration) io.Writer {
|
||||
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)
|
||||
w = &lumberjack.Logger{
|
||||
Filename: staticConfiguration.Log.FilePath,
|
||||
MaxSize: staticConfiguration.Log.MaxSize,
|
||||
MaxBackups: staticConfiguration.Log.MaxBackups,
|
||||
MaxAge: staticConfiguration.Log.MaxAge,
|
||||
Compress: true,
|
||||
}
|
||||
}
|
||||
|
||||
if staticConfiguration.Log == nil || staticConfiguration.Log.Format != "json" {
|
||||
w = zerolog.ConsoleWriter{
|
||||
Out: w,
|
||||
TimeFormat: time.RFC3339,
|
||||
NoColor: staticConfiguration.Log != nil && (staticConfiguration.Log.NoColor || len(staticConfiguration.Log.FilePath) > 0),
|
||||
}
|
||||
}
|
||||
|
||||
return w
|
||||
}
|
||||
|
||||
func getLogLevel(staticConfiguration *static.Configuration) zerolog.Level {
|
||||
levelStr := "error"
|
||||
if staticConfiguration.Log != nil && staticConfiguration.Log.Level != "" {
|
||||
levelStr = strings.ToLower(staticConfiguration.Log.Level)
|
||||
}
|
||||
|
||||
logLevel, err := zerolog.ParseLevel(strings.ToLower(levelStr))
|
||||
if err != nil {
|
||||
log.Error().Err(err).
|
||||
Str("logLevel", levelStr).
|
||||
Msg("Unspecified or invalid log level, setting the level to default (ERROR)...")
|
||||
|
||||
logLevel = zerolog.ErrorLevel
|
||||
}
|
||||
|
||||
return logLevel
|
||||
}
|
||||
@@ -2,43 +2,62 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/traefik/traefik/v2/pkg/config/static"
|
||||
"github.com/traefik/traefik/v2/pkg/plugins"
|
||||
"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 {
|
||||
|
||||
@@ -3,54 +3,59 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
stdlog "log"
|
||||
"maps"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"slices"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-systemd/daemon"
|
||||
"github.com/coreos/go-systemd/v22/daemon"
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
gokitmetrics "github.com/go-kit/kit/metrics"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spiffe/go-spiffe/v2/workloadapi"
|
||||
"github.com/traefik/paerser/cli"
|
||||
"github.com/traefik/traefik/v2/cmd"
|
||||
"github.com/traefik/traefik/v2/cmd/healthcheck"
|
||||
cmdVersion "github.com/traefik/traefik/v2/cmd/version"
|
||||
tcli "github.com/traefik/traefik/v2/pkg/cli"
|
||||
"github.com/traefik/traefik/v2/pkg/collector"
|
||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v2/pkg/config/runtime"
|
||||
"github.com/traefik/traefik/v2/pkg/config/static"
|
||||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
"github.com/traefik/traefik/v2/pkg/metrics"
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares/accesslog"
|
||||
"github.com/traefik/traefik/v2/pkg/provider/acme"
|
||||
"github.com/traefik/traefik/v2/pkg/provider/aggregator"
|
||||
"github.com/traefik/traefik/v2/pkg/provider/traefik"
|
||||
"github.com/traefik/traefik/v2/pkg/safe"
|
||||
"github.com/traefik/traefik/v2/pkg/server"
|
||||
"github.com/traefik/traefik/v2/pkg/server/middleware"
|
||||
"github.com/traefik/traefik/v2/pkg/server/service"
|
||||
traefiktls "github.com/traefik/traefik/v2/pkg/tls"
|
||||
"github.com/traefik/traefik/v2/pkg/tracing"
|
||||
"github.com/traefik/traefik/v2/pkg/tracing/jaeger"
|
||||
"github.com/traefik/traefik/v2/pkg/types"
|
||||
"github.com/traefik/traefik/v2/pkg/version"
|
||||
"github.com/vulcand/oxy/v2/roundrobin"
|
||||
"github.com/traefik/traefik/v3/cmd"
|
||||
"github.com/traefik/traefik/v3/cmd/healthcheck"
|
||||
cmdVersion "github.com/traefik/traefik/v3/cmd/version"
|
||||
tcli "github.com/traefik/traefik/v3/pkg/cli"
|
||||
"github.com/traefik/traefik/v3/pkg/collector"
|
||||
"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/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"
|
||||
"github.com/traefik/traefik/v3/pkg/provider/traefik"
|
||||
"github.com/traefik/traefik/v3/pkg/proxy"
|
||||
"github.com/traefik/traefik/v3/pkg/proxy/httputil"
|
||||
"github.com/traefik/traefik/v3/pkg/redactor"
|
||||
"github.com/traefik/traefik/v3/pkg/safe"
|
||||
"github.com/traefik/traefik/v3/pkg/server"
|
||||
"github.com/traefik/traefik/v3/pkg/server/middleware"
|
||||
"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/version"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// traefik config inits
|
||||
tConfig := cmd.NewTraefikConfiguration()
|
||||
|
||||
loaders := []cli.ResourceLoader{&tcli.FileLoader{}, &tcli.FlagLoader{}, &tcli.EnvLoader{}}
|
||||
loaders := []cli.ResourceLoader{&tcli.DeprecationLoader{}, &tcli.FileLoader{}, &tcli.FlagLoader{}, &tcli.EnvLoader{}}
|
||||
|
||||
cmdTraefik := &cli.Command{
|
||||
Name: "traefik",
|
||||
@@ -77,7 +82,7 @@ Complete documentation is available at https://traefik.io`,
|
||||
|
||||
err = cli.Execute(cmdTraefik)
|
||||
if err != nil {
|
||||
stdlog.Println(err)
|
||||
log.Error().Err(err).Msg("Command error")
|
||||
logrus.Exit(1)
|
||||
}
|
||||
|
||||
@@ -85,32 +90,36 @@ Complete documentation is available at https://traefik.io`,
|
||||
}
|
||||
|
||||
func runCmd(staticConfiguration *static.Configuration) error {
|
||||
configureLogging(staticConfiguration)
|
||||
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.6/migrate/v3/#encoded-characters-configuration-default-values")
|
||||
|
||||
http.DefaultTransport.(*http.Transport).Proxy = http.ProxyFromEnvironment
|
||||
|
||||
if err := roundrobin.SetDefaultWeight(0); err != nil {
|
||||
log.WithoutContext().Errorf("Could not set round robin default weight: %v", err)
|
||||
}
|
||||
|
||||
staticConfiguration.SetEffectiveConfiguration()
|
||||
if err := staticConfiguration.ValidateConfiguration(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.WithoutContext().Infof("Traefik version %s built on %s", version.Version, version.BuildDate)
|
||||
log.Info().Str("version", version.Version).
|
||||
Msgf("Traefik version %s built on %s", version.Version, version.BuildDate)
|
||||
|
||||
jsonConf, err := json.Marshal(staticConfiguration)
|
||||
redactedStaticConfiguration, err := redactor.RemoveCredentials(staticConfiguration)
|
||||
if err != nil {
|
||||
log.WithoutContext().Errorf("Could not marshal static configuration: %v", err)
|
||||
log.WithoutContext().Debugf("Static configuration loaded [struct] %#v", staticConfiguration)
|
||||
log.Error().Err(err).Msg("Could not redact static configuration")
|
||||
} else {
|
||||
log.WithoutContext().Debugf("Static configuration loaded %s", string(jsonConf))
|
||||
log.Debug().RawJSON("staticConfiguration", []byte(redactedStaticConfiguration)).Msg("Static configuration loaded [json]")
|
||||
}
|
||||
|
||||
if staticConfiguration.Global.CheckNewVersion {
|
||||
checkNewVersion()
|
||||
}
|
||||
checkNewVersion(staticConfiguration)
|
||||
|
||||
stats(staticConfiguration)
|
||||
|
||||
@@ -119,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)
|
||||
}
|
||||
@@ -130,16 +137,16 @@ func runCmd(staticConfiguration *static.Configuration) error {
|
||||
|
||||
sent, err := daemon.SdNotify(false, "READY=1")
|
||||
if !sent && err != nil {
|
||||
log.WithoutContext().Errorf("Failed to notify: %v", err)
|
||||
log.Error().Err(err).Msg("Failed to notify")
|
||||
}
|
||||
|
||||
t, err := daemon.SdWatchdogEnabled(false)
|
||||
if err != nil {
|
||||
log.WithoutContext().Errorf("Could not enable Watchdog: %v", err)
|
||||
log.Error().Err(err).Msg("Could not enable Watchdog")
|
||||
} else if t != 0 {
|
||||
// Send a ping each half time given
|
||||
t /= 2
|
||||
log.WithoutContext().Infof("Watchdog activated with timer duration %s", t)
|
||||
log.Info().Msgf("Watchdog activated with timer duration %s", t)
|
||||
safe.Go(func() {
|
||||
tick := time.Tick(t)
|
||||
for range tick {
|
||||
@@ -150,17 +157,17 @@ func runCmd(staticConfiguration *static.Configuration) error {
|
||||
|
||||
if staticConfiguration.Ping == nil || errHealthCheck == nil {
|
||||
if ok, _ := daemon.SdNotify(false, "WATCHDOG=1"); !ok {
|
||||
log.WithoutContext().Error("Fail to tick watchdog")
|
||||
log.Error().Msg("Fail to tick watchdog")
|
||||
}
|
||||
} else {
|
||||
log.WithoutContext().Error(errHealthCheck)
|
||||
log.Error().Err(errHealthCheck).Send()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
svr.Wait()
|
||||
log.WithoutContext().Info("Shutting down")
|
||||
log.Info().Msg("Shutting down")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -178,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()
|
||||
@@ -187,11 +196,30 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
acmeProviders := initACMEProvider(staticConfiguration, &providerAggregator, tlsManager, httpChallengeProvider, tlsChallengeProvider)
|
||||
acmeProviders := initACMEProvider(staticConfiguration, providerAggregator, tlsManager, httpChallengeProvider, tlsChallengeProvider, routinesPool)
|
||||
|
||||
// Tailscale
|
||||
|
||||
tsProviders := initTailscaleProviders(staticConfiguration, providerAggregator)
|
||||
|
||||
// Observability
|
||||
|
||||
metricRegistries := registerMetricClients(staticConfiguration.Metrics)
|
||||
var semConvMetricRegistry *metrics.SemConvMetricsRegistry
|
||||
if staticConfiguration.Metrics != nil && staticConfiguration.Metrics.OTLP != nil {
|
||||
semConvMetricRegistry, err = metrics.NewSemConvMetricRegistry(ctx, staticConfiguration.Metrics.OTLP)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create SemConv metric registry: %w", err)
|
||||
}
|
||||
}
|
||||
metricsRegistry := metrics.NewMultiRegistry(metricRegistries)
|
||||
accessLog := setupAccessLog(ctx, staticConfiguration.AccessLog)
|
||||
tracer, tracerCloser := setupTracing(ctx, staticConfiguration.Tracing)
|
||||
observabilityMgr := middleware.NewObservabilityMgr(*staticConfiguration, metricsRegistry, semConvMetricRegistry, accessLog, tracer, tracerCloser)
|
||||
|
||||
// Entrypoints
|
||||
|
||||
serverEntryPointsTCP, err := server.NewTCPEntryPoints(staticConfiguration.EntryPoints, staticConfiguration.HostResolver)
|
||||
serverEntryPointsTCP, err := server.NewTCPEntryPoints(staticConfiguration.EntryPoints, staticConfiguration.HostResolver, metricsRegistry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -201,15 +229,29 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if staticConfiguration.Pilot != nil {
|
||||
log.WithoutContext().Warn("Traefik Pilot has been removed.")
|
||||
if staticConfiguration.API != nil {
|
||||
version.DisableDashboardAd = staticConfiguration.API.DisableDashboardAd
|
||||
}
|
||||
|
||||
// Plugins
|
||||
pluginLogger := log.Ctx(ctx).With().Logger()
|
||||
hasPlugins := staticConfiguration.Experimental != nil && (staticConfiguration.Experimental.Plugins != nil || staticConfiguration.Experimental.LocalPlugins != nil)
|
||||
if hasPlugins {
|
||||
pluginsList := slices.Collect(maps.Keys(staticConfiguration.Experimental.Plugins))
|
||||
pluginsList = append(pluginsList, slices.Collect(maps.Keys(staticConfiguration.Experimental.LocalPlugins))...)
|
||||
|
||||
pluginLogger = pluginLogger.With().Strs("plugins", pluginsList).Logger()
|
||||
pluginLogger.Info().Msg("Loading plugins...")
|
||||
}
|
||||
|
||||
pluginBuilder, err := createPluginBuilder(staticConfiguration)
|
||||
if err != nil && staticConfiguration.Experimental != nil && staticConfiguration.Experimental.AbortOnPluginFailure {
|
||||
return nil, fmt.Errorf("plugin: failed to create plugin builder: %w", err)
|
||||
}
|
||||
if err != nil {
|
||||
log.WithoutContext().WithError(err).Error("Plugins are disabled because an error has occurred.")
|
||||
pluginLogger.Err(err).Msg("Plugins are disabled because an error has occurred.")
|
||||
} else if hasPlugins {
|
||||
pluginLogger.Info().Msg("Plugins loaded.")
|
||||
}
|
||||
|
||||
// Providers plugins
|
||||
@@ -230,24 +272,44 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
||||
}
|
||||
}
|
||||
|
||||
// Metrics
|
||||
|
||||
metricRegistries := registerMetricClients(staticConfiguration.Metrics)
|
||||
metricsRegistry := metrics.NewMultiRegistry(metricRegistries)
|
||||
|
||||
// Service manager factory
|
||||
|
||||
roundTripperManager := service.NewRoundTripperManager()
|
||||
var spiffeX509Source *workloadapi.X509Source
|
||||
if staticConfiguration.Spiffe != nil && staticConfiguration.Spiffe.WorkloadAPIAddr != "" {
|
||||
log.Info().Str("workloadAPIAddr", staticConfiguration.Spiffe.WorkloadAPIAddr).
|
||||
Msg("Waiting on SPIFFE SVID delivery")
|
||||
|
||||
spiffeX509Source, err = workloadapi.NewX509Source(
|
||||
ctx,
|
||||
workloadapi.WithClientOptions(
|
||||
workloadapi.WithAddr(
|
||||
staticConfiguration.Spiffe.WorkloadAPIAddr,
|
||||
),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create SPIFFE x509 source: %w", err)
|
||||
}
|
||||
log.Info().Msg("Successfully obtained SPIFFE SVID.")
|
||||
}
|
||||
|
||||
transportManager := service.NewTransportManager(spiffeX509Source)
|
||||
|
||||
var proxyBuilder service.ProxyBuilder = httputil.NewProxyBuilder(transportManager, semConvMetricRegistry)
|
||||
if staticConfiguration.Experimental != nil && staticConfiguration.Experimental.FastProxy != nil {
|
||||
proxyBuilder = proxy.NewSmartBuilder(transportManager, proxyBuilder, *staticConfiguration.Experimental.FastProxy)
|
||||
}
|
||||
|
||||
dialerManager := tcp.NewDialerManager(spiffeX509Source)
|
||||
acmeHTTPHandler := getHTTPChallengeHandler(acmeProviders, httpChallengeProvider)
|
||||
managerFactory := service.NewManagerFactory(*staticConfiguration, routinesPool, metricsRegistry, roundTripperManager, acmeHTTPHandler)
|
||||
managerFactory := service.NewManagerFactory(*staticConfiguration, routinesPool, observabilityMgr, transportManager, proxyBuilder, acmeHTTPHandler)
|
||||
|
||||
// Router factory
|
||||
|
||||
accessLog := setupAccessLog(staticConfiguration.AccessLog)
|
||||
tracer := setupTracing(staticConfiguration.Tracing)
|
||||
|
||||
chainBuilder := middleware.NewChainBuilder(metricsRegistry, accessLog, tracer)
|
||||
routerFactory := server.NewRouterFactory(*staticConfiguration, managerFactory, tlsManager, chainBuilder, pluginBuilder, metricsRegistry)
|
||||
routerFactory, err := server.NewRouterFactory(*staticConfiguration, managerFactory, tlsManager, observabilityMgr, pluginBuilder, dialerManager)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating router factory: %w", err)
|
||||
}
|
||||
|
||||
// Watcher
|
||||
|
||||
@@ -277,7 +339,9 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
||||
|
||||
// Server Transports
|
||||
watcher.AddListener(func(conf dynamic.Configuration) {
|
||||
roundTripperManager.Update(conf.HTTP.ServersTransports)
|
||||
transportManager.Update(conf.HTTP.ServersTransports)
|
||||
proxyBuilder.Update(conf.HTTP.ServersTransports)
|
||||
dialerManager.Update(conf.TCP.ServersTransports)
|
||||
})
|
||||
|
||||
// Switch router
|
||||
@@ -297,13 +361,22 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
||||
// TLS challenge
|
||||
watcher.AddListener(tlsChallengeProvider.ListenConfiguration)
|
||||
|
||||
// ACME
|
||||
// Certificate Resolvers
|
||||
|
||||
resolverNames := map[string]struct{}{}
|
||||
|
||||
// ACME
|
||||
for _, p := range acmeProviders {
|
||||
resolverNames[p.ResolverName] = struct{}{}
|
||||
watcher.AddListener(p.ListenConfiguration)
|
||||
}
|
||||
|
||||
// Tailscale
|
||||
for _, p := range tsProviders {
|
||||
resolverNames[p.ResolverName] = struct{}{}
|
||||
watcher.AddListener(p.HandleConfigUpdate)
|
||||
}
|
||||
|
||||
// Certificate resolver logs
|
||||
watcher.AddListener(func(config dynamic.Configuration) {
|
||||
for rtName, rt := range config.HTTP.Routers {
|
||||
@@ -312,12 +385,13 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
||||
}
|
||||
|
||||
if _, ok := resolverNames[rt.TLS.CertResolver]; !ok {
|
||||
log.WithoutContext().Errorf("the router %s uses a non-existent resolver: %s", rtName, rt.TLS.CertResolver)
|
||||
log.Error().Err(err).Str(logs.RouterName, rtName).Str("certificateResolver", rt.TLS.CertResolver).
|
||||
Msg("Router uses a nonexistent certificate resolver")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return server.NewServer(routinesPool, serverEntryPointsTCP, serverEntryPointsUDP, watcher, chainBuilder, accessLog), nil
|
||||
return server.NewServer(routinesPool, serverEntryPointsTCP, serverEntryPointsUDP, watcher, observabilityMgr), nil
|
||||
}
|
||||
|
||||
func getHTTPChallengeHandler(acmeProviders []*acme.Provider, httpChallengeProvider http.Handler) http.Handler {
|
||||
@@ -333,11 +407,27 @@ func getHTTPChallengeHandler(acmeProviders []*acme.Provider, httpChallengeProvid
|
||||
|
||||
func getDefaultsEntrypoints(staticConfiguration *static.Configuration) []string {
|
||||
var defaultEntryPoints []string
|
||||
|
||||
// Determines if at least one EntryPoint is configured to be used by default.
|
||||
var hasDefinedDefaults bool
|
||||
for _, ep := range staticConfiguration.EntryPoints {
|
||||
if ep.AsDefault {
|
||||
hasDefinedDefaults = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for name, cfg := range staticConfiguration.EntryPoints {
|
||||
// By default all entrypoints are considered.
|
||||
// If at least one is flagged, then only flagged entrypoints are included.
|
||||
if hasDefinedDefaults && !cfg.AsDefault {
|
||||
continue
|
||||
}
|
||||
|
||||
protocol, err := cfg.GetProtocol()
|
||||
if err != nil {
|
||||
// Should never happen because Traefik should not start if protocol is invalid.
|
||||
log.WithoutContext().Errorf("Invalid protocol: %v", err)
|
||||
log.Error().Err(err).Msg("Invalid protocol")
|
||||
}
|
||||
|
||||
if protocol != "udp" && name != static.DefaultInternalEntryPointName {
|
||||
@@ -345,7 +435,7 @@ func getDefaultsEntrypoints(staticConfiguration *static.Configuration) []string
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(defaultEntryPoints)
|
||||
slices.Sort(defaultEntryPoints)
|
||||
return defaultEntryPoints
|
||||
}
|
||||
|
||||
@@ -360,8 +450,8 @@ func switchRouter(routerFactory *server.RouterFactory, serverEntryPointsTCP serv
|
||||
}
|
||||
}
|
||||
|
||||
// initACMEProvider creates an acme provider from the ACME part of globalConfiguration.
|
||||
func initACMEProvider(c *static.Configuration, providerAggregator *aggregator.ProviderAggregator, tlsManager *traefiktls.Manager, httpChallengeProvider, tlsChallengeProvider challenge.Provider) []*acme.Provider {
|
||||
// initACMEProvider creates and registers acme.Provider instances corresponding to the configured ACME certificate resolvers.
|
||||
func initACMEProvider(c *static.Configuration, providerAggregator *aggregator.ProviderAggregator, tlsManager *traefiktls.Manager, httpChallengeProvider, tlsChallengeProvider challenge.Provider, routinesPool *safe.Pool) []*acme.Provider {
|
||||
localStores := map[string]*acme.LocalStore{}
|
||||
|
||||
var resolvers []*acme.Provider
|
||||
@@ -371,7 +461,7 @@ func initACMEProvider(c *static.Configuration, providerAggregator *aggregator.Pr
|
||||
}
|
||||
|
||||
if localStores[resolver.ACME.Storage] == nil {
|
||||
localStores[resolver.ACME.Storage] = acme.NewLocalStore(resolver.ACME.Storage)
|
||||
localStores[resolver.ACME.Storage] = acme.NewLocalStore(resolver.ACME.Storage, routinesPool)
|
||||
}
|
||||
|
||||
p := &acme.Provider{
|
||||
@@ -383,7 +473,7 @@ func initACMEProvider(c *static.Configuration, providerAggregator *aggregator.Pr
|
||||
}
|
||||
|
||||
if err := providerAggregator.AddProvider(p); err != nil {
|
||||
log.WithoutContext().Errorf("The ACME resolver %q is skipped from the resolvers list because: %v", name, err)
|
||||
log.Error().Err(err).Str("resolver", name).Msg("The ACME resolve is skipped from the resolvers list")
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -397,7 +487,28 @@ func initACMEProvider(c *static.Configuration, providerAggregator *aggregator.Pr
|
||||
return resolvers
|
||||
}
|
||||
|
||||
func registerMetricClients(metricsConfig *types.Metrics) []metrics.Registry {
|
||||
// initTailscaleProviders creates and registers tailscale.Provider instances corresponding to the configured Tailscale certificate resolvers.
|
||||
func initTailscaleProviders(cfg *static.Configuration, providerAggregator *aggregator.ProviderAggregator) []*tailscale.Provider {
|
||||
var providers []*tailscale.Provider
|
||||
for name, resolver := range cfg.CertificatesResolvers {
|
||||
if resolver.Tailscale == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
tsProvider := &tailscale.Provider{ResolverName: name}
|
||||
|
||||
if err := providerAggregator.AddProvider(tsProvider); err != nil {
|
||||
log.Error().Err(err).Str(logs.ProviderName, name).Msg("Unable to create Tailscale provider")
|
||||
continue
|
||||
}
|
||||
|
||||
providers = append(providers, tsProvider)
|
||||
}
|
||||
|
||||
return providers
|
||||
}
|
||||
|
||||
func registerMetricClients(metricsConfig *otypes.Metrics) []metrics.Registry {
|
||||
if metricsConfig == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -405,42 +516,59 @@ func registerMetricClients(metricsConfig *types.Metrics) []metrics.Registry {
|
||||
var registries []metrics.Registry
|
||||
|
||||
if metricsConfig.Prometheus != nil {
|
||||
ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "prometheus"))
|
||||
prometheusRegister := metrics.RegisterPrometheus(ctx, metricsConfig.Prometheus)
|
||||
logger := log.With().Str(logs.MetricsProviderName, "prometheus").Logger()
|
||||
|
||||
prometheusRegister := metrics.RegisterPrometheus(logger.WithContext(context.Background()), metricsConfig.Prometheus)
|
||||
if prometheusRegister != nil {
|
||||
registries = append(registries, prometheusRegister)
|
||||
log.FromContext(ctx).Debug("Configured Prometheus metrics")
|
||||
logger.Debug().Msg("Configured Prometheus metrics")
|
||||
}
|
||||
}
|
||||
|
||||
if metricsConfig.Datadog != nil {
|
||||
ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "datadog"))
|
||||
registries = append(registries, metrics.RegisterDatadog(ctx, metricsConfig.Datadog))
|
||||
log.FromContext(ctx).Debugf("Configured Datadog metrics: pushing to %s once every %s",
|
||||
metricsConfig.Datadog.Address, metricsConfig.Datadog.PushInterval)
|
||||
logger := log.With().Str(logs.MetricsProviderName, "datadog").Logger()
|
||||
|
||||
registries = append(registries, metrics.RegisterDatadog(logger.WithContext(context.Background()), metricsConfig.Datadog))
|
||||
logger.Debug().
|
||||
Str("address", metricsConfig.Datadog.Address).
|
||||
Str("pushInterval", metricsConfig.Datadog.PushInterval.String()).
|
||||
Msgf("Configured Datadog metrics")
|
||||
}
|
||||
|
||||
if metricsConfig.StatsD != nil {
|
||||
ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "statsd"))
|
||||
registries = append(registries, metrics.RegisterStatsd(ctx, metricsConfig.StatsD))
|
||||
log.FromContext(ctx).Debugf("Configured StatsD metrics: pushing to %s once every %s",
|
||||
metricsConfig.StatsD.Address, metricsConfig.StatsD.PushInterval)
|
||||
}
|
||||
logger := log.With().Str(logs.MetricsProviderName, "statsd").Logger()
|
||||
|
||||
if metricsConfig.InfluxDB != nil {
|
||||
ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "influxdb"))
|
||||
registries = append(registries, metrics.RegisterInfluxDB(ctx, metricsConfig.InfluxDB))
|
||||
log.FromContext(ctx).Debugf("Configured InfluxDB metrics: pushing to %s once every %s",
|
||||
metricsConfig.InfluxDB.Address, metricsConfig.InfluxDB.PushInterval)
|
||||
registries = append(registries, metrics.RegisterStatsd(logger.WithContext(context.Background()), metricsConfig.StatsD))
|
||||
logger.Debug().
|
||||
Str("address", metricsConfig.StatsD.Address).
|
||||
Str("pushInterval", metricsConfig.StatsD.PushInterval.String()).
|
||||
Msg("Configured StatsD metrics")
|
||||
}
|
||||
|
||||
if metricsConfig.InfluxDB2 != nil {
|
||||
ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "influxdb2"))
|
||||
influxDB2Register := metrics.RegisterInfluxDB2(ctx, metricsConfig.InfluxDB2)
|
||||
logger := log.With().Str(logs.MetricsProviderName, "influxdb2").Logger()
|
||||
|
||||
influxDB2Register := metrics.RegisterInfluxDB2(logger.WithContext(context.Background()), metricsConfig.InfluxDB2)
|
||||
if influxDB2Register != nil {
|
||||
registries = append(registries, influxDB2Register)
|
||||
log.FromContext(ctx).Debugf("Configured InfluxDB v2 metrics: pushing to %s (%s org/%s bucket) once every %s",
|
||||
metricsConfig.InfluxDB2.Address, metricsConfig.InfluxDB2.Org, metricsConfig.InfluxDB2.Bucket, metricsConfig.InfluxDB2.PushInterval)
|
||||
logger.Debug().
|
||||
Str("address", metricsConfig.InfluxDB2.Address).
|
||||
Str("bucket", metricsConfig.InfluxDB2.Bucket).
|
||||
Str("organization", metricsConfig.InfluxDB2.Org).
|
||||
Str("pushInterval", metricsConfig.InfluxDB2.PushInterval.String()).
|
||||
Msg("Configured InfluxDB v2 metrics")
|
||||
}
|
||||
}
|
||||
|
||||
if metricsConfig.OTLP != nil {
|
||||
logger := log.With().Str(logs.MetricsProviderName, "openTelemetry").Logger()
|
||||
|
||||
openTelemetryRegistry := metrics.RegisterOpenTelemetry(logger.WithContext(context.Background()), metricsConfig.OTLP)
|
||||
if openTelemetryRegistry != nil {
|
||||
registries = append(registries, openTelemetryRegistry)
|
||||
logger.Debug().
|
||||
Str("pushInterval", metricsConfig.OTLP.PushInterval.String()).
|
||||
Msg("Configured OpenTelemetry metrics")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,7 +576,7 @@ func registerMetricClients(metricsConfig *types.Metrics) []metrics.Registry {
|
||||
}
|
||||
|
||||
func appendCertMetric(gauge gokitmetrics.Gauge, certificate *x509.Certificate) {
|
||||
sort.Strings(certificate.DNSNames)
|
||||
slices.Sort(certificate.DNSNames)
|
||||
|
||||
labels := []string{
|
||||
"cn", certificate.Subject.CommonName,
|
||||
@@ -461,159 +589,69 @@ 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.WithoutContext().Warnf("Unable to create access logger: %v", err)
|
||||
log.Warn().Err(err).Msg("Unable to create access logger")
|
||||
return nil
|
||||
}
|
||||
|
||||
return accessLoggerMiddleware
|
||||
}
|
||||
|
||||
func setupTracing(conf *static.Tracing) *tracing.Tracing {
|
||||
func setupTracing(ctx context.Context, conf *static.Tracing) (*tracing.Tracer, io.Closer) {
|
||||
if conf == nil {
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var backend tracing.Backend
|
||||
|
||||
if conf.Jaeger != nil {
|
||||
backend = conf.Jaeger
|
||||
}
|
||||
|
||||
if conf.Zipkin != nil {
|
||||
if backend != nil {
|
||||
log.WithoutContext().Error("Multiple tracing backend are not supported: cannot create Zipkin backend.")
|
||||
} else {
|
||||
backend = conf.Zipkin
|
||||
}
|
||||
}
|
||||
|
||||
if conf.Datadog != nil {
|
||||
if backend != nil {
|
||||
log.WithoutContext().Error("Multiple tracing backend are not supported: cannot create Datadog backend.")
|
||||
} else {
|
||||
backend = conf.Datadog
|
||||
}
|
||||
}
|
||||
|
||||
if conf.Instana != nil {
|
||||
if backend != nil {
|
||||
log.WithoutContext().Error("Multiple tracing backend are not supported: cannot create Instana backend.")
|
||||
} else {
|
||||
backend = conf.Instana
|
||||
}
|
||||
}
|
||||
|
||||
if conf.Haystack != nil {
|
||||
if backend != nil {
|
||||
log.WithoutContext().Error("Multiple tracing backend are not supported: cannot create Haystack backend.")
|
||||
} else {
|
||||
backend = conf.Haystack
|
||||
}
|
||||
}
|
||||
|
||||
if conf.Elastic != nil {
|
||||
if backend != nil {
|
||||
log.WithoutContext().Error("Multiple tracing backend are not supported: cannot create Elastic backend.")
|
||||
} else {
|
||||
backend = conf.Elastic
|
||||
}
|
||||
}
|
||||
|
||||
if backend == nil {
|
||||
log.WithoutContext().Debug("Could not initialize tracing, using Jaeger by default")
|
||||
defaultBackend := &jaeger.Config{}
|
||||
defaultBackend.SetDefaults()
|
||||
backend = defaultBackend
|
||||
}
|
||||
|
||||
tracer, err := tracing.NewTracing(conf.ServiceName, conf.SpanNameLimit, backend)
|
||||
tracer, closer, err := tracing.NewTracing(ctx, conf)
|
||||
if err != nil {
|
||||
log.WithoutContext().Warnf("Unable to create tracer: %v", err)
|
||||
return nil
|
||||
log.Warn().Err(err).Msg("Unable to create tracer")
|
||||
return nil, nil
|
||||
}
|
||||
return tracer
|
||||
|
||||
return tracer, closer
|
||||
}
|
||||
|
||||
func configureLogging(staticConfiguration *static.Configuration) {
|
||||
// configure default log flags
|
||||
stdlog.SetFlags(stdlog.Lshortfile | stdlog.LstdFlags)
|
||||
func checkNewVersion(staticConfiguration *static.Configuration) {
|
||||
logger := log.With().Logger()
|
||||
|
||||
// configure log level
|
||||
// an explicitly defined log level always has precedence. if none is
|
||||
// given and debug mode is disabled, the default is ERROR, and DEBUG
|
||||
// otherwise.
|
||||
levelStr := "error"
|
||||
if staticConfiguration.Log != nil && staticConfiguration.Log.Level != "" {
|
||||
levelStr = strings.ToLower(staticConfiguration.Log.Level)
|
||||
}
|
||||
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/`)
|
||||
|
||||
level, err := logrus.ParseLevel(levelStr)
|
||||
if err != nil {
|
||||
log.WithoutContext().Errorf("Error getting level: %v", err)
|
||||
}
|
||||
log.SetLevel(level)
|
||||
|
||||
var logFile string
|
||||
if staticConfiguration.Log != nil && len(staticConfiguration.Log.FilePath) > 0 {
|
||||
logFile = staticConfiguration.Log.FilePath
|
||||
}
|
||||
|
||||
// configure log format
|
||||
var formatter logrus.Formatter
|
||||
if staticConfiguration.Log != nil && staticConfiguration.Log.Format == "json" {
|
||||
formatter = &logrus.JSONFormatter{}
|
||||
} else {
|
||||
disableColors := len(logFile) > 0
|
||||
formatter = &logrus.TextFormatter{DisableColors: disableColors, FullTimestamp: true, DisableSorting: true}
|
||||
}
|
||||
log.SetFormatter(formatter)
|
||||
|
||||
if len(logFile) > 0 {
|
||||
dir := filepath.Dir(logFile)
|
||||
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
log.WithoutContext().Errorf("Failed to create log path %s: %s", dir, err)
|
||||
}
|
||||
|
||||
err = log.OpenFile(logFile)
|
||||
logrus.RegisterExitHandler(func() {
|
||||
if err := log.CloseFile(); err != nil {
|
||||
log.WithoutContext().Errorf("Error while closing log: %v", err)
|
||||
ticker := time.Tick(24 * time.Hour)
|
||||
safe.Go(func() {
|
||||
for time.Sleep(10 * time.Minute); ; <-ticker {
|
||||
version.CheckNewVersion()
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
log.WithoutContext().Errorf("Error while opening log file %s: %v", logFile, err)
|
||||
}
|
||||
} 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 checkNewVersion() {
|
||||
ticker := time.Tick(24 * time.Hour)
|
||||
safe.Go(func() {
|
||||
for time.Sleep(10 * time.Minute); ; <-ticker {
|
||||
version.CheckNewVersion()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func stats(staticConfiguration *static.Configuration) {
|
||||
logger := log.WithoutContext()
|
||||
logger := log.With().Logger()
|
||||
|
||||
if staticConfiguration.Global.SendAnonymousUsage {
|
||||
logger.Info(`Stats collection is enabled.`)
|
||||
logger.Info(`Many thanks for contributing to Traefik's improvement by allowing us to receive anonymous information from your configuration.`)
|
||||
logger.Info(`Help us improve Traefik by leaving this feature on :)`)
|
||||
logger.Info(`More details on: https://doc.traefik.io/traefik/contributing/data-collection/`)
|
||||
logger.Info().Msg(`Stats collection is enabled.`)
|
||||
logger.Info().Msg(`Many thanks for contributing to Traefik's improvement by allowing us to receive anonymous information from your configuration.`)
|
||||
logger.Info().Msg(`Help us improve Traefik by leaving this feature on :)`)
|
||||
logger.Info().Msg(`More details on: https://doc.traefik.io/traefik/contributing/data-collection/`)
|
||||
collect(staticConfiguration)
|
||||
} else {
|
||||
logger.Info(`
|
||||
logger.Info().Msg(`
|
||||
Stats collection is disabled.
|
||||
Help us improve Traefik by turning this feature on :)
|
||||
More details on: https://doc.traefik.io/traefik/contributing/data-collection/
|
||||
@@ -626,7 +664,7 @@ func collect(staticConfiguration *static.Configuration) {
|
||||
safe.Go(func() {
|
||||
for time.Sleep(10 * time.Minute); ; <-ticker {
|
||||
if err := collector.Collect(staticConfiguration); err != nil {
|
||||
log.WithoutContext().Debug(err)
|
||||
log.Debug().Err(err).Send()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/go-kit/kit/metrics"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/traefik/traefik/v3/pkg/config/static"
|
||||
)
|
||||
|
||||
// FooCert is a PEM-encoded TLS cert.
|
||||
@@ -94,7 +95,6 @@ func TestAppendCertMetric(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -114,3 +114,73 @@ func TestAppendCertMetric(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDefaultsEntrypoints(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
entrypoints static.EntryPoints
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
desc: "Skips special names",
|
||||
entrypoints: map[string]*static.EntryPoint{
|
||||
"web": {
|
||||
Address: ":80",
|
||||
},
|
||||
"traefik": {
|
||||
Address: ":8080",
|
||||
},
|
||||
},
|
||||
expected: []string{"web"},
|
||||
},
|
||||
{
|
||||
desc: "Two EntryPoints not attachable",
|
||||
entrypoints: map[string]*static.EntryPoint{
|
||||
"web": {
|
||||
Address: ":80",
|
||||
},
|
||||
"websecure": {
|
||||
Address: ":443",
|
||||
},
|
||||
},
|
||||
expected: []string{"web", "websecure"},
|
||||
},
|
||||
{
|
||||
desc: "Two EntryPoints only one attachable",
|
||||
entrypoints: map[string]*static.EntryPoint{
|
||||
"web": {
|
||||
Address: ":80",
|
||||
},
|
||||
"websecure": {
|
||||
Address: ":443",
|
||||
AsDefault: true,
|
||||
},
|
||||
},
|
||||
expected: []string{"websecure"},
|
||||
},
|
||||
{
|
||||
desc: "Two attachable EntryPoints",
|
||||
entrypoints: map[string]*static.EntryPoint{
|
||||
"web": {
|
||||
Address: ":80",
|
||||
AsDefault: true,
|
||||
},
|
||||
"websecure": {
|
||||
Address: ":443",
|
||||
AsDefault: true,
|
||||
},
|
||||
},
|
||||
expected: []string{"web", "websecure"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
actual := getDefaultsEntrypoints(&static.Configuration{
|
||||
EntryPoints: test.entrypoints,
|
||||
})
|
||||
|
||||
assert.ElementsMatch(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"text/template"
|
||||
|
||||
"github.com/traefik/paerser/cli"
|
||||
"github.com/traefik/traefik/v2/pkg/version"
|
||||
"github.com/traefik/traefik/v3/pkg/version"
|
||||
)
|
||||
|
||||
var versionTemplate = `Version: {{.Version}}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
FROM alpine:3.14
|
||||
# Feel free to add below any helpful dependency for debugging.
|
||||
# iproute2 is for ss.
|
||||
RUN apk --no-cache --no-progress add bash curl ca-certificates tzdata lsof iproute2 \
|
||||
&& update-ca-certificates \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
COPY dist/traefik /
|
||||
EXPOSE 80
|
||||
VOLUME ["/tmp"]
|
||||
ENTRYPOINT ["/traefik"]
|
||||
@@ -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,20 +1,19 @@
|
||||
FROM alpine:3.14 as alpine
|
||||
FROM alpine:3.23
|
||||
|
||||
RUN apk --no-cache --no-progress add \
|
||||
build-base \
|
||||
gcompat \
|
||||
libcurl \
|
||||
libxml2-dev \
|
||||
libxslt-dev \
|
||||
ruby \
|
||||
ruby-bigdecimal \
|
||||
ruby-dev \
|
||||
ruby-etc \
|
||||
ruby-ffi \
|
||||
ruby-json \
|
||||
zlib-dev
|
||||
|
||||
RUN gem install nokogiri --version 1.13.3 --no-document -- --use-system-libraries
|
||||
RUN gem install html-proofer --version 3.19.3 --no-document -- --use-system-libraries
|
||||
RUN gem install nokogiri --version 1.18.6 --no-document -- --use-system-libraries
|
||||
RUN gem install html-proofer --version 5.0.10 --no-document -- --use-system-libraries
|
||||
|
||||
# After Ruby, some NodeJS YAY!
|
||||
RUN apk --no-cache --no-progress add \
|
||||
@@ -22,12 +21,9 @@ RUN apk --no-cache --no-progress add \
|
||||
nodejs \
|
||||
npm
|
||||
|
||||
# To handle 'not get uid/gid'
|
||||
RUN npm config set unsafe-perm true
|
||||
|
||||
RUN npm install --global \
|
||||
markdownlint@0.22.0 \
|
||||
markdownlint-cli@0.26.0
|
||||
markdownlint@0.29.0 \
|
||||
markdownlint-cli@0.35.0
|
||||
|
||||
# Finally the shell tools we need for later
|
||||
# tini helps to terminate properly all the parallelized tasks when sending CTRL-C
|
||||
@@ -38,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,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,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;
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 520 KiB |
|
After Width: | Height: | Size: 610 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 878 KiB |
|
After Width: | Height: | Size: 791 KiB |
|
After Width: | Height: | Size: 603 KiB |
|
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 |
|
After Width: | Height: | Size: 482 KiB |
|
After Width: | Height: | Size: 731 KiB |
|
After Width: | Height: | Size: 715 KiB |
|
After Width: | Height: | Size: 733 KiB |
|
After Width: | Height: | Size: 358 KiB |
|
Before Width: | Height: | Size: 452 KiB After Width: | Height: | Size: 1010 KiB |
|
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 241 KiB |
@@ -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,14 +1,14 @@
|
||||
---
|
||||
title: "Traefik Advocation Documentation"
|
||||
description: "There are many ways to contribute to Traefik Proxy. If you're talking about Traefik, let us know and we'll promote your enthusiasm!"
|
||||
description: "There are many ways to contribute to Traefik Proxy. Let us know if you’re talking about Traefik, and we'll promote your enthusiasm!"
|
||||
---
|
||||
|
||||
# Advocating
|
||||
|
||||
Spread the Love & Tell Us about It
|
||||
Spread the Love & Tell Us About It
|
||||
{: .subtitle }
|
||||
|
||||
Traefik Proxy was started by the community for the community.
|
||||
Traefik Proxy was started by the community and for the community.
|
||||
You can contribute to the Traefik community in three main ways:
|
||||
|
||||
**Spread the word!** Guides, videos, blog posts, how-to articles, and showing off your network design all help spread the word about Traefik Proxy
|
||||
|
||||
@@ -13,67 +13,13 @@ Let's see how.
|
||||
|
||||
## Building
|
||||
|
||||
You need either [Docker](https://github.com/docker/docker "Link to website of Docker") and `make` (Method 1), or [Go](https://go.dev/ "Link to website of Go") (Method 2) in order to build Traefik.
|
||||
For changes to its dependencies, the `dep` dependency management tool is required.
|
||||
|
||||
### Method 1: Using `Docker` and `Makefile`
|
||||
|
||||
Run make with the `binary` target.
|
||||
|
||||
```bash
|
||||
make binary
|
||||
```
|
||||
|
||||
This will create binaries for the Linux platform in the `dist` folder.
|
||||
|
||||
In case when you run build on CI, you may probably want to run docker in non-interactive mode. To achieve that define `DOCKER_NON_INTERACTIVE=true` environment variable.
|
||||
|
||||
```bash
|
||||
$ make binary
|
||||
docker build -t traefik-webui -f webui/Dockerfile webui
|
||||
Sending build context to Docker daemon 2.686MB
|
||||
Step 1/11 : FROM node:8.15.0
|
||||
---> 1f6c34f7921c
|
||||
[...]
|
||||
Successfully built ce4ff439c06a
|
||||
Successfully tagged traefik-webui:latest
|
||||
[...]
|
||||
docker build -t "traefik-dev:4475--feature-documentation" -f build.Dockerfile .
|
||||
Sending build context to Docker daemon 279MB
|
||||
Step 1/10 : FROM golang:1.16-alpine
|
||||
---> f4bfb3d22bda
|
||||
[...]
|
||||
Successfully built 5c3c1a911277
|
||||
Successfully tagged traefik-dev:4475--feature-documentation
|
||||
docker run -e "TEST_CONTAINER=1" -v "/var/run/docker.sock:/var/run/docker.sock" -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -e VERBOSE -e VERSION -e CODENAME -e TESTDIRS -e CI -e CONTAINER=DOCKER -v "/home/ldez/sources/go/src/github.com/traefik/traefik/"dist":/go/src/github.com/traefik/traefik/"dist"" "traefik-dev:4475--feature-documentation" ./script/make.sh generate binary
|
||||
---> Making bundle: generate (in .)
|
||||
removed 'autogen/genstatic/gen.go'
|
||||
|
||||
---> Making bundle: binary (in .)
|
||||
|
||||
$ ls dist/
|
||||
traefik*
|
||||
```
|
||||
|
||||
The following targets can be executed outside Docker by setting the variable `IN_DOCKER` to an empty string (although be aware that some of the tests might fail in that context):
|
||||
|
||||
- `test-unit`
|
||||
- `test-integration`
|
||||
- `validate`
|
||||
- `binary` (the webUI is still generated by using Docker)
|
||||
|
||||
ex:
|
||||
|
||||
```bash
|
||||
IN_DOCKER= make test-unit
|
||||
```
|
||||
|
||||
### Method 2: Using `go`
|
||||
|
||||
Requirements:
|
||||
|
||||
- `go` v1.16+
|
||||
- environment variable `GO111MODULE=on`
|
||||
You need:
|
||||
- [Docker](https://github.com/docker/docker "Link to website of Docker")
|
||||
- `make`
|
||||
- [Go](https://go.dev/ "Link to website of Go")
|
||||
- [misspell](https://github.com/golangci/misspell)
|
||||
- [shellcheck](https://github.com/koalaman/shellcheck)
|
||||
- [Tailscale](https://tailscale.com/) if you are using Docker Desktop
|
||||
|
||||
!!! tip "Source Directory"
|
||||
|
||||
@@ -106,43 +52,34 @@ Requirements:
|
||||
## ... and the list goes on
|
||||
```
|
||||
|
||||
#### Build Traefik
|
||||
### Build Traefik
|
||||
|
||||
Once you've set up your go environment and cloned the source repository, you can build Traefik.
|
||||
|
||||
```bash
|
||||
# Generate UI static files
|
||||
make clean-webui generate-webui
|
||||
$ make binary
|
||||
SHA: 8fddfe118288bb5280eb5e77fa952f52def360b4 cheddar 2024-01-11_03:14:57PM
|
||||
CGO_ENABLED=0 GOGC=off GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w \
|
||||
-X github.com/traefik/traefik/v2/pkg/version.Version=8fddfe118288bb5280eb5e77fa952f52def360b4 \
|
||||
-X github.com/traefik/traefik/v2/pkg/version.Codename=cheddar \
|
||||
-X github.com/traefik/traefik/v2/pkg/version.BuildDate=2024-01-11_03:14:57PM" \
|
||||
-installsuffix nocgo -o "./dist/darwin/arm64/traefik" ./cmd/traefik
|
||||
|
||||
# required to merge non-code components into the final binary,
|
||||
# such as the web dashboard/UI
|
||||
go generate
|
||||
$ ls dist/
|
||||
traefik*
|
||||
```
|
||||
|
||||
```bash
|
||||
# Standard go build
|
||||
go build ./cmd/traefik
|
||||
```
|
||||
|
||||
You will find the Traefik executable (`traefik`) in the `~/go/src/github.com/traefik/traefik` directory.
|
||||
You will find the Traefik executable (`traefik`) in the `./dist` directory.
|
||||
|
||||
## Testing
|
||||
|
||||
### Method 1: `Docker` and `make`
|
||||
|
||||
Run unit tests using the `test-unit` target.
|
||||
Run integration tests using the `test-integration` target.
|
||||
Run all tests (unit and integration) using the `test` target.
|
||||
|
||||
```bash
|
||||
$ make test-unit
|
||||
docker build -t "traefik-dev:your-feature-branch" -f build.Dockerfile .
|
||||
# […]
|
||||
docker run --rm -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -v "/home/user/go/src/github/traefik/traefik/dist:/go/src/github.com/traefik/traefik/dist" "traefik-dev:your-feature-branch" ./script/make.sh generate test-unit
|
||||
---> Making bundle: generate (in .)
|
||||
removed 'gen.go'
|
||||
|
||||
---> Making bundle: test-unit (in .)
|
||||
GOOS=darwin GOARCH=arm64 go test -cover "-coverprofile=cover.out" -v ./pkg/... ./cmd/...
|
||||
+ go test -cover -coverprofile=cover.out .
|
||||
ok github.com/traefik/traefik 0.005s coverage: 4.1% of statements
|
||||
|
||||
@@ -151,28 +88,30 @@ Test success
|
||||
|
||||
For development purposes, you can specify which tests to run by using (only works the `test-integration` target):
|
||||
|
||||
??? note "Configuring Tailscale for Docker Desktop user"
|
||||
|
||||
Create `tailscale.secret` file in `integration` directory.
|
||||
|
||||
This file needs to contain a [Tailscale auth key](https://tailscale.com/kb/1085/auth-keys)
|
||||
(an ephemeral, but reusable, one is recommended).
|
||||
|
||||
Add this section to your tailscale ACLs to auto-approve the routes for the
|
||||
containers in the docker subnet:
|
||||
|
||||
```json
|
||||
"autoApprovers": {
|
||||
// Allow myself to automatically
|
||||
// advertize routes for docker networks
|
||||
"routes": {
|
||||
"172.31.42.0/24": ["your_tailscale_identity"],
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
```bash
|
||||
# Run every tests in the MyTest suite
|
||||
TESTFLAGS="-check.f MyTestSuite" make test-integration
|
||||
TESTFLAGS="-test.run TestAccessLogSuite" make test-integration
|
||||
|
||||
# Run the test "MyTest" in the MyTest suite
|
||||
TESTFLAGS="-check.f MyTestSuite.MyTest" make test-integration
|
||||
|
||||
# Run every tests starting with "My", in the MyTest suite
|
||||
TESTFLAGS="-check.f MyTestSuite.My" make test-integration
|
||||
|
||||
# Run every tests ending with "Test", in the MyTest suite
|
||||
TESTFLAGS="-check.f MyTestSuite.*Test" make test-integration
|
||||
TESTFLAGS="-test.run TestAccessLogSuite -testify.m ^TestAccessLog$" make test-integration
|
||||
```
|
||||
|
||||
Check [gocheck](https://labix.org/gocheck "Link to website of gocheck") for more information.
|
||||
|
||||
### Method 2: `go`
|
||||
|
||||
Unit tests can be run from the cloned directory using `$ go test ./...` which should return `ok`, similar to:
|
||||
|
||||
```test
|
||||
ok _/home/user/go/src/github/traefik/traefik 0.004s
|
||||
```
|
||||
|
||||
Integration tests must be run from the `integration/` directory and require the `-integration` switch: `$ cd integration && go test -integration ./...`.
|
||||
|
||||
@@ -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).
|
||||
@@ -66,7 +106,6 @@ providers:
|
||||
docker:
|
||||
endpoint: "tcp://10.10.10.10:2375"
|
||||
exposedByDefault: true
|
||||
swarmMode: true
|
||||
|
||||
tls:
|
||||
ca: dockerCA
|
||||
@@ -86,7 +125,6 @@ providers:
|
||||
docker:
|
||||
endpoint: "xxxx"
|
||||
exposedByDefault: true
|
||||
swarmMode: true
|
||||
|
||||
tls:
|
||||
ca: xxxx
|
||||
@@ -95,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,13 +15,13 @@ Let's see how.
|
||||
|
||||
### General
|
||||
|
||||
This [documentation](https://doc.traefik.io/traefik/ "Link to the official Traefik documentation") is built with [MkDocs](https://mkdocs.org/ "Link to website of MkDocs").
|
||||
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`
|
||||
|
||||
Please make sure you have the following requirements installed:
|
||||
|
||||
- [Docker](https://www.docker.com/ "Link to website of Docker")
|
||||
- [Docker](https://www.docker.com/ "Link to the website of Docker")
|
||||
|
||||
You can build the documentation and test it locally (with live reloading), using the `docs-serve` target:
|
||||
|
||||
@@ -51,7 +51,7 @@ $ make docs-build
|
||||
|
||||
Please make sure you have the following requirements installed:
|
||||
|
||||
- [Python](https://www.python.org/ "Link to website of Python")
|
||||
- [Python](https://www.python.org/ "Link to the website of Python")
|
||||
- [pip](https://pypi.org/project/pip/ "Link to the website of pip on PyPI")
|
||||
|
||||
```bash
|
||||
|
||||
@@ -7,89 +7,75 @@ description: "Interested in contributing more to the community and becoming a Tr
|
||||
|
||||

|
||||
|
||||
Note: the document is a work in progress.
|
||||
|
||||
Welcome to the Traefik Community.
|
||||
This document describes how to be part of the core team
|
||||
together with various responsibilities
|
||||
and guidelines for Traefik maintainers.
|
||||
|
||||
We are strongly promoting a philosophy of openness and sharing,
|
||||
and firmly standing against the elitist closed approach.
|
||||
Being part of the core team should be accessible to anyone motivated
|
||||
and wants to be part of that journey!
|
||||
|
||||
## Onboarding Process
|
||||
## Becoming a Maintainer
|
||||
|
||||
If you consider joining our community, please drop us a line using Twitter or leave a note in the issue.
|
||||
We will schedule a quick call to meet you and learn more about your motivation.
|
||||
During the call, the team will discuss the process of becoming a maintainer.
|
||||
We will be happy to answer any questions and explain all your doubts.
|
||||
Before a contributor becomes a maintainer, they should meet the following requirements:
|
||||
|
||||
## Maintainer's Requirements
|
||||
- The contributor enabled [2FA](https://docs.github.com/en/authentication/securing-your-account-with-two-factor-authentication-2fa/configuring-two-factor-authentication) on their GitHub account
|
||||
|
||||
Note: you do not have to meet all the listed requirements,
|
||||
but must have achieved several.
|
||||
- The contributor showed a consistent pattern of helpful, non-threatening, and friendly behavior towards other community members in the past.
|
||||
|
||||
- The contributor has read and accepted the maintainer's guidelines.
|
||||
|
||||
The contributor should also meet one or several of the following requirements:
|
||||
|
||||
- Enabled [2FA](https://docs.github.com/en/authentication/securing-your-account-with-two-factor-authentication-2fa/configuring-two-factor-authentication) on your GitHub account
|
||||
- The contributor has opened and successfully run medium to large PR’s in the past 6 months.
|
||||
|
||||
- The contributor has participated in multiple code reviews of other PR’s,
|
||||
including those of other maintainers and contributors.
|
||||
- The contributor showed a consistent pattern of helpful, non-threatening, and friendly behavior towards other community members in the past.
|
||||
|
||||
- The contributor is active on Traefik Community forums
|
||||
or other technical forums/boards such as K8S slack, Reddit, StackOverflow, hacker news.
|
||||
- Have read and accepted the contributor guidelines.
|
||||
or other technical forums/boards, such as K8S Slack, Reddit, StackOverflow, and Hacker News.
|
||||
|
||||
Any existing active maintainer can create an issue to discuss promoting a contributor to maintainer.
|
||||
Other maintainers can vote on the issue, and if the quorum is reached, the contributor is promoted to maintainer.
|
||||
If the quorum is not reached within one month after the issue is created, it is closed.
|
||||
|
||||
## Maintainer's Responsibilities and Privileges
|
||||
|
||||
There are lots of areas where you can contribute to the project,
|
||||
but we can suggest you start with activities such as:
|
||||
As a maintainer, you are granted a vote for the following:
|
||||
|
||||
- PR reviewing.
|
||||
- According to our guidelines we require you have at least 3 reviewers,
|
||||
thus you can review a PR and leave the relevant comment if it is necessary.
|
||||
- Participating in a daily [issue triage](https://github.com/traefik/contributors-guide/blob/master/issue_triage.md).
|
||||
- The process helps to understand and prioritize the reported issue according to its importance and severity.
|
||||
This is crucial to learn how our users implement Traefik.
|
||||
Each of the issues that are labeled as bug/possible bug/confirmed requires a reproducible use case.
|
||||
You can help in creating a reproducible use case if it has not been added to the issue
|
||||
or use the sample code provided by the reporter.
|
||||
Typically, a simple Docker Compose should be enough to reproduce the issue.
|
||||
- Code contribution.
|
||||
- Documentation contribution.
|
||||
- Technical documentation is one of the most important components of the product.
|
||||
The ability to set up a testing environment in a few minutes,
|
||||
using the official documentation,
|
||||
is a game changer.
|
||||
- You will be listed on our Maintainers GitHub page
|
||||
and on our website in the section [maintainers](maintainers.md).
|
||||
- We will be promoting you on social channels (mostly on Twitter).
|
||||
- [PR review](https://github.com/traefik/contributors-guide/blob/master/pr_guidelines.md).
|
||||
|
||||
## Governance
|
||||
- [Design review](https://github.com/traefik/contributors-guide/blob/master/proposals.md).
|
||||
|
||||
- Roadmap meetings on a regular basis where all maintainers are welcome.
|
||||
- [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.
|
||||
|
||||
As a maintainer, you should:
|
||||
|
||||
- Prioritize PR reviews, design reviews, and issue triage above any other task.
|
||||
|
||||
Making sure contributors and community members are listened to and have an impact on the project is essential to keeping the project active and develop a thriving community.
|
||||
|
||||
- Prioritize helping contributors reaching the expecting quality level over rewriting contributions.
|
||||
|
||||
Any triage activity on issues and PRs (e.g. labels, marking messages as off-topic, refusing, marking duplicates) should result from a collective decision to ensure knowledge is shared among maintainers.
|
||||
|
||||
## Communicating
|
||||
|
||||
- All of our maintainers are added to Slack #traefik-maintainers channel that belongs to Traefik labs workspace.
|
||||
- All of our maintainers are added to the Traefik Maintainers Discord server that belongs to Traefik labs.
|
||||
Having the team in one place helps us to communicate effectively.
|
||||
You can reach Traefik core developers directly,
|
||||
which offers the possibility to discuss issues, pull requests, enhancements more efficiently
|
||||
Maintainers can discuss issues, pull requests, enhancements more efficiently
|
||||
and get the feedback almost immediately.
|
||||
Fewer blockers mean more fun and engaging work.
|
||||
|
||||
- On a daily basis, we publish a report that includes all the activities performed during the day.
|
||||
You are updated in regard to the workload that has been processed including:
|
||||
working on the new features and enhancements,
|
||||
activities related to the reported issues and PR’s,
|
||||
other important project-related announcements.
|
||||
- Every decision made on the discord server among maintainers is documented so it's visible to the rest of the community.
|
||||
|
||||
- At 5:00 PM CET every day we review all the created issues that have been reported,
|
||||
assign them the appropriate *[labels](maintainers.md#labels)*
|
||||
and prioritize them based on the severity of the problem.
|
||||
The process is called *[issue triaging](https://github.com/traefik/contributors-guide/blob/master/issue_triage.md)*.
|
||||
Each of the maintainers is welcome to join the meeting.
|
||||
For that purpose, we use a dedicated Discord server
|
||||
where you are invited once you have become the official maintainer.
|
||||
- Maintainers express their opinions on issues and reviews.
|
||||
It is fine to have different point of views.
|
||||
We encourage active and open conversations which goals are to improve Traefik.
|
||||
|
||||
- When discussing issues and proposals, maintainers should share as much information as possible to help solve the issue.
|
||||
|
||||
## Maintainers Activity
|
||||
|
||||
@@ -97,38 +83,45 @@ In order to keep the core team efficient and dynamic,
|
||||
maintainers' activity and involvement will be reviewed on a regular basis.
|
||||
|
||||
- Has the maintainer engaged with the team and the community by meeting two or more of these benchmarks in the past six months?
|
||||
|
||||
- Has the maintainer participated in at least two or three maintainer meetings?
|
||||
|
||||
- Substantial review of at least one or two PRs from either contributors or maintainers.
|
||||
|
||||
- Opened at least one or two bug fixes or feature request PRs
|
||||
that were eventually merged (or on a trajectory for merge).
|
||||
|
||||
- Substantial participation in the Help Wanted program (answered questions, helped identify issues, applied guidelines from the Help Wanted guide to open issues).
|
||||
|
||||
- Substantial participation with the community in general.
|
||||
|
||||
- Has the maintainer shown a consistent pattern of helpful,
|
||||
non-threatening,
|
||||
and friendly behavior towards other people on the maintainer team and with our community?
|
||||
|
||||
## Additional Comments for (not only) Maintainers
|
||||
## Additional Comments for Maintainers (that should apply to any contributor)
|
||||
|
||||
- Be respectful with other maintainers and other community members.
|
||||
|
||||
- Be open minded when participating in conversations: try to put yourself in others’ shoes.
|
||||
|
||||
- Be able to put yourself in users’ shoes.
|
||||
- Be open-minded and respectful with other maintainers and other community members.
|
||||
- Keep the communication public -
|
||||
if anyone tries to communicate with you directly,
|
||||
ask politely to move the conversation to a public communication channel.
|
||||
|
||||
- Stay away from defensive comments.
|
||||
|
||||
- Please try to express your thoughts clearly enough
|
||||
and note that some of us are not native English speakers.
|
||||
Try to rephrase your sentences, avoiding mental shortcuts;
|
||||
none of us is able to predict your thoughts.
|
||||
- There are a lot of use cases of using Traefik
|
||||
and even more issues that are difficult to reproduce.
|
||||
If the issue can’t be replicated due to a lack of reproducible case (a simple Docker Compose should be enough) -
|
||||
set your time limits while working on the issue
|
||||
and express clearly that you were not able to replicate it.
|
||||
You can come back later to that case.
|
||||
none of us is able to predict anyone's thoughts.
|
||||
|
||||
- Be proactive.
|
||||
|
||||
- Emoji are fine,
|
||||
but if you express yourself clearly enough they are not necessary.
|
||||
They will not replace good communication.
|
||||
- Embrace mentorship.
|
||||
- Keep in mind that we all have the same intent to improve the project.
|
||||
|
||||
- Embrace mentorship: help others grow and match the quality level we strive for.
|
||||
|
||||
- Keep in mind that we all have the same goal: improve the project.
|
||||
|
||||
@@ -5,18 +5,12 @@ description: "Traefik Proxy is an open source software with a thriving community
|
||||
|
||||
# Maintainers
|
||||
|
||||
## The Team
|
||||
## Active Maintainers
|
||||
|
||||
* Emile Vauge [@emilevauge](https://github.com/emilevauge)
|
||||
* Vincent Demeester [@vdemeester](https://github.com/vdemeester)
|
||||
* Ed Robinson [@errm](https://github.com/errm)
|
||||
* Daniel Tomcej [@dtomcej](https://github.com/dtomcej)
|
||||
* Manuel Zapf [@SantoDE](https://github.com/SantoDE)
|
||||
* Timo Reimann [@timoreimann](https://github.com/timoreimann)
|
||||
* Ludovic Fernandez [@ldez](https://github.com/ldez)
|
||||
* Julien Salleyron [@juliens](https://github.com/juliens)
|
||||
* Nicolas Mengin [@nmengin](https://github.com/nmengin)
|
||||
* Marco Jantke [@mjantke](https://github.com/mjeri)
|
||||
* Michaël Matur [@mmatur](https://github.com/mmatur)
|
||||
* Gérald Croës [@geraldcroes](https://github.com/geraldcroes)
|
||||
* Jean-Baptiste Doumenjou [@jbdoumenjou](https://github.com/jbdoumenjou)
|
||||
@@ -25,109 +19,23 @@ description: "Traefik Proxy is an open source software with a thriving community
|
||||
* Kevin Pollet [@kevinpollet](https://github.com/kevinpollet)
|
||||
* Harold Ozouf [@jspdown](https://github.com/jspdown)
|
||||
* Tom Moulard [@tommoulard](https://github.com/tommoulard)
|
||||
* Landry Benguigui [@lbenguigui](https://github.com/lbenguigui)
|
||||
* 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)
|
||||
|
||||
## Past Maintainers
|
||||
|
||||
People who have had an incredibly positive impact on the project, and are now focusing on other projects.
|
||||
|
||||
* Vincent Demeester [@vdemeester](https://github.com/vdemeester)
|
||||
* Ed Robinson [@errm](https://github.com/errm)
|
||||
* Daniel Tomcej [@dtomcej](https://github.com/dtomcej)
|
||||
* Timo Reimann [@timoreimann](https://github.com/timoreimann)
|
||||
* Marco Jantke [@mjantke](https://github.com/mjeri)
|
||||
* Ludovic Fernandez [@ldez](https://github.com/ldez)
|
||||
|
||||
## Maintainer's Guidelines
|
||||
|
||||
Please read the [maintainer's guidelines](maintainers-guidelines.md)
|
||||
|
||||
## Issue Triage
|
||||
|
||||
Issues and PRs are triaged daily and the process for triaging may be found under [triaging issues](https://github.com/traefik/contributors-guide/blob/master/issue_triage.md) in our [contributors guide repository](https://github.com/traefik/contributors-guide).
|
||||
|
||||
## PR Review Process
|
||||
|
||||
The process for reviewing PRs may be found under [review guidelines](https://github.com/traefik/contributors-guide/blob/master/review_guidelines.md) in our contributors guide repository.
|
||||
|
||||
## Labels
|
||||
|
||||
A maintainer that looks at an issue/PR must define its `kind/*`, `area/*`, and `status/*`.
|
||||
|
||||
### Status - Workflow
|
||||
|
||||
The `status/*` labels represent the desired state in the workflow.
|
||||
|
||||
* `status/0-needs-triage`: all the new issues and PRs have this status. _[bot only]_
|
||||
* `status/1-needs-design-review`: needs a design review. **(only for PR)**
|
||||
* `status/2-needs-review`: needs a code/documentation review. **(only for PR)**
|
||||
* `status/3-needs-merge`: ready to merge. **(only for PR)**
|
||||
* `status/4-merge-in-progress`: merge is in progress. _[bot only]_
|
||||
|
||||
### Contributor
|
||||
|
||||
* `contributor/need-more-information`: we need more information from the contributor in order to analyze a problem.
|
||||
* `contributor/waiting-for-feedback`: we need the contributor to give us feedback.
|
||||
* `contributor/waiting-for-corrections`: we need the contributor to take actions in order to move forward with a PR. **(only for PR)** _[bot, humans]_
|
||||
* `contributor/needs-resolve-conflicts`: use it only when there is some conflicts (and an automatic rebase is not possible). **(only for PR)** _[bot, humans]_
|
||||
|
||||
### Kind
|
||||
|
||||
* `kind/enhancement`: a new or improved feature.
|
||||
* `kind/question`: a question. **(only for issue)**
|
||||
* `kind/proposal`: a proposal that needs to be discussed.
|
||||
* _Proposal issues_ are design proposals
|
||||
* _Proposal PRs_ are technical prototypes that need to be refined with multiple contributors.
|
||||
|
||||
* `kind/bug/possible`: a possible bug that needs analysis before it is confirmed or fixed. **(only for issues)**
|
||||
* `kind/bug/confirmed`: a confirmed bug (reproducible). **(only for issues)**
|
||||
* `kind/bug/fix`: a bug fix. **(only for PR)**
|
||||
|
||||
### Resolution
|
||||
|
||||
* `resolution/duplicate`: a duplicate issue/PR.
|
||||
* `resolution/declined`: declined (Rule #1 of open-source: no is temporary, yes is forever).
|
||||
* `WIP`: Work In Progress. **(only for PR)**
|
||||
|
||||
### Platform
|
||||
|
||||
* `platform/windows`: Windows related.
|
||||
|
||||
### Area
|
||||
|
||||
* `area/acme`: ACME related.
|
||||
* `area/api`: Traefik API related.
|
||||
* `area/authentication`: Authentication related.
|
||||
* `area/cluster`: Traefik clustering related.
|
||||
* `area/documentation`: Documentation related.
|
||||
* `area/infrastructure`: CI or Traefik building scripts related.
|
||||
* `area/healthcheck`: Health-check related.
|
||||
* `area/logs`: Logs related.
|
||||
* `area/middleware`: Middleware related.
|
||||
* `area/middleware/metrics`: Metrics related. (Prometheus, StatsD, ...)
|
||||
* `area/middleware/tracing`: Tracing related. (Jaeger, Zipkin, ...)
|
||||
* `area/oxy`: Oxy related.
|
||||
* `area/provider`: related to all providers.
|
||||
* `area/provider/boltdb`: Boltd DB related.
|
||||
* `area/provider/consul`: Consul related.
|
||||
* `area/provider/docker`: Docker and Swarm related.
|
||||
* `area/provider/ecs`: ECS related.
|
||||
* `area/provider/etcd`: Etcd related.
|
||||
* `area/provider/eureka`: Eureka related.
|
||||
* `area/provider/file`: file provider related.
|
||||
* `area/provider/k8s`: Kubernetes related.
|
||||
* `area/provider/kv`: KV related.
|
||||
* `area/provider/marathon`: Marathon related.
|
||||
* `area/provider/mesos`: Mesos related.
|
||||
* `area/provider/rancher`: Rancher related.
|
||||
* `area/provider/servicefabric`: Azure service fabric related.
|
||||
* `area/provider/zk`: Zoo Keeper related.
|
||||
* `area/rules`: Rules related.
|
||||
* `area/server`: Server related.
|
||||
* `area/sticky-session`: Sticky session related.
|
||||
* `area/tls`: TLS related.
|
||||
* `area/websocket`: WebSocket related.
|
||||
* `area/webui`: Web UI related.
|
||||
|
||||
### Issues Priority
|
||||
|
||||
* `priority/P0`: needs hot fix.
|
||||
* `priority/P1`: need to be fixed in next release.
|
||||
* `priority/P2`: need to be fixed in the future.
|
||||
* `priority/P3`: maybe.
|
||||
|
||||
### PR Size
|
||||
|
||||
Automatically set by a bot.
|
||||
|
||||
* `size/S`: small PR.
|
||||
* `size/M`: medium PR.
|
||||
* `size/L`: Large PR.
|
||||
Please read the [maintainer's guidelines](maintainers-guidelines.md).
|
||||
|
||||
@@ -11,8 +11,8 @@ Help Us Help You!
|
||||
Issues are perfect for requesting a feature/enhancement or reporting a suspected bug.
|
||||
We use the [GitHub issue tracker](https://github.com/traefik/traefik/issues) to keep track of issues in Traefik.
|
||||
|
||||
The process of sorting and checking the issues is a daunting task, and requires a lot of work (more than an hour a day ... just for sorting).
|
||||
To help us (and other community members) quickly and effortlessly understand what you need,
|
||||
The process of sorting and checking the issues is a daunting task, and requires a lot of work.
|
||||
To help maintainers (and other community members) quickly and effortlessly understand what you need,
|
||||
be sure to follow the guidelines below.
|
||||
|
||||
!!! important "Getting Help Vs Reporting an Issue"
|
||||
|
||||
@@ -17,12 +17,9 @@ or the list of [confirmed bugs](https://github.com/traefik/traefik/labels/kind%2
|
||||
|
||||
## How We Prioritize
|
||||
|
||||
We wish we could review every pull request right away.
|
||||
Unfortunately, our team has to prioritize pull requests (PRs) for review
|
||||
(but we are welcoming new [maintainers](https://github.com/traefik/traefik/blob/master/docs/content/contributing/maintainers-guidelines.md) to speed this up,
|
||||
if you are interested, check it out and apply).
|
||||
We wish we could review every pull request right away, but because it's a time-consuming operation, it's not always possible.
|
||||
|
||||
The PRs we are able to handle fastest are:
|
||||
The PRs we are able to handle the fastest are:
|
||||
|
||||
* Documentation updates.
|
||||
* Bug fixes.
|
||||
@@ -57,9 +54,10 @@ Merging a PR requires the following steps to be completed before it is merged au
|
||||
* Keep "allows edit from maintainer" checked.
|
||||
* Use semantic line breaks for documentation.
|
||||
* Ensure your PR is not a draft. We do not review drafts, but do answer questions and confer with developers on them as needed.
|
||||
* Ensure that the dependencies in the `go.mod` file reference a tag. If referencing a tag is not possible, add a comment explaining why.
|
||||
* Pass the validation check.
|
||||
* Pass all tests.
|
||||
* Receive 3 approving reviews maintainers.
|
||||
* Receive 2 approving reviews from maintainers.
|
||||
|
||||
## Pull Request Review Cycle
|
||||
|
||||
@@ -92,6 +90,9 @@ in short, it looks like this:
|
||||
You must run these local verifications before you submit your pull request to predict the pass or failure of continuous integration.
|
||||
Your PR will not be reviewed until these are green on the CI.
|
||||
|
||||
* `make generate`
|
||||
* `make generate-crd`
|
||||
* `make test-gateway-api-conformance`
|
||||
* `make validate`
|
||||
* `make pull-images`
|
||||
* `make test`
|
||||
@@ -115,7 +116,7 @@ In such a situation, solve the conflicts/CI/... and then remove the label `bot/n
|
||||
|
||||
To prevent the bot from automatically merging a PR, add the label `bot/no-merge`.
|
||||
|
||||
The label `bot/light-review` decreases the number of required LGTM from 3 to 1.
|
||||
The label `bot/light-review` decreases the number of required LGTM from 2 to 1.
|
||||
|
||||
This label can be used when:
|
||||
|
||||
@@ -129,7 +130,7 @@ This label can be used when:
|
||||
Traefik Proxy is made by the community for the community,
|
||||
as such the goal is to engage the community to make Traefik the best reverse proxy available.
|
||||
Part of this goal is maintaining a lean codebase and ensuring code velocity.
|
||||
unfortunately, this means that sometimes we will not be able to merge a pull request.
|
||||
Unfortunately, this means that sometimes we will not be able to merge a pull request.
|
||||
|
||||
Because we respect the work you did, you will always be told why we are closing your pull request.
|
||||
If you do not agree with our decision, do not worry; closed pull requests are effortless to recreate,
|
||||
@@ -198,7 +199,7 @@ here are some things you can do to move the process along:
|
||||
* If you have fixed all the issues from a review,
|
||||
remember to re-request a review (using the designated button) to let your reviewer know that you are ready.
|
||||
You can choose to comment with the changes you made.
|
||||
* Ping `@tfny` if you have not been assigned to a reviewer.
|
||||
* Kindly comment on the pull request. Doing so will automatically give your PR visibility during the triage process.
|
||||
|
||||
For more information on best practices, try these links:
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ description: "Security is a key part of Traefik Proxy. Read the technical docume
|
||||
## Security Advisories
|
||||
|
||||
We strongly advise you to join our mailing list to be aware of the latest announcements from our security team.
|
||||
You can subscribe sending a mail to security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
|
||||
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).
|
||||
|
||||
## CVE
|
||||
|
||||
@@ -18,4 +18,6 @@ Reported vulnerabilities can be found on
|
||||
## Report a Vulnerability
|
||||
|
||||
We want to keep Traefik safe for everyone.
|
||||
If you've discovered a security vulnerability in Traefik, we appreciate your help in disclosing it to us in a responsible manner, using [this form](https://security.traefik.io).
|
||||
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).
|
||||
|
||||
@@ -9,7 +9,7 @@ _You_ Made It
|
||||
{: .subtitle}
|
||||
|
||||
Traefik Proxy truly is an [open-source project](https://github.com/traefik/traefik/),
|
||||
and wouldn't have become what it is today without the help of our [many contributors](https://github.com/traefik/traefik/graphs/contributors) (at the time of writing this),
|
||||
and wouldn't have become what it is today without the help of our [many contributors](https://github.com/traefik/traefik/graphs/contributors),
|
||||
not accounting for people having helped with issues, tests, comments, articles, ... or just enjoy using Traefik Proxy and share with others.
|
||||
|
||||
So once again, thank you for your invaluable help in making Traefik such a good product!
|
||||
@@ -17,16 +17,16 @@ So once again, thank you for your invaluable help in making Traefik such a good
|
||||
!!! question "Where to Go Next?"
|
||||
If you want to:
|
||||
|
||||
- Propose and idea, request a feature a report a bug,
|
||||
read the page [Submitting Issues](./submitting-issues.md).
|
||||
- Propose an idea, request a feature, or report a bug,
|
||||
then read [Submitting Issues](./submitting-issues.md).
|
||||
- Discover how to make an efficient contribution,
|
||||
read the page [Submitting Pull Requests](./submitting-pull-requests.md).
|
||||
then read [Submitting Pull Requests](./submitting-pull-requests.md).
|
||||
- Learn how to build and test Traefik,
|
||||
the page [Building and Testing](./building-testing.md) is for you.
|
||||
then the page [Building and Testing](./building-testing.md) is for you.
|
||||
- Contribute to the documentation,
|
||||
read the related page [Documentation](./documentation.md).
|
||||
then read the page about [Documentation](./documentation.md).
|
||||
- Understand how do we learn about Traefik usage,
|
||||
read the [Data Collection](./data-collection.md) page.
|
||||
- Spread the love about Traefik, please check the [Advocating](./advocating.md) page.
|
||||
- Learn about who are the maintainers and how they work on the project,
|
||||
read the [Maintainers](./maintainers.md) page.
|
||||
read the [Maintainers](./maintainers.md) and [Maintainer Guidelines](./maintainers-guidelines.md) pages.
|
||||
|
||||
@@ -2,43 +2,19 @@
|
||||
|
||||
This page is maintained and updated periodically to reflect our roadmap and any decisions around feature deprecation.
|
||||
|
||||
| Feature | Deprecated | End of Support | Removal |
|
||||
|-----------------------------------------------------------------------------------------------------|------------|----------------|---------|
|
||||
| [Pilot](#pilot) | 2.7 | 2.8 | 2.9 |
|
||||
| [Consul Enterprise Namespace](#consul-enterprise-namespace) | 2.8 | N/A | 3.0 |
|
||||
| [TLS 1.0 and 1.1 Support](#tls-10-and-11) | N/A | 2.8 | N/A |
|
||||
| [Nomad Namespace](#nomad-namespace) | 2.10 | N/A | 3.0 |
|
||||
| [Kubernetes CRDs API Group `traefik.containo.us`](#kubernetes-crds-api-group-traefikcontainous) | 2.10 | N/A | 3.0 |
|
||||
| [Kubernetes CRDs API Version `traefik.io/v1alpha1`](#kubernetes-crds-api-version-traefikiov1alpha1) | N/A | N/A | 3.0 |
|
||||
| Feature | Deprecated | End of Support | Removal |
|
||||
|----------------------------------------------------------------------------------------------------------------------|------------|----------------|---------|
|
||||
| [Kubernetes Ingress API Version `networking.k8s.io/v1beta1`](#kubernetes-ingress-api-version-networkingk8siov1beta1) | N/A | N/A | 3.0 |
|
||||
| [CRD API Version `apiextensions.k8s.io/v1beta1`](#kubernetes-ingress-api-version-networkingk8siov1beta1) | N/A | N/A | 3.0 |
|
||||
|
||||
## Impact
|
||||
|
||||
### Pilot
|
||||
### Kubernetes Ingress API Version `networking.k8s.io/v1beta1`
|
||||
|
||||
Metrics will continue to function normally up to 2.8, when they will be disabled.
|
||||
In 2.9, the Pilot platform and all Traefik integration code will be permanently removed.
|
||||
The Kubernetes Ingress API Version `networking.k8s.io/v1beta1` support is removed in v3.
|
||||
Please use the API Group `networking.k8s.io/v1` instead.
|
||||
|
||||
Starting on 2.7 the pilot token will not be a requirement anymore for plugins.
|
||||
Since 2.8, a [new plugin catalog](https://plugins.traefik.io) is available, decoupled from Pilot.
|
||||
### Traefik CRD Definitions API Version `apiextensions.k8s.io/v1beta1`
|
||||
|
||||
### Consul Enterprise Namespace
|
||||
|
||||
Starting on 2.8 the `namespace` option of Consul and Consul Catalog providers is deprecated,
|
||||
please use the `namespaces` options instead.
|
||||
|
||||
### TLS 1.0 and 1.1
|
||||
|
||||
Starting on 2.8 the default TLS options will use the minimum version of TLS 1.2. Of course, it can still be overridden with custom configuration.
|
||||
|
||||
### Nomad Namespace
|
||||
|
||||
Starting on 2.10 the `namespace` option of the Nomad provider is deprecated,
|
||||
please use the `namespaces` options instead.
|
||||
|
||||
### Kubernetes CRDs API Group `traefik.containo.us`
|
||||
|
||||
In v2.10, the Kubernetes CRDs API Group `traefik.containo.us` is deprecated, and its support will end starting with Traefik v3. Please use the API Group `traefik.io` instead.
|
||||
|
||||
### Kubernetes CRDs API Version `traefik.io/v1alpha1`
|
||||
|
||||
The newly introduced Kubernetes CRD API Version `traefik.io/v1alpha1` will subsequently be removed in Traefik v3. The following version will be `traefik.io/v1`.
|
||||
The Traefik CRD definitions API Version `apiextensions.k8s.io/v1beta1` support is removed in v3.
|
||||
Please use the API Group `apiextensions.k8s.io/v1` instead.
|
||||
|
||||
@@ -4,28 +4,26 @@
|
||||
|
||||
Below is a non-exhaustive list of versions and their maintenance status:
|
||||
|
||||
| Version | Release Date | Active Support | Security Support |
|
||||
|---------|--------------|--------------------|------------------|
|
||||
| 2.9 | Oct 03, 2022 | Yes | Yes |
|
||||
| 2.8 | Jun 29, 2022 | Ended Oct 03, 2022 | No |
|
||||
| 2.7 | May 24, 2022 | Ended Jun 29, 2022 | No |
|
||||
| 2.6 | Jan 24, 2022 | Ended May 24, 2022 | No |
|
||||
| 2.5 | Aug 17, 2021 | Ended Jan 24, 2022 | No |
|
||||
| 2.4 | Jan 19, 2021 | Ended Aug 17, 2021 | No |
|
||||
| 2.3 | Sep 23, 2020 | Ended Jan 19, 2021 | No |
|
||||
| 2.2 | Mar 25, 2020 | Ended Sep 23, 2020 | No |
|
||||
| 2.1 | Dec 11, 2019 | Ended Mar 25, 2020 | No |
|
||||
| 2.0 | Sep 16, 2019 | Ended Dec 11, 2019 | No |
|
||||
| 1.7 | Sep 24, 2018 | Ended Dec 31, 2021 | Contact Support |
|
||||
| Version | Release Date | Active Support | Security Support |
|
||||
|---------|--------------|--------------------|-------------------|
|
||||
| 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 |
|
||||
|
||||
??? example "Active Support / Security Support"
|
||||
|
||||
**Active support**: receives any bug fixes.
|
||||
**Security support**: receives only critical bug and security fixes.
|
||||
- **Active support**: Receives any bug fixes.
|
||||
|
||||
- **Security support**: Receives only critical bug and security fixes.
|
||||
|
||||
This page is maintained and updated periodically to reflect our roadmap and any decisions affecting the end of support for Traefik Proxy.
|
||||
|
||||
Please refer to our migration guides for specific instructions on upgrading between versions, an example is the [v1 to v2 migration guide](../migration/v1-to-v2.md).
|
||||
Please refer to our migration guides for specific instructions on upgrading between versions, an example is the [v2 to v3 migration guide](../migrate/v2-to-v3.md).
|
||||
|
||||
!!! important "All target dates for end of support or feature removal announcements may be subject to change."
|
||||
|
||||
|
||||
@@ -0,0 +1,405 @@
|
||||
# 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
|
||||
|
||||
## 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).
|
||||
|
||||
## 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
|
||||
|
||||
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
|
||||
@@ -0,0 +1,830 @@
|
||||
# Exposing Services with Traefik on Kubernetes - 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 Kubernetes 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 (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)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Completed the [Basic Guide](basic.md)
|
||||
- A Kubernetes cluster with Traefik Proxy installed
|
||||
- `kubectl` configured to interact with your cluster
|
||||
- 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.
|
||||
|
||||
### Create Middlewares
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
Save this as `middlewares.yaml` and apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f middlewares.yaml
|
||||
```
|
||||
|
||||
### Apply Middlewares with Gateway API
|
||||
|
||||
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.
|
||||
|
||||
Now, update your `HTTPRoute` to reference these middlewares using the `ExtensionRef` filter:
|
||||
|
||||
```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: /api
|
||||
filters:
|
||||
- type: ExtensionRef
|
||||
extensionRef: # Headers Middleware Definition
|
||||
group: traefik.io
|
||||
kind: Middleware
|
||||
name: secure-headers
|
||||
- type: ExtensionRef
|
||||
extensionRef: # IP AllowList Middleware Definition
|
||||
group: traefik.io
|
||||
kind: Middleware
|
||||
name: ip-allowlist
|
||||
backendRefs:
|
||||
- name: whoami-api
|
||||
port: 80
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /
|
||||
filters:
|
||||
- type: ExtensionRef
|
||||
extensionRef: # Headers Middleware Definition
|
||||
group: traefik.io
|
||||
kind: Middleware
|
||||
name: secure-headers
|
||||
- type: ExtensionRef
|
||||
extensionRef: # IP AllowList Middleware Definition
|
||||
group: traefik.io
|
||||
kind: Middleware
|
||||
name: ip-allowlist
|
||||
backendRefs:
|
||||
- name: whoami
|
||||
port: 80
|
||||
```
|
||||
|
||||
Update the file `whoami-route.yaml` and apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f whoami-route.yaml
|
||||
```
|
||||
|
||||
This approach uses the Gateway API's native filter mechanism rather than annotations. The `ExtensionRef` filter type allows you to reference Traefik middlewares directly within the HTTPRoute specification, which is more consistent with the Gateway API design principles.
|
||||
|
||||
### Apply Middlewares with IngressRoute
|
||||
|
||||
Update your existing IngressRoute to include middlewares:
|
||||
|
||||
```yaml
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: whoami
|
||||
namespace: default
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`whoami.docker.localhost`) && Path(`/api`)
|
||||
kind: Rule
|
||||
middlewares: # Middleware Definition
|
||||
- name: secure-headers
|
||||
- name: ip-allowlist
|
||||
services:
|
||||
- name: whoami-api
|
||||
port: 80
|
||||
- match: Host(`whoami.docker.localhost`)
|
||||
kind: Rule
|
||||
middlewares: # Middleware Definition
|
||||
- name: secure-headers
|
||||
- name: ip-allowlist
|
||||
services:
|
||||
- name: whoami
|
||||
port: 80
|
||||
tls:
|
||||
certResolver: le
|
||||
```
|
||||
|
||||
Update the file `whoami-ingressroute.yaml` and apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f whoami-ingressroute.yaml
|
||||
```
|
||||
|
||||
### Verify Middleware Effects
|
||||
|
||||
Check that the security headers are being applied:
|
||||
|
||||
```bash
|
||||
curl -k -I -H "Host: whoami.docker.localhost" https://localhost/
|
||||
```
|
||||
|
||||
You should see security headers in the response, such as:
|
||||
|
||||
```bash
|
||||
HTTP/2 200
|
||||
x-content-type-options: nosniff
|
||||
x-frame-options: DENY
|
||||
x-xss-protection: 1; mode=block
|
||||
strict-transport-security: max-age=31536000; includeSubDomains; preload
|
||||
content-type: text/plain; charset=utf-8
|
||||
content-length: 403
|
||||
```
|
||||
|
||||
To test the IP allowlist, you can modify the `sourceRange` in the middleware to exclude your IP and verify that access is blocked.
|
||||
|
||||
## Generate Certificates with Let's Encrypt
|
||||
|
||||
!!! info
|
||||
Traefik's built-in Let's Encrypt integration works with IngressRoute but does not automatically issue certificates for Gateway API listeners. For Gateway API, you should use cert-manager or another certificate controller.
|
||||
|
||||
### Using IngressRoute with Let's Encrypt
|
||||
|
||||
Configure a certificate resolver in your Traefik values.yaml:
|
||||
|
||||
```yaml
|
||||
additionalArguments:
|
||||
- "--certificatesresolvers.le.acme.email=your-email@example.com" #replace with your email
|
||||
- "--certificatesresolvers.le.acme.storage=/data/acme.json"
|
||||
- "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
|
||||
```
|
||||
|
||||
!!! 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.
|
||||
|
||||
Update your Traefik installation with this configuration:
|
||||
|
||||
```bash
|
||||
helm upgrade traefik traefik/traefik -n traefik --reuse-values -f values.yaml
|
||||
```
|
||||
|
||||
Update your IngressRoute with the Let's Encrypt certificate:
|
||||
|
||||
```yaml
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: whoami
|
||||
namespace: default
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`whoami.docker.localhost`) && Path(`/api`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: secure-headers
|
||||
- name: ip-allowlist
|
||||
services:
|
||||
- name: whoami-api
|
||||
port: 80
|
||||
- match: Host(`whoami.docker.localhost`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: secure-headers
|
||||
- name: ip-allowlist
|
||||
services:
|
||||
- name: whoami
|
||||
port: 80
|
||||
tls:
|
||||
certResolver: le
|
||||
```
|
||||
|
||||
Apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f whoami-ingressroute.yaml
|
||||
```
|
||||
|
||||
### Using Gateway API with cert-manager
|
||||
|
||||
For Gateway API, install cert-manager:
|
||||
|
||||
```bash
|
||||
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.10.0/cert-manager.yaml
|
||||
```
|
||||
|
||||
Create an Issuer & Certificate:
|
||||
|
||||
```yaml
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Issuer
|
||||
metadata:
|
||||
name: letsencrypt
|
||||
spec:
|
||||
acme:
|
||||
email: your-email@example.com # replace with your email
|
||||
server: https://acme-v02-staging.api.letsencrypt.org/directory # Replace with the production server in production
|
||||
privateKeySecretRef:
|
||||
name: letsencrypt-account-key
|
||||
solvers:
|
||||
- http01:
|
||||
gatewayHTTPRoute:
|
||||
parentRefs:
|
||||
- name: traefik
|
||||
namespace: default
|
||||
kind: Gateway
|
||||
---
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: whoami
|
||||
namespace: default
|
||||
spec:
|
||||
secretName: whoami-tls-le # Name of secret where the generated certificate will be stored.
|
||||
dnsNames:
|
||||
- "whoami.docker.localhost" # Replace a real domain
|
||||
issuerRef:
|
||||
name: letsencrypt
|
||||
kind: Issuer
|
||||
```
|
||||
|
||||
!!! important "Public DNS Required"
|
||||
Let's Encrypt requires a publicly accessible domain to verify ownership. When using a local domain like `whoami.docker.localhost`, cert-manager will attempt the challenge but it will fail, and the certificate will remain self-signed. For production use, replace the domain with one that has a public DNS record pointing to your cluster's ingress point.
|
||||
|
||||
Save the YAML file and apply:
|
||||
|
||||
```bash
|
||||
kubectl apply -f letsencrypt-issuer-andwhoami-certificate.yaml
|
||||
```
|
||||
|
||||
Now, update your Gateway to use the generated certificate:
|
||||
|
||||
```yaml
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: traefik-gateway
|
||||
namespace: default
|
||||
spec:
|
||||
gatewayClassName: traefik
|
||||
listeners:
|
||||
- name: web
|
||||
port: 80
|
||||
protocol: HTTP
|
||||
allowedRoutes:
|
||||
namespaces:
|
||||
from: All
|
||||
- name: websecure
|
||||
port: 443
|
||||
protocol: HTTPS
|
||||
allowedRoutes:
|
||||
namespaces:
|
||||
from: All
|
||||
tls:
|
||||
certificateRefs:
|
||||
- name: whoami-tls-le # References the secret created by cert-manager
|
||||
```
|
||||
|
||||
Apply the updated Gateway:
|
||||
|
||||
```bash
|
||||
kubectl apply -f gateway.yaml
|
||||
```
|
||||
|
||||
Your existing `HTTPRoute` will now use this certificate when connecting to the secured gateway listener.
|
||||
|
||||
### Verify the Let's Encrypt Certificate
|
||||
|
||||
Once the certificate is issued, you can verify it:
|
||||
|
||||
```bash
|
||||
# Check certificate status
|
||||
kubectl get certificate -n default
|
||||
|
||||
# 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, Scale Up the Deployment
|
||||
|
||||
To demonstrate sticky sessions, first scale up the deployment to 3 replicas:
|
||||
|
||||
```bash
|
||||
kubectl scale deployment whoami --replicas=3
|
||||
```
|
||||
|
||||
### Using Gateway API with TraefikService
|
||||
|
||||
First, create the `TraefikService` for sticky sessions:
|
||||
|
||||
```yaml
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: TraefikService
|
||||
metadata:
|
||||
name: whoami-sticky
|
||||
namespace: default
|
||||
spec:
|
||||
weighted:
|
||||
services:
|
||||
- name: whoami
|
||||
port: 80
|
||||
weight: 1
|
||||
sticky:
|
||||
cookie:
|
||||
name: sticky_cookie
|
||||
secure: true
|
||||
httpOnly: true
|
||||
```
|
||||
|
||||
Save this as `whoami-sticky-service.yaml` and apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f whoami-sticky-service.yaml
|
||||
```
|
||||
|
||||
Now update your `HTTPRoute` with an annotation referencing the `TraefikService`:
|
||||
|
||||
```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: /api
|
||||
filters:
|
||||
- type: ExtensionRef
|
||||
extensionRef: # Headers Middleware Definition
|
||||
group: traefik.io
|
||||
kind: Middleware
|
||||
name: secure-headers
|
||||
- type: ExtensionRef
|
||||
extensionRef: # IP AllowList Middleware Definition
|
||||
group: traefik.io
|
||||
kind: Middleware
|
||||
name: ip-allowlist
|
||||
backendRefs:
|
||||
- name: whoami-api
|
||||
port: 80
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /
|
||||
backendRefs:
|
||||
- group: traefik.io # <── tell Gateway this is a TraefikService
|
||||
kind: TraefikService
|
||||
name: whoami-sticky
|
||||
filters:
|
||||
- type: ExtensionRef
|
||||
extensionRef: # Headers Middleware Definition
|
||||
group: traefik.io
|
||||
kind: Middleware
|
||||
name: secure-headers
|
||||
- type: ExtensionRef
|
||||
extensionRef: # IP AllowList Middleware Definition
|
||||
group: traefik.io
|
||||
kind: Middleware
|
||||
name: ip-allowlist
|
||||
backendRefs:
|
||||
- name: whoami
|
||||
port: 80
|
||||
```
|
||||
|
||||
Update the file `whoami-route.yaml` and apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f whoami-route.yaml
|
||||
```
|
||||
|
||||
### Using IngressRoute with TraefikService
|
||||
|
||||
First, create the `TraefikService` for sticky sessions:
|
||||
|
||||
```yaml
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: TraefikService
|
||||
metadata:
|
||||
name: whoami-sticky
|
||||
namespace: default
|
||||
spec:
|
||||
weighted:
|
||||
services:
|
||||
- name: whoami
|
||||
port: 80
|
||||
sticky:
|
||||
cookie:
|
||||
name: sticky_cookie
|
||||
secure: true
|
||||
httpOnly: true
|
||||
```
|
||||
|
||||
Save this as `whoami-sticky-service.yaml` and apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f whoami-sticky-service.yaml
|
||||
```
|
||||
|
||||
Now update your IngressRoute to use this `TraefikService`:
|
||||
|
||||
```yaml
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: whoami
|
||||
namespace: default
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`whoami.docker.localhost`) && Path(`/api`)
|
||||
kind: Rule
|
||||
middlewares: # Middleware Definition
|
||||
- name: secure-headers
|
||||
- name: ip-allowlist
|
||||
services:
|
||||
- name: whoami-api
|
||||
port: 80
|
||||
- match: Host(`whoami.docker.localhost`)
|
||||
kind: Rule
|
||||
middlewares: # Middleware Definition
|
||||
- name: secure-headers
|
||||
- name: ip-allowlist
|
||||
services:
|
||||
- name: whoami-sticky # Changed from whoami to whoami-sticky
|
||||
kind: TraefikService # Added kind: TraefikService
|
||||
tls:
|
||||
certResolver: le
|
||||
```
|
||||
|
||||
Update the file `whoami-ingressroute.yaml` and apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f whoami-ingressroute.yaml
|
||||
```
|
||||
|
||||
### Test Sticky Sessions
|
||||
|
||||
You can test the sticky sessions by making multiple requests and observing that they all go to the same backend pod:
|
||||
|
||||
```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 pods
|
||||
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 pod.
|
||||
|
||||
!!! 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).
|
||||
|
||||
## 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).
|
||||
|
||||
## 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 (IngressRoute) and cert-manager (Gateway API)
|
||||
- Implement sticky sessions for stateful applications
|
||||
- Setup multi-layer routing for authentication-based routing (IngressRoute only)
|
||||
|
||||
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've mastered both basic and advanced Traefik features with Kubernetes, 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
|
||||
- [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
|
||||
@@ -0,0 +1,116 @@
|
||||
# Exposing Services with Traefik Proxy
|
||||
|
||||
This section guides you through exposing services securely with Traefik Proxy. You'll learn how to route HTTP and HTTPS traffic to your services, add security features, and implement advanced load balancing.
|
||||
|
||||
## What You'll Accomplish
|
||||
|
||||
Following these guides, you'll learn how to:
|
||||
|
||||
- Route HTTP traffic to your services with [Gateway API](../reference/routing-configuration/kubernetes/gateway-api.md) and [IngressRoute](../reference/routing-configuration/kubernetes/crd/http/ingressroute.md)
|
||||
- Configure routing rules to direct requests
|
||||
- Enable HTTPS with TLS
|
||||
- Add security middlewares
|
||||
- Generate certificates automatically with Let's Encrypt
|
||||
- Implement sticky sessions for session persistence
|
||||
|
||||
## Platform-Specific Guides
|
||||
|
||||
For detailed steps tailored to your environment, follow the guide for your platform:
|
||||
|
||||
- [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.
|
||||
@@ -0,0 +1,405 @@
|
||||
# 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
|
||||
|
||||
## 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).
|
||||
|
||||
## 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
|
||||
|
||||
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 provider documentation](../../reference/install-configuration/providers/docker.md) for more details about the Docker 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.
|
||||
@@ -11,7 +11,7 @@ This page explains the base concepts of Traefik.
|
||||
|
||||
## Introduction
|
||||
|
||||
Traefik is based on the concept of EntryPoints, Routers, Middelwares and Services.
|
||||
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.
|
||||
|
||||
@@ -25,7 +25,7 @@ The main features include dynamic configuration, automatic service discovery, an
|
||||
|
||||
## Edge Router
|
||||
|
||||
Traefik is an *Edge Router*, it means that it's the door to your platform, and that it intercepts and routes every incoming request:
|
||||
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.).
|
||||
|
||||

|
||||
@@ -38,7 +38,7 @@ Deploying your services, you attach information that tells Traefik the character
|
||||
|
||||

|
||||
|
||||
It means that when a service is deployed, Traefik detects it immediately and updates the routing rules in real time.
|
||||
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.
|
||||
@@ -57,4 +57,4 @@ You no longer need to create and synchronize configuration files cluttered with
|
||||
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!}
|
||||
{% include-markdown "includes/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](../providers/overview.md) and define the [entrypoints](../routing/entrypoints.md) Traefik will listen to (these elements don't change often).
|
||||
|
||||
The _dynamic configuration_ contains everything that defines how the requests are handled by your system.
|
||||
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](../providers/overview.md): whether an orchestrator, a service registry, or a plain old configuration file.
|
||||
|
||||
Since this configuration is specific to your infrastructure choices, we invite you to refer to the [dedicated section of this documentation](../routing/overview.md).
|
||||
|
||||
!!! info ""
|
||||
|
||||
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](../getting-started/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,14 +77,14 @@ traefik --help
|
||||
# or
|
||||
|
||||
docker run traefik[:version] --help
|
||||
# ex: docker run traefik:v2.10 --help
|
||||
# ex: docker run traefik:v3.6 --help
|
||||
```
|
||||
|
||||
All available arguments can also be found [here](../reference/static-configuration/cli.md).
|
||||
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 [here](../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
|
||||
|
||||
@@ -94,4 +92,4 @@ 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.
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
{% include-markdown "includes/traefik-for-business-applications.md" %}
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
---
|
||||
title: "Docker and Traefik Quick Start"
|
||||
description: "Deploy Traefik in Docker and expose your first service"
|
||||
---
|
||||
|
||||
# Getting Started with Docker and Traefik
|
||||
|
||||
Docker is a first-class citizen in Traefik, offering native support for Docker containers and services.
|
||||
Whether you're using Docker Compose or running containers directly, Traefik provides a seamless experience for managing your Docker traffic.
|
||||
|
||||
This guide shows you how to:
|
||||
|
||||
- Install Traefik using Docker
|
||||
- Expose the Traefik dashboard
|
||||
- Deploy a sample application
|
||||
- Configure basic routing
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Docker
|
||||
- Docker Compose (optional)
|
||||
|
||||
## Install Traefik
|
||||
|
||||
### Using Docker Compose
|
||||
|
||||
Create a Docker Compose file.
|
||||
This configuration:
|
||||
|
||||
- Exposes ports 80 and 8080.
|
||||
- Enables the Docker provider
|
||||
- Configures the dashboard with basic settings. Port 8080 serves the dashboard because we enabled `--api.insecure=true` (development use only)
|
||||
- Mounts the Docker socket for container discovery
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v3.6
|
||||
command:
|
||||
- "--api.insecure=true"
|
||||
- "--providers.docker=true"
|
||||
- "--entrypoints.web.address=:80"
|
||||
ports:
|
||||
- "80:80"
|
||||
- "8080:8080"
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
```
|
||||
|
||||
Start Traefik:
|
||||
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### Using Docker CLI
|
||||
|
||||
Alternatively, you can run Traefik directly with Docker.
|
||||
This command:
|
||||
|
||||
- Exposes ports 80 and 8080 for web traffic and dashboard access
|
||||
- Mounts the configuration file and Docker socket
|
||||
- Uses the same configuration as the Docker Compose example
|
||||
|
||||
Create a configuration file:
|
||||
|
||||
```yaml
|
||||
# traefik.yml
|
||||
api:
|
||||
insecure: true
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
providers:
|
||||
docker: {}
|
||||
```
|
||||
|
||||
Start Traefik:
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
-p 80:80 \
|
||||
-p 8080:8080 \
|
||||
-v $PWD/traefik.yml:/etc/traefik/traefik.yml \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
traefik:v3.6
|
||||
```
|
||||
|
||||
## Expose the Dashboard
|
||||
|
||||
Because we explicitly enabled insecure mode, the [dashboard](../reference/install-configuration/api-dashboard.md) is reachable on port 8080 without authentication.
|
||||
**Do not enable this flag in production**.
|
||||
|
||||
You can access the dashboard at:
|
||||
|
||||
[http://localhost:8080/dashboard/](http://localhost:8080/dashboard/)
|
||||
|
||||

|
||||
|
||||
## Deploy a Sample Application
|
||||
|
||||
Create a whoami service:
|
||||
|
||||
```yaml
|
||||
# whoami.yml
|
||||
services:
|
||||
whoami:
|
||||
image: traefik/whoami
|
||||
labels:
|
||||
- "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)"
|
||||
```
|
||||
|
||||
Apply the configuration:
|
||||
|
||||
```bash
|
||||
docker-compose -f whoami.yml up -d
|
||||
```
|
||||
|
||||
## Test Your Setup
|
||||
|
||||
You can use the following curl command to verify that the application is correctly exposed:
|
||||
|
||||
```bash
|
||||
curl http://whoami.localhost
|
||||
|
||||
Hostname: 068c0a29a8b7
|
||||
IP: 127.0.0.1
|
||||
IP: ::1
|
||||
IP: 192.168.147.3
|
||||
RemoteAddr: 192.168.147.2:56006
|
||||
GET / HTTP/1.1
|
||||
Host: whoami.localhost
|
||||
User-Agent: curl/8.7.1
|
||||
Accept: */*
|
||||
Accept-Encoding: gzip
|
||||
X-Forwarded-For: 192.168.147.1
|
||||
X-Forwarded-Host: whoami.localhost
|
||||
X-Forwarded-Port: 80
|
||||
X-Forwarded-Proto: http
|
||||
X-Forwarded-Server: 9232cdd4fd6c
|
||||
X-Real-Ip: 192.168.147.1
|
||||
```
|
||||
|
||||
You can also open [http://whoami.localhost](http://whoami.localhost) in a browser to test the application:
|
||||
|
||||

|
||||
|
||||
If you navigate to the **HTTP Routers** section of the Traefik dashboard, you can see that the `whoami.localhost` route is managed by the Traefik Docker provider:
|
||||
|
||||

|
||||
|
||||
That's it! You've successfully deployed Traefik and configured routing in Docker.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Configure TLS](../reference/routing-configuration/http/tls/overview.md)
|
||||
- [Set up Middlewares](../reference/routing-configuration/http/middlewares/overview.md)
|
||||
- [Enable Metrics](../reference/install-configuration/observability/metrics.md)
|
||||
- [Learn more about Docker provider](../reference/install-configuration/providers/docker.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, a part of the configuration is [static](./configuration-overview.md#the-static-configuration),
|
||||
and can be provided by a file on startup, whereas various providers,
|
||||
such as the file provider,
|
||||
contribute dynamically all along the traefik instance lifetime to its [dynamic configuration](../configuration-overview/#the-dynamic-configuration) changes.
|
||||
contribute dynamically all along the traefik instance lifetime to its [dynamic configuration](./configuration-overview.md#the-dynamic-configuration) changes.
|
||||
|
||||
In addition, the configuration englobes concepts such as the EntryPoint which can be seen as a listener on the Transport Layer (TCP),
|
||||
as apposed to the Router which is more about the Presentation (TLS) and Application layers (HTTP).
|
||||
@@ -29,7 +29,7 @@ Not to mention that dynamic configuration changes potentially make that kind of
|
||||
Therefore, in this dynamic context,
|
||||
the static configuration of an `entryPoint` does not give any hint whatsoever about how the traffic going through that `entryPoint` is going to be routed.
|
||||
Or whether it's even going to be routed at all,
|
||||
i.e. whether there is a Router matching the kind of traffic going through it.
|
||||
that is whether there is a Router matching the kind of traffic going through it.
|
||||
|
||||
### `404 Not found`
|
||||
|
||||
@@ -71,7 +71,7 @@ Traefik returns a `502` response code when an error happens while contacting the
|
||||
|
||||
### `503 Service Unavailable`
|
||||
|
||||
Traefik returns a `503` response code when a Router has been matched
|
||||
Traefik returns a `503` response code when a Router has been matched,
|
||||
but there are no servers ready to handle the request.
|
||||
|
||||
This situation is encountered when a service has been explicitly configured without servers,
|
||||
@@ -84,7 +84,7 @@ Sometimes, the `404` response code doesn't play well with other parties or servi
|
||||
In these situations, you may want Traefik to always reply with a `503` response code,
|
||||
instead of a `404` response code.
|
||||
|
||||
To achieve this behavior, a simple catchall router,
|
||||
To achieve this behavior, a catchall router,
|
||||
with the lowest possible priority and routing to a service without servers,
|
||||
can handle all the requests when no other router has been matched.
|
||||
|
||||
@@ -93,7 +93,7 @@ The example below is a file provider only version (`yaml`) of what this configur
|
||||
```yaml tab="Static configuration"
|
||||
# traefik.yml
|
||||
|
||||
entrypoints:
|
||||
entryPoints:
|
||||
web:
|
||||
address: :80
|
||||
|
||||
@@ -130,7 +130,7 @@ http:
|
||||
the principle of the above example above (a catchall router) still stands,
|
||||
but the `unavailable` service should be adapted to fit such a need.
|
||||
|
||||
## Why Is My TLS Certificate Not Reloaded When Its Contents Change?
|
||||
## 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.
|
||||
@@ -147,13 +147,13 @@ 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.
|
||||
@@ -216,7 +216,7 @@ error: field not found, node: -badField-
|
||||
|
||||
The "field not found" error occurs, when an unknown property is encountered in the dynamic or static configuration.
|
||||
|
||||
One easy way to check whether a configuration file is well-formed, is to validate it with:
|
||||
One way to check whether a configuration file is well-formed, is to validate it with:
|
||||
|
||||
- [JSON Schema of the static configuration](https://json.schemastore.org/traefik-v2.json)
|
||||
- [JSON Schema of the dynamic configuration](https://json.schemastore.org/traefik-v2-file-provider.json)
|
||||
@@ -226,11 +226,11 @@ One easy way to check whether a configuration file is well-formed, is to validat
|
||||
As a common tip, if a resource is dropped/not created by Traefik after the dynamic configuration was evaluated,
|
||||
one should look for an error in the logs.
|
||||
|
||||
If found, the error obviously confirms that something went wrong while creating the resource,
|
||||
If found, the error confirms that something went wrong while creating the resource,
|
||||
and the message should help in figuring out the mistake(s) in the configuration, and how to fix it.
|
||||
|
||||
When using the file provider,
|
||||
one easy way to check if the dynamic configuration is well-formed is to validate it with the [JSON Schema of the dynamic configuration](https://json.schemastore.org/traefik-v2-file-provider.json).
|
||||
one way to check if the dynamic configuration is well-formed is to validate it with the [JSON Schema of the dynamic configuration](https://json.schemastore.org/traefik-v2-file-provider.json).
|
||||
|
||||
## Why does Let's Encrypt wildcard certificate renewal/generation with DNS challenge fail?
|
||||
|
||||
@@ -248,6 +248,8 @@ then it could be due to `CNAME` support.
|
||||
In which case, you should make sure your infrastructure is properly set up for a
|
||||
`DNS` challenge that does not rely on `CNAME`, and you should try disabling `CNAME` support with:
|
||||
|
||||
```bash
|
||||
```shell
|
||||
LEGO_DISABLE_CNAME_SUPPORT=true
|
||||
```
|
||||
|
||||
{% include-markdown "includes/traefik-for-business-applications.md" %}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
title: "Getting Started with Traefik"
|
||||
description: "Quick start guides for deploying Traefik in Kubernetes and Docker environments"
|
||||
---
|
||||
|
||||
# Getting Started with Traefik
|
||||
|
||||
Traefik can be deployed in various environments. Choose your preferred deployment method:
|
||||
|
||||
- [Kubernetes Quick Start](./kubernetes.md) - Deploy Traefik using Helm
|
||||
- [Docker Quick Start](./docker.md) - Deploy Traefik using Docker
|
||||
|
||||
Each guide will help you:
|
||||
|
||||
- Install Traefik
|
||||
- Expose the dashboard
|
||||
- Deploy a sample application
|
||||
- Configure basic routing
|
||||
|
||||
## Before You Begin
|
||||
|
||||
Make sure you have the necessary prerequisites for your chosen environment:
|
||||
|
||||
- **Kubernetes**: A running Kubernetes cluster, Helm 3, and kubectl
|
||||
- **Docker**: Docker and optionally Docker Compose
|
||||
@@ -16,12 +16,12 @@ You can install Traefik with the following flavors:
|
||||
|
||||
Choose one of the [official Docker images](https://hub.docker.com/_/traefik) and run it with one sample configuration file:
|
||||
|
||||
* [YAML](https://raw.githubusercontent.com/traefik/traefik/v2.10/traefik.sample.yml)
|
||||
* [TOML](https://raw.githubusercontent.com/traefik/traefik/v2.10/traefik.sample.toml)
|
||||
* [YAML](https://raw.githubusercontent.com/traefik/traefik/v3.6/traefik.sample.yml)
|
||||
* [TOML](https://raw.githubusercontent.com/traefik/traefik/v3.6/traefik.sample.toml)
|
||||
|
||||
```bash
|
||||
```shell
|
||||
docker run -d -p 8080:8080 -p 80:80 \
|
||||
-v $PWD/traefik.yml:/etc/traefik/traefik.yml traefik:v2.10
|
||||
-v $PWD/traefik.yml:/etc/traefik/traefik.yml traefik:v3.6
|
||||
```
|
||||
|
||||
For more details, go to the [Docker provider documentation](../providers/docker.md)
|
||||
@@ -29,22 +29,17 @@ For more details, go to the [Docker provider documentation](../providers/docker.
|
||||
!!! tip
|
||||
|
||||
* Prefer a fixed version than the latest that could be an unexpected version.
|
||||
ex: `traefik:v2.10`
|
||||
ex: `traefik:v3.6`
|
||||
* Docker images are based from the [Alpine Linux Official image](https://hub.docker.com/_/alpine).
|
||||
* Any orchestrator using docker images can fetch the official Traefik docker image.
|
||||
|
||||
## Use the Helm Chart
|
||||
|
||||
!!! warning
|
||||
|
||||
The Traefik Chart from
|
||||
[Helm's default charts repository](https://github.com/helm/charts/tree/master/stable/traefik) is still using [Traefik v1.7](https://doc.traefik.io/traefik/v1.7).
|
||||
|
||||
Traefik can be installed in Kubernetes using the Helm chart from <https://github.com/traefik/traefik-helm-chart>.
|
||||
|
||||
Ensure that the following requirements are met:
|
||||
|
||||
* Kubernetes 1.16+
|
||||
* Kubernetes 1.22+
|
||||
* Helm version 3.9+ is [installed](https://helm.sh/docs/intro/install/)
|
||||
|
||||
Add Traefik Labs chart repository to Helm:
|
||||
@@ -59,7 +54,7 @@ You can update the chart repository by running:
|
||||
helm repo update
|
||||
```
|
||||
|
||||
And install it with the `helm` command line:
|
||||
And install it with the Helm command line:
|
||||
|
||||
```bash
|
||||
helm install traefik traefik/traefik
|
||||
@@ -69,7 +64,7 @@ helm install traefik traefik/traefik
|
||||
|
||||
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).
|
||||
Examples are provided [here](https://github.com/traefik/traefik-helm-chart/blob/master/EXAMPLES.md).
|
||||
|
||||
For instance, installing the chart in a dedicated namespace:
|
||||
|
||||
@@ -104,38 +99,6 @@ helm install traefik traefik/traefik
|
||||
- "--log.level=DEBUG"
|
||||
```
|
||||
|
||||
### Exposing the Traefik dashboard
|
||||
|
||||
This HelmChart does not expose the Traefik dashboard by default, for security concerns.
|
||||
Thus, there are multiple ways to expose the dashboard.
|
||||
For instance, the dashboard access could be achieved through a port-forward:
|
||||
|
||||
```shell
|
||||
kubectl port-forward $(kubectl get pods --selector "app.kubernetes.io/name=traefik" --output=name) 9000:9000
|
||||
```
|
||||
|
||||
It can then be reached at: `http://127.0.0.1:9000/dashboard/`
|
||||
|
||||
Another way would be to apply your own configuration, for instance,
|
||||
by defining and applying an IngressRoute CRD (`kubectl apply -f dashboard.yaml`):
|
||||
|
||||
```yaml
|
||||
# dashboard.yaml
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: dashboard
|
||||
spec:
|
||||
entryPoints:
|
||||
- web
|
||||
routes:
|
||||
- match: Host(`traefik.localhost`) && (PathPrefix(`/dashboard`) || PathPrefix(`/api`))
|
||||
kind: Rule
|
||||
services:
|
||||
- name: api@internal
|
||||
kind: TraefikService
|
||||
```
|
||||
|
||||
## Use the Binary Distribution
|
||||
|
||||
Grab the latest binary from the [releases](https://github.com/traefik/traefik/releases) page.
|
||||
@@ -181,4 +144,4 @@ And run it:
|
||||
|
||||
All the details are available in the [Contributing Guide](../contributing/building-testing.md)
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
{% include-markdown "includes/traefik-for-business-applications.md" %}
|
||||
|
||||