1
0
mirror of https://github.com/containous/traefik.git synced 2025-09-06 05:44:21 +03:00

Compare commits

..

1465 Commits
v1.3.6 ... v1.7

Author SHA1 Message Date
Fernandez Ludovic
8b33c54f08 chore: fix PyYAML version 2023-07-19 21:55:48 +02:00
Fernandez Ludovic
67c3f6acb4 fix: doc requirements 2022-07-18 12:16:21 +02:00
Fernandez Ludovic
25757d2a27 fix: hashstructure 2022-04-28 17:20:50 +02:00
Fernandez Ludovic
1aaf9d04a7 fix: hashstructure 2022-04-28 16:10:58 +02:00
Kevin Pollet
492d90c843 Update copyright for 2022 2022-01-06 11:36:06 +01:00
Romain
bf650d7623 Prepare release v1.7.34 2021-12-10 17:54:05 +01:00
Romain
0528af1e66 require that secret is a valid PEM
Co-authored-by: Jared Rodriguez <jared@blacknode.net>
2021-12-10 17:16:04 +01:00
Kevin Pollet
c309b3b006 Prepare release v1.7.33 2021-10-07 16:32:07 +02:00
Ludovic Fernandez
e9d3c9a1cb acme: fix non-cluster mode 2021-10-07 09:52:07 +02:00
Romain
8cfb429dec Prepare release v1.7.32 2021-10-06 18:50:09 +02:00
Ludovic Fernandez
e1418fa622 acme: add missing preferred chain 2021-10-06 18:30:13 +02:00
Romain
d3afb20890 Prepare release v1.7.31 2021-10-04 18:16:07 +02:00
Ludovic Fernandez
df6aab811d acme: add support of preferredchain in Traefik v1 2021-10-04 17:52:11 +02:00
Ludovic Fernandez
8d76f52b85 fix: restore CI for Traefik v1.7
Co-authored-by: Michael <michael.matur@gmail.com>
2021-10-04 17:36:05 +02:00
Jean-Baptiste Doumenjou
a0d8ee5a02 Prepare release v1.7.30 2021-04-08 16:30:04 +02:00
Tom Moulard
bc8d36a68e prometheus: fixing prom Handler to use the custom registry
Co-authored-by: Jean-Baptiste Doumenjou <925513+jbdoumenjou@users.noreply.github.com>
2021-04-07 17:16:03 +02:00
Jean-Baptiste Doumenjou
e73fd70add Prepare release v1.7.29 2021-03-29 18:12:04 +02:00
Ludovic Fernandez
c47849d43a Update to go1.16
Co-authored-by: Julien Salleyron <julien.salleyron@gmail.com>
Co-authored-by: Michael Matur <michael.matur@gmail.com>
2021-03-22 18:00:04 +01:00
Romain
2921e1059a Prepare release v1.7.28 2021-01-13 18:14:03 +01:00
Fernandez Ludovic
fe8e539422 chore: Do a Docker login on Travis CI. 2021-01-13 17:43:55 +01:00
Fernandez Ludovic
45f0591cf6 fix: encrypted key in Travis CI. 2021-01-13 17:39:05 +01:00
Romain
d7f752de46 Prepare release v1.7.27 2021-01-13 17:16:05 +01:00
Romain
b7adc1960c Update copyright year for 2021 2021-01-13 16:02:03 +01:00
Jean-Baptiste Doumenjou
0fb277fd25 Fix tlsclientheaders integration test 2021-01-13 12:02:04 +01:00
Travis Payne
6c9e1d996e Fix stickiness support in Service Fabric. 2021-01-07 18:14:04 +01:00
Amos Shapira
0e942ace3b docs: fix language "if challenge is not known" 2021-01-04 11:42:03 +01:00
Romain
d1befe7b12 Fix documentation deployment. 2020-09-23 10:00:03 +02:00
Romain
274cab4bb3 chore: apply new documentation style.
Co-authored-by: jbdoumenjou <jb.doumenjou@gmail.com>
2020-09-23 09:26:04 +02:00
Nick Douma
d04b812412 Do not SetError on tracing event when whitelist allowed request 2020-09-21 12:00:04 +02:00
Amos Shapira
a3d37f636e Fix typo "CA's" instead of "CA:s" 2020-09-21 10:24:04 +02:00
Romain
e148a5b8f7 chore: move to Traefik organization.
Co-authored-by: jbdoumenjou <jb.doumenjou@gmail.com>
2020-09-21 10:20:04 +02:00
Michael
e382d2940f Add mixtus for documentation 2020-08-07 16:36:03 +02:00
Ludovic Fernandez
5afa78e18e Prepare release v1.7.26 2020-07-28 17:32:03 +02:00
Ludovic Fernandez
cfa04c300c fix: clean X-Forwarded-Prefix header for the dashboard. 2020-07-28 10:10:03 +02:00
Cory Claflin
cb6b94ff40 Change apiVersion to apps/v1 in examples/k8s/cheese-deployments.yaml 2020-07-27 16:24:03 +02:00
Ludovic Fernandez
0b2a6cecd1 Prepare release v1.7.25 2020-07-15 12:28:03 +02:00
Fernandez Ludovic
fee89273a3 chore: update doc analytics. 2020-07-03 12:07:46 +02:00
Richard Kojedzinszky
463e2285d6 Backport "Same Configuration Check" from master 2020-07-01 11:36:03 +02:00
Romain
c65277a6f0 Fix ipv6 handling in redirect middleware 2020-06-19 10:08:04 +02:00
Douglas De Toni Machado
bb14c3e812 Doc improvement on entrypoints whitelist for v1.7 2020-05-20 10:44:04 +02:00
kobayashi
85580dde3c Add ingress setup for minikube in docs 2020-05-14 18:06:05 +02:00
Ludovic Fernandez
4bb6d1bf4f Prepare release v1.7.24 2020-03-25 14:48:05 +01:00
Ludovic Fernandez
459cabca8e fix: stickiness annotations support. 2020-03-25 10:58:04 +01:00
Ludovic Fernandez
dcca37fe29 Prepare release v1.7.23 2020-03-23 16:50:04 +01:00
Julien Salleyron
a610f0b2a1 Force http/1.1 for upgrade (Traefik v1) 2020-03-23 15:18:04 +01:00
Ludovic Fernandez
ded285be29 Fix sameSite (Traefik v1) 2020-03-23 13:10:04 +01:00
Ludovic Fernandez
4b31d3306b fix: max TLS version. 2020-03-22 17:54:03 +01:00
Ludovic Fernandez
5a6652fc20 Prepare release v1.7.22 2020-03-18 15:00:07 +01:00
robotte
91b0e2e4dc Complete TLS example for Kubernetes Ingress in user guide 2020-03-09 19:16:05 +01:00
Ludovic Fernandez
554bceb75b Skip redirection with invalid regex syntax. 2020-03-09 13:24:05 +01:00
Ludovic Fernandez
ce76212605 fix: manual provider code name. 2020-03-08 12:08:03 +01:00
bjthomas1
d474c87cde Updated rbac and demonset example bloc 2020-03-02 09:48:04 +01:00
Ludovic Fernandez
862772230c Update to go1.14 2020-03-02 09:30:05 +01:00
Ludovic Fernandez
09fa1e6546 Fix typo in user guide. 2020-02-27 21:06:05 +01:00
Omer Karjevsky
c3125104d9 Clear closed hijacked h2c connections 2020-02-27 14:08:04 +01:00
Ludovic Fernandez
31786f7163 Prepare release v1.7.21 2020-02-20 18:22:04 +01:00
pierresteiner
d7ed42d614 Fix sample for ssl-header in docs 2020-02-19 17:18:04 +01:00
mpl
4c709c2fcd Improve rate-limiting doc 2020-02-07 17:26:05 +01:00
Yazan Dabain
8dac076711 Fix finding proper provided certificate when ACME is enabled 2020-02-06 10:08:04 +01:00
Julien Salleyron
0b039499c6 don't create http client in each request in forward auth 2020-02-04 18:36:04 +01:00
Steven
2124975432 Fix dnspod update. 2020-01-26 12:42:03 +01:00
Jean-Baptiste Doumenjou
e4725619ea Update the k8s api version in the documentation 2020-01-13 15:44:05 +01:00
Ludovic Fernandez
d24f78222c fix: skip ECS container when information are missing instead of panic. 2020-01-06 15:10:05 +01:00
Daniel Tomcej
3f1925e20e Add edge case for root path with rewrite-target 2019-12-19 09:24:04 +01:00
Ludovic Fernandez
a8393faf0a Prepare release v1.7.20 2019-12-10 17:50:05 +01:00
Daniel Tomcej
4b8ece5b42 Truncate key for identification in log 2019-12-09 11:50:06 +01:00
Brad Jones
7574bb9226 Add a warning note regarding optional TLS mutual auth 2019-11-27 17:18:05 +01:00
Ludovic Fernandez
f68b629469 fix: location header rewrite.
Co-authored-by: Daniel Tomcej <daniel.tomcej@gmail.com>
2019-11-18 14:28:06 +01:00
Ludovic Fernandez
f9e9e11035 Prepare release v1.7.19 2019-10-28 14:58:04 +01:00
Daniel Tomcej
772c9ca4d5 Allow Default Certificate to work on macOS 10.15 2019-10-25 17:08:05 +02:00
Ismail Alidzhikov
208d0fa471 Update DaemonSet apiVersion 2019-10-20 23:50:04 +02:00
mikezhang
9f72b6d1d5 Add functions to support precise float compute of weight (#5663) 2019-10-16 17:29:21 +02:00
Ingo Gottwald
29ef007917 Backport Go 1.13 integration test fixes 2019-10-07 10:46:04 +02:00
Eliel Goncalves
590a0d67bb Fix Location response header http to https when SSL 2019-10-07 10:16:05 +02:00
mpl
74ad83f05a Prepare release v1.7.18 2019-09-26 15:46:05 +02:00
Ludovic Fernandez
d707c8ba93 Prepare release v1.7.17 2019-09-23 19:48:04 +02:00
Nicholas Wiersma
640eb62ca1 Avoid closing stdout when the accesslog handler is closed
Co-authored-by: Ludovic Fernandez <ldez@users.noreply.github.com>
Co-authored-by: jlevesy <julien.levesy@containo.us>
2019-09-23 14:50:06 +02:00
mpl
216710864e Actually send header and code during WriteHeader, if needed
Co-authored-by: Julien Salleyron <julien.salleyron@gmail.com>
2019-09-20 18:42:03 +02:00
Brad Jones
226f20b626 Add note clarifying client certificate header 2019-09-19 09:06:03 +02:00
Ludovic Fernandez
151be83bce Update docs links. 2019-09-18 15:38:04 +02:00
Ludovic Fernandez
d1a8c7fa78 Update Traefik image version. 2019-09-17 14:12:04 +02:00
Ludovic Fernandez
254dc38c3d Prepare release v1.7.16 2019-09-13 15:04:04 +02:00
mpl
24d084d7e6 implement Flusher and Hijacker for codeCatcher 2019-09-13 14:32:03 +02:00
Ludovic Fernandez
df5f530058 Prepare release v1.7.15 2019-09-12 18:10:05 +02:00
mpl
753d173965 error pages: do not buffer response when it's not an error 2019-09-12 16:20:05 +02:00
Daniel Tomcej
ffd1f122de Add TLS minversion constraint 2019-09-12 14:48:05 +02:00
Piotr Majkrzak
f98b57fdf4 Fix wrong handling of insecure tls auth forward ingress annotation 2019-09-12 11:44:05 +02:00
Damien Duportal
2d37f08864 Improve Access Logs Documentation page 2019-09-11 18:14:03 +02:00
Nicholas Wiersma
a7dbcc282c Consider default cert domain in certificate store
Co-authored-by: Nicolas Mengin <nmengin.pro@gmail.com>
2019-09-11 17:46:04 +02:00
David Dymko
f4f62e7fb3 Update Acme doc - Vultr Wildcard & Root 2019-09-09 09:26:04 +02:00
mpl
4cae8bcb10 Finish kubernetes throttling refactoring 2019-08-31 05:10:04 -07:00
Ben Weissmann
bee370ec6b Throttle Kubernetes config refresh 2019-08-30 03:16:04 -07:00
pitan
f1d016b893 Typo in basic auth usersFile label consul-catalog 2019-08-21 01:36:03 -07:00
Erik Wegner
4defbbe848 Kubernetes support for Auth.HeaderField 2019-08-21 01:30:05 -07:00
Ludovic Fernandez
f397342f16 Prepare release v1.7.14 2019-08-14 02:06:03 -07:00
Ludovic Fernandez
989a59cc29 Update to go1.12.8 2019-08-14 01:52:05 -07:00
Julien Levesy
c5b71592c8 Make hijackConnectionTracker.Close thread safe 2019-08-12 02:34:04 -07:00
Ludovic Fernandez
9de3129a55 Prepare release v1.7.13 2019-08-08 09:04:03 -07:00
Douglas Wagner
40ab1f325c Wrr loadbalancer honors old weight on recovered servers 2019-08-07 08:14:04 -07:00
Daniel Becker
73e0561610 Update lego 2019-08-05 09:08:04 -07:00
Jean Prat
0a89cccdc0 warning should not be a fail status 2019-08-05 08:40:04 -07:00
Daniel Tomcej
b102e6de5a Add missing KeyUsages for default generated certificate 2019-08-05 06:20:05 -07:00
alvarezbruned
2064a6f805 Add example for CLI 2019-08-01 05:38:04 -07:00
Amir Keibi
72e2ddff98 Updating Service Fabric documentation 2019-07-26 02:30:05 -07:00
David Dymko
7db41967c6 Fixed doc link for AlibabaCloud 2019-07-16 16:02:04 +02:00
Jean-Baptiste Doumenjou
a8680a8719 Fixes the TLS Mutual Authentication documentation 2019-07-15 08:46:04 +02:00
Ludovic Fernandez
7e11fa1193 Update Docker version. 2019-07-15 07:04:02 +02:00
gurayyildirim
489b5a6150 Format YAML example on user guide 2019-07-08 10:40:03 +02:00
Daniel Tomcej
8027e8ee23 Check for multiport services on Global Backend Ingress 2019-06-27 09:42:03 +02:00
Damien Duportal
d35d5bd2e8 Update documentation to clarify the default format for logs 2019-06-26 14:28:04 +02:00
stffabi
8e47bdedc6 Clear TLS client headers if TLSMutualAuth is optional 2019-06-26 11:32:04 +02:00
Damien Duportal
51419a9235 Update Slack support channel references to Discourse community forum 2019-06-25 10:06:03 +02:00
Ludovic Fernandez
32fd52c698 Use dynamodbav tags to override json tags. 2019-06-24 09:14:03 +02:00
Ludovic Fernandez
468e4ebd98 Add remarks about Rancher 2 2019-06-21 19:04:04 +02:00
Damien Duportal
92a57384a4 Allows logs to use local time zone instead of UTC
Co-authored-by: Ludovic Fernandez <ldez@users.noreply.github.com>
2019-06-17 09:20:03 +02:00
Damien Duportal
2067a433cd Improve API / Dashboard wording in documentation 2019-06-04 00:42:05 +02:00
kolaente
16f1f851cc Use the latest stable version of traefik in the docs 2019-06-03 17:38:07 +02:00
Daniel Tomcej
8e992c7cfb Update docker api version 2019-06-03 11:28:06 +02:00
Ludovic Fernandez
9da0bcf3aa Prepare release v1.7.12 2019-05-29 20:42:06 +02:00
Ludovic Fernandez
51287d9316 Remove authentication hashes from API 2019-05-29 19:22:07 +02:00
Ludovic Fernandez
29307fe9fa fix: update-lego. 2019-05-28 17:32:05 +02:00
ravilr
52772c9642 fix kubernetes template for backend responseforwarding flushinterval setting 2019-05-23 18:14:05 +02:00
ravilr
8f135fdb0a Add missing callback on close of hijacked connections 2019-05-23 09:40:04 +02:00
Ludovic Fernandez
7722a41270 Adds a log fields documentation. 2019-05-21 17:02:11 +02:00
Alex Antonov
cd11b1aab4 Upgraded DataDog tracing library to 1.13.0 2019-05-15 18:04:06 +02:00
Ludovic Fernandez
e7564d4cf8 Enhance KV logs. 2019-05-15 17:20:08 +02:00
Wenxuan Zhao
cc130fb673 Allow SANs for wildcards domain. (#4821) 2019-05-03 18:08:37 +02:00
Greg Berns
4106cf647b Docs: Troubleshooting help for Docker Swarm labels 2019-05-03 10:28:05 +02:00
Ludovic Fernandez
8a1c3510ea Prepare release v1.7.11 2019-04-26 10:34:06 +02:00
Jean-Baptiste Doumenjou
787b0a3ac7 Enhance KV client error management
Co-authored-by: Ludovic Fernandez <ldez@users.noreply.github.com>
2019-04-26 09:36:03 +02:00
Ludovic Fernandez
a6d86cdddb fix: update lego. 2019-04-23 11:40:05 +02:00
mpl
9559a56011 Improve log message about redundant TLS certificate 2019-04-15 12:32:03 +02:00
ctas582
07e8042192 Forward all header values from forward auth response 2019-04-10 17:18:06 +02:00
Martin Hoefling
edd8e36dcc Documentation Update: Hosting.de wildcard support tested 2019-04-09 16:52:05 +02:00
Ludovic Fernandez
e291dcb8d1 Get Structor version from CI env var. 2019-04-08 08:44:03 +02:00
Ludovic Fernandez
de6e365b74 Update zipkin-go-opentracing. 2019-04-05 11:18:04 +02:00
Amir Aslaminejad
98a5e08553 Remove usage of github.com/satori/go.uuid 2019-04-05 08:44:03 +02:00
Brandon McNama
ba44619828 Update Wildcard Domain documentation 2019-04-01 17:42:04 +02:00
Ludovic Fernandez
86d700c845 Enhance k8s tests maintainability 2019-04-01 17:02:06 +02:00
Ludovic Fernandez
b2f11afcc2 Update certificates for some Docker images 2019-04-01 00:54:03 +02:00
Maxim Fominykh
cfb356c68e Keep consistent order 2019-03-29 16:40:03 +01:00
Ludovic Fernandez
266f5d18a8 Prepare release v1.7.10 2019-03-28 15:42:05 +01:00
Julien Salleyron
305af43fb9 Add entrypoints prefix in kubernetes frontend/backend id 2019-03-28 14:22:05 +01:00
Julien Salleyron
30545808d9 Remove thoas/stats fork 2019-03-28 13:46:04 +01:00
Michael
358f125a58 Migrate to dep 0.5 (#4684) 2019-03-28 12:58:57 +01:00
Andy Kipp
57ae9a80d5 Support external name service on global default backend 2019-03-27 17:48:04 +01:00
Alex Antonov
e32c021f16 Added support for configuring trace headers for DataDog tracing 2019-03-27 17:20:05 +01:00
Siyu
0db2a9aadd Exclude websocket connections from Average Response Time 2019-03-27 15:48:04 +01:00
tomberek
4f4dab3ca5 Reorder Auth and TLSClientHeaders middleware 2019-03-27 11:54:05 +01:00
Ludovic Fernandez
eaee39e534 fix: update lego. 2019-03-27 11:18:04 +01:00
Jonas Thelemann
d85eb0495c Add _FILE Environment Variable Documentation 2019-03-27 10:50:03 +01:00
Ludovic Fernandez
8aa618775d New build system for experimental Docker image. 2019-03-19 11:58:03 +01:00
Ludovic Fernandez
f6b7e333be Change deploy script. 2019-03-19 07:20:02 +01:00
Ludovic Fernandez
108d9dbb3f Update structor to restore permalink for the latest version 2019-03-18 11:32:03 +01:00
Emile Vauge
2a1fa32950 Add TraefikEE as security workaround 2019-03-15 18:54:05 +01:00
Ludovic Fernandez
ee7aa77833 Update structor 2019-03-15 16:04:04 +01:00
Ludovic Fernandez
fcc4cab614 Migrate to go-acme/lego. 2019-03-12 16:40:04 +01:00
Damien Duportal
1206cd52fc Travis: switch fallback dockerfile for structor 2019-02-27 18:56:04 +01:00
Daniel Tomcej
5cdba752a4 Loop through service ports for global backend 2019-02-26 17:18:04 +01:00
Damien Duportal
b48ea1e173 Update Structor to v1.4.0 2019-02-20 13:50:03 +01:00
Ludovic Fernandez
373040f552 Applies new goimports recommendations. 2019-02-18 07:50:03 +01:00
Ludovic Fernandez
443902a0f0 Prepare release v1.7.9 2019-02-11 12:24:03 +01:00
Ludovic Fernandez
9eb02d9b03 Add support for specifying the name of the endpoint. 2019-02-11 09:12:04 +01:00
Ludovic Fernandez
2eb651645d Updates of Lego. 2019-02-11 08:52:03 +01:00
Ludovic Fernandez
630571fdc8 Fixes the display of the associativity rules. 2019-02-08 09:06:03 +01:00
Rémy G
00fc43ebce Fixed curl example 2019-02-06 17:10:09 +01:00
Mohamed Abdelkader Hizaoui
6d906fa4c8 Add Tracing Header Context Name option for Jaeger 2019-02-05 18:20:03 +01:00
Doctori
6a4c7796e3 app-root on non-explicit path include "/" in the redirect 2019-02-05 17:30:07 +01:00
Adam Gołąb
67704e333d Update default value in docs of buckets for Prometheus 2019-02-05 16:36:08 +01:00
SALLEYRON Julien
76c9cea856 fix missing trailers with retry 2019-02-01 09:50:04 +01:00
apsifly
0366fb9bc2 handle errors when working with rancher 2019-01-30 17:10:03 +01:00
Jean-Baptiste Doumenjou
5fed947eaa insecureSkipVerify for the passTLSCert transport 2019-01-30 16:50:05 +01:00
Ludovic Fernandez
c289279d24 doc: update change log. 2019-01-30 16:26:06 +01:00
Ludovic Fernandez
418dca1113 Prepare release v1.7.8 2019-01-29 17:22:07 +01:00
Ludovic Fernandez
ed293e3058 Fixes docker swarm mode refresh second for KV. 2019-01-29 17:08:08 +01:00
Foivos Filippopoulos
40bb0cd879 Check for dynamic tls updates on configuration preload 2019-01-29 16:46:09 +01:00
Ludovic Fernandez
32f5e0df8f Updates lego. 2019-01-25 10:24:04 +01:00
Maarten van der Hoef
40b8a93930 Generic awsvpc support, not just Fargate 2019-01-21 17:00:07 +01:00
hwhelan-CB
b52b0f58b9 Cache exising task definitions to avoid rate limiting 2019-01-19 08:56:02 +01:00
David Birks
0016db0856 Minor formatting fixes 2019-01-16 20:10:04 +01:00
Joost Cassee
5aeca4507e Support Datadog tracer priority sampling 2019-01-16 17:08:06 +01:00
Thorsten
11bfb49e65 Route priorities: document minimum priority value 2019-01-16 16:44:05 +01:00
Dragnucs
d0cdb5608f Note about quotes for entrypoint definition with docker-compose 2019-01-16 16:36:07 +01:00
Timo Reimann
8bb8ad5e02 Assert that test timeout service is ready. 2019-01-16 15:00:09 +01:00
rbq
b488d8365c Allow Træfik to update Ingress status 2019-01-15 16:42:05 +01:00
Ludovic Fernandez
e2bd7b45d1 doc: more detailed info about Google Cloud DNS. 2019-01-15 16:30:08 +01:00
Tim Stackhouse
c8ea2ce703 Tested wildcard ACME challenge with DNSimple 2019-01-14 17:40:04 +01:00
Henri Larget
7d1edcd735 doc missing information about statistics parameter 2019-01-14 17:28:04 +01:00
Ishaan Bahal
9660c31d3d Removed repeated entryPoints.http from grpc.md 2019-01-11 16:34:04 +01:00
Ludovic Fernandez
37ac19583a fix: update lego. 2019-01-11 16:22:03 +01:00
Emile Vauge
ca60c52199 Happy 2019 2019-01-08 16:22:04 +01:00
Ludovic Fernandez
9cddf5616a Prepare release v1.7.7 2019-01-08 10:52:03 +01:00
Jean-Baptiste Doumenjou
f8ba843ad2 Add Pass TLS Cert Issuer and Domain Component 2019-01-08 09:46:04 +01:00
Daniel Tomcej
e598d6bcca Skip TLS section with no secret in Kubernetes ingress 2019-01-08 09:22:03 +01:00
Julien Levesy
52ec0e8d34 Retry middleware : store headers per attempts and propagate them when responding. 2019-01-07 23:18:03 +01:00
Jack
13c32dee11 Harden Traefik systemd service 2019-01-07 19:02:03 +01:00
Tim Möhlmann
6db120fd8e Test and exit for jq error before domain loop 2019-01-07 18:50:04 +01:00
Ludovic Fernandez
b6b3a9ed7f Update Lego 2019-01-07 18:30:06 +01:00
Radoslaw Wesolowski
b5a4d0797a Redirection status codes for methods different than GET 2019-01-07 17:56:04 +01:00
Michael
9ec2887494 Fix html-proofer and nokogiri version 2019-01-07 17:28:03 +01:00
Asmir Mustafic
94de6eb7f2 Letsencrypt - Add info on httpreq format 2019-01-04 17:24:04 +01:00
Wim Fournier
0e643f06a3 Proposing a small update to documentation 2019-01-03 18:30:05 +01:00
Daniel Tomcej
4db0dd9a7f Allow empty path with App-root annotation 2018-12-26 11:52:03 +01:00
Daniel Tomcej
ee19e66aba Check for watched namespace before getting kubernetes objects 2018-12-26 11:30:05 +01:00
zarqman
16ed13ba6f kubernetes: sort and uniq TLS secrets 2018-12-20 20:08:03 +01:00
Joost Cassee
8f5e972843 Update contributing docs: use dep version < 0.5.0 2018-12-20 17:38:04 +01:00
Damien Duportal
4b70ff82b9 Check for anchors (hashes) for external links on the documentation 2018-12-19 17:36:04 +01:00
Damien Duportal
1468e1f697 Rephrase the traefik.backend definition in documentation 2018-12-19 16:50:04 +01:00
Cleber Rech
7a963e75fd Update broken link for Docker service constraints 2018-12-18 12:26:03 +01:00
Ludovic Fernandez
9203ba5f95 Prepare release v1.7.6 2018-12-14 04:42:03 +01:00
Wim Fournier
302849282c Fix label segmentation when using custom prefix 2018-12-07 16:38:03 +01:00
Michael
2a227fa4c7 Prepare release v1.7.5 2018-12-03 11:34:04 +01:00
hwhelan-CB
31b80683c8 Filter ECS tasks by LastStatus before adding to list of service tasks 2018-12-03 10:54:07 +01:00
Michael
6aa040ad9f Fix upgrade flaeg 2018-12-03 10:34:04 +01:00
SALLEYRON Julien
617de78d14 Fix unannonced trailers problem when body is empty 2018-11-30 16:56:03 +01:00
Ludovic Fernandez
44605731dc Matcher associativity rule. 2018-11-30 09:08:03 +01:00
Thomas Krzero
9d26839e54 [docker backend] - Add config flag to set refreshSeconds for swarmmode ticker 2018-11-27 17:44:04 +01:00
Damien Duportal
7a93bcc869 Add a "Security Consideration" section in the Docker's backend section of the documentation 2018-11-27 13:54:04 +01:00
Gérald Croës
0840fd0f96 Log configuration errors from providers and keeps listening 2018-11-26 18:52:03 +01:00
Daniel Tomcej
4a5f5440d7 Implement Case-insensitive SNI matching 2018-11-26 10:38:03 +01:00
Ludovic Fernandez
479ee9af49 Fix partial declaration of authentication. 2018-11-22 16:52:03 +01:00
Siyu
ef6c1fac4b Update swarm mode endpoint 2018-11-20 16:40:06 +01:00
Ludovic Fernandez
53201b65b0 fix: ACME spec and Cloudflare. 2018-11-16 16:40:04 +01:00
Julien Levesy
9cb4d56c03 Clarify swarm loadbalancer documentation 2018-11-15 19:46:03 +01:00
Michael
f25ebc7e3d Query params in health check 2018-11-15 15:50:03 +01:00
Alex Antonov
4c25cda55f Upgraded DD APM library 2018-11-15 11:16:03 +01:00
Shane Hanna
18962b61d6 Include an explicit list of kubernetes protocol annotations in docs. 2018-11-15 10:52:04 +01:00
Michael Terrington
d0aa993661 Improve kubernetes TLS user guide 2018-11-14 10:24:03 +01:00
Si Westcott
df688dba0e frame-deny should be set to true to enable the header 2018-11-14 08:42:03 +01:00
Demian
69585c58cd Allow usersFile comments 2018-11-12 17:28:04 +01:00
stffabi
b889b0191c Remove X-Forwarded-Uri and X-Forwarded-Method from untrusted IP 2018-11-12 17:06:04 +01:00
SALLEYRON Julien
9b0586cba7 Verify ctx when we send configuration message in docker provider 2018-11-12 09:52:03 +01:00
Giorgio Comai
a20f5bda0b Fix spelling in comment 2018-11-08 08:30:04 +01:00
Tobias Hernstig
28d5731b87 Documentation: Rename "admin panel" to "dashboard 2018-11-08 08:20:03 +01:00
Rouven Bauer
66486eacdc ACME DNS provider is called acme-dns 2018-11-07 16:26:03 +01:00
Kim Min
2b82ed054c Support canary weight for external name service 2018-11-06 07:48:02 +01:00
Michael
cd6f0c0ded Fix ssl force host secure middleware 2018-10-31 15:00:09 +01:00
Ludovic Fernandez
1fad7e5a1c Prepare release v1.7.4 2018-10-30 11:32:04 +01:00
Aaron
19546ab518 Fix mistake in the documentation of several backends (#4133) 2018-10-30 10:49:41 +01:00
SALLEYRON Julien
e6e9a86919 Add flush interval option on backend 2018-10-29 18:42:03 +01:00
Manuel Zapf
c6dd1dccc3 add static redirect 2018-10-29 16:48:06 +01:00
Brendan LE GLAUNEC
993caf5058 Fix access log field parsing 2018-10-29 16:24:04 +01:00
Jean-Baptiste Doumenjou
450471d30a Add the missing pass-client-tls annotation to the kubernetes provider 2018-10-29 16:02:06 +01:00
SALLEYRON Julien
7eeecd23ac Provider docker shutdown problem 2018-10-29 15:30:04 +01:00
Konovalov Nikolay
21c94141ba Update docs/configuration/acme.md 2018-10-29 14:58:03 +01:00
mwvdev
bc2cba5aa4 Removed unused imports 2018-10-29 14:44:03 +01:00
Yoan Blanc
5e49354bf2 acme: exoscale move from .ch to .com 2018-10-29 14:20:03 +01:00
Brendan LE GLAUNEC
55334b2062 Fix display of client username field 2018-10-25 18:00:05 +02:00
Ludovic Fernandez
74dc5b1c58 Support custom DNS resolvers for Let's Encrypt. 2018-10-25 17:38:04 +02:00
Gérald Croës
ac11323fdd Replaces emilevauge/whoami by containous/whoami in the documentation 2018-10-25 17:04:03 +02:00
Ludovic Fernandez
8c2e99432d Add a note about TLS-ALPN challenge. 2018-10-25 16:50:05 +02:00
Benjamin Gandon
aa26927d61 Case insensitive host rule 2018-10-25 10:18:03 +02:00
Manuel Zapf
22ee8700ca add default path if nothing present 2018-10-25 09:50:03 +02:00
Daniel Tomcej
df55c24cb5 Add missing tmp directory to scratch image 2018-10-24 11:00:05 +02:00
herver
99ddd7f9cb domain is also optional for "normal" mode 2018-10-23 18:12:03 +02:00
Daniel Tomcej
82b2a102ed Add double wildcard test 2018-10-23 18:04:05 +02:00
Ludovic Fernandez
c7df82e695 Remove the trailing dot if the domain is not defined. 2018-10-23 17:36:05 +02:00
Ludovic Fernandez
638960284e Typo in the UI. 2018-10-23 13:14:03 +02:00
Ludovic Fernandez
8e9b8a0953 fix: netcup and DuckDNS. 2018-10-23 11:18:02 +02:00
Ludovic Fernandez
3f044c48fa Nil request body with retry 2018-10-23 10:10:04 +02:00
Nick Maliwacki
37d8e32e0b clarify DuckDNS does not support multiple TXT records 2018-10-18 16:42:03 +02:00
Gérald Croës
46ce807624 Adds the note: acme.domains is a startup configuration 2018-10-17 16:30:04 +02:00
Gérald Croës
e6a88f3531 Uses ASCII characters to spell Traefik 2018-10-17 16:24:04 +02:00
SALLEYRON Julien
95d86d84b4 Add keepTrailingSlash option 2018-10-17 14:22:03 +02:00
Michael
70fa42aee0 Improve maintainer documentation 2018-10-16 18:12:03 +02:00
Jean-Baptiste Doumenjou
ba99fbe390 Fix certificate insertion loop to keep valid certificate and ignore the bad one 2018-10-16 11:00:04 +02:00
Gérald Croës
c875819a2e Prepare release v1.7.3 2018-10-15 12:02:03 +02:00
Gérald Croës
6d4cf0d892 usebindportip can fall back on the container ip / port 2018-10-15 11:46:03 +02:00
Crypto89
78a9d20691 Add the AuthResponseHeaders to the labels 2018-10-15 10:54:03 +02:00
Ludovic Fernandez
7c2409b5a7 DNS challenge Cloudflare auth zone 2018-10-15 09:40:02 +02:00
Ludovic Fernandez
0335f6fba9 Log stack when panic 2018-10-12 15:40:03 +02:00
Michael
2c7b7cd6ca Fix recover from panic handler 2018-10-12 13:04:02 +02:00
Ludovic Fernandez
5632952665 fix: golint. 2018-10-12 09:28:03 +02:00
Ludovic Fernandez
7eeac63139 Fix: acme DNS providers 2018-10-11 16:50:04 +02:00
Ludovic Fernandez
1b54f4d32a acme: prevent some malformed errors. 2018-10-11 10:50:03 +02:00
Damien Duportal
e8e9dd9400 Improve the CLI help (Update of the Flaeg dependency) 2018-10-10 19:10:04 +02:00
Michael
b722748ec3 Fix update oxy 2018-10-10 18:20:05 +02:00
Morten Hekkvang
609b2630d7 Add tags label to Docker provider documentation 2018-10-10 17:46:03 +02:00
Ludovic Fernandez
5bdf8a5ea3 ACME DNS challenges 2018-10-10 16:28:04 +02:00
Pascal Andy
7a2592b2fa Added two example / Using labels in docker-compose.yml 2018-10-10 10:12:03 +02:00
Ludovic Fernandez
546bebc860 Move buffering annotation documentation to service 2018-10-09 18:44:03 +02:00
arnydo
ad51f4f2a5 Fix a typo 2018-10-09 18:36:03 +02:00
NicoMen
a777c3553c Not allow ACME provider initialization if storage is empty 2018-10-08 19:24:03 +02:00
Rene Treffer
157580c232 Avoid flapping of multiple Ingress definitions 2018-10-05 18:36:03 +02:00
Jean-Baptiste Doumenjou
a9deeb321b Prepare release v1.7.2 2018-10-04 15:34:02 +02:00
Ludovic Fernandez
ec86149b1e Rule parsing error. 2018-10-04 10:20:03 +02:00
Daniel Tomcej
31f92001e2 Add Template-ability check to Kubernetes API Fields 2018-10-04 09:58:03 +02:00
Timo Reimann
d69977c229 Do not Errorf during file watcher verification test loop. 2018-10-04 09:26:03 +02:00
Oliver Moser
44e06a1a1e Trimming whitespace in XFF for IP whitelisting 2018-10-03 22:52:02 +02:00
Ludovic Fernandez
4cb1ae4626 Colored logs on windows. 2018-10-02 16:28:04 +02:00
Ludovic Fernandez
f04813fa02 Whitelist log for deprecated configuration. 2018-10-01 19:44:03 +02:00
Ludovic Fernandez
742029d8a4 Global configuration log at start 2018-10-01 19:18:03 +02:00
Dan Fredell
f74526a36e Document the default accessLog format 2018-10-01 18:54:04 +02:00
Fabrice
61e1836472 Return an error if kv store CA cert is invalid 2018-10-01 17:24:03 +02:00
Fabrice
8d8e509fe6 Correctly initialize kv store if storage key missing 2018-10-01 17:02:02 +02:00
Ludovic Fernandez
147e79ea07 TLS, ACME, cluster and several entrypoints. 2018-10-01 16:56:03 +02:00
Ludovic Fernandez
9e26f0b058 Prepare release v1.7.1 2018-09-29 00:16:03 +02:00
SALLEYRON Julien
8cc3c4a6b7 Use the first static certificate as a fallback when no default is given 2018-09-29 00:04:02 +02:00
SALLEYRON Julien
1d8bdd4384 Don't remove static certs from config when cluster mode 2018-09-28 17:54:04 +02:00
SALLEYRON Julien
7033b996c6 Don't challenge ACME when host rule on another entry point 2018-09-27 18:04:03 +02:00
Ludovic Fernandez
0c76a8ac89 Fix TLS ALPN cluster mode. 2018-09-27 16:54:05 +02:00
Ludovic Fernandez
d4311f9cf5 Prepare release v1.7.0 2018-09-24 11:44:03 +02:00
Manfred Dreese
6a50a6fd5a Added Dashboard table item for Rate Limits 2018-09-24 11:20:03 +02:00
stffabi
29473ef356 Do not copy hop-by-hop headers to forward auth request 2018-09-24 10:42:03 +02:00
Ludovic Fernandez
1f1ecb15f6 Fix logger in Oxy 2018-09-24 10:04:03 +02:00
Ludovic Fernandez
38d655636d Fix some DNS providers issues 2018-09-21 18:38:02 +02:00
Manfred Dreese
9ab5cbf235 Removed non-applicable default tests and fixed custom tests 2018-09-21 16:44:02 +02:00
Damien Duportal
f63873cc73 Prepare release 1.7.0-rc5 2018-09-18 15:36:03 +02:00
stffabi
c2938ff138 Remove hop-by-hop headers from forward auth response 2018-09-18 14:22:03 +02:00
Daniel Tomcej
ab2c98d931 Ensure only certificates from ACME enabled entrypoint are used 2018-09-18 08:22:03 +02:00
SALLEYRON Julien
0ae8cd9a9d Fix error pages 2018-09-17 20:40:04 +02:00
NicoMen
f3aefe282c Avoid panic during stop 2018-09-17 16:26:03 +02:00
Ludovic Fernandez
a80cca95a2 Update lego 2018-09-17 15:16:03 +02:00
NicoMen
c52f4b043d Add interface to Træfik logger 2018-09-14 13:34:03 +02:00
Ludovic Fernandez
253060b4f3 Update Lego 2018-09-14 10:06:03 +02:00
Nathanael Marchand
36966da701 Add missing quotes around backendName in kv template 2018-09-14 09:00:03 +02:00
Michael
bb7c4aaf7e Fix tracing duplicated headers 2018-09-12 16:32:04 +02:00
SALLEYRON Julien
c68ebaa2ca Prepare release v1.7.0-rc4 2018-09-07 11:34:03 +02:00
Fernandez Ludovic
538424b01c Merge branch 'v1.6' into v1.7 2018-09-07 10:11:30 +02:00
Ludovic Fernandez
48e7a87741 Update go version 2018-09-07 09:40:03 +02:00
NicoMen
74ace58ae1 Avoid goroutine leak in server 2018-09-06 14:24:03 +02:00
NicoMen
913d8737cc Allow Træfik to know if a Host rule is malformed 2018-09-04 17:14:04 +02:00
Ludovic Fernandez
b98f5ed8b1 Remove a dead link. 2018-09-04 15:54:06 +02:00
Gérald Croës
e4bb506ace StoreConfig always initializes the account if it is missing 2018-09-03 10:12:03 +02:00
Damien Duportal
0f0ba099c9 Make the "base domain" on all providers 2018-08-30 02:24:03 +02:00
SALLEYRON Julien
f400292be7 Enable retry on websocket 2018-08-29 11:58:03 +02:00
Jean-Baptiste Doumenjou
efc6560d83 Pass the TLS Cert infos in headers 2018-08-29 11:36:03 +02:00
SALLEYRON Julien
56488d435f Handle Te header when http2 2018-08-27 18:10:03 +02:00
Wim Fournier
f586950528 multiple frontends for consulcatalog 2018-08-27 17:00:05 +02:00
Michael
a302731cd1 Add segment support for ECS 2018-08-27 16:32:05 +02:00
Alex
ef753838e7 Fix documentation for route53 acme provider 2018-08-24 07:14:03 +02:00
Michael
acb79d6f73 Merge branch 'v1.6' into v1.7 2018-08-23 10:49:03 +02:00
Michael
157c796294 Rename traefikproxy twitter account into traefik 2018-08-23 10:44:02 +02:00
Ilya Galimyanov
0861c59bec Remove unnecessary loop 2018-08-23 09:40:03 +02:00
Daniel Tomcej
e4a7375d34 Update kubernetes docs to reflect https options 2018-08-23 08:52:02 +02:00
Andrew Bruce
6bbac65f7e Include missing key in error message for KV store 2018-08-22 16:08:02 +02:00
Emile Vauge
845f1a7377 Add security mailinglist 2018-08-22 10:18:03 +02:00
Ludovic Fernandez
9c8e518423 Auth Forward with certificates in templates. 2018-08-21 17:12:03 +02:00
Fernandez Ludovic
bd3b787fd5 Merge 'v1.6.6' into v1.7 2018-08-21 11:43:34 +02:00
Emile Vauge
27e4a8a227 Fixes bad palette in doc 2018-08-20 17:50:04 +02:00
David Rodríguez
cf2d7497e4 Mention docker-compose as a requirement in the let's encrypt guide 2018-08-20 17:34:05 +02:00
Emile Vauge
df41cd925e Add vulnerability form 2018-08-20 17:08:03 +02:00
Damien Duportal
e46de74328 Improve the wording in the documentation for Docker and fix title for Docker User Guide 2018-08-20 16:46:04 +02:00
Ludovic Fernandez
feeb7f81a6 Prepare Release v1.6.6 2018-08-20 14:46:02 +02:00
Damien Duportal
2beb5236d0 A tiny rewording on the documentation API's page 2018-08-20 13:34:03 +02:00
Damien Duportal
f062ee80c8 Docs: Adding warnings and solution about the configuration exposure 2018-08-20 12:02:03 +02:00
SALLEYRON Julien
a7bb768e98 Remove TLS in API 2018-08-20 11:16:02 +02:00
SALLEYRON Julien
07be89d6e9 Update oxy dependency 2018-08-20 10:38:03 +02:00
NicoMen
d81c4e6d1a Avoid duplicated ACME resolution 2018-08-20 09:40:03 +02:00
Daniel Tomcej
870755e90d Extend https redirection tests, and fix incorrect behavior 2018-08-14 18:38:04 +02:00
Daniel Tomcej
bd3c8c3cde Don't merge kubernetes ingresses when priority is set 2018-08-14 17:40:04 +02:00
Daniel Tomcej
278b3180c3 Prevent unparsable strings from being rendered in the Kubernetes template 2018-08-14 16:48:04 +02:00
Ludovic Fernandez
bb2686a08f Update ACME documentation about TLS-ALPN challenge 2018-08-13 08:46:02 +02:00
Manjunath A Kumatagi
202783ca7d Add ppc64le platform support 2018-08-10 16:56:04 +02:00
Fernandez Ludovic
308904110a Merge branch 'v1.6' into 'v1.7' 2018-08-08 19:16:00 +02:00
macros
60b4095c75 Set keepalive on TCP socket so idleTimeout works 2018-08-08 19:12:03 +02:00
NicoMen
d04b4fa2cc Set a keyType to ACME if the account is stored with no KeyType 2018-08-08 07:58:03 +02:00
Ludovic Fernandez
2d449f63e0 Typo in auth labels. 2018-08-08 02:52:03 +02:00
Ludovic Fernandez
7ff6e6b66f Freeze mkdocs version 2018-08-06 15:50:03 +02:00
nicolas Trauwaen
bb33128552 Change syntax in quick start guide 2018-08-06 14:08:03 +02:00
Ludovic Fernandez
86add29838 Freeze mkdocs version. 2018-08-06 13:50:02 +02:00
Alex
70712a0f62 Typo in docker-and-lets-encrypt.md 2018-08-03 17:52:03 +02:00
NicoMen
4db937b571 Avoid a panic during Prometheus registering 2018-08-03 14:02:02 +02:00
Tom Mast
ad6f41c77a Simple documentation grammar update in tracing 2018-08-03 10:36:03 +02:00
Kevin Maris
e6040e55f5 Update kubernetes.md 2018-08-03 10:00:03 +02:00
Daniel Tomcej
b4ac3d4470 Improve Connection Limit Kubernetes Documentation 2018-08-02 08:14:02 +02:00
Ludovic Fernandez
d62f7e2082 Use official Pebble Image. 2018-08-01 16:56:04 +02:00
Michael
cfe2f1a1e6 Prepare release 1.7.0-rc3 2018-08-01 15:28:03 +02:00
Michael
7732e2307e Fix missing tracing backend in documentation 2018-08-01 13:36:03 +02:00
Andrei Korigodski
8c733abef3 Fix style in examples/quickstart 2018-08-01 11:22:03 +02:00
Alex Antonov
4d79c2a6d2 Added support for Trace name truncation for traces 2018-08-01 00:16:03 +02:00
Damien Duportal
ed0c7d9c49 H2C: Remove buggy line in init to make verbose switch working 2018-07-31 18:56:03 +02:00
Teo Stocco
fb4717d5f3 Add traefik prefix to k8s annotations 2018-07-31 17:58:04 +02:00
Manuel Zapf
09b489a614 Add possibility to set a protocol 2018-07-31 17:12:03 +02:00
NicoMen
402f7011d4 Fix ACME certificate for wildcard and root domains 2018-07-31 12:32:04 +02:00
Lukas Prettenthaler
838dd8c19f Fix acme account deletion without provider change 2018-07-31 11:50:03 +02:00
Daniel Tomcej
91cafd1752 Correct Entrypoint Redirect with Stripped or Added Path 2018-07-31 11:28:03 +02:00
Andrei Korigodski
eea60b6baa Replace unrendered emoji 2018-07-31 10:58:03 +02:00
Michael
baf8d63cb4 Improve TLS integration tests 2018-07-31 10:48:03 +02:00
Rasmus Holm
967e4208da Updating oxy dependency 2018-07-31 10:28:02 +02:00
Daniel Tomcej
ba3a579d07 Fix Rewrite-target regex 2018-07-31 09:08:03 +02:00
Alex Antonov
7d2b7cd7f1 Added default configuration for DataDog APM Tracer 2018-07-26 17:44:03 +02:00
Michael
73b4df4e18 Remove TLS in API 2018-07-26 12:42:03 +02:00
Michael
37aa902cef Docs verify failed only on 4XX 2018-07-25 17:58:02 +02:00
Stoinov
bafb583666 Update Namecheap status 2018-07-24 01:02:03 +02:00
Michael
aabebb2185 Update lego 2018-07-23 17:30:03 +02:00
SALLEYRON Julien
c8ae97fd38 Handle shutdown of Hijacked connections 2018-07-19 17:30:06 +02:00
Gérald Croës
d50b6a34bc Uses both binded HostIP and HostPort when useBindPortIP=true 2018-07-19 16:40:03 +02:00
Ludovic Fernandez
853be929bc fix: some DNS provider link 2018-07-18 14:24:03 +02:00
NicoMen
a1911a9608 Prepare release 1.7.0 rc2 2018-07-17 14:30:03 +02:00
Michael
ff2e2d5026 Fix 400 bad request on AWS ECS API 2018-07-17 12:26:03 +02:00
Ludovic Fernandez
a953d3ad89 Auth section in web UI. 2018-07-17 12:02:03 +02:00
Jean-Baptiste Doumenjou
9ce444b91a Don't pass the Authorization header to the backends 2018-07-16 13:52:03 +02:00
Fernandez Ludovic
ae8be89767 Merge branch 'v1.6' into 'v1.7' 2018-07-13 18:06:07 +02:00
SALLEYRON Julien
5774d100c1 Update service fabric 2018-07-13 18:04:03 +02:00
Daniel Tomcej
dbe720f0f1 Remove unusable --cluster flag 2018-07-13 17:32:03 +02:00
Ludovic Fernandez
5afc8f2b12 KV and authentication 2018-07-13 17:24:03 +02:00
Fernandez Ludovic
c7e008f57a Merge branch 'v1.6' into 'v1.7' 2018-07-12 19:24:11 +02:00
NicoMen
14b7152bf0 Serve TLS-Challenge certificate in first 2018-07-12 19:10:03 +02:00
Damien Duportal
3ef6bf2118 Documentation: Introduces a check stage to validate HTML and links 2018-07-12 18:26:03 +02:00
Jonathan Ballet
f0ab2721a5 Fix path to the debug pprof API 2018-07-12 17:58:02 +02:00
Daniel Tomcej
2721c2017c Correct Modifier in Kubernetes Documentation 2018-07-12 17:42:05 +02:00
Michael
a7c158f0e1 Fix bad condition in ECS provider 2018-07-12 17:40:04 +02:00
Daniel Tomcej
7ff9193cf5 Correct App-Root kubernetes behavior 2018-07-12 15:20:04 +02:00
Fernandez Ludovic
031451abab Merge branch 'v1.6.5' into 'v1.7' 2018-07-11 11:07:42 +02:00
Ludovic Fernandez
8d75aba7eb Send 'Retry-After' to comply with RFC6585. 2018-07-11 10:08:03 +02:00
SALLEYRON Julien
027093a5a5 Create init method on provider interface 2018-07-11 09:08:03 +02:00
Ludovic Fernandez
bdc0e3bfcf Prepare release v1.6.5 2018-07-10 17:46:04 +02:00
Daniel Tomcej
b2a57ca1f3 Add more K8s Unit Tests 2018-07-10 11:22:03 +02:00
Fernandez Ludovic
6ef0e6791b Merge branch 'v1.6' into 'v1.7' 2018-07-10 01:24:14 +02:00
Daniel Tomcej
9374d6b3b9 Fix rewrite-target Annotation behavior 2018-07-10 00:26:03 +02:00
SALLEYRON Julien
f173ff02e3 Add a mutex on local store for HTTPChallenges 2018-07-09 23:28:02 +02:00
Michael
ba2046491a Prepare release 1.7.0-rc1 2018-07-09 16:54:04 +02:00
Fernandez Ludovic
083b471bcf Merge branch 'v1.6' into master 2018-07-09 15:34:47 +02:00
Shane Smith-Sahnow
bf73127e0b Performance enhancements for the rules matchers. 2018-07-09 15:08:04 +02:00
SALLEYRON Julien
333b785061 Add logs when error is generated in error handler 2018-07-07 10:14:03 +02:00
Jean-Baptiste Doumenjou
79bf19c897 Auth support in frontends 2018-07-06 16:52:04 +02:00
Timo Reimann
0c0ecc1cdc Call functions to enable block/mutex pprof profiles. 2018-07-06 10:58:04 +02:00
SALLEYRON Julien
bacd58ed7b Add logs when error is generated in error handler 2018-07-06 10:32:03 +02:00
Daniel Tomcej
689f120410 Improve TLS Handshake 2018-07-06 10:30:03 +02:00
Daniel Tomcej
2303301d38 Add annotation to allow modifiers to be used properly in kubernetes 2018-07-06 10:06:04 +02:00
Or Tzabary
f323df466d Split the error handling from Consul Catalog (deadlock) 2018-07-05 15:12:03 +02:00
Kim Min
b1f1a5b757 Substitude hardcoded "<namespace>/<name>" with k8s ListerGetter 2018-07-04 17:08:03 +02:00
Andrew Stucki
0d262561d1 Allow binding ECS container port 2018-07-04 15:08:03 +02:00
hwhelan-CB
12c713b187 Support traefik.backend for ECS 2018-07-04 14:22:03 +02:00
Fabian Beuke
b1836587f2 Update keyFile first/last line comment in kv-config.md 2018-07-04 14:20:03 +02:00
Trevin Teacutter
04d8b5d483 Adding compatibility for marathon 1.5 2018-07-03 23:42:03 +02:00
Daniel Tomcej
461ebf6d88 Create Global Backend Ingress 2018-07-03 18:58:03 +02:00
Timo Reimann
41eb4f1c70 Add a k8s guide section on traffic splitting via service weights. 2018-07-03 16:50:06 +02:00
Gamalan
31a8e3e39a CNAME flattening 2018-07-03 16:44:05 +02:00
Ludovic Fernandez
139f280f35 ACME TLS ALPN 2018-07-03 12:44:04 +02:00
Ludovic Fernandez
17ad5153b8 Minor changes 2018-07-03 10:02:03 +02:00
Mikael Rapp
bb14ec70bd Auth support in frontends for k8s and file 2018-07-02 11:52:04 +02:00
Kim Min
e8e36bd9d5 Specify backend servers' weight via annotation for kubernetes 2018-07-01 11:26:03 +02:00
hwhelan-CB
f9b1106df2 Forward auth headers 2018-06-30 07:54:03 +02:00
Fernandez Ludovic
df600d6f3c Merge branch 'v1.6' into master 2018-06-30 00:43:32 +02:00
Andrew Stucki
157e76e829 Add support for ECS constraints 2018-06-30 00:14:03 +02:00
John Yani
dbc3b85cd0 Minor formatting issue in user-guide 2018-06-29 17:02:03 +02:00
Drew G. Wallace
11691019a0 Change code block of traefik-web-ui to match file 2018-06-28 23:10:03 +02:00
Alex Antonov
3192307d59 Added integration support for DataDog APM Tracing 2018-06-28 18:40:04 +02:00
Michael
ba8c9295ac Update golang.org/x/net dependency 2018-06-28 17:58:04 +02:00
Michael
d5436fb28b Create a custom logger for jaeger 2018-06-28 17:36:03 +02:00
Michael Arenzon
886cc83ad9 Add support for stale reads from Consul catalog 2018-06-28 16:40:04 +02:00
Ludovic Fernandez
9e012a6b54 Simplify acme e2e tests. 2018-06-27 15:08:05 +02:00
Jean-Baptiste Doumenjou
5eda08e9b8 Better support on same prefix at the same level in the KV 2018-06-26 16:18:05 +02:00
Ludovic Fernandez
ec6e46e2cb segment labels: multiple frontends for one backend. 2018-06-22 19:44:03 +02:00
Rene Treffer
56fe023a12 Allow any kubernetes ingressClass value 2018-06-22 16:54:03 +02:00
Michael
aa705dd691 Create middleware to be able to handle HTTP pipelining correctly 2018-06-20 09:12:03 +02:00
Daniel Tomcej
aa6fea7f21 Add more k8s tests 2018-06-19 22:10:03 +02:00
Marco Jantke
e31c85aace avoid retries when any data was written to the backend 2018-06-19 13:56:04 +02:00
manu5801
1c3e4124f8 The gandiv5 provider works with wildcard 2018-06-18 09:26:02 +02:00
Fernandez Ludovic
586ba31120 Merge v1.6.4 into master 2018-06-15 17:58:20 +02:00
Ludovic Fernandez
c1757372d3 Prepare release v1.6.4 2018-06-15 17:04:03 +02:00
Ludovic Fernandez
7451449dd6 Simplify get acme client 2018-06-15 16:42:03 +02:00
Ludovic Fernandez
5b2b29043c Use logrus writer instead of os.Stderr 2018-06-15 16:08:03 +02:00
Daniel Tomcej
2758664226 Fix panic setting ingress status 2018-06-14 09:22:03 +02:00
Ludovic Fernandez
bb3f28ffa7 Backend name with docker-compose and segments. 2018-06-14 09:20:04 +02:00
Ludovic Fernandez
6ceb2af4a7 Clean metrics documentation. 2018-06-13 17:44:04 +02:00
erik-sjoestedt
d5b649bf1c Add xforwarded method 2018-06-13 15:14:03 +02:00
Jean-Baptiste Doumenjou
81f23cc732 Add a default value for the docker.network configuration 2018-06-13 14:50:04 +02:00
Ludovic Fernandez
b59276ff1c Use net.JoinHostPort for servers URL 2018-06-13 10:08:03 +02:00
herver
2e95832812 Enclose IPv6 addresses in "[]" 2018-06-13 08:06:03 +02:00
Ludovic Fernandez
01f2b3cd20 Update Slack URL. 2018-06-12 17:04:04 +02:00
Yoan Blanc
2240bf9430 The exoscale provider works with wildcard 2018-06-12 16:16:03 +02:00
Shambarick
db036edccd Fix typo in kv user guide 2018-06-12 08:20:03 +02:00
Rodrigo Díez Villamuera
1fbf5b84a2 Added duration filter for logs 2018-06-11 18:40:08 +02:00
Mayank Jethva
08e1f626c1 edit wording 2018-06-11 16:26:03 +02:00
Emile Vauge
c0d08f5e3e Temporary benchmarks removal during performance optimization process 2018-06-11 11:46:03 +02:00
Ludovic Fernandez
eac20d61df Extreme Makeover: server refactoring 2018-06-11 11:36:03 +02:00
Ludovic Fernandez
dec3f0798a Add missing annotation documentation. 2018-06-08 12:54:02 +02:00
Fernandez Ludovic
bddb4cc33c Merge branch 'v1.6' into master 2018-06-07 10:58:59 +02:00
SALLEYRON Julien
62ded580ce Handle buffer pool for oxy 2018-06-07 09:46:03 +02:00
Arne Jørgensen
51227241b7 Fix backend reuse 2018-06-06 17:56:03 +02:00
Geert-Johan Riemer
9cf4e730e7 Fix typo which breaks k8s example manifest 2018-06-06 17:16:04 +02:00
Ludovic Fernandez
e9c63f3988 Custom frontend name for test helper 2018-06-06 15:20:03 +02:00
Pierre Mavro
2c47691cf1 Adding grafana dashboards based on prometheus metrics 2018-06-06 14:54:03 +02:00
Drew Kerrigan
599b699ac9 Segments Labels: Mesos 2018-06-06 00:26:03 +02:00
Fernandez Ludovic
a5beeb4f04 Merge tag 'v1.6.3' into master 2018-06-05 23:07:53 +02:00
Michael
446d73fcf5 Prepare release v1.6.3 2018-06-05 17:20:04 +02:00
Michael
e299775d67 Detect change when service or node are in maintenance mode 2018-06-05 16:42:03 +02:00
Marco Jantke
2c18750537 Improve Prometheus metrics removal 2018-06-05 12:32:03 +02:00
Jonas Thelemann
f317e50136 Docs: ACME Overhaul 2018-06-05 10:36:03 +02:00
Daniel Tomcej
1d84bda7ca Update Kubernetes provider to support IPv6 Backends 2018-06-05 10:28:03 +02:00
Ludovic Fernandez
ae7c947ba5 Add user agent for ACME 2018-06-05 09:54:03 +02:00
Ludovic Fernandez
6d07729c55 Add URL and Host for some access logs. 2018-06-05 09:02:03 +02:00
The Binary
1d7bf200a8 Reflect changes in catalog healthy nodes in healthCheck watch 2018-06-05 08:42:03 +02:00
Gérald Croës
6bc59f8b33 Includes the headers in the HTTP response of a websocket request 2018-06-04 14:14:03 +02:00
Ludovic Fernandez
b2cf03fa5c Use to the stable version of Lego 2018-05-31 09:30:04 +02:00
Erik Aulin
36e273714d helm installation using values 2018-05-30 17:38:04 +02:00
Ludovic Fernandez
6be77b7fb9 Webui Whitelist overflow. 2018-05-30 09:44:02 +02:00
Ludovic Fernandez
6bcf45f136 fix: whitelist and XFF. 2018-05-30 09:26:03 +02:00
Amin Cheloh
8bca8236db Update acme.md 2018-05-30 04:16:03 +02:00
Drew Kerrigan
67a0b4b4b1 Metrics: Add support for InfluxDB Database / RetentionPolicy and HTTP client 2018-05-29 22:58:03 +02:00
Michael
a7200a292b AWS ECS Fargate 2018-05-28 18:52:03 +02:00
Ludovic Fernandez
fb5aa4c9c1 Error pages and header merge 2018-05-28 15:00:04 +02:00
NicoMen
3f5772c62a Improve ACME account registration URI management 2018-05-28 14:40:03 +02:00
SALLEYRON Julien
e76836b948 h2c server 2018-05-28 11:46:03 +02:00
NicoMen
2d946d7ee7 Remove ACME empty certificates from KV store 2018-05-25 19:36:04 +02:00
Daniel Tomcej
10ca35dccd Update Kubernetes Port Documentation 2018-05-25 17:26:05 +02:00
SALLEYRON Julien
bfdd1997f6 Generated assets file are only mandatory in main 2018-05-25 15:10:04 +02:00
SALLEYRON Julien
9420308667 Implement h2c with backend 2018-05-24 10:52:04 +02:00
Ingo Gottwald
83e09acc9f Upgrade dependencies 2018-05-23 17:48:04 +02:00
Ondrej Flidr
d6d795e286 Support 3xx HTTP status codes for health check 2018-05-23 17:06:04 +02:00
ryarnyah
c09febfffc Make accesslogs.logTheRoundTrip async to get lost performance 2018-05-23 16:46:04 +02:00
Ludovic Fernandez
5b3bba8f6e Add missing quotes. 2018-05-23 16:12:05 +02:00
Marco Jantke
085593b9e5 sane default and configurable Marathon request timeouts 2018-05-22 22:38:03 +02:00
Fernandez Ludovic
e2a5d4f83e Merge 'v1.6.2' into master 2018-05-22 19:14:34 +02:00
SALLEYRON Julien
e3671cbb04 Prepare release 1.6.2 2018-05-22 17:06:04 +02:00
Ludovic Fernandez
a525d02cc5 Force to use ACME v02 endpoint. 2018-05-22 16:08:03 +02:00
Ludovic Fernandez
3c8c5ebb96 Use index-based syntax in KV tests. 2018-05-22 12:30:03 +02:00
Ludovic Fernandez
1cc1a4e6e2 WebUI typo into the buffering section. 2018-05-22 12:28:03 +02:00
SALLEYRON Julien
3f0af3fe09 No template parsing on traefik configuration file 2018-05-22 12:02:03 +02:00
Ludovic Fernandez
e2bac47a0a Support all 2xx HTTP status code for health check. 2018-05-22 09:22:03 +02:00
Ludovic Fernandez
bc26d9f0de Enhance Load-balancing method validation log. 2018-05-22 09:20:03 +02:00
Daniel Tomcej
5c4692a0df Modify tests to only run if non-static files have been modified 2018-05-19 00:30:03 +02:00
Florian Woerner
0ba28bbc8b Update caServer to letsencrypt one in examples 2018-05-18 18:16:03 +02:00
Michael Ermer
550184275a Add basicAuth example for KV 2018-05-18 17:54:03 +02:00
DeamonMV
c376083ecb Add command for basic auth with Docker Compose 2018-05-18 17:34:04 +02:00
Ludovic Fernandez
1db5fcf200 Removes ambiguity with the word 'default' 2018-05-18 17:12:03 +02:00
Ludovic Fernandez
16b2555ab3 Fix error pages content. 2018-05-18 16:38:03 +02:00
Daniel Tomcej
9227d32d57 Enable Ingress Status updates 2018-05-18 14:12:03 +02:00
Gildas Cherruel
c37b040217 Mapping ExternalNames to custom ports 2018-05-17 18:46:03 +02:00
Ingo Gottwald
5a1d2aa4b6 Fix contributing guide link in PR template 2018-05-17 12:52:03 +02:00
Thomas Fan
4a3b1f3847 Enable to override certificates in key-value store when using storeconfig 2018-05-17 12:32:03 +02:00
Joni Collinge
d9a5258f40 Update docs to match SF provider labels 2018-05-17 11:04:03 +02:00
Daniel Tomcej
190ebbed27 Update docs to reflect Provider wording 2018-05-16 19:48:03 +02:00
Ludovic Fernandez
a0872c9e31 Route rules overlaps in UI 2018-05-16 13:44:03 +02:00
Michael
68cc826519 Add option to select algorithm to generate ACME certificates 2018-05-16 11:44:03 +02:00
Daniel Tomcej
f5b306e7ff Add redirect-permanent to kubernetes template 2018-05-16 10:46:03 +02:00
Ludovic Fernandez
7a1feb3c51 fix: acme errors management. 2018-05-15 17:28:02 +02:00
Ludovic Fernandez
e691168cdc Add some missing elements in the WebUI 2018-05-15 17:26:04 +02:00
Fernandez Ludovic
4eda1e1bd4 Merge branch 'v1.6' into master 2018-05-15 10:43:27 +02:00
Ludovic Fernandez
1e8df9f245 Prepare release v1.6.1 2018-05-14 21:08:03 +02:00
Ludovic Fernandez
b72937e8fb Fix webui 2018-05-14 19:46:03 +02:00
Ludovic Fernandez
df11e67bb4 Override health check scheme 2018-05-14 12:08:03 +02:00
Ludovic Fernandez
b7d20496f3 Add SSLForceHost support. 2018-05-14 11:44:03 +02:00
Ludovic Fernandez
67847c3117 Error when accesslog and error pages 2018-05-14 10:38:03 +02:00
Ludovic Fernandez
a2a0c80acb Fix segment backend name 2018-05-14 10:18:03 +02:00
Ludovic Fernandez
b3fd06fb45 Add missing deprecation info in CLI help. 2018-05-14 09:52:03 +02:00
Ludovic Fernandez
c5db8d903c Enhance entry point TLS CLI reference. 2018-05-13 17:12:03 +02:00
Daniel Tomcej
8fcd242494 Add Documentation update for Kubernetes Ingress 2018-05-11 12:52:03 +02:00
Blake Patton
ebd9af900e Changed "is could" to should 2018-05-09 17:48:03 +02:00
Michael
b02381c2d5 Fix wrong tag in forward span in tracing middleware 2018-05-08 12:00:03 +02:00
Ludovic Fernandez
dce65ab9c2 Restore default issue template. 2018-05-07 14:16:02 +02:00
Ludovic Fernandez
97295f270b Update issue templates 2018-05-07 12:14:02 +02:00
Fernandez Ludovic
8e64bc8785 Merge branch 'v1.6' into master 2018-05-04 17:57:06 +02:00
Ludovic Fernandez
9b199ea756 fix: 1.6 change log. 2018-05-04 16:08:03 +02:00
Ludovic Fernandez
ec3b913ee4 fix: change log v1.6.0 2018-05-01 00:46:03 +02:00
SALLEYRON Julien
c210ab31d9 Prepare release v1.6.0 2018-04-30 23:20:05 +02:00
Timo Reimann
6c1fa91c70 Document custom k8s ingress class usage in guide. 2018-04-30 20:28:03 +02:00
Ludovic Fernandez
04bab185f6 Add redirect section. 2018-04-30 12:28:03 +02:00
Ludovic Fernandez
2213b4cf37 Cleaning labels/annotations documentation. 2018-04-30 12:08:03 +02:00
Alexy Mikhailichenko
1d770e5636 Fix typo and tweak formatting in quickstart 2018-04-30 09:24:04 +02:00
Ludovic Fernandez
b7e15e0a2c doc: update Traefik images. 2018-04-27 14:54:03 +02:00
Jan Kuri
9c651ae913 New web ui 2018-04-27 13:12:04 +02:00
SALLEYRON Julien
a1bbaec71f Does not generate ACME certificate if domain is checked by dynamic certificate 2018-04-27 10:36:04 +02:00
Fernandez Ludovic
3b3ca89483 Merge branch 'v1.6' into master 2018-04-25 08:22:17 +02:00
SALLEYRON Julien
b4e3bca6fa Remove acme provider dependency in server 2018-04-24 22:40:04 +02:00
Michael
e09d5cb4ec Fix documentation for tracing with Jaeger 2018-04-24 19:22:03 +02:00
NicoMen
cae353b9f6 Add documentation about Templating in backend file 2018-04-24 18:58:03 +02:00
Ludovic Fernandez
edb5b3d711 Fix whitelist and XFF. 2018-04-23 16:20:05 +02:00
Colin Coller
667a0c41ed Minor improvements to documentation 2018-04-23 15:56:03 +02:00
SALLEYRON Julien
9daae9c705 Extract internal router creation from server 2018-04-23 15:30:03 +02:00
Ludovic Fernandez
2975acdc82 Forward auth: copy response headers when auth failed. 2018-04-23 15:28:04 +02:00
Ludovic Fernandez
76dcbe3429 Fix error pages redirect and headers. 2018-04-23 11:28:04 +02:00
Ludovic Fernandez
d8e2d464ad Remove dead code. 2018-04-23 10:54:03 +02:00
Arnas
5f8bcb0c26 Fix typo in documentation 2018-04-23 10:28:04 +02:00
Ludovic Fernandez
7ef8d6fa10 Ignore server for container with empty IP address. 2018-04-22 09:10:03 +02:00
Fernandez Ludovic
5924a40222 Fix Service Fabric docs to use v1.6 labels. 2018-04-19 20:15:20 +02:00
Joni Collinge
05968eb232 Fix Service Fabric docs to use v1.6 labels
Fix docs to use v1.6 labels rather than v1.5.
2018-04-19 16:30:10 +02:00
Ludovic Fernandez
36dcfbfe2d Add HTTP headers to healthcheck. 2018-04-18 18:16:03 +02:00
Fernandez Ludovic
95ce4f5c1e refactor: minor fixes. 2018-04-18 10:48:03 +02:00
Fernandez Ludovic
f258f20b04 doc: add 'traefik.domain'. 2018-04-18 10:48:03 +02:00
Fernandez Ludovic
7e2ad827aa fix: used 'traefik.domain' in frontend rule. 2018-04-18 10:48:03 +02:00
Fernandez Ludovic
e6ce61fdf0 Merge tag 'v1.6.0-rc6' into master 2018-04-18 10:13:22 +02:00
Michael
3df588047d Prepare release v1.6.0-rc6 2018-04-17 13:42:03 +02:00
Ludovic Fernandez
ac0e5cbb29 Use shared label system 2018-04-17 11:18:04 +02:00
Michael
5ab584bc6a Fix panic in atomic on ARM and x86-32 platforms 2018-04-17 10:32:03 +02:00
NicoMen
a2e03e3bd0 Create backup file during migration from ACME V1 to ACME V2 2018-04-16 19:34:04 +02:00
Jean-Baptiste Doumenjou
f0589b310f Fix multiple frontends with docker-compose --scale 2018-04-16 18:14:04 +02:00
Michael
8519b0d353 Fix nil value when tracing is enabled 2018-04-16 17:42:03 +02:00
Timo Reimann
21b8b2deb5 Support multi-port services. 2018-04-16 14:44:04 +02:00
Alex Antonov
6b82a77e36 Improved cluster api to include the current leader node 2018-04-16 14:10:04 +02:00
Jesse Haka
1954a49f37 add http headers to healthcheck 2018-04-16 11:40:03 +02:00
Manuel Zapf
0e3d1e1503 fix: redirect to HTTPS first before basic auth if header redirect (secure) is set 2018-04-16 11:06:03 +02:00
Michael
ebd77f314d Fix duplicated tags in InfluxDB 2018-04-16 10:28:04 +02:00
Ludovic Fernandez
749d833f65 fix: template version documentation. 2018-04-16 00:34:03 +02:00
Cyrille Hemidy
0373cd6f97 Add missing argument in log. 2018-04-13 19:46:03 +02:00
Ludovic Fernandez
1f3fc8a366 fix: backend name for stateful service and more. 2018-04-13 15:44:04 +02:00
Fernandez Ludovic
89c3930b28 Merge branch 'v1.5' into 'v1.6' 2018-04-13 15:02:29 +02:00
Ludovic Fernandez
29e1e9eef2 fix: backend name for stateful service. 2018-04-13 14:38:03 +02:00
Fernandez Ludovic
de3aeb9732 Merge branch 'v1.6' into mrg-current-v1.6 2018-04-12 14:36:36 +02:00
SALLEYRON Julien
85aa1a444a Prepare release 1.6.0-rc5 2018-04-12 11:44:03 +02:00
Timo Reimann
702876ae7f Limit label selector to Ingress factory. 2018-04-12 11:14:05 +02:00
NicoMen
7109910f46 Generate wildcard certificate with SANs in ACME 2018-04-11 17:16:07 +02:00
Ludovic Fernandez
8168d2fdc1 Server weight zero 2018-04-11 16:30:04 +02:00
Fernandez Ludovic
edbcd01fbc Merge branch 'v1.5' into 'v1.6' 2018-04-11 14:20:53 +02:00
Ludovic Fernandez
c99266e961 Fix: error pages 2018-04-11 13:54:03 +02:00
Ludovic Fernandez
f804053736 Factorize labels managements. 2018-04-11 12:26:03 +02:00
Manuel Zapf
2641832304 Default certificate expiry 2018-04-11 10:36:03 +02:00
Andrey Fedoseev
21f6f81914 Update kubernetes.md 2018-04-11 10:34:04 +02:00
Emile Vauge
ccd919aba3 Fix Azure brand 2018-04-10 17:26:04 +02:00
SALLEYRON Julien
2387010556 Disable closeNotify when method GET for http pipelining 2018-04-10 17:24:04 +02:00
Jack Twilley
f35d574759 Minor updates to dumpcerts.sh 2018-04-10 16:52:05 +02:00
NicoMen
3be74bb275 Fix acme.json file automatic creation 2018-04-10 10:52:04 +02:00
Ludovic Fernandez
b1be062437 fix: update lego. 2018-04-09 18:28:03 +02:00
Ludovic Fernandez
2d0d320d05 Remove useless ACME tab from UI. 2018-04-09 16:06:03 +02:00
Jakub Piasecki
1de5111ab5 s/Host/HostRegexp 2018-04-09 11:18:03 +02:00
Ludovic Fernandez
3d530e4747 Command version explanation for alpine image. 2018-04-06 18:14:03 +02:00
Ludovic Fernandez
0ef1b7b683 Fix: Add TTL and custom Timeout in DigitalOcean DNS provider 2018-04-06 17:04:03 +02:00
Alexander Kachkaev
66485e81b4 Normalize parameter names in configs 2018-04-06 09:38:03 +02:00
NicoMen
e74e7cf734 Add ACME certificates only on ACME EntryPoint 2018-04-06 08:44:03 +02:00
Fernandez Ludovic
03ce6a1cc4 Merge tag 'v1.6.0-rc4' into master 2018-04-04 15:10:03 +02:00
Ludovic Fernandez
a19b93c966 fix: overflow on 32 bits arch. 2018-04-04 15:04:04 +02:00
Ludovic Fernandez
f7fd1f2a63 Prepare release v1.6.0-rc4 2018-04-04 14:12:03 +02:00
Ludovic Fernandez
88b71d23db Several apps with same backend name in Marathon. 2018-04-04 12:28:03 +02:00
Ludovic Fernandez
762ef12eb6 Support TLS MinVersion and CipherSuite as CLI option. 2018-04-04 11:56:04 +02:00
Fernandez Ludovic
6845068b82 doc: template version for ECS, Consul Catalog and Mesos. 2018-04-04 11:52:05 +02:00
Fernandez Ludovic
5c0b18efe4 chore: autogen. 2018-04-04 11:52:05 +02:00
Fernandez Ludovic
4b93d040b3 refactor: Mesos labels. 2018-04-04 11:52:05 +02:00
Fernandez Ludovic
ff61cc971e refactor: Consul Catalog labels. 2018-04-04 11:52:05 +02:00
Fernandez Ludovic
46db91ce73 refactor: ECS labels. 2018-04-04 11:52:05 +02:00
Ludovic Fernandez
5921909ef5 Add tests on IPWhiteLister. 2018-04-03 18:36:03 +02:00
Fernandez Ludovic
1537861c61 Merge branch 'v1.5' into 'v1.6' 2018-04-03 12:12:19 +02:00
jakeprem
1b93551572 Update docker-and-lets-encrypt example to show traefik:1.5.4 2018-04-03 11:14:04 +02:00
Rodrigo
197a5fbcf4 Update kubernetes.md 2018-04-03 10:30:03 +02:00
Alex Antonov
ff32529345 Moved /api/cluster/leadership handler under public routes (requires no authentication) 2018-04-03 10:00:07 +02:00
Ludovic Fernandez
a179c3b399 Fixes prefixed annotations support. 2018-04-03 09:40:04 +02:00
Emir Karşıyakalı
a820585f56 Fixed documentation urls on README.md 2018-03-30 11:18:03 +02:00
Ludovic Fernandez
bfb12f415c Prepare release v1.6.0-rc3. 2018-03-28 17:56:04 +02:00
Ludovic Fernandez
a731b43b52 Frontend rule and segment labels. 2018-03-28 17:18:04 +02:00
NicoMen
118b4eb07a Prepare release v1.6.0-rc2 2018-03-27 16:48:05 +02:00
Tait Clarridge
f1a05ab73c Add wildcard match to acme domains 2018-03-27 16:18:03 +02:00
Michael
4c85a41bfb Fix basic documentation 2018-03-27 14:58:03 +02:00
NicoMen
30e048d4ab Fix panic with wrong ACME configuration 2018-03-27 12:22:03 +02:00
Ludovic Fernandez
aa0ab6d387 Update SF white list documentation section. 2018-03-27 10:24:03 +02:00
Fernandez Ludovic
30b87985b7 chore: update changelog. 2018-03-26 16:40:03 +02:00
Fernandez Ludovic
df73211d56 feature: Service Fabric white list. 2018-03-26 16:40:03 +02:00
SALLEYRON Julien
e3a4ddcd08 Prepare release 1.6.0-rc1 2018-03-26 16:04:03 +02:00
Ludovic Fernandez
0ea007b26f Segments Labels: Rancher & Marathon 2018-03-26 15:32:04 +02:00
NicoMen
16bb9b6836 ACME V2 Integration 2018-03-26 14:12:03 +02:00
Ludovic Fernandez
d2766b1b4f Ability to use "X-Forwarded-For" as a source of IP for white list. 2018-03-23 17:40:04 +01:00
Ludovic Fernandez
4802484729 Segment labels: Docker 2018-03-23 13:30:03 +01:00
Marco Jantke
c762b9bb2e add access log filter for retry attempts 2018-03-23 09:28:03 +01:00
ravilr
5792a19b97 Toggle /ping to artificially return unhealthy response on SIGTERM during requestAcceptGraceTimeout interval 2018-03-22 18:18:03 +01:00
Alex Antonov
9699dc2a85 Added cluster/leader endpoint 2018-03-22 18:00:05 +01:00
Ludovic Fernandez
0fa0c2256a Update Service Fabric backend. 2018-03-22 17:42:03 +01:00
Ludovic Fernandez
1b410980ca Remove non-supported label. 2018-03-22 17:16:04 +01:00
nogoegst
be0dbd62c1 Drop capabilities in Kubernetes DaemonSet example 2018-03-22 16:44:05 +01:00
Alex Antonov
1a411b658b Added support for templates to file provider 2018-03-22 16:14:04 +01:00
Fernandez Ludovic
d2e84a700f Merge branch 'v1.5' into master 2018-03-22 12:46:51 +01:00
Gérald Croës
b9af55fc49 Introduction update 2018-03-22 12:34:03 +01:00
Denis Shatilov
e0d92aed6d Miss-leading Docker backend documentation 2018-03-22 10:22:04 +01:00
Kim Min
f94fa78565 Introduce k8s informer factory 2018-03-22 10:14:04 +01:00
Fernandez Ludovic
007a1fc7f2 Merge branch 'v1.5' into master 2018-03-22 08:48:01 +01:00
Michael
a3372acb6d Dependency fsnotify organization has been renamed 2018-03-21 17:04:08 +01:00
Timo Reimann
af7c9b520f Allow overriding the log level in debug mode. 2018-03-21 14:42:07 +01:00
yutopp
43a510c046 Fix goroutine leak in consulcatalog when consul is down 2018-03-20 14:36:03 +01:00
Lawrence Gripper
329c576f44 Add SF to supported backends in docs 2018-03-20 10:34:03 +01:00
Timo Reimann
7afa33dfa1 Fix link to InsecureSkipVerify option. 2018-03-20 09:12:03 +01:00
Timo Reimann
73c6007730 Set INFO log level in Kubernetes guide and examples. 2018-03-19 10:38:04 +01:00
Ludovic Fernandez
526c19181e Merge v1.5.4 into master 2018-03-15 22:22:03 +01:00
Michael
79cd306ac2 Prepare release v1.5.4 2018-03-15 14:26:03 +01:00
Daniel Tomcej
35b83678bd Add TLS Docs 2018-03-15 12:24:03 +01:00
Jérôme Mirc
eacb6ea15a Fix Rancher Healthcheck when upgrading a service 2018-03-15 12:06:04 +01:00
SALLEYRON Julien
d88263dbf9 Use goroutine pool in throttleProvider 2018-03-15 10:54:03 +01:00
Ludovic Fernandez
b1e3444798 Add lower-case passHostHeader key support. 2018-03-15 10:14:03 +01:00
Ludovic Fernandez
f6c6d2bcd0 Add [file] in syntax reference 2018-03-15 09:02:03 +01:00
Michael
8d468925d3 Ultimate Access log filter 2018-03-14 14:12:04 +01:00
SALLEYRON Julien
f99363674b Use context in Server 2018-03-14 13:14:03 +01:00
Fernandez Ludovic
526a04d4c8 Merge branch 'v1.5' into master 2018-03-13 11:38:21 +01:00
Gérald Croës
593c0e7ce2 Updated the test-it example according to the latest docker version 2018-03-13 08:42:03 +01:00
SALLEYRON Julien
e2b42ca57b Handle quoted strings in UnmarshalJSON 2018-03-12 22:00:04 +01:00
Jean-Baptiste Doumenjou
7860534f0c Clarify how setting a frontend priority works 2018-03-12 11:12:05 +01:00
Ludovic Fernandez
fc81d92c88 Enhance Traefik TOML sample. 2018-03-12 10:40:04 +01:00
molsson
8fbac2e39e Fix typo in docs 2018-03-12 10:04:03 +01:00
Aditya C S
b91ae71241 Added entrypoint metrics to influxdb 2018-03-12 09:34:03 +01:00
Fernandez Ludovic
0a41cd43a5 Merge branch 'v1.5' into master 2018-03-09 12:02:29 +01:00
Ludovic Fernandez
59f7b2ea98 Propagate insecure in white list. 2018-03-08 15:08:03 +01:00
Ludovic Fernandez
862957c30c Safe access to ECS API pointer values. 2018-03-08 10:08:03 +01:00
Fernandez Ludovic
4831890232 Merge branch 'v1.5' into master 2018-03-08 01:12:36 +01:00
SALLEYRON Julien
546f0173ab Don't failed traefik start if register and subscribe failed on acme 2018-03-08 00:54:02 +01:00
Ludovic Fernandez
b001b0da86 Add some missing quotes in templates 2018-03-07 22:10:04 +01:00
Michael
04e3f2f401 Unable to generate documentation 2018-03-07 21:50:04 +01:00
Ludovic Fernandez
3a2b421566 Replace Delay by RefreshSecond in Eureka 2018-03-07 10:46:04 +01:00
SALLEYRON Julien
acc432b5a8 capitalize Sec-WebSocket-Protocol Header 2018-03-07 08:08:03 +01:00
NicoMen
c4529820f2 Delete TLS-SNI-01 challenge from ACME 2018-03-06 14:50:03 +01:00
NicoMen
d3edccb839 Check all the C/N and SANs of provided certificates before generating ACME certificates in ACME provider 2018-03-06 10:12:04 +01:00
NicoMen
8380de1bd9 Create ACME Provider 2018-03-05 20:54:04 +01:00
Fernandez Ludovic
bf43149d7e Merge branch 'v1.5' into master 2018-03-02 16:44:24 +01:00
Ludovic Fernandez
13e2358815 Clarify some deprecations. 2018-03-02 14:46:03 +01:00
Michael
1f6f8d5e0f New option in secure middleware 2018-03-02 14:24:03 +01:00
Ludovic Fernandez
716eca5976 fix: gh-pages drop edge. 2018-03-02 14:20:04 +01:00
Ludovic Fernandez
9ae808aac4 Fix panic when parsing resolv.conf 2018-03-02 10:46:04 +01:00
Michael
c77fe6b434 Use responseModifier to override secure headers 2018-03-01 16:42:04 +01:00
Ludovic Fernandez
f149b56063 Enhance API, REST, ping documentation. 2018-03-01 08:42:03 +01:00
SALLEYRON Julien
831a3e384b Separate command from the main package 2018-03-01 08:10:04 +01:00
Gerben Welter
49a9e2a9e0 Second defaultEntryPoint should be https, not http. 2018-02-28 14:24:03 +01:00
Fernandez Ludovic
a2db3e0499 Merge branch 'v1.5' into master 2018-02-27 14:10:24 +01:00
Ludovic Fernandez
422109b82f Prepare release v1.5.3 2018-02-27 12:28:03 +01:00
NicoMen
c864a7297b Add DEBUG log when no provided certificate can check a domain 2018-02-27 11:10:03 +01:00
SALLEYRON Julien
8da038041d Default value for lifecycle 2018-02-27 10:24:03 +01:00
Ludovic Fernandez
dd954f3c0a Fix Duration JSON unmarshal 2018-02-26 22:14:03 +01:00
NicoMen
6f81e3479a Externalize Træfik rules in a dedicated package 2018-02-26 15:34:04 +01:00
NicoMen
db483e9d34 Check all the C/N and SANs of provided certificates before to generat… 2018-02-26 11:38:03 +01:00
Ludovic Fernandez
700b7a1b51 Add a CLI help command for Docker. 2018-02-26 10:00:05 +01:00
Ludovic Fernandez
ed65d00574 Infinite entry point redirection. 2018-02-26 09:34:03 +01:00
Ludovic Fernandez
0306b5e8f7 Missing annotation prefix support. 2018-02-23 18:36:03 +01:00
Michael
cb54e414ed TLS Support for ConsulCatalog 2018-02-23 12:14:03 +01:00
psalaberria002
bad71d1a36 Remove hardcoded frontend prefix in Kubernetes template 2018-02-23 11:38:02 +01:00
Michael
088b8fb348 Fix <nil> tracer value in KV 2018-02-23 11:16:03 +01:00
Michael
e28ebf1c62 Change port of traefik for error pages integration test 2018-02-23 10:52:03 +01:00
Michael
39eeb67d91 Upgrade AWS SKD to version v1.13.1 2018-02-22 14:58:04 +01:00
NicoMen
f460c1990e Starting Træfik even if TLS certificates are in error 2018-02-22 14:38:04 +01:00
Kondrashov Ilia
0c0949679f Check for endpoints while detecting Consul service changes 2018-02-22 09:48:03 +01:00
Jonas Bröms
58d4481118 Docs: Fix typos in k8s user-guide 2018-02-21 17:34:04 +01:00
Pierre Carru
83381e99cf it's -> its 2018-02-21 17:18:05 +01:00
Fernandez Ludovic
21e28ae848 Merge branch 'v1.5' into master 2018-02-21 16:55:57 +01:00
Michael
31550fd2c9 Replace nginx by whoami in integration tests 2018-02-21 16:28:03 +01:00
Alex Antonov
7c7ee2ca61 Added missing metrics to registry for DataDog and StatsD 2018-02-21 10:04:03 +01:00
Emile Vauge
ba046b4d3a Fix doc cipher suites 2018-02-21 08:00:03 +01:00
Ludovic Fernandez
d675d46930 Multiple issue and pull request templates. 2018-02-20 10:44:03 +01:00
Michael
7ea76929d4 Empty ip address when endpoint mode dnsrr 2018-02-20 08:12:02 +01:00
Kim Min
5ef55dd8b4 Add app-root annotation support for kubernetes ingress 2018-02-19 15:36:03 +01:00
Emile Vauge
d47c1a7975 Update to golang 1.10 2018-02-19 13:40:03 +01:00
Fernandez Ludovic
8068057040 chore: add gometlinter configuration. 2018-02-19 10:54:03 +01:00
Fernandez Ludovic
fcdeec0bfa refactor: use positive error code. 2018-02-19 10:54:03 +01:00
Fernandez Ludovic
b9d8eff994 refactor: some minor changes. 2018-02-19 10:54:03 +01:00
ferhat elmas
529e34d2ae Remove unnecessary returns in tracing setup 2018-02-17 08:32:03 +01:00
Ludovic Fernandez
26b3fe201b Split security labels and custom labels documentation. 2018-02-16 16:04:05 +01:00
Ludovic Fernandez
f98c537ec2 Smooth dashboard refresh. 2018-02-16 16:02:03 +01:00
Emile Vauge
083bde64ee Fix traffic pronounce dead link 2018-02-16 13:22:02 +01:00
Tim Hobbs
462dcbcf03 Change boolean annotation values to string 2018-02-16 12:20:04 +01:00
SALLEYRON Julien
45fe218ee2 Isolate backend with same name on different provider 2018-02-16 11:04:04 +01:00
SALLEYRON Julien
d54777236c Update documentation on onHostRule, ping examples, and web deprecation 2018-02-16 10:32:03 +01:00
Ludovic Fernandez
dafdaa4208 Correct conditional setting of buffering retry expression. 2018-02-15 16:06:03 +01:00
ferhat elmas
5212b7d3bd Handle zipkin collector creation 2018-02-15 10:28:03 +01:00
Kim Min
83a92596c3 Bump kubernetes/client-go 2018-02-14 09:56:04 +01:00
Ludovic Fernandez
4f3b06472b Check ping configuration. 2018-02-13 23:42:03 +01:00
ferhat elmas
029fa83690 Remove unnecessary conversion 2018-02-13 17:14:04 +01:00
Ludovic Fernandez
abdcb9e332 Homogenization of templates: Eureka 2018-02-13 09:20:04 +01:00
Timo Reimann
17e85e31cd Remove health check filter from Marathon tasks. 2018-02-13 08:00:05 +01:00
Ludovic Fernandez
7d3dd5a0e4 Update Lego (Gandi API v5, cloudxns, ...) 2018-02-12 18:10:05 +01:00
Michael
dd873fbeee Fix support for macvlan driver in docker provider 2018-02-12 17:50:05 +01:00
Ludovic Fernandez
38a4c80995 Logger and Leaks 2018-02-12 17:24:03 +01:00
Ludovic Fernandez
91fa727c74 Update Staert. 2018-02-12 16:42:02 +01:00
Fernandez Ludovic
794c0206f3 Merge 'v1.5.2' into master 2018-02-12 15:39:12 +01:00
Michael
52bad03c8d Prepare release v1.5.2 2018-02-12 11:46:03 +01:00
Ludovic Fernandez
2fde3e8679 Continue refresh the configuration after a failure. 2018-02-12 09:28:03 +01:00
Michael
1e71f52b72 Explain how to write entrypoints definition in a compose file 2018-02-09 18:16:04 +01:00
NicoMen
2b1d2853cd Compress ACME certificates in KV stores. 2018-02-09 10:38:03 +01:00
Ludovic Fernandez
6a92ac0b7b Add new options to the CLI entrypoint definition. 2018-02-08 09:30:06 +01:00
SALLEYRON Julien
f07e8f58e6 Fix goroutine leaks in websocket 2018-02-08 08:24:03 +01:00
Ludovic Fernandez
7b19cb5631 Migrate to dep 0.4 2018-02-07 23:30:05 +01:00
Fernandez Ludovic
f5adea1061 Merge branch 'v1.5' into master 2018-02-07 16:20:40 +01:00
djeeg
dbd173b4e4 Docs: regex+replacement hints for URL rewriting 2018-02-07 13:42:04 +01:00
Sune Keller
85cfd87c44 Clarify how setting a frontend priority works 2018-02-07 13:00:04 +01:00
Ludovic Fernandez
c867f48f11 Change go-bindata 2018-02-07 12:40:03 +01:00
Timo Reimann
514f9a7215 Reduce oxy round trip logs to debug. 2018-02-07 11:32:03 +01:00
Wilhelm Uschtrin
0b0380b690 Fix typo 2018-02-06 14:30:04 +01:00
Sonu Kumar
4d0c8c189a Fixed typo. 2018-02-06 14:04:03 +01:00
SALLEYRON Julien
afe4c307f9 Traefik still start when Let's encrypt is down 2018-02-05 18:20:04 +01:00
pigletfly
c0563f1a39 Remove web provider in example 2018-02-05 15:04:02 +01:00
Michael
ce3a0fdd46 Fix dnsrr endpoint mode excluded when not using swarm LB 2018-02-05 11:34:03 +01:00
Anoop Vijayan Maniankara
ce3c72e9d9 Update traefik-ds.yaml with --api command line parameter 2018-02-05 10:50:03 +01:00
Ludovic Fernandez
dcba74deb9 Display file log when test fails. 2018-02-05 09:48:03 +01:00
Ludovic Fernandez
203a5c5c48 Hide the pflag error when displaying help. 2018-02-05 09:12:03 +01:00
Ludovic Fernandez
be4aeaacde Add documentation about entry points definition with CLI. 2018-02-05 08:54:03 +01:00
Yuvi Panda
04ebd9d46a Allow custom value for kubernetes.io/ingress.class annotation 2018-02-01 19:04:04 +01:00
Michael
52b4e93c38 Fix typo in doc for rate limit label 2018-02-01 08:04:03 +01:00
Ludovic Fernandez
58d6681824 Redirection: permanent move option. 2018-01-31 19:10:04 +01:00
Fernandez Ludovic
c944d203fb chore(k8s): gen templates. 2018-01-31 17:16:05 +01:00
Fernandez Ludovic
62df067fac doc(k8s): add documentation. 2018-01-31 17:16:05 +01:00
Fernandez Ludovic
7c80b9a692 refactor(k8s): new annotations style. 2018-01-31 17:16:05 +01:00
Fernandez Ludovic
a4a8345a33 refactor(k8s): baseName and ObjectMeta. 2018-01-31 17:16:05 +01:00
Fernandez Ludovic
742dde72bb refactor(k8s): template readability.
fix(k8s): redirection template.
2018-01-31 17:16:05 +01:00
Fernandez Ludovic
4497ddbb0e refactor(k8s): default to nil headers struct. 2018-01-31 17:16:05 +01:00
Fernandez Ludovic
53388a3570 feat(k8s): add max conn annotations. 2018-01-31 17:16:05 +01:00
Fernandez Ludovic
1c495d7ea4 feat(k8s): add rate limit annotations. 2018-01-31 17:16:05 +01:00
Fernandez Ludovic
4c0d6e211b feat(k8s): add error pages annotations. 2018-01-31 17:16:05 +01:00
Fernandez Ludovic
5bfd6acd52 fix(k8s): add passTLSCert to template. 2018-01-31 17:16:05 +01:00
Fernandez Ludovic
0b49de94c6 refactor(k8s): extract functions. 2018-01-31 17:16:05 +01:00
Fernandez Ludovic
7c0e557f84 refactor(k8s): template readability. 2018-01-31 17:16:05 +01:00
Łukasz Harasimowicz
a81171d5f1 Request buffering middleware 2018-01-31 15:32:04 +01:00
Ludovic Fernandez
26dc2f4d61 doc: option not available in 1.5. 2018-01-30 17:16:03 +01:00
Fernandez Ludovic
d426126a92 Merge 'v1.5.1' into master 2018-01-29 17:12:23 +01:00
Alexandre Guédon
6aac78fc36 typo in "i"ngress annotations. 2018-01-29 16:48:05 +01:00
Ludovic Fernandez
f6c53f0450 Rebuild experimental image 2018-01-29 16:08:03 +01:00
NicoMen
54e09b98c7 Prepare release v1.5.1 2018-01-29 15:04:03 +01:00
SALLEYRON Julien
395b1702de Extract providers configuration from server.go 2018-01-29 14:58:03 +01:00
Ludovic Fernandez
4eebaa1a80 Enhance file provider documentation. 2018-01-29 14:36:03 +01:00
NicoMen
cb9bf3ce68 Fix domain names in dynamic TLS configuration 2018-01-29 10:48:03 +01:00
Marco Jantke
ef4aa202d0 Fix high memory usage in retry middleware 2018-01-26 18:22:03 +01:00
Marco Jantke
cc5ee00b89 extend metrics and rebuild prometheus exporting logic 2018-01-26 11:58:03 +01:00
SALLEYRON Julien
49a8cb76f5 Add note on redirect for ACME http challenge 2018-01-26 09:22:03 +01:00
SALLEYRON Julien
bf12306f17 Change gzipwriter receiver to implement CloseNotifier 2018-01-25 21:46:04 +01:00
Mattias Wadman
fa1f4f761d Add status code text to webui bar chart tooltip 2018-01-25 16:54:03 +01:00
Michael
b50aebd2ed Tracing statusCodeTracker need to implement CloseNotify 2018-01-25 16:34:02 +01:00
SALLEYRON Julien
323b8237a0 Handle undefined entrypoint on ACME config and frontend config 2018-01-25 12:02:04 +01:00
Michael
9f741abd84 Fix integration tests in tracing 2018-01-25 12:00:05 +01:00
Bastiaan Bakker
32ccc26712 Add username in accesslog 2018-01-24 18:18:03 +01:00
NicoMen
563a0bd274 Migrate from libkv to valkeyrie library 2018-01-24 17:52:03 +01:00
Fernandez Ludovic
a91080b060 Merge branch 'v1.5' into master 2018-01-24 17:01:05 +01:00
Michael
039ccaf4f1 Fix tar gz source only on tags on travis 2018-01-24 16:10:04 +01:00
Fernandez Ludovic
c878d262bf refactor: remove Tracing integration tests. 2018-01-24 15:20:47 +01:00
Fernandez Ludovic
c8446c2dc8 Merge branch 'v1.5' into master 2018-01-24 15:11:21 +01:00
Michael
4afb39778a Fix add src.tar.gz in Træfik release 2018-01-24 10:40:04 +01:00
Ludovic Fernandez
751781a3b7 Increase integration tests timeout. 2018-01-24 09:14:02 +01:00
Ludovic Fernandez
f5d150c3b4 Fix the k8s redirection template. 2018-01-24 08:12:03 +01:00
Ludovic Fernandez
ae9342208e Prepare release v1.5.0 2018-01-23 17:34:04 +01:00
Michael
3040d9df0d Build cross binary only on tags in travis 2018-01-23 17:00:06 +01:00
Ludovic Fernandez
00e0571811 Rename TLSConfigurations to TLS. 2018-01-23 16:30:07 +01:00
Ludovic Fernandez
bfb07746fe Deploy pages on all branches. 2018-01-23 14:48:04 +01:00
Ludovic Fernandez
171cda6186 New multi version documentation mechanism 2018-01-23 14:18:03 +01:00
Timo Reimann
4cc17e112f Fix goroutine leak in throttler logic. 2018-01-23 12:44:03 +01:00
Ludovic Fernandez
b6af61fa6e ACME and corporate proxy. 2018-01-23 09:52:03 +01:00
Emile Vauge
4e07d92190 Fix doc dynamic certificates 2018-01-23 09:12:03 +01:00
Ludovic Fernandez
fb4ba7af2b Migrate Sirupsen to sirupsen. 2018-01-22 12:16:03 +01:00
Marco Jantke
c134dcd6fe remove unecessary mutex usage in healthchecks 2018-01-22 11:32:02 +01:00
Roman Pridybailo
fc00e1c228 Don't reload configuration when rancher server is down 2018-01-22 11:00:07 +01:00
Eldon
ae34486b57 Fix some doc links 2018-01-22 10:26:03 +01:00
SALLEYRON Julien
d7b513e9aa Disable websocket compression 2018-01-19 17:34:03 +01:00
SALLEYRON Julien
d8297a055a Fix breaking change in web metrics 2018-01-19 14:30:04 +01:00
Fernandez Ludovic
5140bbe99a doc(mesos): add documentation. 2018-01-18 18:26:03 +01:00
Fernandez Ludovic
0c33d110f4 chore(mesos): gen templates. 2018-01-18 18:26:03 +01:00
Fernandez Ludovic
5b37fb83fd feat(mesos): add all labels. 2018-01-18 18:26:03 +01:00
Fernandez Ludovic
bc6879ecc1 fix(mesos): invalid ip source. 2018-01-18 18:26:03 +01:00
Fernandez Ludovic
17137ba3e7 refactor(mesos) be testable. 2018-01-18 18:26:03 +01:00
Fernandez Ludovic
e9d2124885 refactor(mesos): template readability. 2018-01-18 18:26:03 +01:00
Michael
f1f2e1bf64 Fix missing configuration for jaeger reporter 2018-01-18 17:24:03 +01:00
SALLEYRON Julien
ced5aa5dc6 Challenge HTTP must ignore deprecated web.path option 2018-01-17 18:46:03 +01:00
Ludovic Fernandez
72bc74001f Add Tracing entry in the documentation. 2018-01-17 13:14:03 +01:00
Martijn Heemels
adfa3f795c Fix typo in anonymous usage log message. 2018-01-17 12:20:04 +01:00
Fernandez Ludovic
89d90de7d8 Merge branch '1.5.0-rc5' into master 2018-01-15 17:27:37 +01:00
Michael
fe426f6fb2 Prepare release v1.5.0-rc5 2018-01-15 16:48:03 +01:00
SALLEYRON Julien
3e439cc39b Add Let's Encrypt HTTP Challenge 2018-01-15 16:04:05 +01:00
Blake Mesdag
56c0634918 Return errors from Docker client.Events 2018-01-15 14:26:03 +01:00
Tristan Colgate-McFarlane
bcadd68904 Fix data races. 2018-01-15 11:46:04 +01:00
Timo Reimann
9790aa91fe Apply various contentual and stylish improvements to the k8s docs. 2018-01-15 09:40:05 +01:00
Michael
5316b412d2 Fix concurrent map writes on digest auth 2018-01-12 20:00:05 +01:00
SALLEYRON Julien
b5ee5c34f2 Add compression and better error handling 2018-01-12 17:52:03 +01:00
Ludovic Fernandez
2618aef008 Switch to golang/dep. 2018-01-11 17:46:04 +01:00
Ludovic Fernandez
8239e04a19 fix: typo in Docker template. 2018-01-11 15:20:06 +01:00
Michael
709d50836b Fix bad access log 2018-01-11 10:04:03 +01:00
SALLEYRON Julien
e2c5f3712f Fix redirect problem on dashboard + docs/tests on [web] 2018-01-11 09:46:03 +01:00
Ludovic Fernandez
ee71b4bfef homogenization of templates: ECS 2018-01-10 18:28:03 +01:00
Ludovic Fernandez
0d57e2aed9 homogenization of templates: Rancher 2018-01-10 18:08:03 +01:00
Michael
30ffba78e6 Opentracing support 2018-01-10 17:48:04 +01:00
Ludovic Fernandez
8394549857 homogenization of templates: Consul Catalog 2018-01-10 16:20:10 +01:00
NicoMen
d0f3ad6024 Modify DEBUG messages to get ACME certificates 2018-01-10 15:20:03 +01:00
Ludovic Fernandez
870c0b5cf4 homogenization of templates: Marathon 2018-01-10 11:58:03 +01:00
Ludovic Fernandez
044d87d96d Switch to golang/dep. 2018-01-09 21:46:04 +01:00
Fernandez Ludovic
b60edd9ee9 fix: list parsing. 2018-01-09 20:00:05 +01:00
Fernandez Ludovic
b1ea36793b refactor: simplify template and use typed function. 2018-01-09 20:00:05 +01:00
Ludovic Fernandez
750878d668 homogenization of templates: Docker 2018-01-09 16:26:03 +01:00
Fernandez Ludovic
617b8b20f0 Merge branch 'v1.5' into master 2018-01-09 15:46:56 +01:00
Ludovic Fernandez
d88554fa92 fix: list entries parsing. 2018-01-09 12:40:04 +01:00
Timo Reimann
e74a20de24 Document rewrite-target annotation. 2018-01-09 11:56:02 +01:00
Ludovic Fernandez
7c227392fa fix: glide files. 2018-01-09 11:24:03 +01:00
Ludovic Fernandez
8a697f7a39 Fix: timeout integration test 2018-01-09 10:08:03 +01:00
gopenguin
8327dd0c0b Add support for fetching k8s Ingress TLS data from secrets 2018-01-08 00:36:03 +01:00
Julien Maitrehenry
60fd26e0b7 Add a clustering example with Docker Swarm 2018-01-07 15:54:03 +01:00
SALLEYRON Julien
acd0c1bcd5 GzipResponse must implement CloseNotifier if ResponseWriter implement it 2018-01-05 02:26:03 +01:00
Fernandez Ludovic
9b3750320b chore(kv): gen templates. 2018-01-04 18:42:02 +01:00
Fernandez Ludovic
b9f1f7752d feat(kv): add more tests. 2018-01-04 18:42:02 +01:00
Fernandez Ludovic
944008661f feat(kv): add custom headers configuration. 2018-01-04 18:42:02 +01:00
Fernandez Ludovic
79ae52aca7 feat(kv): add rate limits configuration. 2018-01-04 18:42:02 +01:00
Fernandez Ludovic
51390aa874 feat(kv): add error pages configuration. 2018-01-04 18:42:02 +01:00
Fernandez Ludovic
cfa1f47226 feat(kv): add frontend redirect configuration. 2018-01-04 18:42:02 +01:00
Fernandez Ludovic
40b59da224 feat(kv): add basicAuth configuration. 2018-01-04 18:42:02 +01:00
Fernandez Ludovic
f7ed4a5805 feat(kv): add whitelistSourceRange configuration. 2018-01-04 18:42:02 +01:00
Fernandez Ludovic
3d47030349 feat(kv): add health check port configuration. 2018-01-04 18:42:02 +01:00
Fernandez Ludovic
34eb2e371e feat(kv): add passTLSCert configuration. 2018-01-04 18:42:02 +01:00
Fernandez Ludovic
6573634012 feat(kv): constants and generic methods. 2018-01-04 18:42:02 +01:00
Ludovic Fernandez
61ecb4cd18 Split consul and consul catalog. 2018-01-04 15:56:02 +01:00
SALLEYRON Julien
22bdbd2498 Prepare release 1.5.0-rc4 2018-01-04 15:22:03 +01:00
Ludovic Fernandez
287fb78654 Split Consul and Consul Catalog documentation 2018-01-04 14:48:03 +01:00
Fernandez Ludovic
06d528a2bd refactor(consulcatalog): enhance tests. 2018-01-04 11:42:03 +01:00
Fernandez Ludovic
1fe6a8b04d chore(consulcatalog): gen templates. 2018-01-04 11:42:03 +01:00
Fernandez Ludovic
bd5cab6e87 doc(consulcatalog): add tags documentation
- add a dedicate page for Consul Catalog.
2018-01-04 11:42:03 +01:00
Fernandez Ludovic
238acd9330 feat(consulcatalog): add custom Headers tags. 2018-01-04 11:42:03 +01:00
Fernandez Ludovic
8e7ac513b6 refactor: add backend name functions. 2018-01-04 11:42:03 +01:00
Fernandez Ludovic
e56551d047 feat(consulcatalog): add rate limit and error pages tags. 2018-01-04 11:42:03 +01:00
Fernandez Ludovic
170fc13e02 feat(consulcatalog): add frontend redirect tags. 2018-01-04 11:42:03 +01:00
Fernandez Ludovic
97ce77169a feat(consulcatalog): add HealthCheck tags. 2018-01-04 11:42:03 +01:00
Fernandez Ludovic
c9b871a03a feat(consulcatalog): add WhitelistSourceRange tags. 2018-01-04 11:42:03 +01:00
Fernandez Ludovic
2fdefa258e feat(consulcatalog): add passTLSCert tags. 2018-01-04 11:42:03 +01:00
Fernandez Ludovic
f0a733d6d6 refactor(consulcatalog): new template methods approach. 2018-01-04 11:42:03 +01:00
Fernandez Ludovic
586b5714a7 refactor(consulcatalog): reorganize code. 2018-01-04 11:42:03 +01:00
Fernandez Ludovic
6e23454202 refactor(consulcatalog): template readability. 2018-01-04 11:42:03 +01:00
SALLEYRON Julien
5b24403c8e Don't panic if ResponseWriter does not implement CloseNotify 2018-01-04 11:18:03 +01:00
Julien Maitrehenry
e83599dd08 Add a note on how to add label to a docker compose file 2018-01-04 10:34:03 +01:00
Fernandez Ludovic
de7dd068d9 refactor(ecs): headers as pointer. 2018-01-03 23:52:03 +01:00
Fernandez Ludovic
a33476dea8 doc(ecs): update labels documentation. 2018-01-03 23:52:03 +01:00
Fernandez Ludovic
dceccbdb92 chore(ecs): gen templates. 2018-01-03 23:52:03 +01:00
Fernandez Ludovic
393651f5e2 feat(ecs): add error pages and rate limits. 2018-01-03 23:52:03 +01:00
Fernandez Ludovic
5acee9e11d refactor: enhance template function readability. 2018-01-03 23:52:03 +01:00
Fernandez Ludovic
81626eef38 feat(ecs): add headers labels. 2018-01-03 23:52:03 +01:00
Fernandez Ludovic
e60fbbbebe feat(ecs): add frontend redirect labels. 2018-01-03 23:52:03 +01:00
Fernandez Ludovic
e45e63dc37 feat(ecs): add whitelistSourceRange label. 2018-01-03 23:52:03 +01:00
Fernandez Ludovic
c3d5ad2eeb feat(ecs): add PassTLSCert label. 2018-01-03 23:52:03 +01:00
Fernandez Ludovic
7c64f5d31e feat(ecs): add MaxConn label. 2018-01-03 23:52:03 +01:00
Fernandez Ludovic
66f46c5b96 feat(ecs): add circuit Breaker label. 2018-01-03 23:52:03 +01:00
Fernandez Ludovic
07a6d48a27 refactor(ecs): add hasLoadBalancerLabel function. 2018-01-03 23:52:03 +01:00
Fernandez Ludovic
722ea28e3a feat(ecs): add HealthCheck port label. 2018-01-03 23:52:03 +01:00
Fernandez Ludovic
f195ef27f3 refactor(ecs): template readability. 2018-01-03 23:52:03 +01:00
Fernandez Ludovic
7e5c258266 refactor(rancher): headers as pointer. 2018-01-03 23:26:03 +01:00
Fernandez Ludovic
38b5aef208 chore(rancher): gen templates. 2018-01-03 23:26:03 +01:00
Fernandez Ludovic
a7e4ded722 doc(rancher): add labels documentation. 2018-01-03 23:26:03 +01:00
Fernandez Ludovic
22405a1259 feat(rancher): add rate limit and error pages labels. 2018-01-03 23:26:03 +01:00
Fernandez Ludovic
d0a6689413 feat(rancher): add headers labels. 2018-01-03 23:26:03 +01:00
Fernandez Ludovic
a1f47cb4db feat(rancher): add HealthCheck labels. 2018-01-03 23:26:03 +01:00
Fernandez Ludovic
c884c7bb8a feat(rancher): add WhitelistSourceRange label. 2018-01-03 23:26:03 +01:00
Fernandez Ludovic
c042098889 feat(rancher): add PassTLSCert label. 2018-01-03 23:26:03 +01:00
Fernandez Ludovic
571f41dcf0 refactor(rancher): template readability. 2018-01-03 23:26:03 +01:00
SALLEYRON Julien
f30ad20c9b Use gorilla readMessage and writeMessage instead of just an io.Copy 2018-01-03 15:32:03 +01:00
Marco Jantke
cbd54470ba Improve logging output for failing healthchecks 2018-01-03 12:32:03 +01:00
Timo Reimann
01e17b6c3e k8s guide: Leave note about assumed DaemonSet usage. 2018-01-03 09:12:03 +01:00
SALLEYRON Julien
3e13ebec93 We need to flush the end of the body when retry is streamed 2018-01-02 16:02:03 +01:00
Fernandez Ludovic
c84fb9895e Merge branch 'v1.5' into master 2018-01-02 15:21:17 +01:00
Fernandez Ludovic
23c1a9ca8e Merge branch 'v1.4' into v1.5 2018-01-02 13:10:11 +01:00
Michael
741c739ef1 Prepare release v1.4.6 2018-01-02 12:54:03 +01:00
SALLEYRON Julien
52f16e11a8 Use gorilla readMessage and writeMessage instead of just an io.Copy 2018-01-02 12:30:05 +01:00
Fernandez Ludovic
5623a53464 chore(kv): gen templates. 2018-01-02 11:46:02 +01:00
Fernandez Ludovic
c95393b238 refactor(kv): template readability. 2018-01-02 11:46:02 +01:00
Fernandez Ludovic
be0dd71bb4 refactor(kv): split provide and configuration. 2018-01-02 11:46:02 +01:00
Michael
0ee6973e2f Upgrade docs dependencies and adapt configuration 2018-01-02 11:28:02 +01:00
Timo Reimann
4819974a1c Improve Marathon service label documentation. 2018-01-02 11:08:02 +01:00
Michael
e8e8b41eed Normalize serviceName added to the service backend names 2018-01-02 10:52:03 +01:00
Krzysztof Pędrys
7d23d3c0a4 Typo in docker.endpoint TCP port. 2018-01-02 10:38:03 +01:00
Ludovic Fernandez
718fc7a79d Fix bug report command 2018-01-02 10:14:03 +01:00
Ludovic Fernandez
bfd142b13b Fix custom headers template 2018-01-02 10:10:04 +01:00
Ludovic Fernandez
75533b2beb Use prefix for sticky and stickiness tags. 2018-01-02 09:44:02 +01:00
Ludovic Fernandez
e3d1201b46 Fix custom headers template 2018-01-02 09:42:03 +01:00
ferhat elmas
8f982ff1f2 Simplify storing renewed acme certificate 2017-12-27 14:22:02 +01:00
Ludovic Fernandez
0391e21c84 Add default values for some Docker labels 2017-12-27 07:14:02 +01:00
Fernandez Ludovic
b8a1cb5c68 refactor(marathon): fix labels and add tests. 2017-12-26 12:46:03 +01:00
Fernandez Ludovic
7a71cd3012 doc(marathon): update labels documentation. 2017-12-26 12:46:03 +01:00
Fernandez Ludovic
26bedced35 chore(marathon): gen templates. 2017-12-26 12:46:03 +01:00
Fernandez Ludovic
c1aefb8ad8 feat(marathon): add error pages and rate limits. 2017-12-26 12:46:03 +01:00
Fernandez Ludovic
576e87f398 feat(marathon): add headers labels. 2017-12-26 12:46:03 +01:00
Fernandez Ludovic
b4f6bf0f6a feat(marathon): add frontend redirect labels. 2017-12-26 12:46:03 +01:00
Fernandez Ludovic
edc55aad3c feat(marathon): add whitelistSourceRange label. 2017-12-26 12:46:03 +01:00
Fernandez Ludovic
38a3fe4316 feat(marathon): add PassTLSCert label. 2017-12-26 12:46:03 +01:00
Fernandez Ludovic
81e3b2dd4c feat(marathon): add HealthCheck port label. 2017-12-26 12:46:03 +01:00
Fernandez Ludovic
4524cdc151 refactor(marathon): template readability. 2017-12-26 12:46:03 +01:00
NicoMen
9a7821b8fa Send empty configuration from file provider 2017-12-21 21:24:03 +01:00
lishaoxiong
e8333883df Add tests for TLS dynamic configuration in ETCD3 2017-12-21 18:02:04 +01:00
Fernandez Ludovic
aeffe1036d refactor: no rate limits must return nil. 2017-12-21 17:44:03 +01:00
Fernandez Ludovic
987e8a93bd refactor: no error pages must return nil. 2017-12-21 17:44:03 +01:00
Ludovic Fernandez
2cb4acd6cc Remove obsolete paragraph about error pages. 2017-12-21 15:40:05 +01:00
NicoMen
1e44e339ad Allow deleting dynamically all TLS certificates from an entryPoint 2017-12-21 14:16:03 +01:00
Ludovic Fernandez
59549d5f39 Use pointer of error pages 2017-12-21 11:26:03 +01:00
Fernandez Ludovic
4a7297d05c Merge branch 'v1.5' into master 2017-12-20 15:47:15 +01:00
Fernandez Ludovic
a5335667bb refactor(docker): move services tests. 2017-12-20 15:20:07 +01:00
Fernandez Ludovic
498b806ca9 chore(docker): gen templates. 2017-12-20 15:20:07 +01:00
Fernandez Ludovic
dd7a8a9a87 fix(docker): whitelist on services. 2017-12-20 15:20:07 +01:00
Fernandez Ludovic
133aa77c21 doc(docker): add ratelimit and errorpages doc. 2017-12-20 15:20:07 +01:00
Fernandez Ludovic
942614dd23 feat(docker): add rate limit labels. 2017-12-20 15:20:07 +01:00
Fernandez Ludovic
c30ebe5f90 feat(docker): add error pages labels. 2017-12-20 15:20:07 +01:00
Fernandez Ludovic
50757b5e99 refactor(docker): re-order templates functions. 2017-12-20 15:20:07 +01:00
Fernandez Ludovic
42b900b9b2 doc(docker): HealthCheck labels. 2017-12-20 15:20:07 +01:00
Fernandez Ludovic
c26b9b1a5d feat(docker): add HealthCheck labels. 2017-12-20 15:20:07 +01:00
Fernandez Ludovic
9ee642a7db doc(docker): order labels. 2017-12-20 15:20:07 +01:00
Fernandez Ludovic
423385bca0 feat(docker): add passTLSCert label. 2017-12-20 15:20:07 +01:00
Fernandez Ludovic
6e5f7650a5 refactor(docker): template readability. 2017-12-20 15:20:07 +01:00
Ludovic Fernandez
89a79d0f1b Prepare release 1.5.0-rc3 2017-12-20 15:10:06 +01:00
NicoMen
9e41485ff1 Modify ACME configuration migration into KV store 2017-12-20 14:40:07 +01:00
Nimi Wariboko Jr
3c7c6c4d9f Mesos: Use slave.PID.Host as task SlaveIP. 2017-12-20 12:12:03 +01:00
Ludovic Fernandez
cd1b3904da Add missing entrypoints template. 2017-12-20 10:26:03 +01:00
Emile Vauge
b23b2611b3 Add non regex pathPrefix 2017-12-19 17:00:12 +01:00
Timo Reimann
877770f7cf Update go-marathon 2017-12-19 16:00:09 +01:00
lishaoxiong
3142a4f4b3 Fix stickiness bug due to template syntax error 2017-12-19 14:08:03 +01:00
Ludovic Fernandez
b4dc96527d Move rate limit documentation. 2017-12-19 09:48:03 +01:00
Ludovic Fernandez
35b5ca4c63 fix isHealthy logic. 2017-12-18 10:30:08 +01:00
Ludovic Fernandez
daf3023b02 Change Zookeeper default prefix. 2017-12-18 09:22:03 +01:00
ferhat elmas
705f3f1372 Drop unnecessary type conversions 2017-12-18 09:14:03 +01:00
Fernandez Ludovic
f6520727a3 Merge branch 'v1.5' into master 2017-12-15 22:16:48 +01:00
Michael
b17d5b80b8 Reload configuration when port change for one service 2017-12-15 20:52:03 +01:00
Michael
48b4eb5c0d Fix bad Træfik update on Consul Catalog 2017-12-15 16:00:14 +01:00
Ludovic Fernandez
7ecd6d20ba Support regex redirect by frontend 2017-12-15 11:48:03 +01:00
Kevin Risden
bddad57a7b Fix RawPath handling in addPrefix 2017-12-15 03:50:07 +01:00
Ludovic Fernandez
799136a714 fix: backend name for Stateful services. (Service Fabric) 2017-12-15 01:22:03 +01:00
Timo Reimann
350d61b4a6 Fix github.com/containous/traefik-extra-service-fabric dep to v1.0.1. 2017-12-14 16:06:03 +01:00
Gérald Croës
b6f5a66fab Grammar 2017-12-13 18:22:05 +01:00
Ludovic Fernandez
b0c12e2422 Fix: frontend redirect 2017-12-13 17:02:04 +01:00
Michael MATUR
623a7dc7e6 Fix small missing property in documentation for consul catalog 2017-12-13 11:56:02 +01:00
Michael MATUR
709c7e5707 Improve documentation for Cloudflare API key 2017-12-13 11:56:02 +01:00
Ludovic Fernandez
5f6c5025d5 Fix Flaky Consul Catalog tests. 2017-12-13 11:54:03 +01:00
Sebastian Bauer
328be161d6 Forward Authentication: add X-Forwarded-Uri 2017-12-10 00:58:21 +01:00
Mikhail Vasin
ee04f52a16 Fix broken links and improve ResponseCodeRatio() description 2017-12-08 16:12:04 +01:00
NicoMen
c446c291d9 Use default entryPoints when certificates are added with no entryPoints. 2017-12-08 11:02:03 +01:00
Tiscs Sun
c66d9de759 Custom headers by service labels for docker backends 2017-12-06 22:26:03 +01:00
Fernandez Ludovic
260ee980e0 Merge 'v1.5.0-rc2' into master 2017-12-06 20:24:46 +01:00
Ludovic Fernandez
7d98c1c4e0 Prepare release v1.5.0-rc2 2017-12-06 15:58:03 +01:00
Timo Reimann
4387cf38d7 Close ring buffer used in throttling function. 2017-12-06 14:54:03 +01:00
Michael MATUR
a9d38570ab Merge tag 'v1.4.5' into v1.5 2017-12-06 13:05:08 +01:00
SALLEYRON Julien
0e619369fd fix healthcheck when web is not specified 2017-12-06 11:20:03 +01:00
ferhat elmas
6890dc1844 Simplification 2017-12-06 10:52:03 +01:00
Michael
cda09c843a Prepare release v1.4.5 2017-12-06 10:44:03 +01:00
Ludovic Fernandez
e2190bd9d5 Builders in k8s tests 2017-12-05 20:24:03 +01:00
Fernandez Ludovic
0472d19bd4 refactor: fix some code. 2017-12-05 19:40:04 +01:00
Fernandez Ludovic
07524f5c99 refactor: remove common_label.go 2017-12-05 19:40:04 +01:00
Fernandez Ludovic
1710800cc0 refactor(consulCatalog): review and split. 2017-12-05 19:40:04 +01:00
Fernandez Ludovic
c705d6f9b3 refactor(ecs): rewrite configuration system. 2017-12-05 19:40:04 +01:00
Fernandez Ludovic
be718aea11 refactor(mesos): rewrite configuration system. 2017-12-05 19:40:04 +01:00
Fernandez Ludovic
ca680710a2 refactor(rancher): rewrite configuration system. 2017-12-05 19:40:04 +01:00
Fernandez Ludovic
5f71a43758 refactor(eureka): rewrite configuration system. 2017-12-05 19:40:04 +01:00
Fernandez Ludovic
04dd63da1c refactor(k8s): rewrite configuration system. 2017-12-05 19:40:04 +01:00
Fernandez Ludovic
cee022b935 refactor(marathon): rewrite configuration system. 2017-12-05 19:40:04 +01:00
Fernandez Ludovic
ae2ae85070 refactor(docker): rewrite configuration system. 2017-12-05 19:40:04 +01:00
Fernandez Ludovic
ce6bbbaa33 feat: labels/annotations parser. 2017-12-05 19:40:04 +01:00
NicoMen
6333bfe6e8 Modify the ACME renewing logs level 2017-12-05 15:42:03 +01:00
Timo Reimann
41d8863d2f Fix pprof route order. 2017-12-05 10:50:03 +01:00
Jan Mara
523b7f96f8 Add note to Kubernetes RBAC docs about RoleBindings and namespaces 2017-12-05 02:46:03 +01:00
Mikhail Vasin
ab1a930705 Emphasize the necessity of enabling file backend 2017-12-05 02:30:02 +01:00
Fernandez Ludovic
dc74f76a03 Merge branch 'v1.5' into master 2017-12-04 13:35:02 +01:00
Ludovic Fernandez
3a99c86cb3 Change custom headers separator 2017-12-04 11:40:03 +01:00
Michael
d6ad7e2e64 Fix empty IP for backend when dnsrr in Docker swarm mode 2017-12-01 14:34:03 +01:00
Ludovic Fernandez
aaf120f263 Reduce logs with new Kubernetes security annotations 2017-12-01 14:00:04 +01:00
Ludovic Fernandez
c228e73b26 fix Docker labels documentation render. 2017-12-01 09:36:02 +01:00
SALLEYRON Julien
e27e65eb76 Fix wrong defaultentrypoint and unexisting entrypoint issue 2017-11-30 16:10:02 +01:00
SALLEYRON Julien
1c8acf3929 Doesn't ignore web params when web.metrics.prometheus is set 2017-11-30 14:12:04 +01:00
SALLEYRON Julien
40b3c17703 Fix metrics problem on multiple entrypoints 2017-11-30 12:18:03 +01:00
Sami Jawhar
e042ef3f27 Bump Lego Version for GoDaddy DNS Provider 2017-11-30 10:44:03 +01:00
Daniel Tomcej
313357a6b3 quote template strings 2017-11-30 10:42:02 +01:00
Michael
37a1aaad64 Improve consul documentation 2017-11-30 10:12:03 +01:00
Ludovic Fernandez
f084d2a28b Fix Labels/annotation logs and values. 2017-11-30 09:26:03 +01:00
Michael
077b39d7c6 Add option -s to gofmt for autogen 2017-11-30 08:52:03 +01:00
Ludovic Fernandez
7081f3df58 Sync vendor and glide. 2017-11-29 13:26:03 +01:00
Ludovic Fernandez
9fe6a0a894 Prepare release v1.5.0-rc1 2017-11-28 14:50:06 +01:00
Fernandez Ludovic
3d452fd5b9 Merge branch 'v1.4' into master 2017-11-28 14:03:55 +01:00
Michael
47a5cfbd3e Fix empty ip when container is stopped 2017-11-28 13:58:04 +01:00
Daniel Tomcej
4cb6241e93 Kubernetes security header annotations 2017-11-28 13:36:03 +01:00
Ludovic Fernandez
b572879691 Add link to futur 1.5 documentation. 2017-11-28 13:06:03 +01:00
Ludovic Fernandez
ad07a6ab2b fix: Service Fabric 'expose' as boolean. 2017-11-28 12:02:02 +01:00
Ludovic Fernandez
4bdeb33ac1 Docker labels 2017-11-28 11:16:03 +01:00
Ludovic Fernandez
101a4d0d8d Describe 'refreshSecond' configuration. 2017-11-27 17:02:05 +01:00
Ludovic Fernandez
89e07d0c55 Add link to crypto/tls godoc. 2017-11-27 15:24:03 +01:00
Lawrence Gripper
39c1cc1b3c Add Service Fabric Provider 2017-11-27 14:26:04 +01:00
Fernandez Ludovic
9f6f637527 Merge branch 'v1.4' into master 2017-11-27 11:40:50 +01:00
Kwok-kuen Cheung
0f09551a76 Fix kubernetes path prefix rule with rewrite-target 2017-11-27 11:22:03 +01:00
Marco Jantke
8cd72cfc1b remove obsolete links in k8s docs 2017-11-27 10:04:02 +01:00
Timo Reimann
7a141c8616 Document filename parameter for Kubernetes. 2017-11-26 01:02:03 +01:00
Ludovic Fernandez
0ca65f955d Stats collection. 2017-11-25 13:36:03 +01:00
Ludovic Fernandez
011b748a55 Change server receiver name. 2017-11-24 19:18:03 +01:00
Michael
f6181ef3e2 Fix custom headers replacement 2017-11-23 17:40:03 +01:00
Guilhem Lettron
24368747ab Use healthcheck for systemd watchdog 2017-11-23 16:10:04 +01:00
Fernandez Ludovic
66591cf216 Merge tag 'v1.4.4' into master 2017-11-23 15:21:47 +01:00
lishaoxiong
1feeeb2eec Manage certificates dynamically in kv store 2017-11-23 11:50:03 +01:00
SALLEYRON Julien
419d46c958 Prepare release v1.4.4 2017-11-23 11:48:03 +01:00
Daniel Tomcej
7063da1c7d Add docker security headers via labels 2017-11-22 19:40:04 +01:00
SALLEYRON Julien
bee8ebb00b Resync oxy with original repository 2017-11-22 18:20:03 +01:00
SALLEYRON Julien
da5e4a13bf add entrypoint in prometheus doc and remove web on influxdb doc 2017-11-22 16:28:03 +01:00
Ludovic Fernandez
5dc1ec68a3 Uncompress generated files. 2017-11-22 12:00:04 +01:00
lishaoxiong
3d2e5ebe39 Fix typo in examples 2017-11-22 10:16:03 +01:00
Ludovic Fernandez
f5130db6b0 gofmt generated file. 2017-11-21 21:30:03 +01:00
Marco Jantke
676b79db42 Fix raw path handling in strip prefix 2017-11-21 14:28:03 +01:00
Tait Clarridge
6d2f4a0813 Add health check label to ECS 2017-11-21 11:06:03 +01:00
Alex Antonov
4b91204686 Marathon constraints filtering 2017-11-21 10:48:04 +01:00
Emile Vauge
7ddefcef72 Add file to storeconfig 2017-11-21 10:24:03 +01:00
Ludovic Fernandez
0f3e42d463 autogen file mode 2017-11-21 08:20:04 +01:00
Ludovic Fernandez
c9129b8ecf Remove GzipHandler Fork 2017-11-20 18:32:03 +01:00
Ludovic Fernandez
a6955ecf59 Vendor generated file from template 2017-11-20 15:26:03 +01:00
NicoMen
6619a787a3 Fix problems about duplicated and missing Docker backends/frontends. 2017-11-20 15:16:03 +01:00
Raúl Sánchez
aae17c817b Fix issue with label traefik.backend.loadbalancer.stickiness.cookieName 2017-11-20 11:42:03 +01:00
Ludovic Fernandez
ab87bad952 Run Rancher tests cases in parallel. 2017-11-20 11:40:04 +01:00
Timo Reimann
be306d651e Register pprof handlers. 2017-11-20 11:04:03 +01:00
Ludovic Fernandez
8fe5c22075 Exclude RC from doc publication. 2017-11-20 09:42:02 +01:00
Ludovic Fernandez
05a9350e57 Use contants from http package. 2017-11-20 09:40:03 +01:00
ryarnyah
7ed4ae2f8c Add labels for traefik.frontend.entryPoints & PassTLSCert to Kubernetes 2017-11-20 02:12:03 +01:00
Manuel Zapf
5d6384e101 redirect to another entryPoint per frontend 2017-11-18 13:50:03 +01:00
Ludovic Fernandez
1a4564d998 http.Server log goes to Debug level. 2017-11-18 01:10:03 +01:00
NicoMen
66e489addb Update libkv dependency 2017-11-17 17:22:03 +01:00
Marco Jantke
cdab6b1796 fix concurrent provider config reloads 2017-11-17 10:26:03 +01:00
Ludovic Fernandez
722f299306 Support template as raw string. 2017-11-17 10:12:03 +01:00
Ludovic Fernandez
66be04f39e Documentation archive 2017-11-16 09:20:03 +01:00
Fernandez Ludovic
8719f2836e Merge 'v1.4.3' into master
Release v1.4.3
2017-11-15 23:01:08 +01:00
Ludovic Fernandez
0c702b0b6b Revert "Merge v1.4.2 into master" 2017-11-15 18:18:03 +01:00
Ludovic Fernandez
6fcab72ec7 Merge v1.4.2 into master 2017-11-14 16:48:03 +01:00
NicoMen
77b111702b Prepare release v1.4.3 2017-11-14 12:06:03 +01:00
NicoMen
96a7cc483f Add Traefik prefix to the KV key 2017-11-14 11:38:03 +01:00
Ludovic Fernandez
1e3506848a Flush and errorcode 2017-11-14 11:16:03 +01:00
Michael
5ee2cae85c Fix Traefik reload if Consul Catalog tags change 2017-11-13 12:14:02 +01:00
Ludovic Fernandez
5c119fe2d6 Exclude GRPC from compress 2017-11-10 14:12:02 +01:00
ferhat elmas
d55115844a Fix typos in changelog 2017-11-10 11:12:02 +01:00
NicoMen
4f4491c247 Allow adding optional Client CA files 2017-11-10 10:30:04 +01:00
Ludovic Fernandez
1691f586d7 fix: flaky test influxdb. 2017-11-09 17:22:03 +01:00
Ludovic Fernandez
04dfe0de84 Put subcommand in dedicated files. 2017-11-09 17:08:03 +01:00
SALLEYRON Julien
27d1b46835 Split Web into API/Dashboard, ping, metric and Rest Provider 2017-11-09 16:12:04 +01:00
Ivan Rogov
2f62ec3632 Link corrected 2017-11-09 15:54:04 +01:00
Timo Reimann
384488ac02 Remove unused lightMarathonClient. 2017-11-09 12:40:02 +01:00
NicoMen
c469e669fd Make the TLS certificates management dynamic. 2017-11-09 12:16:03 +01:00
Levi Blaney
56affb90ae Add secret creation to docs for kubernetes backend 2017-11-09 10:52:03 +01:00
SALLEYRON Julien
f6aa147c78 Add tests for websocket headers 2017-11-09 10:04:03 +01:00
SALLEYRON Julien
9bd0fff319 Keep status when stream mode and compress 2017-11-09 00:48:03 +01:00
Aditya C S
00d7c5972f Add InfluxDB support for traefik metrics 2017-11-08 15:14:03 +01:00
Jan Collijs
58a438167b Minor fix for docker volume vs created directory 2017-11-08 15:12:03 +01:00
Michael
e3131481e9 chore: sort imports 2017-11-08 11:40:04 +01:00
Tom Saleeba
bc8d68bd31 docs: fix some typos 2017-11-07 11:50:03 +01:00
Raúl Sánchez
07c6e33598 Update Rancher API integration to go-rancher client v2. 2017-11-05 13:02:03 +01:00
Bernhard Millauer
70812c70fc Postfix windows binaries with .exe 2017-11-03 17:02:14 +01:00
Nico Mandery
d89b234cad Fix typo in frontend.headers.customresponseheaders label 2017-11-03 14:32:03 +01:00
Fernandez Ludovic
2070aa9443 Merge 'v1.4.2' into master 2017-11-03 13:51:24 +01:00
Nils Knappmeier
91ff94ea56 dumpcerts.sh: Fix call to "base64" for Alpine 2017-11-02 15:52:04 +01:00
Ludovic Fernandez
0347537f43 Freeze version of mkdocs-material. 2017-11-02 14:38:03 +01:00
Ludovic Fernandez
db9b18f121 Prepare release v1.4.2 2017-11-02 12:28:03 +01:00
Michael MATUR
ee70001be3 [doc] - update documentation to add AWS_HOSTED_ZONE_ID 2017-11-02 11:44:04 +01:00
Michael MATUR
972eea97fe [ecs] - fix import order 2017-11-02 11:44:04 +01:00
Kendrick Erickson
2b4d33e919 Pass through certain forward auth negative response headers 2017-11-02 11:06:03 +01:00
Jim Hribar
fc4d670c88 Minor grammar change 2017-11-02 10:38:03 +01:00
Alex Antonov
02035d4942 Missing Backend key in configuration when application has no tasks 2017-11-01 11:26:03 +01:00
Félix P
93a46089ce Support Host NetworkMode for ECS provider 2017-10-31 11:44:03 +01:00
Tait Clarridge
e8d63b2a3b Update github.com/xenolf/lego to 0.4.1 2017-10-31 10:42:03 +01:00
Ludovic Fernandez
d3c7681bc5 New PR template 2017-10-30 16:38:03 +01:00
NicoMen
dc66db4abe Make the traefik.port label optional when using service labels in Docker containers. 2017-10-30 15:10:05 +01:00
NicoMen
a0e1cf8376 Fix IP address when Docker container network mode is container 2017-10-30 14:36:04 +01:00
Daniel König
5292b84f4f fixed dead link in kubernetes backend config docs 2017-10-30 14:04:03 +01:00
burningTyger
b27455a36f entrypoints -> entryPoints 2017-10-30 13:20:03 +01:00
Tiscs Sun
5042c5bf40 Added ReplacePathRegex middleware 2017-10-30 12:54:03 +01:00
NicoMen
da7b6f0baf Make frontend names differents for similar routes 2017-10-30 12:06:03 +01:00
Simon Elsbrock
9b5845f1cb Fix datastore corruption on reload due to shrinking config size 2017-10-30 11:22:04 +01:00
Emile Vauge
e8633d17e8 Add proxy protocol tests 2017-10-30 10:02:03 +01:00
Blake Mesdag
d1d8b01dfb Use Node IP in Swarm Standalone with "host" NetworkMode 2017-10-25 20:20:03 +02:00
Tait Clarridge
7c4353a0ac Add missing functions for ECS template 2017-10-25 17:18:03 +02:00
Erwin de Keijzer
1b2cb53d4f Fix the k8s docs example deployment yaml 2017-10-25 16:58:04 +02:00
Ludovic Fernandez
3158e51c62 Remove hardcoded runtime.GOMAXPROCS. 2017-10-25 16:16:02 +02:00
Fernandez Ludovic
a0c72cdf00 Merge v1.4.1 into master 2017-10-25 11:36:14 +02:00
NicoMen
f0371da838 Add unique ID to Docker services replicas 2017-10-25 10:00:03 +02:00
NicoMen
44b82e6231 Fix mkdocs version 2017-10-24 18:06:03 +02:00
Michael
04f0bf3070 Prepare release v1.4.1 2017-10-24 15:52:04 +02:00
SALLEYRON Julien
7400c39511 Stream mode when http2 2017-10-24 14:38:02 +02:00
Emile Vauge
008a5af6d6 Add mmatur to maintainers 2017-10-24 13:18:03 +02:00
Ludovic Fernandez
35ca40c3de Enhance Trust Forwarded Headers 2017-10-23 16:12:03 +02:00
Emile Vauge
de821fc305 fix healthcheck path 2017-10-23 15:48:03 +02:00
Fernandez Ludovic
e3cac7d0e5 fix(docker): Network filter. 2017-10-23 14:20:03 +02:00
Ludovic Fernandez
81f7aa9df2 Regex capturing group. 2017-10-23 10:20:02 +02:00
NicoMen
6bce298d90 Add a note about redirection rule to precise how regex/replacement work. 2017-10-22 09:44:03 +02:00
SALLEYRON Julien
afbad56012 Force http/1.1 for websocket 2017-10-20 17:38:04 +02:00
Daniel Tomcej
d973096464 Add Custom header parsing to Docker Provider 2017-10-20 17:14:03 +02:00
Fernandez Ludovic
7192aa86b5 Merge 'v1.4.0' into master 2017-10-16 23:10:44 +02:00
Ludovic Fernandez
9c8df8b9ce Fix 1.4.0 release date 2017-10-16 19:44:02 +02:00
Ludovic Fernandez
ff4c7b82bc Prepare release v1.4.0 2017-10-16 18:42:03 +02:00
Emile Vauge
47ff51e640 add retry backoff to staert config loading 2017-10-16 18:06:04 +02:00
Ludovic Fernandez
08503655d9 Backward compatibility for sticky 2017-10-16 17:38:03 +02:00
Michael
3afd6024b5 Fix consul catalog retry 2017-10-16 16:58:03 +02:00
Ludovic Fernandez
aa308b7a3a Add TrustForwardHeader options. 2017-10-16 12:46:03 +02:00
Ludovic Fernandez
9598f646f5 New entry point parser. 2017-10-13 15:04:02 +02:00
Sergey Kirillov
8af39bdaf7 Changed Docker network filter to allow any swarm network 2017-10-13 12:00:03 +02:00
Timo Reimann
914f3d1fa3 Do not run integration tests by default. 2017-10-13 11:08:03 +02:00
Ludovic Fernandez
8cb3f0835a Stickiness cookie name. 2017-10-12 17:50:03 +02:00
Manuel Zapf
cba0898e4f fix seconds to really be seconds 2017-10-12 16:26:03 +02:00
Timo Reimann
8d158402f3 Continue processing on invalid auth-realm annotation. 2017-10-12 15:48:03 +02:00
SALLEYRON Julien
7f2582e3b6 Nil body retries 2017-10-12 15:10:04 +02:00
Emile Vauge
dbc796359f Fix Proxy Protocol documentation 2017-10-12 11:10:03 +02:00
Thibault Coupin
4d1285d8e5 Add docker things for documentation 2017-10-11 14:46:03 +02:00
Marco Jantke
871d097b30 Fix traefik logs to behave like configured 2017-10-11 10:38:03 +02:00
Timo Reimann
1532033a7f Create dummy main() function in generate.go. 2017-10-10 18:20:02 +02:00
Fernandez Ludovic
9faae7387e Merge tag 'v1.4.0-rc5' into master 2017-10-10 17:17:44 +02:00
Timo Reimann
a5c644e719 Only listen to configured k8s namespaces. 2017-10-10 16:26:03 +02:00
Ludovic Fernandez
7a2ce59563 Prepare release v1.4.0-rc5 2017-10-10 15:50:03 +02:00
Ludovic Fernandez
14cec7e610 Stickiness documentation 2017-10-10 15:24:03 +02:00
Emile Vauge
6287a3dd53 Add trusted whitelist proxy protocol 2017-10-10 14:50:03 +02:00
SALLEYRON Julien
93a1db77c5 Move http2 configure transport 2017-10-10 12:14:03 +02:00
Ludovic Fernandez
a9d4b09bdb Stickiness cookie name 2017-10-10 11:10:02 +02:00
Timo Reimann
ed2eb7b5a6 Quote priority values in annotation examples. 2017-10-09 14:16:03 +02:00
Timo Reimann
18d8537d29 Document ways to partition Ingresses in the k8s guide. 2017-10-09 12:36:03 +02:00
Timo Reimann
72f3b1ed39 Remove pod from RBAC rules. 2017-10-09 12:12:03 +02:00
Marco Jantke
fd70e6edb1 enable prefix matching within slash boundaries 2017-10-06 11:34:03 +02:00
Shane Smith-Sahnow
5a578c5375 Updating make run-dev 2017-10-06 10:44:03 +02:00
Marco Jantke
9db8773055 fix flakiness in log rotation test 2017-10-06 09:20:13 +02:00
Timo Reimann
8a67434380 Sanitize cookie names. 2017-10-05 12:14:03 +02:00
Emile Vauge
c94e5f3589 Delay first version check 2017-10-05 08:42:02 +02:00
vermishelle
adef7200f6 Fix grammar 2017-10-03 10:22:03 +02:00
Fernandez Ludovic
cf508b6d48 Merge 'v1.4.0-rc4' into master 2017-10-02 17:18:24 +02:00
NicoMen
f8d36fda28 Prepare release v1.4.0-rc4 2017-10-02 16:00:03 +02:00
SALLEYRON Julien
4fe9cc7730 Add tests for urlencoded part in url 2017-10-02 15:36:02 +02:00
Chris Aumann
758b7f875b Fix grammar mistake in the kv-config docs 2017-10-02 14:58:04 +02:00
Ludovic Fernandez
0b97a67cfa CI: speed up pull images. 2017-10-02 14:22:03 +02:00
Julien Senon
ec5976bbc9 Update gRPC example 2017-10-02 11:34:03 +02:00
Ludovic Fernandez
5cc49e2931 bug command. 2017-10-02 10:32:02 +02:00
SALLEYRON Julien
b6752a2c02 Forward upgrade error from backend 2017-09-29 21:04:03 +02:00
jeffreykoetsier
d41e28fc36 Handle empty ECS Clusters properly 2017-09-29 16:56:03 +02:00
SALLEYRON Julien
64c52a6921 Consul catalog remove service failed 2017-09-29 16:30:03 +02:00
Ed Robinson
691a678b19 Improve compression documentation 2017-09-29 10:34:03 +02:00
Timo Reimann
1ba7fd91ff grep to-be-pulled-images directly to avoid newline issue. 2017-09-26 14:44:03 +02:00
Timo Reimann
1c98a9ad3e Add request accepting grace period delaying graceful shutdown. 2017-09-26 10:22:03 +02:00
Jiri Tyr
dd23ceeead Updating Docker output and curl for sticky sessions 2017-09-22 17:22:03 +02:00
Ludovic Fernandez
058fa1367b CI: speed up pull images. 2017-09-22 16:46:03 +02:00
Philippe M. Chiasson
9db12374ea Be certain to clear our marshalled representation before reloading it 2017-09-22 16:14:03 +02:00
Sami Jawhar
fc550ac1fc Dumpcerts.sh: fixed sed, extracted domain keys 2017-09-22 15:18:03 +02:00
Fernandez Ludovic
d6ef8ec3d1 Merge branch 'v1.4' into master 2017-09-21 11:37:33 +02:00
Marco Jantke
837db9a2d9 add json format support for traefik logs 2017-09-21 10:42:02 +02:00
SALLEYRON Julien
a941739f8a Change pull image command in Makefile 2017-09-20 20:02:02 +02:00
SALLEYRON Julien
795a346006 Flaky tests and refresh problem in consul catalog 2017-09-20 19:08:02 +02:00
Marco Jantke
9d00da7285 fix SSE subscriptions when retries are enabled 2017-09-20 18:40:03 +02:00
Marco Jantke
52c1909f24 Fix deprecated IdleTimeout config 2017-09-20 18:14:03 +02:00
Fernandez Ludovic
2cbf9cae71 Merge tag 'v1.4.0-rc3' into master 2017-09-18 21:52:44 +02:00
SALLEYRON Julien
f9225c54ff Prepare release v1.4.0-rc3 2017-09-18 18:20:03 +02:00
Ludovic Fernandez
cb05f36976 Manage Headers for the Authentication forwarding. 2017-09-18 17:48:07 +02:00
Frédéric Logier
49e0e20ce2 fix healthcheck port 2017-09-18 15:50:03 +02:00
Ludovic Fernandez
7c35337999 Remove GZIPHandler fork. 2017-09-18 11:04:03 +02:00
Fernandez Ludovic
2296aab5a8 refactor: unflaky access log. 2017-09-18 09:44:03 +02:00
Fernandez Ludovic
ce3b255f1a chore: Use go-check fork. 2017-09-18 09:44:03 +02:00
SALLEYRON Julien
3942f3366d User guide gRPC 2017-09-16 10:56:02 +02:00
Ludovic Fernandez
df76cc33a5 Fixes entry points configuration. 2017-09-15 20:56:04 +02:00
Marco Jantke
cf387d5a6d Enable loss less rotation of log files 2017-09-15 15:02:03 +02:00
Martin Proks
0a0cf87625 Fix rancher host IP address 2017-09-15 12:30:03 +02:00
Ludovic Fernandez
1a2544610d Enhance web backend documentation 2017-09-15 09:18:03 +02:00
Ludovic Fernandez
5229b7cfba Add forward auth documentation. 2017-09-14 21:26:02 +02:00
Timo Reimann
243b45881d Document custom error page restrictions. 2017-09-14 08:50:02 +02:00
Avi Deitcher
883028d981 Add examples of proxying ping 2017-09-13 15:24:03 +02:00
Ludovic Fernandez
bdeb7bfb9f Display Traefik logs in integration test 2017-09-13 10:34:04 +02:00
Ludovic Fernandez
808ffb0491 Explains new bot features. 2017-09-12 21:04:03 +02:00
Timo Reimann
5305a16350 Add guide section on production advice, esp. CPU. 2017-09-12 19:56:04 +02:00
Manuel Zapf
63b581935d Add stack name to backend name generation to fix rancher metadata backend 2017-09-12 15:06:04 +02:00
Ludovic Fernandez
c7c9349b00 Enhance documentation readability. 2017-09-11 19:10:04 +02:00
Ben Parli
d54417acfe Rate limiting for frontends 2017-09-09 13:36:03 +02:00
Fernandez Ludovic
9fba37b409 Merge v1.4.0-rc2 into master 2017-09-09 01:00:48 +02:00
Ludovic Fernandez
6d28c52f59 Prepare release v1.4.0-rc2 2017-09-08 21:28:02 +02:00
SALLEYRON Julien
f80a6ef2a6 Fix consul catalog refresh problems 2017-09-08 20:50:04 +02:00
SALLEYRON Julien
ecf31097ea Upgrade oxy for websocket bug 2017-09-08 16:14:03 +02:00
Ludovic Fernandez
16fc3675db Force GOARM to v6. 2017-09-08 14:50:04 +02:00
Ludovic Fernandez
651d993d9c prometheus, HTTP method and utf8 2017-09-08 11:22:03 +02:00
Ludovic Fernandez
03eb5139a2 Anonymize contributing doc 2017-09-08 10:28:03 +02:00
Ludovic Fernandez
286d882f1e Remove old glide elements for integration tests. 2017-09-08 10:26:03 +02:00
Emile Vauge
3b6afdf80c Fix error in prepareServer 2017-09-07 20:14:03 +02:00
Michael
c19cce69fa Add basic auth for ecs 2017-09-07 17:34:03 +02:00
SALLEYRON Julien
5c4931e235 Update oxy for websocket bug 2017-09-07 16:06:04 +02:00
Michael
b705e64a8a Add Basic auth for consul catalog 2017-09-07 15:28:02 +02:00
NicoMen
7fd1eb3780 Upgrade libkermit/compose version 2017-09-07 15:14:03 +02:00
Chulki Lee
8c5514612f Fix whitespaces 2017-09-07 12:02:03 +02:00
Chulki Lee
924e82ab0c doc: add notes on server urls with path 2017-09-07 11:40:03 +02:00
Keith Bremner
adcb99d330 Update cluster.md 2017-09-07 11:16:03 +02:00
Ludovic Fernandez
8339139400 Access log default values 2017-09-07 10:54:03 +02:00
Charlie O'Leary
a43cf8d2b8 Fix IAM policy sid. 2017-09-07 10:08:04 +02:00
NicoMen
2b863d9bc2 Upgrade libkermit/compose version 2017-09-06 15:02:03 +02:00
Michael
9ce4f94818 ECS provider refactoring 2017-09-06 12:10:05 +02:00
Marco Jantke
5157a6ad47 Send traefik logs to stdout 2017-09-06 11:58:03 +02:00
Manuel Zapf
cd6c58a372 fix rancher api environment get 2017-09-06 10:50:04 +02:00
SALLEYRON Julien
03ba8396f3 Add test for SSL TERMINATION in Websocket 2017-09-06 09:36:02 +02:00
Ludovic Fernandez
b0a0e16136 Enhance documentation. 2017-09-05 15:58:03 +02:00
Kyle Bai
732d73dd43 [Docs] Fix invalid service yaml example 2017-09-05 11:42:03 +02:00
Fernandez Ludovic
e075dfe911 refactor: re-organize doc. 2017-09-01 20:38:03 +02:00
Fernandez Ludovic
425b53585a doc: fix error pages configuration. 2017-09-01 20:38:03 +02:00
Ludovic Fernandez
d5bbb103d4 HTTPS for images, video and links in docs. 2017-09-01 19:44:03 +02:00
Ludovic Fernandez
5c2849ea07 Enhance security headers doc. 2017-09-01 17:44:03 +02:00
Ludovic Fernandez
723418e2cc fix: documentation Mesos. 2017-08-30 14:52:03 +02:00
Emile Vauge
45e2e8baec Update traefik SSH key take 2 (#2023) 2017-08-29 09:37:47 +02:00
Ludovic Fernandez
b0ae6bc049 Prepare release v1.4.0-rc1 2017-08-29 02:10:03 +02:00
Fernandez Ludovic
ffb53c07b8 refactor: basic configuration. 2017-08-28 23:02:04 +02:00
Fernandez Ludovic
f329b3b51d chore: change CODEOWNERS file. 2017-08-28 23:02:04 +02:00
Fernandez Ludovic
5b27aba3e1 doc: Material Theme. 2017-08-28 23:02:04 +02:00
Fernandez Ludovic
7c2ba62b56 doc: structural review
- user-guide review.
- add DataDog and StatD configuration.
- sync sample.toml and doc.
- split entry points doc.
- Deprecated.
2017-08-28 23:02:04 +02:00
Julien Maitrehenry
24862402e5 Refactor doc pages 2017-08-28 23:02:04 +02:00
ArikaChen
d568d2f55a Update golang version in contributing guide 2017-08-28 15:20:03 +02:00
Marco Jantke
dae7e7a80a add RetryAttempts to AccessLog in JSON format 2017-08-28 12:50:02 +02:00
Emile Vauge
23cdb37165 Update Traefiker SSH key 2017-08-28 11:48:03 +02:00
Fernandez Ludovic
2c82dfd444 Merge v1.3.7 2017-08-25 22:58:49 +02:00
Emile Vauge
c8c31aea62 Add proxy protocol 2017-08-25 21:32:03 +02:00
NicoMen
89b0037ec1 Improve Let's Encrypt documentation 2017-08-25 21:10:03 +02:00
Emile Vauge
b75fb23887 Update documentation for 1.4 release 2017-08-25 20:40:03 +02:00
Daniel Rampelt
52b69fbcb8 Add forward authentication option 2017-08-25 18:22:03 +02:00
Michael
f16219f90a Exposed by default feature in Consul Catalog 2017-08-25 17:32:03 +02:00
Ludovic Fernandez
7b0cef0fac Prepare release v1.3.7 2017-08-25 17:08:02 +02:00
SALLEYRON Julien
e0af17a17a Refactor globalConfiguration / WebProvider 2017-08-25 16:10:03 +02:00
mildis
92fb86b66f log X-Forwarded-For as ClientHost if present 2017-08-25 13:00:03 +02:00
Ludovic Fernandez
919295cffc Only forward X-Fowarded-Port. 2017-08-25 12:14:03 +02:00
Michael
086a85d2f0 Enable loadbalancer.sticky for ECS 2017-08-25 11:42:03 +02:00
Fernandez Ludovic
8235cd3645 refactor: minor changes. 2017-08-25 11:08:02 +02:00
Fernandez Ludovic
f1a257abf8 refactor: enhance bug report command. 2017-08-25 11:08:02 +02:00
Alex Antonov
98dfd2ba0e Added a check to ensure clientTLS configuration contains either a cert or a key 2017-08-25 10:26:02 +02:00
Ludovic Fernandez
87e6285cf6 Update certificates. 2017-08-25 09:20:03 +02:00
Luís Duarte
0d56a98836 Add support for Query String filtering 2017-08-24 20:28:03 +02:00
Nicolas Bonneval
8105f1c379 Enable loadbalancer.sticky for Consul Catalog 2017-08-24 18:38:05 +02:00
Marco Jantke
e6c2040ea8 Extract metrics to own package and refactor implementations 2017-08-23 20:46:03 +02:00
Julien Maitrehenry
c1b5b740ff toml page - replace li by table 2017-08-23 19:46:03 +02:00
Timo Reimann
1d2d0cefaa Fix documentation glitches. 2017-08-23 09:22:03 +02:00
Fernandez Ludovic
04e65958ee Merge 'v1.3.6' 2017-08-22 16:23:18 +02:00
Michael
8765494cbd Add support for several ECS backends 2017-08-22 11:46:03 +02:00
Julien Maitrehenry
05665f4eec Add more visibility to docker stack deploy label issue 2017-08-22 10:56:03 +02:00
Emile Vauge
396449c07f Add healthcheck command 2017-08-21 23:18:02 +02:00
Emile Vauge
eda679776e Add Marco Jantke to maintainers 2017-08-21 22:22:04 +02:00
Max van der Stam
69d57d602f Add guide for Docker, Traefik & Letsencrypt 2017-08-21 21:20:04 +02:00
Ludovic Fernandez
32b2736efd Bump gorilla/mux version. 2017-08-21 20:40:02 +02:00
Ludovic Fernandez
3f650bbd11 Support X-Forwarded-Port. 2017-08-21 17:54:02 +02:00
Ludovic Fernandez
5313922bb7 compress: preserve status code 2017-08-21 11:10:03 +02:00
Alex Antonov
ec3e2c08b8 Support multi-port service routing for containers running on Marathon 2017-08-21 10:46:03 +02:00
Timo Reimann
0367034f93 Fix docs about default namespaces. 2017-08-18 19:18:02 +02:00
Timo Reimann
b80ecd51a7 Use default frontend priority of zero. 2017-08-18 16:14:03 +02:00
Marco Jantke
14a0d66410 Add configurable timeouts and curate default timeout settings 2017-08-18 15:34:04 +02:00
Fernandez Ludovic
d84ccbc52a doc: add bots documentation.
- move contributing guide
- move maintainer guide
2017-08-18 10:24:03 +02:00
Fernandez Ludovic
1190768f4b chore: remove all PR scripts. 2017-08-18 10:24:03 +02:00
Timo Reimann
ea3510d1f3 Add support for readiness checks. 2017-08-18 03:08:03 +02:00
Timo Reimann
3f76f73e8c Mark Marathon and Kubernetes as constraint-supporting. 2017-08-18 02:40:03 +02:00
Ludovic Fernandez
759c269dee Code cleaning. 2017-08-18 02:18:02 +02:00
Boran Car
c360395afc examples/k8s: fix ui ingress port out of sync with deployment 2017-08-18 01:40:03 +02:00
Marco Paga
60a35c8aba Setting the Cookie Path explicitly to root 2017-08-13 11:34:34 +02:00
Emile Vauge
50dd2b8cff Change Traefik intro video 2017-08-11 15:19:36 +02:00
Richard Shepherd
4e5fcac9cb Add log file close and reopen on receipt of SIGUSR1 2017-08-11 12:04:58 +02:00
Timo Reimann
64b8fc52c3 [marathon] Fix and extend integration tests.
- Update compose file.
- Add integration test for Marathon application deployment.
2017-08-10 21:58:08 +02:00
Timo Reimann
19a5ba3264 Update github.com/docker/libcompose
Update github.com/docker/libcompose in glide.* files.
Vendor github.com/docker/libcompose update.
2017-08-10 21:58:08 +02:00
thomasbach76
7ff6c32452 Add the sprig functions in the template engine 2017-08-10 20:42:39 +02:00
Christoph Glaubitz
ff11467022 Bind healthcheck to backend by entryPointName 2017-08-10 18:00:31 +02:00
Ludovic Fernandez
7d3878214a Update documentation 2017-08-10 17:29:32 +02:00
Ludovic Fernandez
984817d3a0 Add more files to CODEOWNERS. 2017-08-10 16:47:11 +02:00
Alex Antonov
6b133e24b9 Added RetryMetrics to DataDog and StatsD providers 2017-08-09 02:54:35 +02:00
SALLEYRON Julien
990ee89650 Add helloworld tests with gRPC 2017-08-06 11:55:42 +02:00
Mark Dastmalchi-Round
8071f31721 Moved namespace to correct place 2017-08-03 10:25:05 +02:00
Fernandez Ludovic
d456c2ce6a Merge 'v1.3.5' 2017-08-01 19:32:44 +02:00
Kirill Orlov
94f922cd28 Added ability to override frontend priority for k8s ingress router 2017-07-29 18:35:23 +02:00
Sascha Grunert
29390a3c4a Update the documentation to use DaemonSet or Deployment (#1735) 2017-07-29 12:50:04 +02:00
Timo Reimann
765c44d77f [kubernetes] Add secrets resource to in-line RBAC spec.
Previously only existed in the Github-hosted example file.
2017-07-27 10:02:02 +02:00
Fernandez Ludovic
64ee68763b feat: match doc also. 2017-07-24 10:06:22 +02:00
Fernandez Ludovic
4122aef12e chore: fix CODEOWERS file. 2017-07-24 10:06:22 +02:00
Timo Reimann
8cb44598c0 [marathon] Use test builder.
This change introduces the builder pattern to the Marathon unit tests in
order to simplify and reduce the amount of testing boilerplate.

Additional changes:

- Add missing unit tests.
- Make all tests look consistent.
- Use dedicated type for task states for increased type safety.
- Remove obsoleted getApplication function.
2017-07-21 17:15:29 +02:00
Alex Antonov
69c628b626 DataDog and StatsD Metrics Support
* Added support for DataDog and StatsD monitoring
* Added documentation
2017-07-21 00:26:43 +02:00
Marco Jantke
cd28e7b24f fix GraceTimeOut description
Documentation stated that GraceTimeOut describes the timeout between
hot-reloads, which is not the case. GraceTimeOut describes the timeout
Traefik uses to finish serving active requests before stopping only.
2017-07-20 23:42:43 +02:00
Fernandez Ludovic
40d9058bb6 refactor: migration Negroni from codegangsta to urfave 2017-07-20 15:19:15 +02:00
Fernandez Ludovic
c36e0b3b06 refactor: add Safe everywhere is needing. 2017-07-20 14:59:54 +02:00
Timo Reimann
3174fb8861 [marathon] Assign filtered tasks to apps contained in slice.
We previously assigned them to a copy of each application, which
effectively disabled all filtering.

Fixes a bug introduced along commit 779eeba.
2017-07-20 10:39:27 +02:00
Marco Jantke
074b31b5e9 respond with 503 on empty backend 2017-07-19 19:28:24 +02:00
Pierre Ugaz
16609cd485 Update docs for dnsimple env vars.
* Lego library uses DNS_OAUTH_TOKEN instead of DNSIMPLE_OAUTH_TOKEN
2017-07-19 18:01:24 +02:00
bitsofinfo
70ab34cfb8 doc change regarding consul SSL
document change to clarify consul ssl, vs consul ssl client certificate security
2017-07-18 17:22:08 +02:00
Fernandez Ludovic
c53be185f4 chore(glide): change nergoni git url. 2017-07-12 10:22:39 +02:00
Timo Reimann
779eeba650 [marathon] Use single API call to fetch Marathon resources.
Change Marathon provider to make just one API call instead of two per
configuration update by means of specifying embedded resources, which
enable retrieving multiple response types from the API at once. Apart
from the obvious savings in API calls, we primarily gain a consistent
view on both applications and tasks that allows us to drop a lot of
correlation logic.  Additionally, it will serve as the basis for the
introduction of readiness checks which require application/task
consistency for correct leverage on the proxy end.

Additional changes:

marathon.go:
- Filter on tasks now embedded inside the applications.
- Reduce/simplify signature on multiple template functions as we do not
  need to check for proper application/task correlation anymore.
- Remove getFrontendBackend in favor of just getBackend.
- Move filtering on enabled/exposed applications from `taskFilter` to
  `applicationFilter`. (The task filter just reached out to the
  applications anyway, so it never made sense to locate it with the
  tasks where the filter was called once for every task even though the
  result would never change.)
- Remove duplicate constraints filter in tasks, where it neither made
  sense to keep as it operates on the application level only.
- Add context to rendering error.

marathon_test.go:
- Simplify and reduce numerous tests.
- Convert tests with high number of cases into parallelized sub-tests.
- Improve readability/structure for several tests.
- Add missing test for enabled/exposed applications.
- Simplify the mocked Marathon server.

marathon.tmpl:
- Update application/task iteration.
- Replace `getFrontendBackend` by `getBackend`.
2017-07-11 14:35:01 +02:00
Marco Jantke
58ffea6627 extract lb configuration steps into method 2017-07-10 19:18:31 +02:00
Fernandez Ludovic
a2d68ed881 chore: GitHub Code Owners. 2017-07-10 17:45:58 +02:00
Ludovic Fernandez
d653a348b1 Factorize labels
* refactor(accesslog): factorize file name.
* traefik.frontend.rule
* traefik.frontend.value
* traefik.backend.circuitbreaker.expression
* traefik.enable
* traefik.backend.loadbalancer.method
* traefik.backend.loadbalancer.sticky
* traefik.backend.maxconn.amount
* traefik.backend.maxconn.extractorfunc
* traefik.port
* traefik.tags
* traefik.backend
* traefik.weight
* traefik.domain
* traefik.protocol
* traefik.frontend.passHostHeader
* traefik.frontend.whitelistSourceRange
* traefik.frontend.priority
* traefik.frontend.entryPoints
* traefik.frontend.auth.basic
* traefik.backend.id
* traefik.backend.circuitbreaker
* traefik.frontend.rule.type
* traefik.portIndex
* refactor(docker): specific labels
* refactor(rancher): specific labels
* traefik.backend.healthcheck.*
* refactor(providers): factorize labels.
2017-07-10 16:58:12 +02:00
Ludovic Fernandez
2e84b1e556 Enhance integration tests
* refactor: remove unused code.
* refactor: factorize Traefik cmd start.
* refactor(whitelist): minor change.
* refactor(accesslog): better use of checker.
* refactor(errorpages): factorize containers IP variables.
* refactor(integration): refactor cmdTraefikWithConfigFile.
2017-07-10 14:58:31 +02:00
Fernandez Ludovic
bbb133d94c doc: remove glide integration. 2017-07-10 11:33:05 +02:00
Timo Reimann
d90fa5ab3e [kubernetes] Improve documentation.
- Add details to the labelselector parameter.
- Add section on ExternalNames in the guide.
2017-07-08 12:59:12 +02:00
Christophe Robin
759a19bc4f Add whitelist configuration option for entrypoints
* Add whitelist configuration option for entrypoints
* Add whitelist support to --entrypoint flag
2017-07-08 12:21:14 +02:00
Fernandez Ludovic
a7ec785994 refactor(dynamodb): Use Traefik Logger. 2017-07-08 00:05:53 +02:00
Fernandez Ludovic
46faa7a745 refactor(ecs): Use Traefik Logger. 2017-07-08 00:05:53 +02:00
Fernandez Ludovic
54e3f08833 refactor(marathon): Use Traefik Logger. 2017-07-08 00:05:53 +02:00
Fernandez Ludovic
b365836c57 feat: Add Trace in Base Provider. 2017-07-08 00:05:53 +02:00
Fernandez Ludovic
242f1b9c3c feat(logger): Expose Logrus writer.
- Hack logrus scanner buffer size.
- dedicate method for large scanner buffer.
2017-07-08 00:05:53 +02:00
Matt Christiansen
4dfbb6d489 Add marathon label to configure basic auth, similar to docker and rancher providers 2017-07-07 23:36:04 +02:00
James Sturtevant
c31b4c55c2 Update contributing guide build steps 2017-07-07 23:13:23 +02:00
Salvatore Pinto
ca5bbab20a traefik controller access to secrets
The traefik controller shall have access to secrets for the k8s basic authentication (#1488) to work
2017-07-07 22:35:03 +02:00
Michael Laccetti
41dd124a4b kubernetes ingress rewrite-target implementation
* Adding support for `ingress.kubernetes.io/rewrite-target`

We create a rule using the `PathPrefixStrip` to trim out the bit in the rewrite rule.
2017-07-07 21:27:54 +02:00
Marco Jantke
dbf6161fa1 always rebuild webui on 'make image'
and introduce a new make target image-dirty that is used for the Traefik
deployment.
2017-07-07 17:56:48 +02:00
Marcos Nils
7aabd6e385 Update README.md 2017-07-07 14:34:25 +02:00
NicoMen
cb203f8e7e Make the ACME developements testing easier
* ADD docker-compose and shell script to allow developers to get ACME environment easily
2017-07-07 11:36:32 +02:00
Fernandez Ludovic
8f845bac74 Merge tag 'v1.3.3' 2017-07-06 19:37:12 +02:00
Timo Reimann
a89eb122a0 Clarify that provider-enabling argument parameters set all defaults. 2017-07-06 17:00:44 +02:00
Vincent Demeester
b7daa2f3a4 Update traefik dependencies (docker/docker and related) (#1823)
Update traefik dependencies (docker/docker and related)

- Update dependencies
- Fix compilation problems
- Remove vdemeester/docker-events (in docker api now)
- Remove `integration/vendor`
- Use `testImport`
- update some deps.
- regenerate the lock from scratch (after a `glide cc`)
2017-07-06 16:28:13 +02:00
Vincent Demeester
7d178f49b4 Update docker version to 17.03.2…
… and also update the url to get static binaries.

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
2017-07-03 16:21:28 +02:00
Fernandez Ludovic
85f4f26942 doc: release cycle. 2017-07-03 14:57:19 +02:00
Fernandez Ludovic
eee8ba8a53 doc: Traefik bug command. 2017-07-03 12:42:06 +02:00
Ludovic Fernandez
22aceec426 Re-think integration vendoring
- remove docker/docker from  Traefik vendor (unused)
- use `ignore` for all Traefik vendor in integration glide.
- defined only integration specific version of the dependencies.
2017-07-03 11:53:31 +02:00
Ben Parli
121c057b90 Custom Error Pages (#1675)
* custom error pages
2017-07-01 01:04:18 +02:00
Marco Jantke
2c976227dd remove confusing go-marathon log message
Log message produced by go-marathon was:
time="2017-06-28T09:08:19Z" level=debug msg="listenToSSE(): failed to
handle event: failed to decode the event type, content: , error: EOF"

The fix for this was done in the upstream project of go-marathon
donovanhide/eventsource.

Background is that Marathon periodically sends a \n over the SSE
subscription, in order to keep the connection alive. This was parsed as
empty event by the eventsource and published. go-marathon in turn was
not able to do something with this empty event was producing the log
message above. By getting rid of publishing empty events in the
downstream library, we also get rid of this log message.
2017-06-30 22:14:57 +02:00
Julien Salleyron
81d011e57d Handle RootCAs Certificate 2017-06-30 14:56:55 +02:00
Fernandez Ludovic
3776e58041 Merge branch 'v1.3' 2017-06-30 00:04:04 +02:00
Fernandez Ludovic
36c88111de Merge branch 'v1.3' 2017-06-27 23:27:00 +02:00
Fernandez Ludovic
2201dcd505 doc: Manuel Laufenberg become Manuel Zapf. 2017-06-27 22:02:23 +02:00
Emile Vauge
7a7cafcbaa Add Nicolas Mengin to maintainers 2017-06-27 22:02:23 +02:00
Emile Vauge
efb671401d Add Julien Salleyron to maintainers 2017-06-27 21:35:47 +02:00
Richard Shepherd
4128c1ac8d Allow file provider to load config from files in a directory. 2017-06-27 16:58:04 +02:00
Fernandez Ludovic
73e10c96cc Merge branch 'v1.3' 2017-06-27 14:42:12 +02:00
Marco Jantke
0055965295 add status code to request duration metric 2017-06-26 18:21:28 +02:00
Emile Vauge
34e60a8404 Change to a more flexible PR review process
Signed-off-by: Emile Vauge <emile@vauge.com>
2017-06-26 11:04:12 +02:00
Timo Reimann
ceec81011b Address review comments. 2017-06-24 12:32:05 +02:00
Brian 'Redbeard' Harrington
927003329e contrib: Dump keys/certs from acme.json to files
In the event that a user needs to explode their acme.json file into
a set of directories and relevant files for troubleshooting or use
with other programs this script will parse them into the components
in the following path structure:

```
certdir
├── certs
│   ├── domain-1.example.com
│   ├── domain-2.example.com
│   └── domain-n.example.com
└── private
    └── letsencrypt.key
```
2017-06-24 12:32:05 +02:00
Fernandez Ludovic
01bb0a80ab doc: update Rancher documentation. 2017-06-21 14:54:36 +02:00
vholovko
db1baf80a9 Speeding up health change detection by separating it from catalog services check. 2017-06-20 20:27:04 +02:00
Martin Baillie
9cb07d026f Refactor into dual Rancher API/Metadata providers
Introduces Rancher's metadata service as an optional provider source for
Traefik, enabled by setting `rancher.MetadataService`.

The provider uses a long polling technique to watch the metadata service and
obtain near instantaneous updates. Alternatively it can be configured to poll
the metadata service every `rancher.RefreshSeconds` by setting
`rancher.MetadataPoll`.

The refactor splits API and metadata service code into separate source
files respectively, and specific configuration is deferred to
sub-structs.

Incorporates bugfix #1414
2017-06-20 19:08:53 +02:00
tanyadegurechaff
984ea1040f Fix error handling for docker swarm mode 2017-06-20 18:10:21 +02:00
Martin Baillie
447109e868 Add HTTP HEAD handling to /ping endpoint
Also updates documentation to reflect new method.
2017-06-20 11:40:14 +02:00
Marco Jantke
f79317a435 retry only on real network errors
Now retries only happen when actual network errors occur and not only
anymore based on the HTTP status code. This is because the backend could
also send this status codes as their normal interface and in that case
we don't want to retry.
2017-06-19 20:13:46 +02:00
Fernandez Ludovic
131d8dd765 Merge tag 'v1.3.1' 2017-06-16 16:52:53 +02:00
bitsofinfo
b452695c20 added consul acl token note 2017-06-16 16:31:03 +02:00
Mat Byczkowski
f17785c3ab doc: fix typo in maintainer.md 2017-06-16 14:00:24 +02:00
Fernandez Ludovic
2a578748fd Merge branch 'v1.3' 2017-06-14 22:26:35 +02:00
Marco Jantke
2ddae2e856 update go-marathon to 441a03a
in order to get the latest fixes regarding SSE subscription failover.
2017-06-14 10:03:49 +02:00
Marco Jantke
885b9f371c enable logging to stdout for access logs 2017-06-13 23:43:38 +02:00
Daniel Tomcej
f275e4ad3c Create Header Middleware 2017-06-13 12:34:17 +02:00
Fernandez Ludovic
aea7bc0c07 chore: update Glide hash. 2017-06-12 22:15:33 +02:00
Fernandez Ludovic
a457392ec3 refactor: clean coreos/etcd dependency. 2017-06-12 22:15:33 +02:00
Fernandez Ludovic
37ec7d0505 refactor: subpackage for x/oauth2. 2017-06-12 22:15:33 +02:00
Fernandez Ludovic
8f6404ab3a fix: sirupsen/logrus version
State:
- Current version: 10f801ebc38b33738c9d17d50860f484a0988ff5
- Glide suggest: f7f79f729e0fbe2fcc061db48a9ba0263f588252

https://github.com/sirupsen/logrus/commits/master?after=85b1699d505667d13f8ac4478c1debbf85d6c5de+34
10f801ebc3 (17 Mar 2017)
f7f79f729e (19 Jan 2016)
2017-06-12 22:15:33 +02:00
Fernandez Ludovic
1538b16b21 fix: golang/protobuf version
`github.com/golang/protobuf`:
- `github.com/prometheus/client_golang` (no version)
- `github.com/gogo/protobuf` (no version)
- `google.golang.org/appengine` (no version)
- `github.com/matttproud/golang_protobuf_extensions` (no version)

State:
- Current version: 2bba0603135d7d7f5cb73b2125beeda19c09f4ef
- Glide suggest: 8616e8ee5e20a1704615e6c8d7afcdac06087a67

Force to keep the current version.

Refs
- 2bba060313 (Mar 31, 2017) next commit the Apr 27, 2017.
- 8616e8ee5e (8 Jun 2016)
2017-06-12 22:15:33 +02:00
Fernandez Ludovic
a6477fbd95 fix: Prometheus dependency version: matttproud/golang_protobuf_extensions
`matttproud/golang_protobuf_extensions` is used by:
- `github.com/prometheus/client_golang`
- `github.com/prometheus/common`

Force to the latest version.

Refs:
- https://github.com/matttproud/golang_protobuf_extensions/commits/master (no dependencies manager)
- 24 Apr 2016, c12348ce28 (master, HEAD)
- 6 Apr 2015, fc2b8d3a73
2017-06-12 22:15:33 +02:00
Fernandez Ludovic
e802dcd189 fix: Mesos/k8s dependency version: golang/glog
`golang/glog` is used by:
- `github.com/mesos/mesos-go` (no version)
- `k8s.io/client-go` (`44145f04b68cf362d9c4df2182967c2275eaefed`)

In #353 (add Mesos provider, 20 Jul 2016), the `golang/glog` hash is `fca8c8854093a154ff1eb580aae10276ad6b1b5f`.

The problem appear in #836 (use k8s client, 1 Dec 2016).

Refs:
- Traefik:
  - https://github.com/containous/traefik/pull/836
  - 131f581f77
- Glog
  - https://github.com/golang/glog/commits/master
  - https://github.com/golang/glog/pull/13
  - 44145f04b6
  - fca8c88540
- k8s
  - e121606b0d/Godeps/Godeps.json
  - https://github.com/kubernetes/client-go/blob/master/Godeps/Godeps.json
2017-06-12 22:15:33 +02:00
Fernandez Ludovic
931dc02c09 fix: Vulcand dependency version : vulcand/predicate
`vulcand/predicate` is used by:
- `github.com/vulcand/oxy` (no dependencies manager)
- `github.com/vulcand/route` (used by `github.com/vulcand/vulcand`)

`github.com/vulcand/vulcand` (Godeps) required a old version `cb0bff91a7ab7cf7571e661ff883fc997bc554a3`.

`19b9dde14240d94c804ae5736ad0e1de10bf8fe6` is the only commit before `cb0bff91a7ab7cf7571e661ff883fc997bc554a3`.

refs:
- 42492a3a85/Godeps/Godeps.json
- https://github.com/vulcand/predicate/commits/master
- 19b9dde142
2017-06-12 22:15:33 +02:00
Fernandez Ludovic
7017cdcf49 fix: oxy dependency version: mailgun/timetools. 2017-06-12 22:15:33 +02:00
Fernandez Ludovic
5aa017d9b5 fix: k8s dependency version: emicklei/go-restful
`emicklei/go-restful` is used by:
- `k8s.io/client-go`  (Godeps)

Refs:
- e121606b0d/Godeps/Godeps.json
2017-06-12 22:15:33 +02:00
Fernandez Ludovic
a7297b49a4 fix: Prometheus dependencies
Prometheus is related to `go-kit/kit`.
`go-kit/kit` doesn't have dependency manager.

We use `go-kit/kit` v0.3.0 (15 Nov 2016).

We must explicitly declare the Prometheus dependencies.
Prometheus doesn't have dependency manager.
Use the commit date to fixed all hash.

refs:
- go-kit
  - https://github.com/go-kit/kit/tree/v0.3.0 (15 Nov 2016)
- Prometheus
  - https://github.com/prometheus/client_golang/commits/master
  - 08fd2e1237 (Apr 1, 2017)
  - https://github.com/prometheus/common/commits/master
  - 49fee292b2 (Feb 20, 2017)
  - https://github.com/prometheus/client_model/commits/master
  - 6f38060186 (Feb 16, 2017, master, HEAD)
  - https://github.com/prometheus/procfs/commits/master
  - a1dba9ce8b (Feb 16, 2017)
2017-06-12 22:15:33 +02:00
Zachary Seguin
3eaeb81831 Adds definitions to backend kv template for health checking 2017-06-12 21:54:08 +02:00
Alex Antonov
7d6c778211 Enhanced flexibility in Consul Catalog configuration 2017-06-12 21:18:55 +02:00
Fernandez Ludovic
9c27a98821 refactor: move Marathon client mock.
refactor: remove old Marathon mock.
refactor: generate new Marathon mock.

mockery -recursive -dir=vendor/github.com/gambol99/ -name=Marathon -output=provider/marathon/mocks
2017-06-12 20:27:54 +02:00
djalal
ad54c5a278 drop "slave" wording for "worker"
Traefik should follow modern IT trends, and use manager/leader/worker/agent, etc. instead of "master/slave".

e.g jenkinsci/jenkins#2007 (https://issues.jenkins-ci.org/browse/JENKINS-27268)

NB: of course, it can only apply where possible, since backends like Mesos should retain their own concepts, and not add more confusion.
2017-06-12 20:07:39 +02:00
Fernandez Ludovic
96939e2990 chore: Enhance GitHub issue template. 2017-06-12 19:29:23 +02:00
Fernandez Ludovic
5268db47a1 fix: glide go-marathon 2017-06-11 21:44:36 +02:00
Drew Wells
3048509807 enable TLS client forwarding
Copys the incoming TLS client certificate to the outgoing
request. The backend can then use this certificate for
client authentication ie. k8s client cert authentication
2017-06-11 15:24:29 +02:00
Fernandez Ludovic
7399a83c74 refactor: Use Statefull interface in access log. 2017-06-09 23:55:49 +02:00
Fernandez Ludovic
18c3d8dc62 test: add AddPrefix test. 2017-06-09 23:55:49 +02:00
Fernandez Ludovic
2d1ddcf28b test: HealthCheck review. 2017-06-09 23:55:49 +02:00
Fernandez Ludovic
a1a0420314 test: use MustNewRequest. 2017-06-09 23:55:49 +02:00
Fernandez Ludovic
2223587fc0 refactor: ordering imports. 2017-06-09 23:55:49 +02:00
Fernandez Ludovic
63f9bccf9f refactor: fix typos.
refactor: typo in whitelister file name.
2017-06-09 23:55:49 +02:00
Fernandez Ludovic
18d11e02d0 test: simplify stripPrefix* tests. 2017-06-09 23:55:49 +02:00
Richard Quintin
a71d69cc3c make the cookie name unique to the backend being served 2017-06-07 20:18:16 +02:00
Marco Jantke
e007bb7546 add metrics for backend_retries_total 2017-06-07 08:56:50 +02:00
Mihai Todor
7874ffd506 Minor Health UI fixes
- format the Oy axis ticks as integers on the Total Status Code
Count chart
- prevent the Average Response Time chart from showing negative
values on the Oy axis
- remove the deprecated transitionDuration field
- set the transition duration to 0 on the Average Response Time
chart to avoid triggering an NVD3 marker placement bug
2017-06-06 22:40:39 +02:00
Richard Shepherd
a9216e24f5 Add JSON as access logging format 2017-06-06 16:26:22 +02:00
Alex Antonov
39388a2199 Exported getSubDomain function from Marathon provider to be able to use in custom templates 2017-06-06 14:31:30 +02:00
Fernandez Ludovic
71111708d4 Merge branch 'v1.3' into master 2017-06-02 19:56:15 +02:00
Fernandez Ludovic
d5efc99876 doc: Enhance GitHub issue template. 2017-06-01 21:53:05 -07:00
Fernandez Ludovic
1e84e77a67 Merge branch 'v1.3' into master 2017-06-01 20:53:02 +02:00
Fernandez Ludovic
d6b448f430 Merge branch 'v1.3' into master 2017-05-31 23:29:23 +02:00
Fernandez Ludovic
e426b27581 refactor: valid Git branch name must work. 2017-05-31 10:34:00 +02:00
Fernandez Ludovic
b6c5c14447 refactor: Enhance rules tests.
- refactor: change incorrect package.
- refactor: test readability.
2017-05-31 10:34:00 +02:00
Fernandez Ludovic
cbccdd51c5 refactor: Logs & errors review.
- log & error: remove format if not necessary, add if necessary.
- add constants for k8s annotations.
- fix typos
2017-05-30 23:33:27 +02:00
Fernandez Ludovic
994e135368 refactor: typo in misspelling. 2017-05-26 16:42:26 -07:00
Timo Reimann
87e5cda506 Update CONTRIBUTING.md.
- Go 1.8 is the current minimum requirement.
- The main binary moved to cmd/traefik.
- Remove obsolete gox example.
2017-05-25 00:18:22 +02:00
Fernandez Ludovic
2833d68f15 Merge branch 'v1.3' into merge-back-1_3_0-rc3 2017-05-24 20:39:38 +02:00
Richard Shepherd
64e8b31d49 Switch access logging to logrus 2017-05-24 14:20:42 +02:00
Igor
2643271053 Use more inclusive language in README.md {guys => folks}
While usage of the word "guys" can be considered gender neutral depending on location and context, it is widely considered to be gendered -- and more inclusive options are readily available. 💜

References:

* [When is "guys" gender neutral? I did a survey! -- Julia Evans](https://jvns.ca/blog/2013/12/27/guys-guys-guys/)
2017-05-22 21:14:43 +02:00
Ludovic Fernandez
5b36b274a3 doc(maintainer): add contributor/needs-resolve-conflicts
Replace `contributor/needs-rebase` by `contributor/needs-resolve-conflicts`.
2017-05-22 20:05:10 +02:00
Fernandez Ludovic
8ad31d6eb4 Merge remote-tracking branch 'upstream/v1.3' into merge-v1_3 2017-05-22 11:38:28 +02:00
Brian Akins
13e8a875cf Allow overriding port for backend healthchecks 2017-05-19 17:48:16 +02:00
Ed Robinson
c7281df230 Update usage of .local with .minikube in k8s docs
Fixes #1521
2017-05-19 17:02:39 +02:00
MaZderMind
5f0b215e90 IP Whitelists for Frontend (with Docker- & Kubernetes-Provider Support) 2017-05-19 15:19:29 +02:00
Timo Reimann
55f610422a Install github.com/stretchr/testify/require. 2017-05-19 15:19:29 +02:00
Timo Reimann
a04ef15bcd Issue template: Emphasize SO and Slack for support questions.
- Be more explicit in the purpose of the issue tracker.
- Move SO before Slack since it seems preferable.
- Refer to SO and Slack on first question again.
2017-05-19 10:39:05 +02:00
Ludovic Fernandez
81754840ff Update README.md 2017-05-18 23:17:16 +02:00
Fernandez Ludovic
2610023131 refactor: Deflake and Try package
- feat: add CI multiplier
- refactor: readability
- feat: custom Sleep function
- refactor(integration): use custom Sleep
- feat: show Try progress
- feat(try): try response with status code
- refactor(try): use a dedicate package.
- refactor(integration): Try everywhere
- feat(CI): pass CI env var to Integration Tests.
- refactor(acme): increase timeout.
- feat(acme): show Traefik logs
- refactor(integration): use `http.StatusXXX`
- refactor: remove Sleep
2017-05-18 22:34:15 +02:00
Ludovic Fernandez
ff3481f06b Merge pull request #1613 from containous/merge-v1.3.0-rc2-master
Merge v1.3.0-rc2 master
2017-05-17 12:41:26 +02:00
Emile Vauge
f8ea19d29c Merge branch 'v1.3' into merge-v1.3.0-rc2-master 2017-05-17 11:44:53 +02:00
Ludovic Fernandez
3b8ebf7d33 Merge pull request #1603 from antoine-aumjaud/patch-1
Small toml documentation update
2017-05-17 10:03:57 +02:00
Antoine Aumjaud
5e14f20786 Update documentation
fix some "errors"
2017-05-17 09:45:36 +02:00
Thomas Recloux
96b19deac5 Merge pull request #1616 from containous/remove-trecloux-maintainers
Remove Thomas Recloux from maintainers
2017-05-16 23:42:16 +02:00
Emile Vauge
a6aff7c85c Remove Thomas Recloux from maintainers 2017-05-16 23:20:29 +02:00
Emile Vauge
1310347395 Remove Russell from maintainers (#1614)
It's been a pleasure
2017-05-16 18:10:28 +01:00
Ludovic Fernandez
40c94d80d7 Merge pull request #1582 from ldez/doc/maintainer-labels
doc: add labels documentation.
2017-05-16 17:59:30 +02:00
Fernandez Ludovic
921a704c24 doc: add labels documentation. 2017-05-16 14:21:26 +02:00
Emile Vauge
3f490f95c6 Merge pull request #1589 from containous/add-ldez-maintainers
Add @ldez to maintainers
2017-05-16 11:26:54 +02:00
Emile Vauge
24d80b1909 Add @ldez to maintainers
Signed-off-by: Emile Vauge <emile@vauge.com>
2017-05-16 11:06:59 +02:00
Ludovic Fernandez
78b2fba033 Merge pull request #1595 from ldez/refactor/remove-legacy-ci-data
chore(CI): remove old CI data.
2017-05-12 20:32:29 +02:00
Fernandez Ludovic
218b76275c chore(CI): remove old CI data. 2017-05-12 19:16:36 +02:00
Ludovic Fernandez
cf5b6d837f Merge pull request #1594 from ldez/doc/semaphoreci-badge
doc(CI): Add SemaphoreCI badge.
2017-05-12 19:04:00 +02:00
Fernandez Ludovic
0babc7bb64 doc(CI): Add SemaphoreCI badge. 2017-05-12 18:46:02 +02:00
Ludovic Fernandez
8a551d91fd Merge pull request #1573 from atbore-phx/ci-retry-tests
[CI] retry function
2017-05-12 17:41:21 +02:00
Attilio Borello
eeed035ef0 added retry function to validate script 2017-05-12 17:18:47 +02:00
Attilio Borello
33404a7772 added retry function to tests script 2017-05-12 17:18:47 +02:00
Ludovic Fernandez
bd90745528 Merge pull request #1593 from atbore-phx/ci-switch
[CI] removed unit and integration tests from travis
2017-05-12 17:17:00 +02:00
Attilio Borello
ede1212cb0 removed unit and integration tests from travis 2017-05-12 16:19:35 +02:00
Ludovic Fernandez
2dcbc01e51 Merge pull request #1544 from maxwo/proxy-dev-mode
Proxy in dev mode
2017-05-12 10:11:58 +02:00
Maxime Wojtczak
61ba50fac9 feat(Dev proxy) : Add proxy to localhost:8080 in dev mode. 2017-05-12 09:41:22 +02:00
Ludovic Fernandez
b24b5e20b4 Merge pull request #1548 from timoreimann/kubernetes-ignore-missing-pass-host-header-annotation
Merge v1.3 branch into master [2017-05-11]
2017-05-12 00:36:52 +02:00
Timo Reimann
3112432480 Merge remote-tracking branch 'upstream/v1.3' into HEAD 2017-05-11 21:10:20 +02:00
Ludovic Fernandez
94f5b0d9ff Merge pull request #1571 from containous/restore-access-logger
Restore: First stage of access logging middleware.
2017-05-11 17:24:26 +02:00
Fernandez Ludovic
d2c8824902 refactor: restore "First stage of access logging middleware."
This reverts commit 82651985c4.
2017-05-11 16:27:13 +02:00
Ludovic Fernandez
db09007dbc Merge pull request #1558 from Stibbons/yarnpkg
prefere yarnpkg over yarn
2017-05-10 18:26:05 +02:00
Gaetan Semet
5b2e8990f1 prefere yarnpkg over yarn
to avoid conflict with Hadoop Yarn cli.

I don’t know the best practice, but i do
have Apache Yarn installed on my machine, so
I get this conflict. Of course this conflict does
not arised when building within the docker.

https://github.com/yarnpkg/yarn/issues/2337
Signed-off-by: Gaetan Semet <gaetan@xeberon.net>
2017-05-10 17:35:17 +02:00
Ludovic Fernandez
2f6068decc Merge pull request #1580 from atbore-phx/docker-light
[CI] Reduce size of Docker Images
2017-05-10 17:23:37 +02:00
Attilio Borello
1e591dd188 clean up apt-cache in webui/Dockerfile 2017-05-10 11:24:19 +02:00
Attilio Borello
6838a81e50 replaced docker images with alpine if available (nginx, rabbitmq) 2017-05-10 11:24:19 +02:00
Ludovic Fernandez
ceef5e39b7 Merge pull request #1572 from atbore-phx/ci-docker-version
[CI] set Docker version
2017-05-09 16:04:08 +02:00
Attilio Borello
ef339af623 added DOCKER_VERSION variable 2017-05-09 11:25:25 +02:00
Ludovic Fernandez
acc7865542 Merge pull request #1554 from ldez/feat-push-force-pr
feat(github): push force PR branch.
2017-05-05 17:19:30 +02:00
Fernandez Ludovic
c00c240c14 feat(github): push force contributor branch. 2017-05-05 16:19:23 +02:00
5868 changed files with 116083 additions and 1859122 deletions

View File

@@ -1,3 +1,5 @@
dist/
!dist/traefik
site/
vendor/
/traefik

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
# vendor/github.com/go-acme/lego/providers/dns/cloudxns/cloudxns.go eol=crlf

View File

@@ -1,150 +0,0 @@
# Contributing
### Building
You need either [Docker](https://github.com/docker/docker) and `make` (Method 1), or `go` (Method 2) in order to build traefik. For changes to its dependencies, the `glide` dependency management tool and `glide-vc` plugin are required.
#### Method 1: Using `Docker` and `Makefile`
You need to run the `binary` target. This will create binaries for Linux platform in the `dist` folder.
```bash
$ make binary
docker build -t "traefik-dev:no-more-godep-ever" -f build.Dockerfile .
Sending build context to Docker daemon 295.3 MB
Step 0 : FROM golang:1.7
---> 8c6473912976
Step 1 : RUN go get github.com/Masterminds/glide
[...]
docker run --rm -v "/var/run/docker.sock:/var/run/docker.sock" -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -v "/home/emile/dev/go/src/github.com/containous/traefik/"dist":/go/src/github.com/containous/traefik/"dist"" "traefik-dev:no-more-godep-ever" ./script/make.sh generate binary
---> Making bundle: generate (in .)
removed 'gen.go'
---> Making bundle: binary (in .)
$ ls dist/
traefik*
```
#### Method 2: Using `go`
###### Setting up your `go` environment
- You need `go` v1.7+
- It is recommended you clone Træfik into a directory like `~/go/src/github.com/containous/traefik` (This is the official golang workspace hierarchy, and will allow dependencies to resolve properly)
- This will allow your `GOPATH` and `PATH` variable to be set to `~/go` via:
```bash
$ export GOPATH=~/go
$ export PATH=$PATH:$GOPATH/bin
```
This can be verified via `$ go env`
- You will want to add those 2 export lines to your `.bashrc` or `.bash_profile`
- You need `go-bindata` to be able to use `go generate` command (needed to build) : `$ go get github.com/jteeuwen/go-bindata/...` (Please note, the ellipses are required)
#### Setting up `glide` and `glide-vc` for dependency management
- Glide is not required for building; however, it is necessary to modify dependencies (i.e., add, update, or remove third-party packages)
- Glide can be installed either via homebrew: `$ brew install glide` or via the official glide script: `$ curl https://glide.sh/get | sh`
- The glide plugin `glide-vc` must be installed from source: `go get github.com/sgotti/glide-vc`
If you want to add a dependency, use `$ glide get` to have glide put it into the vendor folder and update the glide manifest/lock files (`glide.yaml` and `glide.lock`, respectively). A following `glide-vc` run should be triggered to trim down the size of the vendor folder. The final result must be committed into VCS.
Dependencies for the integration tests in the `integration` folder are managed in a separate `integration/glide.yaml` file using the same toolset.
Care must be taken to choose the right arguments to `glide` when dealing with either main or integration test dependencies, or otherwise risk ending up with a broken build. For that reason, the helper script `script/glide.sh` encapsulates the gory details and conveniently calls `glide-vc` as well. Call it without parameters for basic usage instructions.
Here's a full example:
```bash
# install the new main dependency github.com/foo/bar and minimize vendor size
$ ./script/glide.sh get github.com/foo/bar
# install another dependency, this time for the integration tests
$ ( cd integration && ../script/glide.sh get github.com/baz/quuz )
# generate (Only required to integrate other components such as web dashboard)
$ go generate
# Standard go build
$ go build
# Using gox to build multiple platform
$ gox "linux darwin" "386 amd64 arm" \
-output="dist/traefik_{{.OS}}-{{.Arch}}" \
./cmd/traefik
# run other commands like tests
```
### Tests
##### Method 1: `Docker` and `make`
You can run unit tests using the `test-unit` target and the
integration test using the `test-integration` target.
```bash
$ make test-unit
docker build -t "traefik-dev:your-feature-branch" -f build.Dockerfile .
# […]
docker run --rm -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -v "/home/vincent/src/github/vdemeester/traefik/dist:/go/src/github.com/containous/traefik/dist" "traefik-dev:your-feature-branch" ./script/make.sh generate test-unit
---> Making bundle: generate (in .)
removed 'gen.go'
---> Making bundle: test-unit (in .)
+ go test -cover -coverprofile=cover.out .
ok github.com/containous/traefik 0.005s coverage: 4.1% of statements
Test success
```
For development purposes, you can specify which tests to run by using:
```bash
# Run every tests in the MyTest suite
TESTFLAGS="-check.f MyTestSuite" make test-integration
# Run the test "MyTest" in the MyTest suite
TESTFLAGS="-check.f MyTestSuite.MyTest" make test-integration
# Run every tests starting with "My", in the MyTest suite
TESTFLAGS="-check.f MyTestSuite.My" make test-integration
# Run every tests ending with "Test", in the MyTest suite
TESTFLAGS="-check.f MyTestSuite.*Test" make test-integration
```
More: https://labix.org/gocheck
##### Method 2: `go`
- Tests can be run from the cloned directory, by `$ go test ./...` which should return `ok` similar to:
```
ok _/home/vincent/src/github/vdemeester/traefik 0.004s
```
### Documentation
The [documentation site](http://docs.traefik.io/) is built with [mkdocs](http://mkdocs.org/)
First make sure you have python and pip installed
```shell
$ python --version
Python 2.7.2
$ pip --version
pip 1.5.2
```
Then install mkdocs with pip
```shell
$ pip install mkdocs
```
To test documentation locally run `mkdocs serve` in the root directory, this should start a server locally to preview your changes.
```shell
$ mkdocs serve
INFO - Building documentation...
WARNING - Config value: 'theme'. Warning: The theme 'united' will be removed in an upcoming MkDocs release. See http://www.mkdocs.org/about/release-notes/ for more details
INFO - Cleaning site directory
[I 160505 22:31:24 server:281] Serving on http://127.0.0.1:8000
[I 160505 22:31:24 handlers:59] Start watching changes
[I 160505 22:31:24 handlers:61] Start detecting changes
```

View File

@@ -1,32 +1,37 @@
<!--
PLEASE READ THIS MESSAGE.
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
Please keep in mind that the GitHub issue tracker is not intended as a general support forum, but for reporting bugs and feature requests.
The issue tracker is for reporting bugs and feature requests only.
For end-user related support questions, please refer to one of the following:
For other type of questions, consider using one of:
- Stack Overflow (using the "traefik" tag): https://stackoverflow.com/questions/tagged/traefik
- the Traefik community Slack channel: https://slack.traefik.io
- the Traefik community Slack channel: https://traefik.herokuapp.com
- StackOverflow: https://stackoverflow.com/questions/tagged/traefik
-->
### Do you want to request a *feature* or report a *bug*?
<!--
If you intend to ask a support question: DO NOT FILE AN ISSUE.
-->
### What did you do?
<!--
HOW TO WRITE A GOOD ISSUE?
- if it's possible use the command `traefik bug`. See https://www.youtube.com/watch?v=Lyz62L8m93I.
- The title must be short and descriptive.
- Explain the conditions which led you to write this issue: the context.
- Respect the issue template as much as possible.
- If possible, use the command `traefik bug`. See https://www.youtube.com/watch?v=Lyz62L8m93I.
- The title should be short and descriptive.
- Explain the conditions which led you to report this issue: the context.
- The context should lead to something, an idea or a problem that youre facing.
- Remain clear and concise.
- Format your messages to help the reader focus on what matters and understand the structure of your message, use Markdown syntax https://help.github.com/articles/github-flavored-markdown
-->
### Do you want to request a *feature* or report a *bug*?
### What did you do?
### What did you expect to see?
@@ -37,6 +42,16 @@ HOW TO WRITE A GOOD ISSUE?
### Output of `traefik version`: (_What version of Traefik are you using?_)
<!--
For the Traefik Docker image:
docker run [IMAGE] version
ex: docker run traefik version
For the alpine Traefik Docker image:
docker run [IMAGE] traefik version
ex: docker run traefik traefik version
-->
```
(paste your output here)
```
@@ -51,7 +66,7 @@ Add more configuration information here.
-->
### If applicable, please paste the log output in debug mode (`--debug` switch)
### If applicable, please paste the log output at DEBUG level (`--logLevel=DEBUG` switch)
```
(paste your output here)

78
.github/ISSUE_TEMPLATE/Bug_report.md vendored Normal file
View File

@@ -0,0 +1,78 @@
---
name: Bug report
about: Create a report to help us improve
---
<!--
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
The issue tracker is for reporting bugs and feature requests only.
For end-user related support questions, please refer to one of the following:
- Stack Overflow (using the "traefik" tag): https://stackoverflow.com/questions/tagged/traefik
- the Traefik community Slack channel: https://slack.traefik.io
-->
### Do you want to request a *feature* or report a *bug*?
Bug
### What did you do?
<!--
HOW TO WRITE A GOOD BUG REPORT?
- Respect the issue template as much as possible.
- If possible, use the command `traefik bug`. See https://www.youtube.com/watch?v=Lyz62L8m93I.
- The title should be short and descriptive.
- Explain the conditions which led you to report this issue: the context.
- The context should lead to something, an idea or a problem that youre facing.
- Remain clear and concise.
- Format your messages to help the reader focus on what matters and understand the structure of your message, use Markdown syntax https://help.github.com/articles/github-flavored-markdown
-->
### What did you expect to see?
### What did you see instead?
### Output of `traefik version`: (_What version of Traefik are you using?_)
<!--
For the Traefik Docker image:
docker run [IMAGE] version
ex: docker run traefik version
For the alpine Traefik Docker image:
docker run [IMAGE] traefik version
ex: docker run traefik traefik version
-->
```
(paste your output here)
```
### What is your environment & configuration (arguments, toml, provider, platform, ...)?
```toml
# (paste your configuration here)
```
<!--
Add more configuration information here.
-->
### If applicable, please paste the log output in DEBUG level (`--logLevel=DEBUG` switch)
```
(paste your output here)
```

View File

@@ -0,0 +1,37 @@
---
name: Feature request
about: Suggest an idea for this project
---
<!--
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
The issue tracker is for reporting bugs and feature requests only.
For end-user related support questions, please refer to one of the following:
- Stack Overflow (using the "traefik" tag): https://stackoverflow.com/questions/tagged/traefik
- the Traefik community Slack channel: https://slack.traefik.io
-->
### Do you want to request a *feature* or report a *bug*?
Feature
### What did you expect to see?
<!--
HOW TO WRITE A GOOD ISSUE?
- Respect the issue template as much as possible.
- If possible, use the command `traefik bug`. See https://www.youtube.com/watch?v=Lyz62L8m93I.
- The title should be short and descriptive.
- Explain the conditions which led you to report this issue: the context.
- The context should lead to something, an idea or a problem that youre facing.
- Remain clear and concise.
- Format your messages to help the reader focus on what matters and understand the structure of your message, use Markdown syntax https://help.github.com/articles/github-flavored-markdown
-->

View File

@@ -12,12 +12,25 @@ HOW TO WRITE A GOOD PULL REQUEST?
- Write useful descriptions and titles.
- Address review comments in terms of additional commits.
- Do not amend/squash existing ones unless the PR is trivial.
- Read the contributing guide: https://github.com/containous/traefik/blob/master/.github/CONTRIBUTING.md.
- Read the contributing guide: https://github.com/traefik/traefik/blob/master/CONTRIBUTING.md.
-->
### Description
### What does this PR do?
<!--
Briefly describe the pull request in a few paragraphs.
-->
<!-- A brief description of the change being made with this pull request. -->
### Motivation
<!-- What inspired you to submit this pull request? -->
### More
- [ ] Added/updated tests
- [ ] Added/updated documentation
### Additional Notes
<!-- Anything else we should know when reviewing? -->

View File

@@ -0,0 +1,7 @@
### What does this PR do?
Merge v{{.Version}} into master
### Motivation
Be sync.

View File

@@ -0,0 +1,7 @@
### What does this PR do?
Prepare release v{{.Version}}.
### Motivation
Create a new release.

26
.github/cpr.sh vendored
View File

@@ -1,26 +0,0 @@
#!/bin/sh
#
# git config --global alias.cpr '!sh .github/cpr.sh'
set -e # stop on error
usage="$(basename "$0") pr -- Checkout a Pull Request locally"
if [ "$#" -ne 1 ]; then
echo "Illegal number of parameters"
echo "$usage" >&2
exit 1
fi
command -v jq >/dev/null 2>&1 || { echo "I require jq but it's not installed. Aborting." >&2; exit 1; }
set -x # echo on
initial=$(git rev-parse --abbrev-ref HEAD)
pr=$1
remote=$(curl -s https://api.github.com/repos/containous/traefik/pulls/$pr | jq -r .head.repo.owner.login)
branch=$(curl -s https://api.github.com/repos/containous/traefik/pulls/$pr | jq -r .head.ref)
git remote add $remote git@github.com:$remote/traefik.git
git fetch $remote $branch
git checkout -t -b "$pr--$branch" $remote/$branch

27
.github/rmpr.sh vendored
View File

@@ -1,27 +0,0 @@
#!/bin/sh
#
# git config --global alias.rmpr '!sh .github/rmpr.sh'
set -e # stop on error
usage="$(basename "$0") pr -- remove a Pull Request local branch & remote"
if [ "$#" -ne 1 ]; then
echo "Illegal number of parameters"
echo "$usage" >&2
exit 1
fi
command -v jq >/dev/null 2>&1 || { echo "I require jq but it's not installed. Aborting." >&2; exit 1; }
set -x # echo on
initial=$(git rev-parse --abbrev-ref HEAD)
pr=$1
remote=$(curl -s https://api.github.com/repos/containous/traefik/pulls/$pr | jq -r .head.repo.owner.login)
branch=$(curl -s https://api.github.com/repos/containous/traefik/pulls/$pr | jq -r .head.ref)
# clean
git checkout $initial
git branch -D "$pr--$branch"
git remote remove $remote

36
.github/rpr.sh vendored
View File

@@ -1,36 +0,0 @@
#!/bin/sh
#
# git config --global alias.rpr '!sh .github/rpr.sh'
set -e # stop on error
usage="$(basename "$0") pr remote/branch -- rebase a Pull Request against a remote branch"
if [ "$#" -ne 2 ]; then
echo "Illegal number of parameters"
echo "$usage" >&2
exit 1
fi
command -v jq >/dev/null 2>&1 || { echo "I require jq but it's not installed. Aborting." >&2; exit 1; }
set -x # echo on
initial=$(git rev-parse --abbrev-ref HEAD)
pr=$1
base=$2
remote=$(curl -s https://api.github.com/repos/containous/traefik/pulls/$pr | jq -r .head.repo.owner.login)
branch=$(curl -s https://api.github.com/repos/containous/traefik/pulls/$pr | jq -r .head.ref)
clean ()
{
git checkout $initial
.github/rmpr.sh $pr
}
trap clean EXIT
.github/cpr.sh $pr
git rebase $base
git push --force-with-lease $remote "$pr--$branch"

95
.github/workflows/build.yaml vendored Normal file
View File

@@ -0,0 +1,95 @@
name: Build Binaries
on:
pull_request:
branches:
- '*'
env:
GO_VERSION: 1.16
CGO_ENABLED: 0
PRE_TARGET: ""
jobs:
build-webui:
runs-on: ubuntu-20.04
steps:
- name: Check out code
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Build webui
env:
DOCKER_RUN_TRAEFIK: ""
run: |
make generate-webui
tar czvf webui.tar.gz ./static/
- name: Artifact webui
uses: actions/upload-artifact@v2
with:
name: webui.tar.gz
path: webui.tar.gz
build:
runs-on: ubuntu-20.04
needs:
- build-webui
defaults:
run:
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
steps:
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v2
with:
go-version: ${{ env.GO_VERSION }}
- name: Check out code
uses: actions/checkout@v2
with:
path: go/src/github.com/traefik/traefik
fetch-depth: 0
- name: Cache Go modules
uses: actions/cache@v2
with:
path: |
~/go/pkg/mod
~/.cache/go-build
~/Library/Caches/go-build
'%LocalAppData%\go-build'
key: ${{ runner.os }}-build-go-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-build-go-
- name: Install gobindata
run: |
curl -fsSL -o $(go env GOPATH)/bin/go-bindata https://github.com/containous/go-bindata/releases/download/v1.0.0/go-bindata
chmod +x $(go env GOPATH)/bin/go-bindata
- name: Artifact webui
uses: actions/download-artifact@v2
with:
name: webui.tar.gz
path: ${{ github.workspace }}/go/src/github.com/traefik/traefik
- name: Untar webui
run: tar xvf webui.tar.gz
- name: Build for darwin
env:
DOCKER_RUN_TRAEFIK: ""
run: GOOS=darwin GOARCH=amd64 make binary
- name: Build for linux
env:
DOCKER_RUN_TRAEFIK: ""
run: GOOS=linux GOARCH=amd64 make binary
- name: Build for windows
env:
DOCKER_RUN_TRAEFIK: ""
run: GOOS=windows GOARCH=amd64 make binary

21
.github/workflows/check_doc.yaml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: Check Documentation
on:
pull_request:
branches:
- '*'
jobs:
docs:
name: Check, verify and build documentation
runs-on: ubuntu-20.04
steps:
- name: Check out code
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Check documentation
run: make docs-verify

52
.github/workflows/documentation.yaml vendored Normal file
View File

@@ -0,0 +1,52 @@
name: Build and Publish Documentation
on:
push:
branches:
- master
- v*
env:
STRUCTOR_VERSION: v1.11.2
MIXTUS_VERSION: v0.4.1
jobs:
docs:
name: Doc Process
runs-on: ubuntu-20.04
if: github.repository == 'traefik/traefik'
steps:
- name: Check out code
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Install Structor ${{ env.STRUCTOR_VERSION }}
run: curl -sSfL https://raw.githubusercontent.com/traefik/structor/master/godownloader.sh | sh -s -- -b $HOME/bin ${STRUCTOR_VERSION}
- name: Install Seo-doc
run: curl -sSfL https://raw.githubusercontent.com/traefik/seo-doc/master/godownloader.sh | sh -s -- -b "${HOME}/bin"
- name: Install Mixtus ${{ env.MIXTUS_VERSION }}
run: curl -sSfL https://raw.githubusercontent.com/traefik/mixtus/master/godownloader.sh | sh -s -- -b $HOME/bin ${MIXTUS_VERSION}
- name: Build documentation
run: $HOME/bin/structor -o traefik -r traefik --dockerfile-url="https://raw.githubusercontent.com/traefik/traefik/v1.7/docs.Dockerfile" --menu.js-url="https://raw.githubusercontent.com/traefik/structor/master/traefik-menu.js.gotmpl" --rqts-url="https://raw.githubusercontent.com/traefik/structor/master/requirements-override.txt" --force-edit-url --exp-branch=master --debug
env:
STRUCTOR_LATEST_TAG: ${{ secrets.STRUCTOR_LATEST_TAG }}
- name: Apply seo
run: $HOME/bin/seo -path=./site
- 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=traefik --src-repo-name=traefik
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_REPO }}

37
.github/workflows/experimental.yaml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: Build experimental image on branch
on:
push:
branches:
- master
- v*
jobs:
experimental:
if: github.repository == 'traefik/traefik'
name: Build experimental image on branch
runs-on: ubuntu-20.04
steps:
# https://github.com/marketplace/actions/checkout
- name: Check out code
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Branch name
run: echo ${GITHUB_REF##*/}
- name: Build docker experimental image
run: docker build -t traefik/traefik:experimental-${GITHUB_REF##*/} -f exp.Dockerfile .
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Push to Docker Hub
run: docker push traefik/traefik:experimental-${GITHUB_REF##*/}

53
.github/workflows/test-unit.yaml vendored Normal file
View File

@@ -0,0 +1,53 @@
name: Test Unit
on:
pull_request:
branches:
- '*'
env:
GO_VERSION: 1.16
PRE_TARGET: ""
jobs:
test-unit:
runs-on: ubuntu-20.04
defaults:
run:
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
steps:
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v2
with:
go-version: ${{ env.GO_VERSION }}
- name: Check out code
uses: actions/checkout@v2
with:
path: go/src/github.com/traefik/traefik
fetch-depth: 0
- name: Cache Go modules
uses: actions/cache@v2
with:
path: |
~/go/pkg/mod
~/.cache/go-build
key: ${{ runner.os }}-test-unit-go-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-test-unit-go-
- name: Install gobindata
run: |
curl -fsSL -o $(go env GOPATH)/bin/go-bindata https://github.com/containous/go-bindata/releases/download/v1.0.0/go-bindata
chmod +x $(go env GOPATH)/bin/go-bindata
- name: Avoid generating webui
run: mkdir -p webui/static && touch webui/static/index.html
- name: Tests
env:
DOCKER_RUN_TRAEFIK: ""
run: make test-unit

102
.github/workflows/validate.yaml vendored Normal file
View File

@@ -0,0 +1,102 @@
name: Validate
on:
pull_request:
branches:
- '*'
env:
GO_VERSION: 1.16
MISSSPELL_VERSION: v0.3.4
PRE_TARGET: ""
jobs:
validate:
runs-on: ubuntu-20.04
defaults:
run:
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
steps:
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v2
with:
go-version: ${{ env.GO_VERSION }}
- name: Check out code
uses: actions/checkout@v2
with:
path: go/src/github.com/traefik/traefik
fetch-depth: 0
- name: Cache Go modules
uses: actions/cache@v2
with:
path: |
~/go/pkg/mod
~/.cache/go-build
key: ${{ runner.os }}-validate-go-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-validate-go-
- name: Install golint
run: go install golang.org/x/lint/golint@latest
- name: Install missspell ${{ env.MISSSPELL_VERSION }}
run: curl -sfL https://raw.githubusercontent.com/client9/misspell/master/install-misspell.sh | sh -s -- -b $(go env GOPATH)/bin ${MISSSPELL_VERSION}
- name: Install gobindata
run: |
curl -fsSL -o $(go env GOPATH)/bin/go-bindata https://github.com/containous/go-bindata/releases/download/v1.0.0/go-bindata
chmod +x $(go env GOPATH)/bin/go-bindata
- name: Avoid generating webui
run: mkdir -p webui/static && touch webui/static/index.html
- name: Validate
env:
DOCKER_RUN_TRAEFIK: ""
run: make validate
validate-generate:
runs-on: ubuntu-20.04
defaults:
run:
working-directory: ${{ github.workspace }}/go/src/github.com/traefik/traefik
steps:
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v2
with:
go-version: ${{ env.GO_VERSION }}
- name: Check out code
uses: actions/checkout@v2
with:
path: go/src/github.com/traefik/traefik
fetch-depth: 0
- name: Cache Go modules
uses: actions/cache@v2
with:
path: |
~/go/pkg/mod
~/.cache/go-build
key: ${{ runner.os }}-validate-generate-go-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-validate-generate-go-
- name: Install gobindata
run: |
curl -fsSL -o $(go env GOPATH)/bin/go-bindata https://github.com/containous/go-bindata/releases/download/v1.0.0/go-bindata
chmod +x $(go env GOPATH)/bin/go-bindata
- name: go generate
run: |
go generate
git diff --exit-code
- name: go mod tidy
run: |
go mod tidy
git diff --exit-code

24
.gitignore vendored
View File

@@ -1,13 +1,21 @@
/dist
/autogen/gen.go
.idea
.intellij
.idea/
.intellij/
*.iml
/traefik
/traefik.toml
/static/
.vscode/
.DS_Store
/static/
/autogen/genstatic/gen.go
/webui/.tmp/
/examples/acme/acme.json
/site/
/docs/site/
/traefik.toml
/traefik.yml
/dist
/traefik
*.log
*.exe
.DS_Store
cover.out
vendor/
plugins-storage/
traefik_changelog.md

106
.semaphore/semaphore.yml Normal file
View File

@@ -0,0 +1,106 @@
version: v1.0
name: Traefik
agent:
machine:
type: e1-standard-4
os_image: ubuntu1804
fail_fast:
stop:
when: "branch != 'master'"
auto_cancel:
queued:
when: "branch != 'master'"
running:
when: "branch != 'master'"
global_job_config:
prologue:
commands:
- curl -sSfL https://raw.githubusercontent.com/ldez/semgo/master/godownloader.sh | sudo sh -s -- -b "/usr/local/bin"
- sudo semgo go1.16
- export "GOPATH=$(go env GOPATH)"
- export "GOROOT=$(go env GOROOT)"
- export "SEMAPHORE_GIT_DIR=${GOPATH}/src/github.com/traefik/${SEMAPHORE_PROJECT_NAME}"
- export "PATH=${GOPATH}/bin:${GOROOT}/bin:${PATH}"
- mkdir -vp "${SEMAPHORE_GIT_DIR}" "${GOPATH}/bin"
- curl -fsSL -o ${GOPATH}/bin/go-bindata https://github.com/containous/go-bindata/releases/download/v1.0.0/go-bindata
- chmod +x ${GOPATH}/bin/go-bindata
- export GOPROXY=https://proxy.golang.org,direct
- checkout
- cache restore traefik-$(checksum go.sum)
blocks:
- name: Test Integration Container
dependencies: []
run:
when: "branch =~ '.*' OR pull_request =~'.*'"
task:
env_vars:
- name: DOCKER_RUN_TRAEFIK
value: ""
- name: TEST_CONTAINER
value: "1"
jobs:
- name: Test Integration Container
commands:
- make pull-images
- mkdir -p webui/static && touch webui/static/index.html # Avoid generating webui
- make binary-with-no-ui
- sudo CONTAINER=DOCKER DOCKER_RUN_TRAEFIK="" TEST_CONTAINER=1 make test-integration-container
- df -h
epilogue:
always:
commands:
- cache store traefik-$(checksum go.sum) $HOME/go/pkg/mod
- name: Test Integration Host
dependencies: []
run:
when: "branch =~ '.*' OR pull_request =~'.*'"
task:
env_vars:
- name: DOCKER_RUN_TRAEFIK
value: ""
jobs:
- name: Test Integration Host
commands:
- mkdir -p webui/static && touch webui/static/index.html # Avoid generating webui
- make binary-with-no-ui
- sudo DOCKER_RUN_TRAEFIK="" TEST_HOST=1 make test-integration-host
epilogue:
always:
commands:
- cache store traefik-$(checksum go.sum) $HOME/go/pkg/mod
- name: Release
dependencies: []
run:
when: "tag =~ '.*'"
task:
agent:
machine:
type: e1-standard-8
os_image: ubuntu1804
secrets:
- name: traefik
env_vars:
- name: GH_VERSION
value: 1.12.1
- name: CODENAME
value: "maroilles"
- name: DOCKER_RUN_TRAEFIK
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
jobs:
- name: Release
commands:
- make crossbinary-parallel
- 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

View File

@@ -1,11 +0,0 @@
#!/usr/bin/env bash
set -e
sudo -E apt-get -yq update
sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install docker-ce=${DOCKER_VERSION}*
docker version
pip install --user -r requirements.txt
make pull-images
ci_retry make validate

View File

@@ -1,6 +0,0 @@
#!/usr/bin/env bash
set -e
make test-unit
ci_retry make test-integration
make -j${N_MAKE_JOBS} crossbinary-default-parallel

View File

@@ -1,39 +0,0 @@
#!/usr/bin/env bash
set -e
export secure='btt4r13t09gQlHb6gYrvGC2yGCMMHfnp1Mz1RQedc4Mpf/FfT8aE6xmK2a2i9CCvskjrP0t/BFaS4yxIURjnFRn+ugQIEa0pLspB9UJArW/vgOSpIWM9/OQ/fg8z5XuMxN6Md4DL1/iLypMNSageA1x0TRdt89+D1N1dALpg5XRCXLFbC84TLi0gjlFuib9ibPKzEhLT+anCRJ6iZMzeupDSoaCVbAtJMoDvXw4+4AcRZ1+k4MybBLyCib5boaEOt4pTT88mz4Kk0YaMwPVJyg9Qv36VqyUcPS09Yd95LuyVQ4+tZt8Y1ccbIzULsK+sLM3hLCzxlmlpN3dQBlZJiiRtQde0mgGAKyC0P0A1XjuDTywcsa5edB+fTk1Dsewz9xZ9V0NmMz8t+UNZnaSsAPga9i86jULbXUUwMVSzVRc+Xgx02liB/8qI1xYC9FM6ilStt7rn7mF0k3KbiWhcptgeXjO6Lah9FjEKd5w4MXsdUSTi/86rQaLo+kj+XdaTrXCTulKHyRyQEUj+8V1w0oVz7pcGjePHd7y5oU9ByifVQy6sytuFBfRZvugM5bKHo+i0pcWvixrZS42DrzwxZJsspANOvqSe5ifVbvOkfUppQdCBIwptxV5N1b49XPKU3W/w34QJ8xGmKp3TFA7WwVCztriFHjPgiRpB3EG99Bg='
export REPO='containous/traefik'
if VERSION=$(git describe --exact-match --abbrev=0 --tags);
then
export VERSION
else
export VERSION=''
fi
export CODENAME=raclette
export N_MAKE_JOBS=2
function ci_retry {
local NRETRY=3
local NSLEEP=5
local n=0
until [ $n -ge $NRETRY ]
do
"$@" && break
n=$[$n+1]
echo "$@ failed, attempt ${n}/${NRETRY}"
sleep $NSLEEP
done
[ $n -lt $NRETRY ]
}
export -f ci_retry

View File

@@ -1,58 +0,0 @@
sudo: required
dist: trusty
services:
- docker
env:
global:
- secure: btt4r13t09gQlHb6gYrvGC2yGCMMHfnp1Mz1RQedc4Mpf/FfT8aE6xmK2a2i9CCvskjrP0t/BFaS4yxIURjnFRn+ugQIEa0pLspB9UJArW/vgOSpIWM9/OQ/fg8z5XuMxN6Md4DL1/iLypMNSageA1x0TRdt89+D1N1dALpg5XRCXLFbC84TLi0gjlFuib9ibPKzEhLT+anCRJ6iZMzeupDSoaCVbAtJMoDvXw4+4AcRZ1+k4MybBLyCib5boaEOt4pTT88mz4Kk0YaMwPVJyg9Qv36VqyUcPS09Yd95LuyVQ4+tZt8Y1ccbIzULsK+sLM3hLCzxlmlpN3dQBlZJiiRtQde0mgGAKyC0P0A1XjuDTywcsa5edB+fTk1Dsewz9xZ9V0NmMz8t+UNZnaSsAPga9i86jULbXUUwMVSzVRc+Xgx02liB/8qI1xYC9FM6ilStt7rn7mF0k3KbiWhcptgeXjO6Lah9FjEKd5w4MXsdUSTi/86rQaLo+kj+XdaTrXCTulKHyRyQEUj+8V1w0oVz7pcGjePHd7y5oU9ByifVQy6sytuFBfRZvugM5bKHo+i0pcWvixrZS42DrzwxZJsspANOvqSe5ifVbvOkfUppQdCBIwptxV5N1b49XPKU3W/w34QJ8xGmKp3TFA7WwVCztriFHjPgiRpB3EG99Bg=
- REPO: $TRAVIS_REPO_SLUG
- VERSION: $TRAVIS_TAG
- CODENAME: raclette
- N_MAKE_JOBS: 2
script:
- echo "Skipping tests... (Tests are executed on SemaphoreCI)"
before_deploy:
- >
if ! [ "$BEFORE_DEPLOY_RUN" ]; then
export BEFORE_DEPLOY_RUN=1;
sudo -E apt-get -yq update;
sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install docker-ce=${DOCKER_VERSION}*;
docker version;
pip install --user -r requirements.txt;
make -j${N_MAKE_JOBS} crossbinary-parallel;
make image;
mkdocs build --clean;
tar cfz dist/traefik-${VERSION}.src.tar.gz --exclude-vcs --exclude dist .;
fi
deploy:
- provider: pages
edge: true
github_token: ${GITHUB_TOKEN}
local_dir: site
skip_cleanup: true
on:
repo: containous/traefik
tags: true
- provider: releases
api_key: ${GITHUB_TOKEN}
file: dist/traefik*
skip_cleanup: true
file_glob: true
on:
repo: containous/traefik
tags: true
- provider: script
script: sh script/deploy.sh
skip_cleanup: true
on:
repo: containous/traefik
tags: true
- provider: script
script: sh script/deploy-docker.sh
skip_cleanup: true
on:
repo: containous/traefik

Binary file not shown.

BIN
.travis/traefiker_rsa.enc Normal file

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at contact@containo.us
reported by contacting the project team at contact@traefik.io
All complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.

297
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,297 @@
# Contributing
## Building
You need either [Docker](https://github.com/docker/docker) and `make` (Method 1), or `go` (Method 2) in order to build Traefik.
For changes to its dependencies, the `dep` dependency management tool is required.
### Method 1: Using `Docker` and `Makefile`
You need to run the `binary` target. This will create binaries for Linux platform in the `dist` folder.
```bash
$ make binary
docker build -t "traefik-dev:no-more-godep-ever" -f build.Dockerfile .
Sending build context to Docker daemon 295.3 MB
Step 0 : FROM golang:1.14-alpine
---> 8c6473912976
Step 1 : RUN go get github.com/golang/dep/cmd/dep
[...]
docker run --rm -v "/var/run/docker.sock:/var/run/docker.sock" -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -v "/home/user/go/src/github.com/traefik/traefik/"dist":/go/src/github.com/traefik/traefik/"dist"" "traefik-dev:no-more-godep-ever" ./script/make.sh generate binary
---> Making bundle: generate (in .)
removed 'gen.go'
---> Making bundle: binary (in .)
$ ls dist/
traefik*
```
### Method 2: Using `go`
##### Setting up your `go` environment
- You need `go` v1.9+
- It is recommended you clone Traefik into a directory like `~/go/src/github.com/traefik/traefik` (This is the official golang workspace hierarchy, and will allow dependencies to resolve properly)
- Set your `GOPATH` and `PATH` variable to be set to `~/go` via:
```bash
export GOPATH=~/go
export PATH=$PATH:$GOPATH/bin
```
> Note: You will want to add those 2 export lines to your `.bashrc` or `.bash_profile`
- Verify your environment is setup properly by running `$ go env`. Depending on your OS and environment you should see output similar to:
```bash
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/<yourusername>/go"
GORACE=""
## more go env's will be listed
```
##### Build Traefik
Once your environment is set up and the Traefik repository cloned you can build Traefik. You need get `go-bindata` once to be able to use `go generate` command as part of the build. The steps to build are:
```bash
cd ~/go/src/github.com/traefik/traefik
# Get go-bindata. Please note, the ellipses are required
go get github.com/containous/go-bindata/...
# Start build
# generate
# (required to merge non-code components into the final binary, such as the web dashboard and provider's Go templates)
go generate
# Standard go build
go build ./cmd/traefik
# run other commands like tests
```
You will find the Traefik executable in the `~/go/src/github.com/traefik/traefik` folder as `traefik`.
### Updating the templates
If you happen to update the provider templates (in `/templates`), you need to run `go generate` to update the `autogen` package.
### Setting up dependency management
[dep](https://github.com/golang/dep) is not required for building; however, it is necessary to modify dependencies (i.e., add, update, or remove third-party packages)
You need to use [dep](https://github.com/golang/dep) >= 0.5.0.
If you want to add a dependency, use `dep ensure -add` to have [dep](https://github.com/golang/dep) put it into the vendor folder and update the dep manifest/lock files (`Gopkg.toml` and `Gopkg.lock`, respectively).
A following `make dep-prune` run should be triggered to trim down the size of the vendor folder.
The final result must be committed into VCS.
Here's a full example using dep to add a new dependency:
```bash
# install the new main dependency github.com/foo/bar and minimize vendor size
$ dep ensure -add github.com/foo/bar
# generate (Only required to integrate other components such as web dashboard)
$ go generate
# Standard go build
$ go build ./cmd/traefik
# run other commands like tests
```
### Tests
#### Method 1: `Docker` and `make`
You can run unit tests using the `test-unit` target and the
integration test using the `test-integration` target.
```bash
$ make test-unit
docker build -t "traefik-dev:your-feature-branch" -f build.Dockerfile .
# […]
docker run --rm -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -v "/home/user/go/src/github/traefik/traefik/dist:/go/src/github.com/traefik/traefik/dist" "traefik-dev:your-feature-branch" ./script/make.sh generate test-unit
---> Making bundle: generate (in .)
removed 'gen.go'
---> Making bundle: test-unit (in .)
+ go test -cover -coverprofile=cover.out .
ok github.com/traefik/traefik 0.005s coverage: 4.1% of statements
Test success
```
For development purposes, you can specify which tests to run by using:
```bash
# Run every tests in the MyTest suite
TESTFLAGS="-check.f MyTestSuite" make test-integration
# Run the test "MyTest" in the MyTest suite
TESTFLAGS="-check.f MyTestSuite.MyTest" make test-integration
# Run every tests starting with "My", in the MyTest suite
TESTFLAGS="-check.f MyTestSuite.My" make test-integration
# Run every tests ending with "Test", in the MyTest suite
TESTFLAGS="-check.f MyTestSuite.*Test" make test-integration
```
More: https://labix.org/gocheck
#### Method 2: `go`
Unit tests can be run from the cloned directory by `$ go test ./...` which should return `ok` similar to:
```
ok _/home/user/go/src/github/traefik/traefik 0.004s
```
Integration tests must be run from the `integration/` directory and require the `-integration` switch to be passed like this: `$ cd integration && go test -integration ./...`.
## Documentation
The [documentation site](https://doc.traefik.io/traefik/v1.7/) is built with [mkdocs](https://mkdocs.org/)
### Building Documentation
#### Method 1: `Docker` and `make`
You can build the documentation and serve it locally with livereloading, using the `docs` target:
```bash
$ make docs
docker build -t traefik-docs -f docs.Dockerfile .
# […]
docker run --rm -v /home/user/go/github/traefik/traefik:/mkdocs -p 8000:8000 traefik-docs mkdocs serve
# […]
[I 170828 20:47:48 server:283] Serving on http://0.0.0.0:8000
[I 170828 20:47:48 handlers:60] Start watching changes
[I 170828 20:47:48 handlers:62] Start detecting changes
```
And go to [http://127.0.0.1:8000](http://127.0.0.1:8000).
If you only want to build the documentation without serving it locally, you can use the following command:
```bash
$ make docs-build
...
```
#### Method 2: `mkdocs`
First make sure you have python and pip installed
```bash
$ python --version
Python 2.7.2
$ pip --version
pip 1.5.2
```
Then install mkdocs with pip
```bash
pip install --user -r requirements.txt
```
To build documentation locally and serve it locally,
run `mkdocs serve` in the root directory,
this should start a server locally to preview your changes.
```bash
$ mkdocs serve
INFO - Building documentation...
INFO - Cleaning site directory
[I 160505 22:31:24 server:281] Serving on http://127.0.0.1:8000
[I 160505 22:31:24 handlers:59] Start watching changes
[I 160505 22:31:24 handlers:61] Start detecting changes
```
### Verify Documentation
You can verify that the documentation meets some expectations, as checking for dead links, html markup validity.
```bash
$ make docs-verify
docker build -t traefik-docs-verify ./script/docs-verify-docker-image ## Build Validator image
...
docker run --rm -v /home/travis/build/traefik/traefik:/app traefik-docs-verify ## Check for dead links and w3c compliance
=== Checking HTML content...
Running ["HtmlCheck", "ImageCheck", "ScriptCheck", "LinkCheck"] on /app/site/basics/index.html on *.html...
```
If you recently changed the documentation, do not forget to clean it to have it rebuilt:
```bash
$ make docs-clean docs-verify
...
```
Please note that verification can be disabled by setting the environment variable `DOCS_VERIFY_SKIP` to `true`:
```shell
DOCS_VERIFY_SKIP=true make docs-verify
...
DOCS_LINT_SKIP is true: no linting done.
```
## How to Write a Good Issue
Please keep in mind that the GitHub issue tracker is not intended as a general support forum, but for reporting bugs and feature requests.
For end-user related support questions, refer to one of the following:
- the Traefik community Slack channel: [![Join the chat at https://slack.traefik.io](https://img.shields.io/badge/style-register-green.svg?style=social&label=Slack)](https://slack.traefik.io)
- [Stack Overflow](https://stackoverflow.com/questions/tagged/traefik) (using the `traefik` tag)
### Title
The title must be short and descriptive. (~60 characters)
### Description
- Respect the issue template as much as possible. [template](.github/ISSUE_TEMPLATE.md)
- If it's possible use the command `traefik bug`. See https://www.youtube.com/watch?v=Lyz62L8m93I.
- Explain the conditions which led you to write this issue: the context.
- The context should lead to something, an idea or a problem that youre facing.
- Remain clear and concise.
- Format your messages to help the reader focus on what matters and understand the structure of your message, use [Markdown syntax](https://help.github.com/articles/github-flavored-markdown)
## How to Write a Good Pull Request
### Title
The title must be short and descriptive. (~60 characters)
### Description
- Respect the pull request template as much as possible. [template](.github/PULL_REQUEST_TEMPLATE.md)
- Explain the conditions which led you to write this PR: the context.
- The context should lead to something, an idea or a problem that youre facing.
- Remain clear and concise.
- Format your messages to help the reader focus on what matters and understand the structure of your message, use [Markdown syntax](https://help.github.com/articles/github-flavored-markdown)
### Content
- Make it small.
- Do only one thing.
- Write useful descriptions and titles.
- Avoid re-formatting.
- Make sure the code builds.
- Make sure all tests pass.
- Add tests.
- Address review comments in terms of additional commits.
- Do not amend/squash existing ones unless the PR is trivial.
- If a PR involves changes to third-party dependencies, the commits pertaining to the vendor folder and the manifest/lock file(s) should be committed separated.
Read [10 tips for better pull requests](http://blog.ploeh.dk/2015/01/15/10-tips-for-better-pull-requests/).

View File

@@ -2,4 +2,5 @@ FROM scratch
COPY script/ca-certificates.crt /etc/ssl/certs/
COPY dist/traefik /
EXPOSE 80
VOLUME ["/tmp"]
ENTRYPOINT ["/traefik"]

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2016-2017 Containous SAS
Copyright (c) 2016-2018 Containous SAS; 2020-2022 Traefik Labs
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

155
MAINTAINER.md Normal file
View File

@@ -0,0 +1,155 @@
# Maintainers
## The team
* Emile Vauge [@emilevauge](https://github.com/emilevauge)
* Vincent Demeester [@vdemeester](https://github.com/vdemeester)
* Ed Robinson [@errm](https://github.com/errm)
* Daniel Tomcej [@dtomcej](https://github.com/dtomcej)
* Manuel Zapf [@SantoDE](https://github.com/SantoDE)
* Timo Reimann [@timoreimann](https://github.com/timoreimann)
* Ludovic Fernandez [@ldez](https://github.com/ldez)
* Julien Salleyron [@juliens](https://github.com/juliens)
* Nicolas Mengin [@nmengin](https://github.com/nmengin)
* Marco Jantke [@marco-jantke](https://github.com/marco-jantke)
* Michaël Matur [@mmatur](https://github.com/mmatur)
## PR review process:
* The status `needs-design-review` is only used in complex/heavy/tricky PRs.
* From `1` to `2`: 1 comment that says “design LGTM” (by a senior maintainer).
* From `2` to `3`: 3 LGTM approvals by any maintainer.
* If needed, a specific maintainer familiar with a particular domain can be requested for the review.
We use [PRM](https://github.com/ldez/prm) to manage locally pull requests.
## Bots
### [Myrmica Lobicornis](https://github.com/traefik/lobicornis/)
**Update and Merge Pull Request**
The maintainer giving the final LGTM must add the `status/3-needs-merge` label to trigger the merge bot.
By default, a squash-rebase merge will be carried out.
To preserve commits, add `bot/merge-method-rebase` before `status/3-needs-merge`.
The status `status/4-merge-in-progress` is only used by the bot.
If the bot is not able to perform the merge, the label `bot/need-human-merge` is added.
In such a situation, solve the conflicts/CI/... and then remove the label `bot/need-human-merge`.
To prevent the bot from automatically merging a PR, add the label `bot/no-merge`.
The label `bot/light-review` decreases the number of required LGTM from 3 to 1.
This label is used when:
- Updating the vendors from previously reviewed PRs
- Merging branches into the master
- Preparing the release
### [Myrmica Bibikoffi](https://github.com/traefik/bibikoffi/)
* closes stale issues [cron]
* use some criterion as number of days between creation, last update, labels, ...
### [Myrmica Aloba](https://github.com/traefik/aloba)
**Manage GitHub labels**
* Add labels on new PR [GitHub WebHook]
* Add milestone to a new PR based on a branch version (1.4, 1.3, ...) [GitHub WebHook]
* Add and remove `contributor/waiting-for-corrections` label when a review request changes [GitHub WebHook]
* Weekly report of PR status on Slack (CaptainPR) [cron]
## Labels
A maintainer that looks at an issue/PR must define its `kind/*`, `area/*`, and `status/*`.
### Contributor
* `contributor/need-more-information`: we need more information from the contributor in order to analyze a problem.
* `contributor/waiting-for-feedback`: we need the contributor to give us feedback.
* `contributor/waiting-for-corrections`: we need the contributor to take actions in order to move forward with a PR. **(only for PR)** _[bot, humans]_
* `contributor/needs-resolve-conflicts`: use it only when there is some conflicts (and an automatic rebase is not possible). **(only for PR)** _[bot, humans]_
### Kind
* `kind/enhancement`: a new or improved feature.
* `kind/question`: 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/oxy`: Oxy related.
* `area/provider`: related to all providers.
* `area/provider/boltdb`: Boltd DB related.
* `area/provider/consul`: Consul related.
* `area/provider/docker`: Docker and Swarm related.
* `area/provider/ecs`: ECS related.
* `area/provider/etcd`: Etcd related.
* `area/provider/eureka`: Eureka related.
* `area/provider/file`: file provider related.
* `area/provider/k8s`: Kubernetes related.
* `area/provider/marathon`: Marathon related.
* `area/provider/mesos`: Mesos related.
* `area/provider/rancher`: Rancher related.
* `area/provider/zk`: Zoo Keeper related.
* `area/sticky-session`: Sticky session related.
* `area/tls`: TLS related.
* `area/websocket`: WebSocket related.
* `area/webui`: Web UI related.
### Priority
* `priority/P0`: needs a hot fix. **(only for issue)**
* `priority/P1`: needs to be fixed the next release. **(only for issue)**
* `priority/P2`: needs to be fixed in the future. **(only for issue)**
* `priority/P3`: maybe. **(only for issue)**
### PR size
* `size/S`: small PR. **(only for PR)** _[bot only]_
* `size/M`: medium PR. **(only for PR)** _[bot only]_
* `size/L`: large PR. **(only for PR)** _[bot only]_
### Status - Workflow
The `status/*` labels represent the desired state in the workflow.
* `status/0-needs-triage`: all 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]_

View File

@@ -1,4 +1,4 @@
.PHONY: all
.PHONY: all docs-verify docs docs-clean docs-build
TRAEFIK_ENVS := \
-e OS_ARCH_ARG \
@@ -7,23 +7,31 @@ TRAEFIK_ENVS := \
-e VERBOSE \
-e VERSION \
-e CODENAME \
-e TESTDIRS
-e TESTDIRS \
-e CI \
-e CONTAINER=DOCKER # Indicator for integration tests that we are running inside a container.
SRCS = $(shell git ls-files '*.go' | grep -v '^vendor/' | grep -v '^integration/vendor/')
SRCS = $(shell git ls-files '*.go' | grep -v '^vendor/')
BIND_DIR := "dist"
TRAEFIK_MOUNT := -v "$(CURDIR)/$(BIND_DIR):/go/src/github.com/containous/traefik/$(BIND_DIR)"
TRAEFIK_MOUNT := -v "$(CURDIR)/$(BIND_DIR):/go/src/github.com/traefik/traefik/$(BIND_DIR)"
GIT_BRANCH := $(subst heads/,,$(shell git rev-parse --abbrev-ref HEAD 2>/dev/null))
TRAEFIK_DEV_IMAGE := traefik-dev$(if $(GIT_BRANCH),:$(GIT_BRANCH))
TRAEFIK_DEV_IMAGE := traefik-dev$(if $(GIT_BRANCH),:$(subst /,-,$(GIT_BRANCH)))
REPONAME := $(shell echo $(REPO) | tr '[:upper:]' '[:lower:]')
TRAEFIK_IMAGE := $(if $(REPONAME),$(REPONAME),"containous/traefik")
INTEGRATION_OPTS := $(if $(MAKE_DOCKER_HOST),-e "DOCKER_HOST=$(MAKE_DOCKER_HOST)", -v "/var/run/docker.sock:/var/run/docker.sock")
TRAEFIK_IMAGE := $(if $(REPONAME),$(REPONAME),"traefik/traefik")
INTEGRATION_OPTS := $(if $(MAKE_DOCKER_HOST),-e "DOCKER_HOST=$(MAKE_DOCKER_HOST)", -e "TEST_CONTAINER=1" -v "/var/run/docker.sock:/var/run/docker.sock")
TRAEFIK_DOC_IMAGE := traefik-docs
TRAEFIK_DOC_VERIFY_IMAGE := $(TRAEFIK_DOC_IMAGE)-verify
DOCS_VERIFY_SKIP ?= false
DOCKER_BUILD_ARGS := $(if $(DOCKER_VERSION), "--build-arg=DOCKER_VERSION=$(DOCKER_VERSION)",)
DOCKER_RUN_OPTS := $(TRAEFIK_ENVS) $(TRAEFIK_MOUNT) "$(TRAEFIK_DEV_IMAGE)"
DOCKER_RUN_TRAEFIK := docker run $(INTEGRATION_OPTS) -it $(DOCKER_RUN_OPTS)
DOCKER_RUN_TRAEFIK ?= docker run $(INTEGRATION_OPTS) -it $(DOCKER_RUN_OPTS)
DOCKER_RUN_TRAEFIK_NOTTY := docker run $(INTEGRATION_OPTS) -i $(DOCKER_RUN_OPTS)
DOCKER_RUN_DOC_PORT := 8000
DOCKER_RUN_DOC_MOUNT := -v $(CURDIR):/mkdocs
DOCKER_RUN_DOC_OPTS := --rm $(DOCKER_RUN_DOC_MOUNT) -p $(DOCKER_RUN_DOC_PORT):8000
print-%: ; @echo $*=$($*)
@@ -36,6 +44,9 @@ all: generate-webui build ## validate all checks, build linux binary, run all te
binary: generate-webui build ## build the linux binary
$(DOCKER_RUN_TRAEFIK) ./script/make.sh generate binary
binary-with-no-ui: ## build the linux binary without the ui generation
$(DOCKER_RUN_TRAEFIK) ./script/make.sh generate binary
crossbinary: generate-webui build ## cross build the non-linux binaries
$(DOCKER_RUN_TRAEFIK) ./script/make.sh generate crossbinary
@@ -65,9 +76,16 @@ test-unit: build ## run the unit tests
test-integration: build ## run the integration tests
$(DOCKER_RUN_TRAEFIK) ./script/make.sh generate binary test-integration
TEST_HOST=1 ./script/make.sh test-integration
validate: build ## validate gofmt, golint and go vet
$(DOCKER_RUN_TRAEFIK) ./script/make.sh validate-glide validate-gofmt validate-govet validate-golint validate-misspell validate-vendor
test-integration-container: build ## Run the container integration tests
$(DOCKER_RUN_TRAEFIK) ./script/make.sh test-integration
test-integration-host: build ## Run the host integration tests
TEST_HOST=1 ./script/make.sh test-integration
validate: build ## validate code, vendor and autogen
$(DOCKER_RUN_TRAEFIK) ./script/make.sh validate-gofmt validate-golint validate-misspell validate-vendor validate-autogen
build: dist
docker build $(DOCKER_BUILD_ARGS) -t "$(TRAEFIK_DEV_IMAGE)" -f build.Dockerfile .
@@ -81,22 +99,50 @@ build-no-cache: dist
shell: build ## start a shell inside the build env
$(DOCKER_RUN_TRAEFIK) /bin/bash
image: binary ## build a docker traefik image
image-dirty: binary ## build a docker traefik image
docker build -t $(TRAEFIK_IMAGE) .
image: clear-static binary ## clean up static directory and build a docker traefik image
docker build -t $(TRAEFIK_IMAGE) .
docs-image:
docker build -t $(TRAEFIK_DOC_IMAGE) -f docs.Dockerfile .
docs: docs-image
docker run $(DOCKER_RUN_DOC_OPTS) $(TRAEFIK_DOC_IMAGE) mkdocs serve
docs-build: site
docs-verify: site
ifeq ($(DOCS_VERIFY_SKIP),false)
docker build -t $(TRAEFIK_DOC_VERIFY_IMAGE) ./script/docs-verify-docker-image
docker run --rm -v $(CURDIR):/app $(TRAEFIK_DOC_VERIFY_IMAGE)
else
@echo "DOCS_LINT_SKIP is true: no linting done."
endif
site: docs-image
docker run $(DOCKER_RUN_DOC_OPTS) $(TRAEFIK_DOC_IMAGE) mkdocs build
docs-clean:
rm -rf $(CURDIR)/site
clear-static:
rm -rf static
dist:
mkdir dist
run-dev:
go generate
go build
go build ./cmd/traefik
./traefik
generate-webui: build-webui
if [ ! -d "static" ]; then \
mkdir -p static; \
docker run --rm -v "$$PWD/static":'/src/static' traefik-webui npm run build; \
echo 'For more informations show `webui/readme.md`' > $$PWD/static/DONT-EDIT-FILES-IN-THIS-DIRECTORY.md; \
echo 'For more information show `webui/readme.md`' > $$PWD/static/DONT-EDIT-FILES-IN-THIS-DIRECTORY.md; \
fi
lint:
@@ -106,9 +152,7 @@ fmt:
gofmt -s -l -w $(SRCS)
pull-images:
for f in $(shell find ./integration/resources/compose/ -type f); do \
docker-compose -f $$f pull; \
done
grep --no-filename -E '^\s+image:' ./integration/resources/compose/*.yml | awk '{print $$2}' | sort | uniq | xargs -P 6 -n 1 docker pull
help: ## this help
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {sub("\\\\n",sprintf("\n%22c"," "), $$2);printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)

202
README.md
View File

@@ -1,147 +1,179 @@
<p align="center">
<img src="docs/img/traefik.logo.png" alt="Træfik" title="Træfik" />
<img src="docs/img/traefik.logo.png" alt="Traefik" title="Traefik" />
</p>
[![Build Status](https://travis-ci.org/containous/traefik.svg?branch=master)](https://travis-ci.org/containous/traefik)
[![Docs](https://img.shields.io/badge/docs-current-brightgreen.svg)](https://docs.traefik.io)
[![Go Report Card](https://goreportcard.com/badge/containous/traefik)](http://goreportcard.com/report/containous/traefik)
[![Build Status SemaphoreCI](https://semaphoreci.com/api/v1/containous/traefik/branches/master/shields_badge.svg)](https://semaphoreci.com/containous/traefik)
[![Docs](https://img.shields.io/badge/docs-current-brightgreen.svg)](https://doc.traefik.io/traefik/v1.7)
[![Go Report Card](https://goreportcard.com/badge/traefik/traefik)](http://goreportcard.com/report/traefik/traefik)
[![](https://images.microbadger.com/badges/image/traefik.svg)](https://microbadger.com/images/traefik)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/containous/traefik/blob/master/LICENSE.md)
[![Join the chat at https://traefik.herokuapp.com](https://img.shields.io/badge/style-register-green.svg?style=social&label=Slack)](https://traefik.herokuapp.com)
[![Twitter](https://img.shields.io/twitter/follow/traefikproxy.svg?style=social)](https://twitter.com/intent/follow?screen_name=traefikproxy)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/traefik/traefik/blob/master/LICENSE.md)
[![Join the chat at https://slack.traefik.io](https://img.shields.io/badge/style-register-green.svg?style=social&label=Slack)](https://slack.traefik.io)
[![Twitter](https://img.shields.io/twitter/follow/traefik.svg?style=social)](https://twitter.com/intent/follow?screen_name=traefik)
Træfik (pronounced like [traffic](https://speak-ipa.bearbin.net/speak.cgi?speak=%CB%88tr%C3%A6f%C9%AAk)) is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
It supports several backends ([Docker](https://www.docker.com/), [Swarm](https://docs.docker.com/swarm), [Kubernetes](http://kubernetes.io), [Marathon](https://mesosphere.github.io/marathon/), [Mesos](https://github.com/apache/mesos), [Consul](https://www.consul.io/), [Etcd](https://coreos.com/etcd/), [Zookeeper](https://zookeeper.apache.org), [BoltDB](https://github.com/boltdb/bolt), [Eureka](https://github.com/Netflix/eureka), [Amazon DynamoDB](https://aws.amazon.com/dynamodb/), Rest API, file...) to manage its configuration automatically and dynamically.
Traefik is a modern HTTP reverse proxy and load balancer that makes deploying microservices easy.
Traefik integrates with your existing infrastructure components ([Docker](https://www.docker.com/), [Swarm mode](https://docs.docker.com/engine/swarm/), [Kubernetes](https://kubernetes.io), [Marathon](https://mesosphere.github.io/marathon/), [Consul](https://www.consul.io/), [Etcd](https://coreos.com/etcd/), [Rancher](https://rancher.com), [Amazon ECS](https://aws.amazon.com/ecs), ...) and configures itself automatically and dynamically.
Pointing Traefik at your orchestrator should be the _only_ configuration step you need.
---
. **[Overview](#overview)** .
**[Features](#features)** .
**[Supported backends](#supported-backends)** .
**[Quickstart](#quickstart)** .
**[Web UI](#web-ui)** .
**[Test it](#test-it)** .
**[Documentation](#documentation)** .
. **[Support](#support)** .
**[Release cycle](#release-cycle)** .
**[Contributing](#contributing)** .
**[Maintainers](#maintainers)** .
**[Plumbing](#plumbing)** .
**[Credits](#credits)** .
---
## Overview
Imagine that you have deployed a bunch of microservices on your infrastructure. You probably used a service registry (like etcd or consul) and/or an orchestrator (swarm, Mesos/Marathon) to manage all these services.
If you want your users to access some of your microservices from the Internet, you will have to use a reverse proxy and configure it using virtual hosts or prefix paths:
Imagine that you have deployed a bunch of microservices with the help of an orchestrator (like Swarm or Kubernetes) or a service registry (like etcd or consul).
Now you want users to access these microservices, and you need a reverse proxy.
- domain `api.domain.com` will point the microservice `api` in your private network
- path `domain.com/web` will point the microservice `web` in your private network
- domain `backoffice.domain.com` will point the microservices `backoffice` in your private network, load-balancing between your multiple instances
Traditional reverse-proxies require that you configure _each_ route that will connect paths and subdomains to _each_ microservice.
In an environment where you add, remove, kill, upgrade, or scale your services _many_ times a day, the task of keeping the routes up to date becomes tedious.
But a microservices architecture is dynamic... Services are added, removed, killed or upgraded often, eventually several times a day.
**This is when Traefik can help you!**
Traditional reverse-proxies are not natively dynamic. You can't change their configuration and hot-reload easily.
Traefik listens to your service registry/orchestrator API and instantly generates the routes so your microservices are connected to the outside world -- without further intervention from your part.
Here enters Træfik.
**Run Traefik and let it do the work for you!**
_(But if you'd rather configure some of your routes manually, Traefik supports that too!)_
![Architecture](docs/img/architecture.png)
Træfik can listen to your service registry/orchestrator API, and knows each time a microservice is added, removed, killed or upgraded, and can generate its configuration automatically.
Routes to your services will be created instantly.
Run it and forget it!
## Features
- [It's fast](http://docs.traefik.io/benchmarks)
- No dependency hell, single binary made with go
- Rest API
- Multiple backends supported: Docker, Swarm, Kubernetes, Marathon, Mesos, Consul, Etcd, and more to come
- Watchers for backends, can listen for changes in backends to apply a new configuration automatically
- Hot-reloading of configuration. No need to restart the process
- Graceful shutdown http connections
- Circuit breakers on backends
- Round Robin, rebalancer load-balancers
- Rest Metrics
- [Tiny](https://microbadger.com/images/traefik) [official](https://hub.docker.com/r/_/traefik/) docker image included
- SSL backends support
- SSL frontend support (with SNI)
- Clean AngularJS Web UI
- Websocket support
- HTTP/2 support
- Retry request if network error
- [Let's Encrypt](https://letsencrypt.org) support (Automatic HTTPS with renewal)
- High Availability with cluster mode
- 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)
- Circuit breakers, retry
- High Availability with cluster mode (beta)
- See the magic through its clean web UI
- Websocket, HTTP/2, GRPC ready
- Provides metrics (Rest, Prometheus, Datadog, Statsd, InfluxDB)
- Keeps access logs (JSON, CLF)
- Fast
- Exposes a Rest API
- Packaged as a single binary file (made with :heart: with go) and available as a [tiny](https://microbadger.com/images/traefik) [official](https://hub.docker.com/r/_/traefik/) docker image
## Supported Backends
- [Docker](https://doc.traefik.io/traefik/v1.7/configuration/backends/docker) / [Swarm mode](https://doc.traefik.io/traefik/v1.7/configuration/backends/docker#docker-swarm-mode)
- [Kubernetes](https://doc.traefik.io/traefik/v1.7/configuration/backends/kubernetes)
- [Mesos](https://doc.traefik.io/traefik/v1.7/configuration/backends/mesos) / [Marathon](https://doc.traefik.io/traefik/v1.7/configuration/backends/marathon)
- [Rancher](https://doc.traefik.io/traefik/v1.7/configuration/backends/rancher) (API, Metadata)
- [Azure Service Fabric](https://doc.traefik.io/traefik/v1.7/configuration/backends/servicefabric)
- [Consul Catalog](https://doc.traefik.io/traefik/v1.7/configuration/backends/consulcatalog)
- [Consul](https://doc.traefik.io/traefik/v1.7/configuration/backends/consul) / [Etcd](https://doc.traefik.io/traefik/v1.7/configuration/backends/etcd) / [Zookeeper](https://doc.traefik.io/traefik/v1.7/configuration/backends/zookeeper) / [BoltDB](https://doc.traefik.io/traefik/v1.7/configuration/backends/boltdb)
- [Eureka](https://doc.traefik.io/traefik/v1.7/configuration/backends/eureka)
- [Amazon ECS](https://doc.traefik.io/traefik/v1.7/configuration/backends/ecs)
- [Amazon DynamoDB](https://doc.traefik.io/traefik/v1.7/configuration/backends/dynamodb)
- [File](https://doc.traefik.io/traefik/v1.7/configuration/backends/file)
- [Rest](https://doc.traefik.io/traefik/v1.7/configuration/backends/rest)
## Quickstart
You can have a quick look at Træfik in this [Katacoda tutorial](https://www.katacoda.com/courses/traefik/deploy-load-balancer) that shows how to load balance requests between multiple Docker containers.
To get your hands on Traefik, you can use the [5-Minute Quickstart](http://doc.traefik.io/traefik/v1.7/#the-traefik-quickstart-using-docker) in our documentation (you will need Docker).
Here is a talk given by [Ed Robinson](https://github.com/errm) at the [ContainerCamp UK](https://container.camp) conference.
You will learn fundamental Træfik features and see some demos with Kubernetes.
Alternatively, if you don't want to install anything on your computer, you can try Traefik online in this great [Katacoda tutorial](https://www.katacoda.com/courses/traefik/deploy-load-balancer) that shows how to load balance requests between multiple Docker containers.
[![Traefik ContainerCamp UK](http://img.youtube.com/vi/aFtpIShV60I/0.jpg)](https://www.youtube.com/watch?v=aFtpIShV60I)
Here is a talk (in French) given by [Emile Vauge](https://github.com/emilevauge) at the [Devoxx France 2016](http://www.devoxx.fr) conference.
You will learn fundamental Træfik features and see some demos with Docker, Mesos/Marathon and Let's Encrypt.
[![Traefik Devoxx France](http://img.youtube.com/vi/QvAz9mVx5TI/0.jpg)](http://www.youtube.com/watch?v=QvAz9mVx5TI)
If you are looking for a more comprehensive and real use-case example, you can also check [Play-With-Docker](http://training.play-with-docker.com/traefik-load-balancing/) to see how to load balance between multiple nodes.
## Web UI
You can access the simple HTML frontend of Træfik.
You can access the simple HTML frontend of Traefik.
![Web UI Providers](docs/img/web.frontend.png)
![Web UI Health](docs/img/traefik-health.png)
## Plumbing
## Documentation
- [Oxy](https://github.com/vulcand/oxy): an awesome proxy library made by Mailgun guys
- [Gorilla mux](https://github.com/gorilla/mux): famous request router
- [Negroni](https://github.com/codegangsta/negroni): web middlewares made simple
- [Lego](https://github.com/xenolf/lego): the best [Let's Encrypt](https://letsencrypt.org) library in go
You can find the complete documentation at [https://doc.traefik.io/traefik/v1.7](https://doc.traefik.io/traefik/v1.7).
A collection of contributions around Traefik can be found at [https://awesome.traefik.io](https://awesome.traefik.io).
## Test it
## Support
- The simple way: grab the latest binary from the [releases](https://github.com/containous/traefik/releases) page and just run it with the [sample configuration file](https://raw.githubusercontent.com/containous/traefik/master/traefik.sample.toml):
To get community support, you can:
- join the Traefik community Slack channel: [![Join the chat at https://slack.traefik.io](https://img.shields.io/badge/style-register-green.svg?style=social&label=Slack)](https://slack.traefik.io)
- use [Stack Overflow](https://stackoverflow.com/questions/tagged/traefik) (using the `traefik` tag)
If you need commercial support, please contact [Traefik.io](https://traefik.io) by mail: <mailto:support@traefik.io>.
## Download
- Grab the latest binary from the [releases](https://github.com/traefik/traefik/releases) page and run it with the [sample configuration file](https://raw.githubusercontent.com/traefik/traefik/v1.7/traefik.sample.toml):
```shell
./traefik --configFile=traefik.toml
```
- Use the tiny Docker image:
- Or use the official tiny Docker image and run it with the [sample configuration file](https://raw.githubusercontent.com/traefik/traefik/v1.7/traefik.sample.toml):
```shell
docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/etc/traefik/traefik.toml traefik
```
- From sources:
- Or get the sources:
```shell
git clone https://github.com/containous/traefik
git clone https://github.com/traefik/traefik
```
## Documentation
## Introductory Videos
You can find the complete documentation [here](https://docs.traefik.io).
Here is a talk given by [Emile Vauge](https://github.com/emilevauge) at GopherCon 2017.
You will learn Traefik basics in less than 10 minutes.
## Contributing
[![Traefik GopherCon 2017](https://img.youtube.com/vi/RgudiksfL-k/0.jpg)](https://www.youtube.com/watch?v=RgudiksfL-k)
Please refer to [this section](.github/CONTRIBUTING.md).
Here is a talk given by [Ed Robinson](https://github.com/errm) at [ContainerCamp UK](https://container.camp) conference.
You will learn fundamental Traefik features and see some demos with Kubernetes.
## Code Of Conduct
Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
## Support
You can join [![Join the chat at https://traefik.herokuapp.com](https://img.shields.io/badge/style-register-green.svg?style=social&label=Slack)](https://traefik.herokuapp.com) to get basic support.
If you prefer commercial support, please contact [containo.us](https://containo.us) by mail: <mailto:support@containo.us>.
[![Traefik ContainerCamp UK](https://img.youtube.com/vi/aFtpIShV60I/0.jpg)](https://www.youtube.com/watch?v=aFtpIShV60I)
## Maintainers
- Emile Vauge [@emilevauge](https://github.com/emilevauge)
- Vincent Demeester [@vdemeester](https://github.com/vdemeester)
- Russell Clare [@Russell-IO](https://github.com/Russell-IO)
- Ed Robinson [@errm](https://github.com/errm)
- Daniel Tomcej [@dtomcej](https://github.com/dtomcej)
- Manuel Laufenberg [@SantoDE](https://github.com/SantoDE)
- Thomas Recloux [@trecloux](https://github.com/trecloux)
- Timo Reimann [@timoreimann](https://github.com/timoreimann)
[Information about process and maintainers](MAINTAINER.md)
## Contributing
If you'd like to contribute to the project, refer to the [contributing documentation](CONTRIBUTING.md).
Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md).
By participating in this project, you agree to abide by its terms.
## Release Cycle
- We release a new version (e.g. 1.1.0, 1.2.0, 1.3.0) every other month.
- Release Candidates are available before the release (e.g. 1.1.0-rc1, 1.1.0-rc2, 1.1.0-rc3, 1.1.0-rc4, before 1.1.0)
- Bug-fixes (e.g. 1.1.1, 1.1.2, 1.2.1, 1.2.3) are released as needed (no additional features are delivered in those versions, bug-fixes only)
Each version is supported until the next one is released (e.g. 1.1.x will be supported until 1.2.0 is out)
We use [Semantic Versioning](http://semver.org/)
## Mailing lists
- General announcements, new releases: mail at news+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/news)
- Security announcements: mail at security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
## Credits
Kudos to [Peka](http://peka.byethost11.com/photoblog/) for his awesome work on the logo ![logo](docs/img/traefik.icon.png).
Traefik's logo licensed under the Creative Commons 3.0 Attributions license.
Traefik's logo is licensed under the Creative Commons 3.0 Attributions license.
Traefik's logo was inspired by the gopher stickers made by Takuya Ueda (https://twitter.com/tenntenn).
The original Go gopher was designed by Renee French (http://reneefrench.blogspot.com/).

View File

@@ -6,24 +6,30 @@ import (
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"errors"
"fmt"
"reflect"
"regexp"
"sort"
"strings"
"sync"
"time"
"github.com/containous/traefik/log"
"github.com/xenolf/lego/acme"
"github.com/go-acme/lego/v4/certcrypto"
"github.com/go-acme/lego/v4/registration"
"github.com/traefik/traefik/log"
acmeprovider "github.com/traefik/traefik/provider/acme"
"github.com/traefik/traefik/types"
)
// Account is used to store lets encrypt registration info
type Account struct {
Email string
Registration *acme.RegistrationResource
Registration *registration.Resource
PrivateKey []byte
KeyType certcrypto.KeyType
DomainsCertificate DomainsCertificates
ChallengeCerts map[string]*ChallengeCert
HTTPChallenge map[string]map[string][]byte
}
// ChallengeCert stores a challenge certificate
@@ -33,13 +39,18 @@ type ChallengeCert struct {
certificate *tls.Certificate
}
// Init inits acccount struct
// Init account struct
func (a *Account) Init() error {
err := a.DomainsCertificate.Init()
if err != nil {
return err
}
err = a.RemoveAccountV1Values()
if err != nil {
log.Errorf("Unable to remove ACME Account V1 values during account initialization: %v", err)
}
for _, cert := range a.ChallengeCerts {
if cert.certificate == nil {
certificate, err := tls.X509KeyPair(cert.Certificate, cert.PrivateKey)
@@ -48,6 +59,7 @@ func (a *Account) Init() error {
}
cert.certificate = &certificate
}
if cert.certificate.Leaf == nil {
leaf, err := x509.ParseCertificate(cert.certificate.Certificate[0])
if err != nil {
@@ -60,17 +72,25 @@ func (a *Account) Init() error {
}
// NewAccount creates an account
func NewAccount(email string) (*Account, error) {
func NewAccount(email string, certs []*DomainsCertificate, keyTypeValue string) (*Account, error) {
keyType := acmeprovider.GetKeyType(keyTypeValue)
// Create a user. New accounts need an email and private key to start
privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
return nil, err
}
domainsCerts := DomainsCertificates{Certs: []*DomainsCertificate{}}
domainsCerts.Init()
domainsCerts := DomainsCertificates{Certs: certs}
err = domainsCerts.Init()
if err != nil {
return nil, err
}
return &Account{
Email: email,
PrivateKey: x509.MarshalPKCS1PrivateKey(privateKey),
KeyType: keyType,
DomainsCertificate: DomainsCertificates{Certs: domainsCerts.Certs},
ChallengeCerts: map[string]*ChallengeCert{}}, nil
}
@@ -81,7 +101,7 @@ func (a *Account) GetEmail() string {
}
// GetRegistration returns lets encrypt registration resource
func (a *Account) GetRegistration() *acme.RegistrationResource {
func (a *Account) GetRegistration() *registration.Resource {
return a.Registration
}
@@ -90,10 +110,39 @@ func (a *Account) GetPrivateKey() crypto.PrivateKey {
if privateKey, err := x509.ParsePKCS1PrivateKey(a.PrivateKey); err == nil {
return privateKey
}
log.Errorf("Cannot unmarshall private key %+v", a.PrivateKey)
keySnippet := ""
if len(a.PrivateKey) >= 16 {
keySnippet = string(a.PrivateKey[:16])
}
log.Errorf("Cannot unmarshall private key beginning with %s", keySnippet)
return nil
}
// RemoveAccountV1Values removes ACME account V1 values
func (a *Account) RemoveAccountV1Values() error {
// Check if ACME Account is in ACME V1 format
if a.Registration != nil {
isOldRegistration, err := regexp.MatchString(acmeprovider.RegistrationURLPathV1Regexp, a.Registration.URI)
if err != nil {
return err
}
if isOldRegistration {
a.reset()
}
}
return nil
}
func (a *Account) reset() {
log.Debug("Reset ACME account object.")
a.Email = ""
a.Registration = nil
a.PrivateKey = nil
}
// Certificate is used to store certificate info
type Certificate struct {
Domain string
@@ -121,9 +170,11 @@ func (dc *DomainsCertificates) Less(i, j int) bool {
if reflect.DeepEqual(dc.Certs[i].Domains, dc.Certs[j].Domains) {
return dc.Certs[i].tlsCert.Leaf.NotAfter.After(dc.Certs[j].tlsCert.Leaf.NotAfter)
}
if dc.Certs[i].Domains.Main == dc.Certs[j].Domains.Main {
return strings.Join(dc.Certs[i].Domains.SANs, ",") < strings.Join(dc.Certs[j].Domains.SANs, ",")
}
return dc.Certs[i].Domains.Main < dc.Certs[j].Domains.Main
}
@@ -141,29 +192,46 @@ func (dc *DomainsCertificates) removeDuplicates() {
}
}
// Init inits DomainsCertificates
func (dc *DomainsCertificates) removeEmpty() {
var certs []*DomainsCertificate
for _, cert := range dc.Certs {
if cert.Certificate != nil && len(cert.Certificate.Certificate) > 0 && len(cert.Certificate.PrivateKey) > 0 {
certs = append(certs, cert)
}
}
dc.Certs = certs
}
// Init DomainsCertificates
func (dc *DomainsCertificates) Init() error {
dc.lock.Lock()
defer dc.lock.Unlock()
dc.removeEmpty()
for _, domainsCertificate := range dc.Certs {
tlsCert, err := tls.X509KeyPair(domainsCertificate.Certificate.Certificate, domainsCertificate.Certificate.PrivateKey)
if err != nil {
return err
}
domainsCertificate.tlsCert = &tlsCert
if domainsCertificate.tlsCert.Leaf == nil {
leaf, err := x509.ParseCertificate(domainsCertificate.tlsCert.Certificate[0])
if err != nil {
return err
}
domainsCertificate.tlsCert.Leaf = leaf
}
}
dc.removeDuplicates()
return nil
}
func (dc *DomainsCertificates) renewCertificates(acmeCert *Certificate, domain Domain) error {
func (dc *DomainsCertificates) renewCertificates(acmeCert *Certificate, domain types.Domain) error {
dc.lock.Lock()
defer dc.lock.Unlock()
@@ -173,15 +241,17 @@ func (dc *DomainsCertificates) renewCertificates(acmeCert *Certificate, domain D
if err != nil {
return err
}
domainsCertificate.Certificate = acmeCert
domainsCertificate.tlsCert = &tlsCert
return nil
}
}
return errors.New("Certificate to renew not found for domain " + domain.Main)
return fmt.Errorf("certificate to renew not found for domain %s", domain.Main)
}
func (dc *DomainsCertificates) addCertificateForDomains(acmeCert *Certificate, domain Domain) (*DomainsCertificate, error) {
func (dc *DomainsCertificates) addCertificateForDomains(acmeCert *Certificate, domain types.Domain) (*DomainsCertificate, error) {
dc.lock.Lock()
defer dc.lock.Unlock()
@@ -189,6 +259,7 @@ func (dc *DomainsCertificates) addCertificateForDomains(acmeCert *Certificate, d
if err != nil {
return nil, err
}
cert := DomainsCertificate{Domains: domain, Certificate: acmeCert, tlsCert: &tlsCert}
dc.Certs = append(dc.Certs, &cert)
return &cert, nil
@@ -197,11 +268,12 @@ func (dc *DomainsCertificates) addCertificateForDomains(acmeCert *Certificate, d
func (dc *DomainsCertificates) getCertificateForDomain(domainToFind string) (*DomainsCertificate, bool) {
dc.lock.RLock()
defer dc.lock.RUnlock()
for _, domainsCertificate := range dc.Certs {
domains := []string{}
domains = append(domains, domainsCertificate.Domains.Main)
domains = append(domains, domainsCertificate.Domains.SANs...)
for _, domain := range domains {
for _, domain := range domainsCertificate.Domains.ToStrArray() {
if strings.HasPrefix(domain, "*.") && types.MatchDomain(domainToFind, domain) {
return domainsCertificate, true
}
if domain == domainToFind {
return domainsCertificate, true
}
@@ -210,9 +282,10 @@ func (dc *DomainsCertificates) getCertificateForDomain(domainToFind string) (*Do
return nil, false
}
func (dc *DomainsCertificates) exists(domainToFind Domain) (*DomainsCertificate, bool) {
func (dc *DomainsCertificates) exists(domainToFind types.Domain) (*DomainsCertificate, bool) {
dc.lock.RLock()
defer dc.lock.RUnlock()
for _, domainsCertificate := range dc.Certs {
if reflect.DeepEqual(domainToFind, domainsCertificate.Domains) {
return domainsCertificate, true
@@ -221,9 +294,29 @@ func (dc *DomainsCertificates) exists(domainToFind Domain) (*DomainsCertificate,
return nil, false
}
func (dc *DomainsCertificates) toDomainsMap() map[string]*tls.Certificate {
domainsCertificatesMap := make(map[string]*tls.Certificate)
for _, domainCertificate := range dc.Certs {
certKey := domainCertificate.Domains.Main
if domainCertificate.Domains.SANs != nil {
sort.Strings(domainCertificate.Domains.SANs)
for _, dnsName := range domainCertificate.Domains.SANs {
if dnsName != domainCertificate.Domains.Main {
certKey += fmt.Sprintf(",%s", dnsName)
}
}
}
domainsCertificatesMap[certKey] = domainCertificate.tlsCert
}
return domainsCertificatesMap
}
// DomainsCertificate contains a certificate for multiple domains
type DomainsCertificate struct {
Domains Domain
Domains types.Domain
Certificate *Certificate
tlsCert *tls.Certificate
}
@@ -235,8 +328,9 @@ func (dc *DomainsCertificate) needRenew() bool {
// If there's an error, we assume the cert is broken, and needs update
return true
}
// <= 30 days left, renew certificate
if crt.NotAfter.Before(time.Now().Add(time.Duration(24 * 30 * time.Hour))) {
if crt.NotAfter.Before(time.Now().Add(24 * 30 * time.Hour)) {
return true
}
}

File diff suppressed because it is too large Load Diff

43
acme/acme_example.json Normal file
View File

@@ -0,0 +1,43 @@
{
"Email": "test@traefik.io",
"Registration": {
"body": {
"resource": "reg",
"id": 3,
"key": {
"kty": "RSA",
"n": "y5a71suIqvEtovDmDVQ3SSNagk5IVCFI_TvqWpEXSrdbcDE2C-PTEtEUJuLkYwygcpiWYbPmXgdS628vQCw5Uo4DeDyHiuysJOWBLaWow3p9goOdhnPbGBq0liIR9xXyRoctdipVk8UiO9scWsu4jMBM3sMr7_yBWPfYYiLEQmZGFO3iE7Oqr55h_kncHIj5lUQY1j_jkftqxlxUB5_0quyJ7l915j5QY--eY7h4GEhRvx0TlUpi-CnRtRblGeDDDilXZD6bQN2962WdKecsmRaYx-ttLz6jCPXz2VDJRWNcIS501ne2Zh3hzw_DS6IRd2GIia1Wg4sisi9epC9sumXPHi6xzR6-_i_nsFjdtTkUcV8HmorOYoc820KQVZaLScxa8e7-ixpOd6mr6AIbEf7dBAkb9f_iK3GwpqKD8yNcaj1EQgNSyJSjnKSulXI_GwkGnuXe00Qpb1a8ha5Z8yWg7XmZZnJyAZrmK60RfwRNQ1rO5ioerNUBJ2KYTYNzVjBdob9Ug6Cjh4bEKNNjqcbjQ50_Z97Vw40xzpDQ_fYllc6n92eSuv6olxFJTmK7EhHuanDzITngaqei3zL9RwQ7P-1jfEZ03qmGrQYYqXcsS46PQ8cE-frzY2mKp16pRNCG7-03gKVGV0JHyW1aYbevNUk7OumCAXhC2YOigBk",
"e": "AQAB"
},
"contact": [
"mailto:test@traefik.io"
],
"agreement": "http://boulder:4000/terms/v1"
},
"uri": "http://127.0.0.1:4000/acme/reg/3",
"new_authzr_uri": "http://127.0.0.1:4000/acme/new-authz",
"terms_of_service": "http://boulder:4000/terms/v1"
},
"PrivateKey": "MIIJJwIBAAKCAgEAy5a71suIqvEtovDmDVQ3SSNagk5IVCFI/TvqWpEXSrdbcDE2C+PTEtEUJuLkYwygcpiWYbPmXgdS628vQCw5Uo4DeDyHiuysJOWBLaWow3p9goOdhnPbGBq0liIR9xXyRoctdipVk8UiO9scWsu4jMBM3sMr7/yBWPfYYiLEQmZGFO3iE7Oqr55h/kncHIj5lUQY1j/jkftqxlxUB5/0quyJ7l915j5QY++eY7h4GEhRvx0TlUpi+CnRtRblGeDDDilXZD6bQN2962WdKecsmRaYx+ttLz6jCPXz2VDJRWNcIS501ne2Zh3hzw/DS6IRd2GIia1Wg4sisi9epC9sumXPHi6xzR6+/i/nsFjdtTkUcV8HmorOYoc820KQVZaLScxa8e7+ixpOd6mr6AIbEf7dBAkb9f/iK3GwpqKD8yNcaj1EQgNSyJSjnKSulXI/GwkGnuXe00Qpb1a8ha5Z8yWg7XmZZnJyAZrmK60RfwRNQ1rO5ioerNUBJ2KYTYNzVjBdob9Ug6Cjh4bEKNNjqcbjQ50/Z97Vw40xzpDQ/fYllc6n92eSuv6olxFJTmK7EhHuanDzITngaqei3zL9RwQ7P+1jfEZ03qmGrQYYqXcsS46PQ8cE+frzY2mKp16pRNCG7+03gKVGV0JHyW1aYbevNUk7OumCAXhC2YOigBkCAwEAAQKCAgA8XW1EuwTC6tAFSDhuK1JZNUpY6K05hMUHkQRj5jFpzgQmt/C2hc7H/YZkIVJmrA/G6sdsINNlffZwKH9yH6q/d6w/snLeFl7UcdhjmIL5sxAT6sKCY0fLVd/FxERfZvp3Pw2Tw+mr7v+/j7BQm6cU1M/2HRiiB9SydIqMTpKyvXB6NC6ceOFbQTL9GxlQvKyEPbS/kiH/3vRB7I5d1GfPZmNfcp6ark9X0my8VK4HRSo36H8t/OhrfLrZXvh/O82aHVf0OTv/d8AgU/jNu+XVXoXegUfWglQFDChJf1KuaE+g5w1tqgFDNgkGRD475soXA6xgZi0Iw/B9tN3zALzT4IiAW1q72feeTgKOMA2zGtKXxQZZSOV+DuWFZNz/tT7XqGQThqxM09CHv2WGOe80vobtegXYTUt90hysrqIZmBW5XYdzQlJh1KWTtfCaTrWd47kbGvhkEPc8aA3Ji4/AqfkVXiqwaLu+MSlgzPpRj7U7UAIDqnpZjgttgLp74Ujnk3bTaUzdyyNqYDBG3IFGr/Sv+2GQDAUn/PYRJKWr0BteqOzX9zvW3zY8g9CYVXfK/AW3RMWLV8ly6vH/gWqa9gEuzRNRlzjUU6/HCVbUx3UT8RMWH2TQ0uuQZr5JX1iTwjeeT0dEIly1NnRQC92wcrE4UUTBEF3krGVpDBf0AQKCAQEA4jB8w+2fwzbF8X+gCODcY7sTeJRunzGy+jbdaLkcThuylga+6W3ZgWx0BD30ql9K2mouCVu86fCTnBeXXEC3QoTdgw/EzJ83+4JU3QSDdzs9Ta9vLHyvrpUkQfZ8UZpeLLmFsmsBMbBbnfw0S1TzXDsgrAc+G4tia8nO/Iqu75kEMGzmHQAvmN3iSqc1aTS4qumbB19g+v+csq9NEht4F9jt39KotG+OD3MxCxtMu7vxAkJRjFFcgcbb2Rtqe/kQEKA1vLEAJg27lV4k8XibCSerVUR6IzT8WZHrNiXmpRguTLl2k8uFUdCOOx6aLGyRVJ6+8SgIsMR540vnxwQzEQKCAQEA5mu2wtWT19mvXopC3easPsXIPzc5oaRkqfWZYT1KHcVQ7NIXsE3vCjcf/3igZ8l/FVQ4G4fpk/GoTqlpV5Aq/JHCpVOR2O69uB+W4kWgliejpHvF9gszzAYnC8lIXqDbWiinBhmm3ii8sDGAoBaSDw5NMUq3mI+nd8zZ+jx1bLBczDafmQ0YKr8k0YaROxIgoBgDOQDdSqG387lwzpza2DKI5Al3HfS42zjT0RmBahPiuT2aEoUZmIYuvFY0fEjfkpbdvLyexHfZCILRUGlG1nAwASFg86lp+mFSBJ3E3cvbP0CpbFGxon5u4Ao3/7htoOh6huh7MQ91h41fv1hsiQKCAQAe7WRR4e7jYVzlbX7zV9Oqq0y5QwpxJ/mB7viNNiphn7Xmf5uhDU0dPjgK0HHgzdDNVpFe5DVLg4KbaDpg+dRU+xfSsNhG5kpgUGzMH67eIbJ7Kc64tX/MDkZ74nkTK1lPIjrer3TlV2jfjDmWR1JTPR51hzP9ziwx8tEjhM7woeqJuIoqUvkvHL+xV3WdIgFSFUkGVAtNpp/FauTN4gWktRupbAN3UH2LLUP6ccwnK0aD+Y9u8T0F3av33qDLvL1umIlgeI89pMkOXmYMwmHoeY0axpcwszECCkqwB7SmxEyoXv+Qq9ZZ3ntkKAYKpvmkKWSQUtoFWYgVBS727mMRAoIBABLdwusU/bPwuPEutObiWjwRiaHTbb6UbUGVQGe70vO5EjUxxorC9s2JUe9i+w9EakleyfFHIZLheHxoVp26yio/7QYIX6q5cYM/4uTH+qwQts9i6wSISkdsQYovguNsnEk3huVy+Dy8bSaoBvYUowTkkOF2Uq4FJRskBLz+ckbh8dcuqcaoUdA+Mk+NixqhE1bIYIssTPItZ5hnGJtyMGD/UkIJnF0ximk4r+8w/W2oDypHpvPZPg1E/1KgZE/Az7166NDpSL6haX3O6ECDPi+Uo/mTuBJ7TpgXm9WQ7WuTo3H8Y2LhFYBOhdmGPKuNeDxyjIW7R0rvDxp4MtzB6rECggEAJIl7/qp1lxUQPQJRTsEYBkOtdRw0IGG1Rcj0emhHaBN05c9opCy+Osb7mVeU5ZiULe5kD02phL+36pEumprz7QzN46Y5pZc8AQ2W/QkeL4Wo9U9QzczvQQzc1EqrBkzvQTZtBhn4DRzz0IuTn1beVyHtBZeNpBFgMQFv9VYQuUNwFoTOkkQrBRnYbXH6KEnhF3c/1Hzi4KHVdHdfZ3LH7KFQJ34xio0q2tWQSQYeybmwOXdd9sxpz/Y4KBS9fqm7UrwnPK8yuOc05HLEaws+1iam5YyJprlQo3mGKe0wRztwn44HDeQr70LlFm0lzigVAv0hSiWO1Q5hJL7nDu8m/Q==",
"DomainsCertificate": {
"Certs": [
{
"Domains": {
"Main": "local1.com",
"SANs": [
"test1.local1.com",
"test2.local1.com"
]
},
"Certificate": {
"Domain": "local1.com",
"CertURL": "http://127.0.0.1:4000/acme/cert/ffc4f3f14def9ee6ec6a0522b5c0baa3379d",
"CertStableURL": "",
"PrivateKey": "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS1FJQkFBS0NBZ0VBdVNoTTR4enF6cE5YcFNaNnAvZnQrRmt5VmgyK1BSZXJUelV0OERRSng2UkVjQS9FCnN2RnNIVmNOSkZMS2twYTNlOEd3SUZBakJQNnJPK3hoR1JjWlJrdENON1gyOW5LZFhGbHZkYzJxd0hyTFF5WWkKTTB3ODhTck41VERiNi96TWU2dTB0dERiYWtDbDd6ZEJKUXJ6a1h5ZU1MeVkzTUs3aVkrMHpwL2JqMVhvbk5DdQpaQStkZ3hsMVNrV01DVUYvQk9HNWFyT1hwb0x4S0dQWGdzV3hOTVNLVmJKSHczL3ZqNTViZU92Um5lT3BNWlhvCmMwOWpZT3VBakNka1Z5czBSWHJLNWNCRDRMbVRXdnN4MFdTK2VMVHlGTTdQTHVZM3lEWkNNWEhjVmlqRHhnbFMKYjB1ZVRQcGFUWEQwYkxqZ0RNOUVEdE15ZEJzMUNPWlpPWG9ickN5Q2I1eWxTOFdVd1NzVXM1UldxZnlVbnAvcgpSNGx2c2RZOWRVZjRPdkNMVnJvWWk5NWFGc1Zxa0xLOExuL0Eyc3kxYWlDTnR4RmpKOXRXbWU0V0NhdzRoU0YvCkR4NWVNNWNYR2JSYXduVlZJQlZXeHhzNTBPMFJlUWRvbXBQZEFNS1RDWk9SRmxYaDdOWTdxQVdWRGtpdzhyam8Kekd3Ni9XdjlOR3hTNTliKzc0YVAxcjBxOTZ2RS9Rdi8zTCtjbjhiN0lBLytPYmFKdzhIT3RGbXc4RjBxQkN3MAprYWVVSloxb1JueGFYQUo4RHhHREpFOVdNUzh0QmJtVm16YkxoRkMzeDdVc0xGeTBrSzh1SFBFT3dQb2NKNUFUCkE1UHBvclNEMmFleHA0Z3VqYVp5c1JManpmY0dnaTdva0JFNlZVNWVqRE1iYS9lNERQNEJQUVg5VmtVQ0F3RUEKQVFLQ0FnQmZjMWdYcUp1ZmZMT3REcVlpbXh4UmIrSVVKT2NpWldaSndmZDVvY244NGtEcHFDZFZ2RUZvNnF4NgpzamQ5MURhb2xOUHdCSC9aSGxRMTR3aTNQNEluQzdzS0wwTXVEeTN5SXFUa0RPOWVwSzdPWWdVMWZyTFgvS0lCCjZlc2x2Ny9HYldFTzhhSjdKdktqM0U4NEFtcEg4UDgzenJIYTlJUnJTT3NEcmNNcEpEZHpSOXp1OW1IVDZMYmYKWC9UdC9KYTNkSW42YUxUZ0FSYkRKSjAvN0J3TFFOcXpqT0dUOWdzUWRhbGdMK2x5eEo4L1ViRndhRmVwNmgzdApvbzBHcHQ0ZWgwdTdueDhlNVd3Q2RnWmJsTnpnS3grMC9Gd3dLRHhQZVRFc2ZpOEJONmlkR2NjbVdzd3prTWdtCnJmbERaeGNSWTNRSlZIVHBCL0dTTWZXRFBPQ3dRdGltQk1WN3kxM2hPMTdPWXpSNDBMZnpUalJBbmtna2V2eWYKcFowb3dLR3o4QS9haHhRWWJmYVQ5VEhXV0wrYUpYeUhFanBKckp5aTg3UExVbzhsOFVydU56MDRWNXpLOFJPbgo2cG9EWmVtbm1EYWRlU09pK3hZRWlGT1NwSXNWbzlpcm9jUGFKN2YzYWpiNUU4RHpuN1o1MmhzL2R6akpLcFZJCm5mVDFkUU9SZEowSXRUNlRlQ2RTL0dpS25IS1RtNjR2T21IbmlJcm8rUGRhUmFjV0IrTUJ0VytRd0cyUStyRGkKc3g4NlpQbHRpTVpLMDZ5TVlyVHZUdGk2aFVGaUY5cWh4b3RGazdNQkNrZlIwYUVhaUREQUpKNm1jb1lpRUQ2QgpBVGJhVmpVaGNaUiswYkRST25PN0ozRk5rZmx3K2dMaVhvcXFRRW9pU2ZWb2h5SWY3UUtDQVFFQThjYTM5K0g4CjN3L2Qrcm0yUGNhM0RMQnBYaWU4Z3ZYcGpjazVYSkpvSGVmbnJjZWQrcFpXaTZEYncwYld0MEdtYkxmVjJNSlAKV2I1aTZzSXhmdkN3YlFqbHY0UnExMVA5ZEswT3poMnVpKzZ6cXVBMG5YTVcrN0lJS0cvdDhmS2NJZGRRNnRGcwpFclFVTFBDak56ODA2cHBiSlhPRmVvMW1BK293TGhHNlA3dDhCdlZHSk1NaTNxejNlSUNuVVE2eDNFY01ITXNuClhrM21DUzI1WUZaNk96cytFK254cGVraTAzZmQwblp3UE1jdElHZys1c3hleE9zREsrTHlvb2FqQnc5N0oyUzIKcUNNWXFtT0tLcmxEQ3Y1WmQ4dlZLN3hXVmpKRVhGTTNMZ2pieHBRcCtuVXNVVWxwS01LOVlGS0lRREl0RU9aMApWcWExTXJaOElzN1l5d0tDQVFFQXhBemZIa2pIVGlvTHdZbG5EcEk0MWlOTDh5Y0ZBallrTC94dWhPU2tlVkE4CjdRWDZPZUpDekR3Z0FUYXVqOWR6Y0wwby9yTndWV0xWcnQ3OXk3YnJvVDdFREZKWVNTY25GRXNMTlVWSXRncGkKckNSUXJTL1F2TkVGTmE5K0pRc1dmYkdBNHdIUTFaSjI4MFp1cWMvNlEyUi9kZVh3cUZBQVBHN2NIcEhHWlR6ZQoyRmFRUHFLRkV4WlEyZkpvRys0SVBRNHVQVERybXlGMmVUWXk2T3BaaDBHbWJRYlVTa1dFWDlQRmF1cHJIWVdGCk8wK25DaVVPNVRaMFZoaGR2dUNKMWdPclZHYzhBUlJtUVZ1aUNEWTZCaGlvVTU0ZmZsSXlDTXZ5a3MwcmRXZ3MKWVJ2TmN4TXNlRGJpTDRKSURkMHhiN1d4VUdmVjRVNHZPMks5Vms1N0x3S0NBUUVBMkd1eE1jcXd1RnRUc0tPYwpaaUFDcXZFZTRKRmhSVGtySHlnSW1MelZSaS9ZU3M1c3MycnZmWDA0T3N5bVZ0UUZUVHdoeUMzbktjWXFkVW52ClZGblBFMHJyblV2Qzk0elBUQ205SHZPaTBzK1JORndOdlFMUWgrME5NR1ZBOFZyaU44aXRQZ1RJWU5XaFdianQKNFA1TE45V0QwVHBmT1J4cFBRZmNxT0JsZjdjcmhtNzNvdUNwemZtMmE3OStCaWpKUFF5NzR1cFhDeXRmeHNlUApNSlU0Uk56NjdJaDFMclpKM2xGbDFvYitZT2xKazhDOHpZd1RLT0hWck9zeGxobyt4SXN2Q2t3MDFMelZ6Mi9hCnRmT3Y5NTlHSnQzbXE0ZWpJUFZPQy9iUlpmdTMvMEdSY2dpQTZ5SnpaM0VxWTVaOU1EbTU3VzdjcE5RRlRxZmEKNXEyUmtRS0NBUUErNGhZSzQ3TXg2aUNkTWxKaEJSdS82OUJucktOWm96NFdPalRFNFlXejk3MmpGU0Mrd2tsRQpzeUJjNDBvNGp4WFRHb2wwc04rZU03WndnY3dNTko3OXVHRXZ4cFhVMlA4YTdqc3BHaEVKZXVsTlo5U015R0orCnZkaWE4TEJZZDJiK2FCbjhOay9pd1Rqd0xTNC92NXI1Vk5uaFdpRElDK2tYZVVPWGRwQ1pWbDN3TEV2V0cxRHQKMzJHTmxzZzM5VENsVE5BZUJudjc1VTdYOEQrQ0gvRVpoa0E0aGxFL2hXN0JRZTczclRzd1creHhLc3BjWWFpVwpjdEg3NzVMYUw3Rm1lUVRTYk01OVZpcTZXZ2J0OVY3Rko5R09DSkQzZHF2ZjBITDlEVndjSzQ3WWt3OWlFc3RYCnY5cnEvREhhYUpGNzBGNlFlTTNNbDhSa212WTZJYkEzQW9JQkFRRGt6RmZLeG9HQ3dWUDlua3k4NmFQSjFvd2kKc2FDZEx6RjRWTENRZzkrUXJITzEyY0p5MFFQUnJ2cUQyMGp1cDFlOWJhWVZzbkdYc1FZTFg2NVR6UzJSSCtlSAp6S0NPTTdnMVE3djMxNWpjMDMvN1lQck4rb3RrV0VBOUkyaDZjUE1vY3c0aERTNk02OFlxQVlKTS9RclVhenZhCnhBTFJaZEVkQW1xWDA4VHhuY1hRUEVxYkk0ZnlSZ2pVM1BYR3RRaFFFbERpR2kwbThjQTJNTXdsR1RmbTdOSXgKaENjZ2ZkL296TEp2VUhiMkxLRi82cXEySmJVRHlOMkVoK0xSZUJjdnp6Y1grZE5MdGQxY0Uvcm1SM2hMbWxmNgo3KzRpTVMxK0t1eWV3VlJVUEE1c1F1aUYyVUVoeEs1MUpZK1FpOG9HbERKdGRrOXB3QlZNN1F0WW9KVEwKLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K",
"Certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZvakNDQklxZ0F3SUJBZ0lUQVAvRTgvRk43NTdtN0dvRklyWEF1cU0zblRBTkJna3Foa2lHOXcwQkFRc0YKQURBZk1SMHdHd1lEVlFRRERCUm9NbkJ3ZVNCb01tTnJaWElnWm1GclpTQkRRVEFlRncweE9EQXhNVFV3TnpJNQpNREJhRncweE9EQTBNVFV3TnpJNU1EQmFNRVF4RXpBUkJnTlZCQU1UQ214dlkyRnNNUzVqYjIweExUQXJCZ05WCkJBVVRKR1ptWXpSbU0yWXhOR1JsWmpsbFpUWmxZelpoTURVeU1tSTFZekJpWVdFek16YzVaRENDQWlJd0RRWUoKS29aSWh2Y05BUUVCQlFBRGdnSVBBRENDQWdvQ2dnSUJBTGtvVE9NYzZzNlRWNlVtZXFmMzdmaFpNbFlkdmowWApxMDgxTGZBMENjZWtSSEFQeExMeGJCMVhEU1JTeXBLV3QzdkJzQ0JRSXdUK3F6dnNZUmtYR1VaTFFqZTE5dlp5Cm5WeFpiM1hOcXNCNnkwTW1Jak5NUFBFcXplVXcyK3Y4ekh1cnRMYlEyMnBBcGU4M1FTVUs4NUY4bmpDOG1OekMKdTRtUHRNNmYyNDlWNkp6UXJtUVBuWU1aZFVwRmpBbEJmd1RodVdxemw2YUM4U2hqMTRMRnNUVEVpbFd5UjhOLwo3NCtlVzNqcjBaM2pxVEdWNkhOUFkyRHJnSXduWkZjck5FVjZ5dVhBUStDNWsxcjdNZEZrdm5pMDhoVE96eTdtCk44ZzJRakZ4M0ZZb3c4WUpVbTlMbmt6NldrMXc5R3k0NEF6UFJBN1RNblFiTlFqbVdUbDZHNndzZ20rY3BVdkYKbE1FckZMT1VWcW44bEo2ZjYwZUpiN0hXUFhWSCtEcndpMWE2R0l2ZVdoYkZhcEN5dkM1L3dOck10V29namJjUgpZeWZiVnBudUZnbXNPSVVoZnc4ZVhqT1hGeG0wV3NKMVZTQVZWc2NiT2REdEVYa0hhSnFUM1FEQ2t3bVRrUlpWCjRleldPNmdGbFE1SXNQSzQ2TXhzT3Yxci9UUnNVdWZXL3UrR2o5YTlLdmVyeFAwTC85eS9uSi9HK3lBUC9qbTIKaWNQQnpyUlpzUEJkS2dRc05KR25sQ1dkYUVaOFdsd0NmQThSZ3lSUFZqRXZMUVc1bFpzMnk0UlF0OGUxTEN4Ywp0SkN2TGh6eERzRDZIQ2VRRXdPVDZhSzBnOW1uc2FlSUxvMm1jckVTNDgzM0JvSXU2SkFST2xWT1hvd3pHMnYzCnVBeitBVDBGL1ZaRkFnTUJBQUdqZ2dHd01JSUJyREFPQmdOVkhROEJBZjhFQkFNQ0JhQXdIUVlEVlIwbEJCWXcKRkFZSUt3WUJCUVVIQXdFR0NDc0dBUVVGQndNQ01Bd0dBMVVkRXdFQi93UUNNQUF3SFFZRFZSME9CQllFRk5LZQpBVUZYc2Z2N2lML0lYVVBXdzY2ZU5jQnhNQjhHQTFVZEl3UVlNQmFBRlB0NFR4TDVZQldETEo4WGZ6UVpzeTQyCjZrR0pNR1lHQ0NzR0FRVUZCd0VCQkZvd1dEQWlCZ2dyQmdFRkJRY3dBWVlXYUhSMGNEb3ZMekV5Tnk0d0xqQXUKTVRvME1EQXlMekF5QmdnckJnRUZCUWN3QW9ZbWFIUjBjRG92THpFeU55NHdMakF1TVRvME1EQXdMMkZqYldVdgphWE56ZFdWeUxXTmxjblF3T1FZRFZSMFJCREl3TUlJS2JHOWpZV3d4TG1OdmJZSVFkR1Z6ZERFdWJHOWpZV3d4CkxtTnZiWUlRZEdWemRESXViRzlqWVd3eExtTnZiVEFuQmdOVkhSOEVJREFlTUJ5Z0dxQVloaFpvZEhSd09pOHYKWlhoaGJYQnNaUzVqYjIwdlkzSnNNR0VHQTFVZElBUmFNRmd3Q0FZR1o0RU1BUUlCTUV3R0F5b0RCREJGTUNJRwpDQ3NHQVFVRkJ3SUJGaFpvZEhSd09pOHZaWGhoYlhCc1pTNWpiMjB2WTNCek1COEdDQ3NHQVFVRkJ3SUNNQk1NCkVVUnZJRmRvWVhRZ1ZHaHZkU0JYYVd4ME1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQ3A0Q2FxZlR4THNQTzQKS2JueDJZdEc4bTN3MC9keTVVR1VRNjZHbGxPVTk0L2I0MmNhbTRuNUZrTWlpZ01IaUx4c2JZVXh0cDZKQ3R5cQpLKzFNcDFWWEtSTTVKbFBTNWRIaWhxdHk1U3BrTUhjampwQSs3U2YyVWtoNmpKRWYxTUVJY2JnWnpJRk5IT0hYClVUUUppVFhKcno3blJDZnlQWFZtbWErUGtIRlU4R0VEVzJGOVptU1kzVFBiQWhiWkV2UkZubjUrR1lxbkZuancKWWw3Y0I2MXYwRzVpOGQwbnVvbTB4a2hiNTU3Y3BiZHhLblhsaFU4N2RZSTR5SUdPdUFGUWpYcXFXN2NIZCtXUQpWSDB2dFA3cEgrRmt2YnY4WkkxMHMrNU5ZcCtzZjFQZGQxekJsRmdNSGF3dnFFYUg3SU9sejdkajlCdmtVc0dpClhxQWVqQnFPCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVpakNDQTNLZ0F3SUJBZ0lDRWswd0RRWUpLb1pJaHZjTkFRRUxCUUF3S3pFcE1DY0dBMVVFQXd3Z1kyRmoKYTJ4cGJtY2dZM0o1Y0hSdlozSmhjR2hsY2lCbVlXdGxJRkpQVDFRd0hoY05NVFV4TURJeE1qQXhNVFV5V2hjTgpNakF4TURFNU1qQXhNVFV5V2pBZk1SMHdHd1lEVlFRREV4Um9ZWEJ3ZVNCb1lXTnJaWElnWm1GclpTQkRRVENDCkFTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTUlLUjNtYUJjVVNzbmNYWXpRVDEzRDUKTnIrWjNtTHhNTWgzVFVkdDZzQUNtcWJKMGJ0UmxnWGZNdE5MTTJPVTFJNmEzSnUrdElaU2RuMnYyMUpCd3Z4VQp6cFpRNHp5MmNpbUlpTVFEWkNRSEp3ekM5R1puOEhhVzA5MWl6OUgwR28zQTdXRFh3WU5tc2RMTlJpMDBvMTRVCmpvYVZxYVBzWXJaV3ZSS2FJUnFhVTBoSG1TMEFXd1FTdk4vOTNpTUlYdXlpd3l3bWt3S2JXbm54Q1EvZ3NjdEsKRlV0Y05yd0V4OVdnajZLbGh3RFR5STFRV1NCYnhWWU55VWdQRnpLeHJTbXdNTzB5TmZmN2hvK1FUOXg1K1kvNwpYRTU5UzRNYzRaWHhjWEtldy9nU2xOOVU1bXZUK0QyQmhEdGtDdXBkZnNaTkNRV3AyN0ErYi9EbXJGSTlOcXNDCkF3RUFBYU9DQWNJd2dnRytNQklHQTFVZEV3RUIvd1FJTUFZQkFmOENBUUF3UXdZRFZSMGVCRHd3T3FFNE1BYUMKQkM1dGFXd3dDb2NJQUFBQUFBQUFBQUF3SW9jZ0FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQQpBQUFBQUFBd0RnWURWUjBQQVFIL0JBUURBZ0dHTUg4R0NDc0dBUVVGQndFQkJITXdjVEF5QmdnckJnRUZCUWN3CkFZWW1hSFIwY0RvdkwybHpjbWN1ZEhKMWMzUnBaQzV2WTNOd0xtbGtaVzUwY25WemRDNWpiMjB3T3dZSUt3WUIKQlFVSE1BS0dMMmgwZEhBNkx5OWhjSEJ6TG1sa1pXNTBjblZ6ZEM1amIyMHZjbTl2ZEhNdlpITjBjbTl2ZEdOaAplRE11Y0Rkak1COEdBMVVkSXdRWU1CYUFGT21rUCs2ZXBlYnkxZGQ1WUR5VHBpNGtqcGVxTUZRR0ExVWRJQVJOCk1Fc3dDQVlHWjRFTUFRSUJNRDhHQ3lzR0FRUUJndDhUQVFFQk1EQXdMZ1lJS3dZQkJRVUhBZ0VXSW1oMGRIQTYKTHk5amNITXVjbTl2ZEMxNE1TNXNaWFJ6Wlc1amNubHdkQzV2Y21jd1BBWURWUjBmQkRVd016QXhvQytnTFlZcgphSFIwY0RvdkwyTnliQzVwWkdWdWRISjFjM1F1WTI5dEwwUlRWRkpQVDFSRFFWZ3pRMUpNTG1OeWJEQWRCZ05WCkhRNEVGZ1FVKzNoUEV2bGdGWU1zbnhkL05CbXpMamJxUVlrd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFBMFkKQWVMWE9rbHg0aGhDaWtVVWwrQmRuRmZuMWcwVzVBaVFMVk5JT0w2UG5xWHUwd2puaE55aHFkd25maFlNbm95NAppZFJoNGxCNnB6OEdmOXBubExkL0RuV1NWM2dTKy9JL21BbDFkQ2tLYnk2SDJWNzkwZTZJSG1JSzJLWW0zam0rClUrK0ZJZEdwQmRzUVRTZG1pWC9yQXl1eE1ETTBhZE1rTkJ3VGZRbVpRQ3o2bkdIdzFRY1NQWk12WnBzQzhTa3YKZWt6eHNqRjFvdE9yTVVQTlBRdnRUV3JWeDhHbFIycWZ4LzR4YlFhMXYyZnJOdkZCQ21PNTlnb3oram5XdmZUdApqMk5qd0RaN3ZsTUJzUG0xNmRiS1lDODQwdXZSb1pqeHFzZGMzQ2hDWmpxaW1GcWxORy94b1BBOCtkVGljWnpDClhFOWlqUEljdlc2eTFhYTNiR3c9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
}
}
]
},
"ChallengeCerts": {}
}

View File

@@ -6,79 +6,126 @@ import (
"net/http"
"net/http/httptest"
"reflect"
"sort"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/xenolf/lego/acme"
acmeprovider "github.com/traefik/traefik/provider/acme"
"github.com/traefik/traefik/tls/generate"
"github.com/traefik/traefik/types"
)
func TestDomainsSet(t *testing.T) {
checkMap := map[string]Domains{
"": {},
"foo.com": {Domain{Main: "foo.com", SANs: []string{}}},
"foo.com,bar.net": {Domain{Main: "foo.com", SANs: []string{"bar.net"}}},
"foo.com,bar1.net,bar2.net,bar3.net": {Domain{Main: "foo.com", SANs: []string{"bar1.net", "bar2.net", "bar3.net"}}},
testCases := []struct {
input string
expected types.Domains
}{
{
input: "",
expected: types.Domains{},
},
{
input: "foo1.com",
expected: types.Domains{
types.Domain{Main: "foo1.com"},
},
},
{
input: "foo2.com,bar.net",
expected: types.Domains{
types.Domain{
Main: "foo2.com",
SANs: []string{"bar.net"},
},
},
},
{
input: "foo3.com,bar1.net,bar2.net,bar3.net",
expected: types.Domains{
types.Domain{
Main: "foo3.com",
SANs: []string{"bar1.net", "bar2.net", "bar3.net"},
},
},
},
}
for in, check := range checkMap {
ds := Domains{}
ds.Set(in)
if !reflect.DeepEqual(check, ds) {
t.Errorf("Expected %+v\nGot %+v", check, ds)
}
for _, test := range testCases {
test := test
t.Run(test.input, func(t *testing.T) {
t.Parallel()
domains := types.Domains{}
domains.Set(test.input)
assert.Exactly(t, test.expected, domains)
})
}
}
func TestDomainsSetAppend(t *testing.T) {
inSlice := []string{
"",
"foo1.com",
"foo2.com,bar.net",
"foo3.com,bar1.net,bar2.net,bar3.net",
testCases := []struct {
input string
expected types.Domains
}{
{
input: "",
expected: types.Domains{},
},
{
input: "foo1.com",
expected: types.Domains{
types.Domain{Main: "foo1.com"},
},
},
{
input: "foo2.com,bar.net",
expected: types.Domains{
types.Domain{Main: "foo1.com"},
types.Domain{
Main: "foo2.com",
SANs: []string{"bar.net"},
},
},
},
{
input: "foo3.com,bar1.net,bar2.net,bar3.net",
expected: types.Domains{
types.Domain{Main: "foo1.com"},
types.Domain{
Main: "foo2.com",
SANs: []string{"bar.net"},
},
types.Domain{
Main: "foo3.com",
SANs: []string{"bar1.net", "bar2.net", "bar3.net"},
},
},
},
}
checkSlice := []Domains{
{},
{
Domain{
Main: "foo1.com",
SANs: []string{}}},
{
Domain{
Main: "foo1.com",
SANs: []string{}},
Domain{
Main: "foo2.com",
SANs: []string{"bar.net"}}},
{
Domain{
Main: "foo1.com",
SANs: []string{}},
Domain{
Main: "foo2.com",
SANs: []string{"bar.net"}},
Domain{Main: "foo3.com",
SANs: []string{"bar1.net", "bar2.net", "bar3.net"}}},
}
ds := Domains{}
for i, in := range inSlice {
ds.Set(in)
if !reflect.DeepEqual(checkSlice[i], ds) {
t.Errorf("Expected %s %+v\nGot %+v", in, checkSlice[i], ds)
}
// append to
domains := types.Domains{}
for _, test := range testCases {
t.Run(test.input, func(t *testing.T) {
domains.Set(test.input)
assert.Exactly(t, test.expected, domains)
})
}
}
func TestCertificatesRenew(t *testing.T) {
foo1Cert, foo1Key, _ := generateKeyPair("foo1.com", time.Now())
foo2Cert, foo2Key, _ := generateKeyPair("foo2.com", time.Now())
foo1Cert, foo1Key, _ := generate.KeyPair("foo1.com", time.Now())
foo2Cert, foo2Key, _ := generate.KeyPair("foo2.com", time.Now())
domainsCertificates := DomainsCertificates{
lock: sync.RWMutex{},
Certs: []*DomainsCertificate{
{
Domains: Domain{
Main: "foo1.com",
SANs: []string{}},
Domains: types.Domain{
Main: "foo1.com"},
Certificate: &Certificate{
Domain: "foo1.com",
CertURL: "url",
@@ -88,9 +135,8 @@ func TestCertificatesRenew(t *testing.T) {
},
},
{
Domains: Domain{
Main: "foo2.com",
SANs: []string{}},
Domains: types.Domain{
Main: "foo2.com"},
Certificate: &Certificate{
Domain: "foo2.com",
CertURL: "url",
@@ -101,7 +147,8 @@ func TestCertificatesRenew(t *testing.T) {
},
},
}
foo1Cert, foo1Key, _ = generateKeyPair("foo1.com", time.Now())
foo1Cert, foo1Key, _ = generate.KeyPair("foo1.com", time.Now())
newCertificate := &Certificate{
Domain: "foo1.com",
CertURL: "url",
@@ -110,17 +157,15 @@ func TestCertificatesRenew(t *testing.T) {
Certificate: foo1Cert,
}
err := domainsCertificates.renewCertificates(
newCertificate,
Domain{
Main: "foo1.com",
SANs: []string{}})
err := domainsCertificates.renewCertificates(newCertificate, types.Domain{Main: "foo1.com"})
if err != nil {
t.Errorf("Error in renewCertificates :%v", err)
}
if len(domainsCertificates.Certs) != 2 {
t.Errorf("Expected domainsCertificates length %d %+v\nGot %+v", 2, domainsCertificates.Certs, len(domainsCertificates.Certs))
}
if !reflect.DeepEqual(domainsCertificates.Certs[0].Certificate, newCertificate) {
t.Errorf("Expected new certificate %+v \nGot %+v", newCertificate, domainsCertificates.Certs[0].Certificate)
}
@@ -128,17 +173,16 @@ func TestCertificatesRenew(t *testing.T) {
func TestRemoveDuplicates(t *testing.T) {
now := time.Now()
fooCert, fooKey, _ := generateKeyPair("foo.com", now)
foo24Cert, foo24Key, _ := generateKeyPair("foo.com", now.Add(24*time.Hour))
foo48Cert, foo48Key, _ := generateKeyPair("foo.com", now.Add(48*time.Hour))
barCert, barKey, _ := generateKeyPair("bar.com", now)
fooCert, fooKey, _ := generate.KeyPair("foo.com", now)
foo24Cert, foo24Key, _ := generate.KeyPair("foo.com", now.Add(24*time.Hour))
foo48Cert, foo48Key, _ := generate.KeyPair("foo.com", now.Add(48*time.Hour))
barCert, barKey, _ := generate.KeyPair("bar.com", now)
domainsCertificates := DomainsCertificates{
lock: sync.RWMutex{},
Certs: []*DomainsCertificate{
{
Domains: Domain{
Main: "foo.com",
SANs: []string{}},
Domains: types.Domain{
Main: "foo.com"},
Certificate: &Certificate{
Domain: "foo.com",
CertURL: "url",
@@ -148,9 +192,8 @@ func TestRemoveDuplicates(t *testing.T) {
},
},
{
Domains: Domain{
Main: "foo.com",
SANs: []string{}},
Domains: types.Domain{
Main: "foo.com"},
Certificate: &Certificate{
Domain: "foo.com",
CertURL: "url",
@@ -160,9 +203,8 @@ func TestRemoveDuplicates(t *testing.T) {
},
},
{
Domains: Domain{
Main: "foo.com",
SANs: []string{}},
Domains: types.Domain{
Main: "foo.com"},
Certificate: &Certificate{
Domain: "foo.com",
CertURL: "url",
@@ -172,9 +214,8 @@ func TestRemoveDuplicates(t *testing.T) {
},
},
{
Domains: Domain{
Main: "bar.com",
SANs: []string{}},
Domains: types.Domain{
Main: "bar.com"},
Certificate: &Certificate{
Domain: "bar.com",
CertURL: "url",
@@ -184,9 +225,8 @@ func TestRemoveDuplicates(t *testing.T) {
},
},
{
Domains: Domain{
Main: "foo.com",
SANs: []string{}},
Domains: types.Domain{
Main: "foo.com"},
Certificate: &Certificate{
Domain: "foo.com",
CertURL: "url",
@@ -217,39 +257,10 @@ func TestRemoveDuplicates(t *testing.T) {
}
}
func TestNoPreCheckOverride(t *testing.T) {
acme.PreCheckDNS = nil // Irreversable - but not expecting real calls into this during testing process
err := dnsOverrideDelay(0)
if err != nil {
t.Errorf("Error in dnsOverrideDelay :%v", err)
}
if acme.PreCheckDNS != nil {
t.Errorf("Unexpected change to acme.PreCheckDNS when leaving DNS verification as is.")
}
}
func TestSillyPreCheckOverride(t *testing.T) {
err := dnsOverrideDelay(-5)
if err == nil {
t.Errorf("Missing expected error in dnsOverrideDelay!")
}
}
func TestPreCheckOverride(t *testing.T) {
acme.PreCheckDNS = nil // Irreversable - but not expecting real calls into this during testing process
err := dnsOverrideDelay(5)
if err != nil {
t.Errorf("Error in dnsOverrideDelay :%v", err)
}
if acme.PreCheckDNS == nil {
t.Errorf("No change to acme.PreCheckDNS when meant to be adding enforcing override function.")
}
}
func TestAcmeClientCreation(t *testing.T) {
acme.PreCheckDNS = nil // Irreversable - but not expecting real calls into this during testing process
// Lengthy setup to avoid external web requests - oh for easier golang testing!
account := &Account{Email: "f@f"}
account.PrivateKey, _ = base64.StdEncoding.DecodeString(`
MIIBPAIBAAJBAMp2Ni92FfEur+CAvFkgC12LT4l9D53ApbBpDaXaJkzzks+KsLw9zyAxvlrfAyTCQ
7tDnEnIltAXyQ0uOFUUdcMCAwEAAQJAK1FbipATZcT9cGVa5x7KD7usytftLW14heQUPXYNV80r/3
@@ -257,29 +268,76 @@ lmnpvjL06dffRpwkYeN8DATQF/QOcy3NNNGDw/4QIhAPAKmiZFxA/qmRXsuU8Zhlzf16WrNZ68K64
asn/h3qZrAiEA1+wFR3WXCPIolOvd7AHjfgcTKQNkoMPywU4FYUNQ1AkCIQDv8yk0qPjckD6HVCPJ
llJh9MC0svjevGtNlxJoE3lmEQIhAKXy1wfZ32/XtcrnENPvi6lzxI0T94X7s5pP3aCoPPoJAiEAl
cijFkALeQp/qyeXdFld2v9gUN3eCgljgcl0QweRoIc=---`)
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{
"new-authz": "https://foo/acme/new-authz",
"new-cert": "https://foo/acme/new-cert",
"new-reg": "https://foo/acme/new-reg",
"revoke-cert": "https://foo/acme/revoke-cert"
_, err := w.Write([]byte(`{
"GPHhmRVEDas": "https://community.letsencrypt.org/t/adding-random-entries-to-the-directory/33417",
"keyChange": "https://foo/acme/key-change",
"meta": {
"termsOfService": "https://boulder:4431/terms/v7"
},
"newAccount": "https://foo/acme/new-acct",
"newNonce": "https://foo/acme/new-nonce",
"newOrder": "https://foo/acme/new-order",
"revokeCert": "https://foo/acme/revoke-cert"
}`))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}))
defer ts.Close()
a := ACME{DNSProvider: "manual", DelayDontCheckDNS: 10, CAServer: ts.URL}
a := ACME{
CAServer: ts.URL,
DNSChallenge: &acmeprovider.DNSChallenge{
Provider: "manual",
DelayBeforeCheck: 10,
DisablePropagationCheck: true,
},
}
client, err := a.buildACMEClient(account)
if err != nil {
t.Errorf("Error in buildACMEClient: %v", err)
}
if client == nil {
t.Errorf("No client from buildACMEClient!")
}
if acme.PreCheckDNS == nil {
t.Errorf("No change to acme.PreCheckDNS when meant to be adding enforcing override function.")
t.Error("No client from buildACMEClient!")
}
}
func TestAcme_getUncheckedCertificates(t *testing.T) {
mm := make(map[string]*tls.Certificate)
mm["*.containo.us"] = &tls.Certificate{}
mm["traefik.acme.io"] = &tls.Certificate{}
dm := make(map[string]struct{})
dm["*.traefik.wtf"] = struct{}{}
a := ACME{TLSConfig: &tls.Config{NameToCertificate: mm}, resolvingDomains: dm}
domains := []string{"traefik.containo.us", "trae.containo.us", "foo.traefik.wtf"}
uncheckedDomains := a.getUncheckedDomains(domains, nil)
assert.Empty(t, uncheckedDomains)
domains = []string{"traefik.acme.io", "trae.acme.io"}
uncheckedDomains = a.getUncheckedDomains(domains, nil)
assert.Len(t, uncheckedDomains, 1)
domainsCertificates := DomainsCertificates{Certs: []*DomainsCertificate{
{
tlsCert: &tls.Certificate{},
Domains: types.Domain{
Main: "*.acme.wtf",
SANs: []string{"trae.acme.io"},
},
},
}}
account := Account{DomainsCertificate: domainsCertificates}
uncheckedDomains = a.getUncheckedDomains(domains, &account)
assert.Empty(t, uncheckedDomains)
domains = []string{"traefik.containo.us", "trae.containo.us", "traefik.wtf"}
uncheckedDomains = a.getUncheckedDomains(domains, nil)
assert.Len(t, uncheckedDomains, 1)
}
func TestAcme_getProvidedCertificate(t *testing.T) {
mm := make(map[string]*tls.Certificate)
mm["*.containo.us"] = &tls.Certificate{}
@@ -287,10 +345,459 @@ func TestAcme_getProvidedCertificate(t *testing.T) {
a := ACME{TLSConfig: &tls.Config{NameToCertificate: mm}}
domains := []string{"traefik.containo.us", "trae.containo.us"}
certificate := a.getProvidedCertificate(domains)
domain := "traefik.containo.us"
certificate := a.getProvidedCertificate(domain)
assert.NotNil(t, certificate)
domains = []string{"traefik.acme.io", "trae.acme.io"}
certificate = a.getProvidedCertificate(domains)
domain = "trae.acme.io"
certificate = a.getProvidedCertificate(domain)
assert.Nil(t, certificate)
}
func TestAcme_getValidDomain(t *testing.T) {
testCases := []struct {
desc string
domains []string
wildcardAllowed bool
dnsChallenge *acmeprovider.DNSChallenge
expectedErr string
expectedDomains []string
}{
{
desc: "valid wildcard",
domains: []string{"*.traefik.wtf"},
dnsChallenge: &acmeprovider.DNSChallenge{},
wildcardAllowed: true,
expectedErr: "",
expectedDomains: []string{"*.traefik.wtf"},
},
{
desc: "no wildcard",
domains: []string{"traefik.wtf", "foo.traefik.wtf"},
dnsChallenge: &acmeprovider.DNSChallenge{},
expectedErr: "",
wildcardAllowed: true,
expectedDomains: []string{"traefik.wtf", "foo.traefik.wtf"},
},
{
desc: "unauthorized wildcard",
domains: []string{"*.traefik.wtf"},
dnsChallenge: &acmeprovider.DNSChallenge{},
wildcardAllowed: false,
expectedErr: "unable to generate a wildcard certificate for domain \"*.traefik.wtf\" from a 'Host' rule",
expectedDomains: nil,
},
{
desc: "no domain",
domains: []string{},
dnsChallenge: nil,
wildcardAllowed: true,
expectedErr: "unable to generate a certificate when no domain is given",
expectedDomains: nil,
},
{
desc: "no DNSChallenge",
domains: []string{"*.traefik.wtf", "foo.traefik.wtf"},
dnsChallenge: nil,
wildcardAllowed: true,
expectedErr: "unable to generate a wildcard certificate for domain \"*.traefik.wtf,foo.traefik.wtf\" : ACME needs a DNSChallenge",
expectedDomains: nil,
},
{
desc: "unauthorized wildcard with SAN",
domains: []string{"*.*.traefik.wtf", "foo.traefik.wtf"},
dnsChallenge: &acmeprovider.DNSChallenge{},
wildcardAllowed: true,
expectedErr: "unable to generate a wildcard certificate for domain \"*.*.traefik.wtf,foo.traefik.wtf\" : ACME does not allow '*.*' wildcard domain",
expectedDomains: nil,
},
{
desc: "wildcard with SANs",
domains: []string{"*.traefik.wtf", "traefik.wtf"},
dnsChallenge: &acmeprovider.DNSChallenge{},
wildcardAllowed: true,
expectedErr: "",
expectedDomains: []string{"*.traefik.wtf", "traefik.wtf"},
},
{
desc: "wildcard SANs",
domains: []string{"*.traefik.wtf", "*.acme.wtf"},
dnsChallenge: &acmeprovider.DNSChallenge{},
wildcardAllowed: true,
expectedErr: "",
expectedDomains: []string{"*.traefik.wtf", "*.acme.wtf"},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
a := ACME{}
if test.dnsChallenge != nil {
a.DNSChallenge = test.dnsChallenge
}
domains, err := a.getValidDomains(test.domains, test.wildcardAllowed)
if len(test.expectedErr) > 0 {
assert.EqualError(t, err, test.expectedErr, "Unexpected error.")
} else {
assert.Equal(t, len(test.expectedDomains), len(domains), "Unexpected domains.")
}
})
}
}
func TestAcme_getCertificateForDomain(t *testing.T) {
testCases := []struct {
desc string
domain string
dc *DomainsCertificates
expected *DomainsCertificate
expectedFound bool
}{
{
desc: "non-wildcard exact match",
domain: "foo.traefik.wtf",
dc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Domains: types.Domain{
Main: "foo.traefik.wtf",
},
},
},
},
expected: &DomainsCertificate{
Domains: types.Domain{
Main: "foo.traefik.wtf",
},
},
expectedFound: true,
},
{
desc: "non-wildcard no match",
domain: "bar.traefik.wtf",
dc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Domains: types.Domain{
Main: "foo.traefik.wtf",
},
},
},
},
expected: nil,
expectedFound: false,
},
{
desc: "wildcard match",
domain: "foo.traefik.wtf",
dc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Domains: types.Domain{
Main: "*.traefik.wtf",
},
},
},
},
expected: &DomainsCertificate{
Domains: types.Domain{
Main: "*.traefik.wtf",
},
},
expectedFound: true,
},
{
desc: "wildcard no match",
domain: "foo.traefik.wtf",
dc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Domains: types.Domain{
Main: "*.bar.traefik.wtf",
},
},
},
},
expected: nil,
expectedFound: false,
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
got, found := test.dc.getCertificateForDomain(test.domain)
assert.Equal(t, test.expectedFound, found)
assert.Equal(t, test.expected, got)
})
}
}
func TestRemoveEmptyCertificates(t *testing.T) {
now := time.Now()
fooCert, fooKey, _ := generate.KeyPair("foo.com", now)
acmeCert, acmeKey, _ := generate.KeyPair("acme.wtf", now.Add(24*time.Hour))
barCert, barKey, _ := generate.KeyPair("bar.com", now)
testCases := []struct {
desc string
dc *DomainsCertificates
expectedDc *DomainsCertificates
}{
{
desc: "No empty certificate",
dc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Certificate: &Certificate{
Certificate: fooCert,
PrivateKey: fooKey,
},
Domains: types.Domain{
Main: "foo.com",
},
},
{
Certificate: &Certificate{
Certificate: acmeCert,
PrivateKey: acmeKey,
},
Domains: types.Domain{
Main: "acme.wtf",
},
},
{
Certificate: &Certificate{
Certificate: barCert,
PrivateKey: barKey,
},
Domains: types.Domain{
Main: "bar.com",
},
},
},
},
expectedDc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Certificate: &Certificate{
Certificate: fooCert,
PrivateKey: fooKey,
},
Domains: types.Domain{
Main: "foo.com",
},
},
{
Certificate: &Certificate{
Certificate: acmeCert,
PrivateKey: acmeKey,
},
Domains: types.Domain{
Main: "acme.wtf",
},
},
{
Certificate: &Certificate{
Certificate: barCert,
PrivateKey: barKey,
},
Domains: types.Domain{
Main: "bar.com",
},
},
},
},
},
{
desc: "First certificate is nil",
dc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Domains: types.Domain{
Main: "foo.com",
},
},
{
Certificate: &Certificate{
Certificate: acmeCert,
PrivateKey: acmeKey,
},
Domains: types.Domain{
Main: "acme.wtf",
},
},
{
Certificate: &Certificate{
Certificate: barCert,
PrivateKey: barKey,
},
Domains: types.Domain{
Main: "bar.com",
},
},
},
},
expectedDc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Certificate: &Certificate{
Certificate: acmeCert,
PrivateKey: acmeKey,
},
Domains: types.Domain{
Main: "acme.wtf",
},
},
{
Certificate: &Certificate{
Certificate: nil,
PrivateKey: barKey,
},
Domains: types.Domain{
Main: "bar.com",
},
},
},
},
},
{
desc: "Last certificate is empty",
dc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Certificate: &Certificate{
Certificate: fooCert,
PrivateKey: fooKey,
},
Domains: types.Domain{
Main: "foo.com",
},
},
{
Certificate: &Certificate{
Certificate: acmeCert,
PrivateKey: acmeKey,
},
Domains: types.Domain{
Main: "acme.wtf",
},
},
{
Certificate: &Certificate{},
Domains: types.Domain{
Main: "bar.com",
},
},
},
},
expectedDc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Certificate: &Certificate{
Certificate: fooCert,
PrivateKey: fooKey,
},
Domains: types.Domain{
Main: "foo.com",
},
},
{
Certificate: &Certificate{
Certificate: acmeCert,
PrivateKey: acmeKey,
},
Domains: types.Domain{
Main: "acme.wtf",
},
},
},
},
},
{
desc: "First and last certificates are nil or empty",
dc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Domains: types.Domain{
Main: "foo.com",
},
},
{
Certificate: &Certificate{
Certificate: acmeCert,
PrivateKey: acmeKey,
},
Domains: types.Domain{
Main: "acme.wtf",
},
},
{
Certificate: &Certificate{},
Domains: types.Domain{
Main: "bar.com",
},
},
},
},
expectedDc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Certificate: &Certificate{
Certificate: acmeCert,
PrivateKey: acmeKey,
},
Domains: types.Domain{
Main: "acme.wtf",
},
},
},
},
},
{
desc: "All certificates are nil or empty",
dc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Domains: types.Domain{
Main: "foo.com",
},
},
{
Domains: types.Domain{
Main: "foo24.com",
},
},
{
Certificate: &Certificate{},
Domains: types.Domain{
Main: "bar.com",
},
},
},
},
expectedDc: &DomainsCertificates{
Certs: []*DomainsCertificate{},
},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
a := &Account{DomainsCertificate: *test.dc}
a.Init()
assert.Equal(t, len(test.expectedDc.Certs), len(a.DomainsCertificate.Certs))
sort.Sort(&a.DomainsCertificate)
sort.Sort(test.expectedDc)
for key, value := range test.expectedDc.Certs {
assert.Equal(t, value.Domains.Main, a.DomainsCertificate.Certs[key].Domains.Main)
}
})
}
}

View File

@@ -1,97 +0,0 @@
package acme
import (
"crypto/tls"
"fmt"
"strings"
"sync"
"time"
"github.com/cenk/backoff"
"github.com/containous/traefik/cluster"
"github.com/containous/traefik/log"
"github.com/containous/traefik/safe"
"github.com/xenolf/lego/acme"
)
var _ acme.ChallengeProviderTimeout = (*challengeProvider)(nil)
type challengeProvider struct {
store cluster.Store
lock sync.RWMutex
}
func (c *challengeProvider) getCertificate(domain string) (cert *tls.Certificate, exists bool) {
log.Debugf("Challenge GetCertificate %s", domain)
if !strings.HasSuffix(domain, ".acme.invalid") {
return nil, false
}
c.lock.RLock()
defer c.lock.RUnlock()
account := c.store.Get().(*Account)
if account.ChallengeCerts == nil {
return nil, false
}
account.Init()
var result *tls.Certificate
operation := func() error {
for _, cert := range account.ChallengeCerts {
for _, dns := range cert.certificate.Leaf.DNSNames {
if domain == dns {
result = cert.certificate
return nil
}
}
}
return fmt.Errorf("Cannot find challenge cert for domain %s", domain)
}
notify := func(err error, time time.Duration) {
log.Errorf("Error getting cert: %v, retrying in %s", err, time)
}
ebo := backoff.NewExponentialBackOff()
ebo.MaxElapsedTime = 60 * time.Second
err := backoff.RetryNotify(safe.OperationWithRecover(operation), ebo, notify)
if err != nil {
log.Errorf("Error getting cert: %v", err)
return nil, false
}
return result, true
}
func (c *challengeProvider) Present(domain, token, keyAuth string) error {
log.Debugf("Challenge Present %s", domain)
cert, _, err := TLSSNI01ChallengeCert(keyAuth)
if err != nil {
return err
}
c.lock.Lock()
defer c.lock.Unlock()
transaction, object, err := c.store.Begin()
if err != nil {
return err
}
account := object.(*Account)
if account.ChallengeCerts == nil {
account.ChallengeCerts = map[string]*ChallengeCert{}
}
account.ChallengeCerts[domain] = &cert
return transaction.Commit(account)
}
func (c *challengeProvider) CleanUp(domain, token, keyAuth string) error {
log.Debugf("Challenge CleanUp %s", domain)
c.lock.Lock()
defer c.lock.Unlock()
transaction, object, err := c.store.Begin()
if err != nil {
return err
}
account := object.(*Account)
delete(account.ChallengeCerts, domain)
return transaction.Commit(account)
}
func (c *challengeProvider) Timeout() (timeout, interval time.Duration) {
return 60 * time.Second, 5 * time.Second
}

View File

@@ -0,0 +1,104 @@
package acme
import (
"fmt"
"sync"
"time"
"github.com/cenk/backoff"
"github.com/go-acme/lego/v4/challenge"
"github.com/traefik/traefik/cluster"
"github.com/traefik/traefik/log"
"github.com/traefik/traefik/safe"
)
var _ challenge.ProviderTimeout = (*challengeHTTPProvider)(nil)
type challengeHTTPProvider struct {
store cluster.Store
lock sync.RWMutex
}
func (c *challengeHTTPProvider) getTokenValue(token, domain string) []byte {
log.Debugf("Looking for an existing ACME challenge for token %v...", token)
c.lock.RLock()
defer c.lock.RUnlock()
account := c.store.Get().(*Account)
if account.HTTPChallenge == nil {
return []byte{}
}
var result []byte
operation := func() error {
var ok bool
if result, ok = account.HTTPChallenge[token][domain]; !ok {
return fmt.Errorf("cannot find challenge for token %v", token)
}
return nil
}
notify := func(err error, time time.Duration) {
log.Errorf("Error getting challenge for token retrying in %s", time)
}
ebo := backoff.NewExponentialBackOff()
ebo.MaxElapsedTime = 60 * time.Second
err := backoff.RetryNotify(safe.OperationWithRecover(operation), ebo, notify)
if err != nil {
log.Errorf("Error getting challenge for token: %v", err)
return []byte{}
}
return result
}
func (c *challengeHTTPProvider) Present(domain, token, keyAuth string) error {
log.Debugf("Challenge Present %s", domain)
c.lock.Lock()
defer c.lock.Unlock()
transaction, object, err := c.store.Begin()
if err != nil {
return err
}
account := object.(*Account)
if account.HTTPChallenge == nil {
account.HTTPChallenge = map[string]map[string][]byte{}
}
if _, ok := account.HTTPChallenge[token]; !ok {
account.HTTPChallenge[token] = map[string][]byte{}
}
account.HTTPChallenge[token][domain] = []byte(keyAuth)
return transaction.Commit(account)
}
func (c *challengeHTTPProvider) CleanUp(domain, token, keyAuth string) error {
log.Debugf("Challenge CleanUp %s", domain)
c.lock.Lock()
defer c.lock.Unlock()
transaction, object, err := c.store.Begin()
if err != nil {
return err
}
account := object.(*Account)
if _, ok := account.HTTPChallenge[token]; ok {
if _, domainOk := account.HTTPChallenge[token][domain]; domainOk {
delete(account.HTTPChallenge[token], domain)
}
if len(account.HTTPChallenge[token]) == 0 {
delete(account.HTTPChallenge, token)
}
}
return transaction.Commit(account)
}
func (c *challengeHTTPProvider) Timeout() (timeout, interval time.Duration) {
return 60 * time.Second, 5 * time.Second
}

View File

@@ -0,0 +1,128 @@
package acme
import (
"crypto/tls"
"fmt"
"strings"
"sync"
"time"
"github.com/cenk/backoff"
"github.com/go-acme/lego/v4/challenge"
"github.com/go-acme/lego/v4/challenge/tlsalpn01"
"github.com/traefik/traefik/cluster"
"github.com/traefik/traefik/log"
"github.com/traefik/traefik/safe"
)
var _ challenge.ProviderTimeout = (*challengeTLSProvider)(nil)
type challengeTLSProvider struct {
store cluster.Store
lock sync.RWMutex
}
func (c *challengeTLSProvider) getCertificate(domain string) (cert *tls.Certificate, exists bool) {
log.Debugf("Looking for an existing ACME challenge for %s...", domain)
if !strings.HasSuffix(domain, ".acme.invalid") {
return nil, false
}
c.lock.RLock()
defer c.lock.RUnlock()
account := c.store.Get().(*Account)
if account.ChallengeCerts == nil {
return nil, false
}
account.Init()
var result *tls.Certificate
operation := func() error {
for _, cert := range account.ChallengeCerts {
for _, dns := range cert.certificate.Leaf.DNSNames {
if domain == dns {
result = cert.certificate
return nil
}
}
}
return fmt.Errorf("cannot find challenge cert for domain %s", domain)
}
notify := func(err error, time time.Duration) {
log.Errorf("Error getting cert: %v, retrying in %s", err, time)
}
ebo := backoff.NewExponentialBackOff()
ebo.MaxElapsedTime = 60 * time.Second
err := backoff.RetryNotify(safe.OperationWithRecover(operation), ebo, notify)
if err != nil {
log.Errorf("Error getting cert: %v", err)
return nil, false
}
return result, true
}
func (c *challengeTLSProvider) Present(domain, token, keyAuth string) error {
log.Debugf("Challenge Present %s", domain)
cert, err := tlsALPN01ChallengeCert(domain, keyAuth)
if err != nil {
return err
}
c.lock.Lock()
defer c.lock.Unlock()
transaction, object, err := c.store.Begin()
if err != nil {
return err
}
account := object.(*Account)
if account.ChallengeCerts == nil {
account.ChallengeCerts = map[string]*ChallengeCert{}
}
account.ChallengeCerts[domain] = cert
return transaction.Commit(account)
}
func (c *challengeTLSProvider) CleanUp(domain, token, keyAuth string) error {
log.Debugf("Challenge CleanUp %s", domain)
c.lock.Lock()
defer c.lock.Unlock()
transaction, object, err := c.store.Begin()
if err != nil {
return err
}
account := object.(*Account)
delete(account.ChallengeCerts, domain)
return transaction.Commit(account)
}
func (c *challengeTLSProvider) Timeout() (timeout, interval time.Duration) {
return 60 * time.Second, 5 * time.Second
}
func tlsALPN01ChallengeCert(domain, keyAuth string) (*ChallengeCert, error) {
tempCertPEM, rsaPrivPEM, err := tlsalpn01.ChallengeBlocks(domain, keyAuth)
if err != nil {
return nil, err
}
certificate, err := tls.X509KeyPair(tempCertPEM, rsaPrivPEM)
if err != nil {
return nil, err
}
return &ChallengeCert{Certificate: tempCertPEM, PrivateKey: rsaPrivPEM, certificate: &certificate}, nil
}

View File

@@ -1,135 +0,0 @@
package acme
import (
"crypto"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/hex"
"encoding/pem"
"fmt"
"math/big"
"time"
)
func generateDefaultCertificate() (*tls.Certificate, error) {
randomBytes := make([]byte, 100)
_, err := rand.Read(randomBytes)
if err != nil {
return nil, err
}
zBytes := sha256.Sum256(randomBytes)
z := hex.EncodeToString(zBytes[:sha256.Size])
domain := fmt.Sprintf("%s.%s.traefik.default", z[:32], z[32:])
certPEM, keyPEM, err := generateKeyPair(domain, time.Time{})
if err != nil {
return nil, err
}
certificate, err := tls.X509KeyPair(certPEM, keyPEM)
if err != nil {
return nil, err
}
return &certificate, nil
}
func generateKeyPair(domain string, expiration time.Time) ([]byte, []byte, error) {
rsaPrivKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, nil, err
}
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(rsaPrivKey)})
certPEM, err := generatePemCert(rsaPrivKey, domain, expiration)
if err != nil {
return nil, nil, err
}
return certPEM, keyPEM, nil
}
func generatePemCert(privKey *rsa.PrivateKey, domain string, expiration time.Time) ([]byte, error) {
derBytes, err := generateDerCert(privKey, expiration, domain)
if err != nil {
return nil, err
}
return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes}), nil
}
func generateDerCert(privKey *rsa.PrivateKey, expiration time.Time, domain string) ([]byte, error) {
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
if err != nil {
return nil, err
}
if expiration.IsZero() {
expiration = time.Now().Add(365)
}
template := x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: "TRAEFIK DEFAULT CERT",
},
NotBefore: time.Now(),
NotAfter: expiration,
KeyUsage: x509.KeyUsageKeyEncipherment,
BasicConstraintsValid: true,
DNSNames: []string{domain},
}
return x509.CreateCertificate(rand.Reader, &template, &template, &privKey.PublicKey, privKey)
}
// TLSSNI01ChallengeCert returns a certificate and target domain for the `tls-sni-01` challenge
func TLSSNI01ChallengeCert(keyAuth string) (ChallengeCert, string, error) {
// generate a new RSA key for the certificates
var tempPrivKey crypto.PrivateKey
tempPrivKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return ChallengeCert{}, "", err
}
rsaPrivKey := tempPrivKey.(*rsa.PrivateKey)
rsaPrivPEM := pemEncode(rsaPrivKey)
zBytes := sha256.Sum256([]byte(keyAuth))
z := hex.EncodeToString(zBytes[:sha256.Size])
domain := fmt.Sprintf("%s.%s.acme.invalid", z[:32], z[32:])
tempCertPEM, err := generatePemCert(rsaPrivKey, domain, time.Time{})
if err != nil {
return ChallengeCert{}, "", err
}
certificate, err := tls.X509KeyPair(tempCertPEM, rsaPrivPEM)
if err != nil {
return ChallengeCert{}, "", err
}
return ChallengeCert{Certificate: tempCertPEM, PrivateKey: rsaPrivPEM, certificate: &certificate}, domain, nil
}
func pemEncode(data interface{}) []byte {
var pemBlock *pem.Block
switch key := data.(type) {
case *ecdsa.PrivateKey:
keyBytes, _ := x509.MarshalECPrivateKey(key)
pemBlock = &pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes}
case *rsa.PrivateKey:
pemBlock = &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)}
break
case *x509.CertificateRequest:
pemBlock = &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: key.Raw}
break
case []byte:
pemBlock = &pem.Block{Type: "CERTIFICATE", Bytes: []byte(data.([]byte))}
}
return pem.EncodeToMemory(pemBlock)
}

View File

@@ -2,22 +2,16 @@ package acme
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"sync"
"github.com/containous/traefik/cluster"
"github.com/containous/traefik/log"
"github.com/traefik/traefik/log"
"github.com/traefik/traefik/provider/acme"
)
var _ cluster.Store = (*LocalStore)(nil)
// LocalStore is a store using a file as storage
type LocalStore struct {
file string
storageLock sync.RWMutex
account *Account
file string
}
// NewLocalStore create a LocalStore
@@ -27,71 +21,157 @@ func NewLocalStore(file string) *LocalStore {
}
}
// Get atomically a struct from the file storage
func (s *LocalStore) Get() cluster.Object {
s.storageLock.RLock()
defer s.storageLock.RUnlock()
return s.account
}
// Load loads file into store
func (s *LocalStore) Load() (cluster.Object, error) {
s.storageLock.Lock()
defer s.storageLock.Unlock()
// Get loads file into store and returns the Account
func (s *LocalStore) Get() (*Account, error) {
account := &Account{}
err := checkPermissions(s.file)
hasData, err := acme.CheckFile(s.file)
if err != nil {
return nil, err
}
f, err := os.Open(s.file)
if err != nil {
return nil, err
if hasData {
f, err := os.Open(s.file)
if err != nil {
return nil, err
}
defer f.Close()
file, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
}
if err := json.Unmarshal(file, &account); err != nil {
return nil, err
}
}
defer f.Close()
file, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
}
if err := json.Unmarshal(file, &account); err != nil {
return nil, err
}
account.Init()
s.account = account
log.Infof("Loaded ACME config from store %s", s.file)
return account, nil
}
// Begin creates a transaction with the KV store.
func (s *LocalStore) Begin() (cluster.Transaction, cluster.Object, error) {
s.storageLock.Lock()
return &localTransaction{LocalStore: s}, s.account, nil
}
// ConvertToNewFormat converts old acme.json format to the new one and store the result into the file (used for the backward compatibility)
func ConvertToNewFormat(fileName string) {
localStore := acme.NewLocalStore(fileName)
var _ cluster.Transaction = (*localTransaction)(nil)
type localTransaction struct {
*LocalStore
dirty bool
}
// Commit allows to set an object in the file storage
func (t *localTransaction) Commit(object cluster.Object) error {
t.LocalStore.account = object.(*Account)
defer t.storageLock.Unlock()
if t.dirty {
return fmt.Errorf("transaction already used, please begin a new one")
storeAccount, err := localStore.GetAccount()
if err != nil {
log.Errorf("Failed to read new account, ACME data conversion is not available : %v", err)
return
}
storeCertificates, err := localStore.GetCertificates()
if err != nil {
log.Errorf("Failed to read new certificates, ACME data conversion is not available : %v", err)
return
}
if storeAccount == nil {
localStore := NewLocalStore(fileName)
account, err := localStore.Get()
if err != nil {
log.Errorf("Failed to read old account, ACME data conversion is not available : %v", err)
return
}
// Convert ACME data from old to new format
newAccount := &acme.Account{}
if account != nil && len(account.Email) > 0 {
err = backupACMEFile(fileName, account)
if err != nil {
log.Errorf("Unable to create a backup for the V1 formatted ACME file: %v", err)
return
}
err = account.RemoveAccountV1Values()
if err != nil {
log.Errorf("Unable to remove ACME Account V1 values during format conversion: %v", err)
return
}
newAccount = &acme.Account{
PrivateKey: account.PrivateKey,
Registration: account.Registration,
Email: account.Email,
KeyType: account.KeyType,
}
var newCertificates []*acme.Certificate
for _, cert := range account.DomainsCertificate.Certs {
newCertificates = append(newCertificates, &acme.Certificate{
Certificate: cert.Certificate.Certificate,
Key: cert.Certificate.PrivateKey,
Domain: cert.Domains,
})
}
// If account is in the old format, storeCertificates is nil or empty and has to be initialized
storeCertificates = newCertificates
}
// Store the data in new format into the file even if account is nil
// to delete Account in ACME v1 format and keeping the certificates
newLocalStore := acme.NewLocalStore(fileName)
newLocalStore.SaveDataChan <- &acme.StoredData{Account: newAccount, Certificates: storeCertificates}
}
}
func backupACMEFile(originalFileName string, account interface{}) error {
// write account to file
data, err := json.MarshalIndent(object, "", " ")
data, err := json.MarshalIndent(account, "", " ")
if err != nil {
return err
}
err = ioutil.WriteFile(t.file, data, 0600)
if err != nil {
return err
}
t.dirty = true
return nil
return ioutil.WriteFile(originalFileName+".bak", data, 0600)
}
// FromNewToOldFormat converts new acme account to the old one (used for the backward compatibility)
func FromNewToOldFormat(fileName string) (*Account, error) {
localStore := acme.NewLocalStore(fileName)
storeAccount, err := localStore.GetAccount()
if err != nil {
return nil, err
}
storeCertificates, err := localStore.GetCertificates()
if err != nil {
return nil, err
}
// Convert ACME Account from new to old format
// (Needed by the KV stores)
var account *Account
if storeAccount != nil {
account = &Account{
Email: storeAccount.Email,
PrivateKey: storeAccount.PrivateKey,
Registration: storeAccount.Registration,
DomainsCertificate: DomainsCertificates{},
KeyType: storeAccount.KeyType,
}
}
// Convert ACME Certificates from new to old format
// (Needed by the KV stores)
if len(storeCertificates) > 0 {
// Account can be nil if data are migrated from new format
// with a ACME V1 Account
if account == nil {
account = &Account{}
}
for _, cert := range storeCertificates {
_, err := account.DomainsCertificate.addCertificateForDomains(&Certificate{
Domain: cert.Domain.Main,
Certificate: cert.Certificate,
PrivateKey: cert.Key,
}, cert.Domain)
if err != nil {
return nil, err
}
}
}
return account, nil
}

31
acme/localStore_test.go Normal file
View File

@@ -0,0 +1,31 @@
package acme
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
)
func TestGet(t *testing.T) {
acmeFile := "./acme_example.json"
folder, prefix := filepath.Split(acmeFile)
tmpFile, err := ioutil.TempFile(folder, prefix)
defer os.Remove(tmpFile.Name())
assert.NoError(t, err)
fileContent, err := ioutil.ReadFile(acmeFile)
assert.NoError(t, err)
tmpFile.Write(fileContent)
localStore := NewLocalStore(tmpFile.Name())
account, err := localStore.Get()
assert.NoError(t, err)
assert.Len(t, account.DomainsCertificate.Certs, 1)
}

View File

@@ -1,25 +0,0 @@
// +build !windows
package acme
import (
"fmt"
"os"
)
// Check file permissions
func checkPermissions(name string) error {
f, err := os.Open(name)
if err != nil {
return err
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
return err
}
if fi.Mode().Perm()&0077 != 0 {
return fmt.Errorf("permissions %o for %s are too open, please use 600", fi.Mode().Perm(), name)
}
return nil
}

View File

@@ -1,6 +0,0 @@
package acme
// Do not check file permissions on Windows right now
func checkPermissions(name string) error {
return nil
}

136
anonymize/anonymize.go Normal file
View File

@@ -0,0 +1,136 @@
package anonymize
import (
"encoding/json"
"fmt"
"reflect"
"regexp"
"github.com/mitchellh/copystructure"
"github.com/mvdan/xurls"
)
const (
maskShort = "xxxx"
maskLarge = maskShort + maskShort + maskShort + maskShort + maskShort + maskShort + maskShort + maskShort
)
// Do configuration.
func Do(baseConfig interface{}, indent bool) (string, error) {
anomConfig, err := copystructure.Copy(baseConfig)
if err != nil {
return "", err
}
val := reflect.ValueOf(anomConfig)
err = doOnStruct(val)
if err != nil {
return "", err
}
configJSON, err := marshal(anomConfig, indent)
if err != nil {
return "", err
}
return doOnJSON(string(configJSON)), nil
}
func doOnJSON(input string) string {
mailExp := regexp.MustCompile(`\w[-._\w]*\w@\w[-._\w]*\w\.\w{2,3}"`)
return xurls.Relaxed.ReplaceAllString(mailExp.ReplaceAllString(input, maskLarge+"\""), maskLarge)
}
func doOnStruct(field reflect.Value) error {
switch field.Kind() {
case reflect.Ptr:
if !field.IsNil() {
if err := doOnStruct(field.Elem()); err != nil {
return err
}
}
case reflect.Struct:
for i := 0; i < field.NumField(); i++ {
fld := field.Field(i)
stField := field.Type().Field(i)
if !isExported(stField) {
continue
}
if stField.Tag.Get("export") == "true" {
if err := doOnStruct(fld); err != nil {
return err
}
} else {
if err := reset(fld, stField.Name); err != nil {
return err
}
}
}
case reflect.Map:
for _, key := range field.MapKeys() {
if err := doOnStruct(field.MapIndex(key)); err != nil {
return err
}
}
case reflect.Slice:
for j := 0; j < field.Len(); j++ {
if err := doOnStruct(field.Index(j)); err != nil {
return err
}
}
}
return nil
}
func reset(field reflect.Value, name string) error {
if !field.CanSet() {
return fmt.Errorf("cannot reset field %s", name)
}
switch field.Kind() {
case reflect.Ptr:
if !field.IsNil() {
field.Set(reflect.Zero(field.Type()))
}
case reflect.Struct:
if field.IsValid() {
field.Set(reflect.Zero(field.Type()))
}
case reflect.String:
if field.String() != "" {
field.Set(reflect.ValueOf(maskShort))
}
case reflect.Map:
if field.Len() > 0 {
field.Set(reflect.MakeMap(field.Type()))
}
case reflect.Slice:
if field.Len() > 0 {
field.Set(reflect.MakeSlice(field.Type(), 0, 0))
}
case reflect.Interface:
if !field.IsNil() {
return reset(field.Elem(), "")
}
default:
// Primitive type
field.Set(reflect.Zero(field.Type()))
}
return nil
}
// isExported return true is a struct field is exported, else false
func isExported(f reflect.StructField) bool {
if f.PkgPath != "" && !f.Anonymous {
return false
}
return true
}
func marshal(anomConfig interface{}, indent bool) ([]byte, error) {
if indent {
return json.MarshalIndent(anomConfig, "", " ")
}
return json.Marshal(anomConfig)
}

View File

@@ -0,0 +1,700 @@
package anonymize
import (
"crypto/tls"
"os"
"testing"
"time"
"github.com/containous/flaeg"
assetfs "github.com/elazarl/go-bindata-assetfs"
"github.com/thoas/stats"
"github.com/traefik/traefik/acme"
"github.com/traefik/traefik/api"
"github.com/traefik/traefik/configuration"
"github.com/traefik/traefik/middlewares"
"github.com/traefik/traefik/provider"
acmeprovider "github.com/traefik/traefik/provider/acme"
"github.com/traefik/traefik/provider/boltdb"
"github.com/traefik/traefik/provider/consul"
"github.com/traefik/traefik/provider/consulcatalog"
"github.com/traefik/traefik/provider/docker"
"github.com/traefik/traefik/provider/dynamodb"
"github.com/traefik/traefik/provider/ecs"
"github.com/traefik/traefik/provider/etcd"
"github.com/traefik/traefik/provider/eureka"
"github.com/traefik/traefik/provider/file"
"github.com/traefik/traefik/provider/kubernetes"
"github.com/traefik/traefik/provider/kv"
"github.com/traefik/traefik/provider/marathon"
"github.com/traefik/traefik/provider/mesos"
"github.com/traefik/traefik/provider/rancher"
"github.com/traefik/traefik/provider/zk"
"github.com/traefik/traefik/safe"
traefiktls "github.com/traefik/traefik/tls"
"github.com/traefik/traefik/types"
)
func TestDo_globalConfiguration(t *testing.T) {
config := &configuration.GlobalConfiguration{}
config.GraceTimeOut = flaeg.Duration(666 * time.Second)
config.Debug = true
config.CheckNewVersion = true
config.AccessLogsFile = "AccessLogsFile"
config.AccessLog = &types.AccessLog{
FilePath: "AccessLog FilePath",
Format: "AccessLog Format",
}
config.TraefikLogsFile = "TraefikLogsFile"
config.LogLevel = "LogLevel"
config.EntryPoints = configuration.EntryPoints{
"foo": {
Address: "foo Address",
TLS: &traefiktls.TLS{
MinVersion: "foo MinVersion",
CipherSuites: []string{"foo CipherSuites 1", "foo CipherSuites 2", "foo CipherSuites 3"},
Certificates: traefiktls.Certificates{
{CertFile: "CertFile 1", KeyFile: "KeyFile 1"},
{CertFile: "CertFile 2", KeyFile: "KeyFile 2"},
},
ClientCA: traefiktls.ClientCA{
Files: traefiktls.FilesOrContents{"foo ClientCAFiles 1", "foo ClientCAFiles 2", "foo ClientCAFiles 3"},
Optional: false,
},
},
Redirect: &types.Redirect{
Replacement: "foo Replacement",
Regex: "foo Regex",
EntryPoint: "foo EntryPoint",
},
Auth: &types.Auth{
Basic: &types.Basic{
UsersFile: "foo Basic UsersFile",
Users: types.Users{"foo Basic Users 1", "foo Basic Users 2", "foo Basic Users 3"},
},
Digest: &types.Digest{
UsersFile: "foo Digest UsersFile",
Users: types.Users{"foo Digest Users 1", "foo Digest Users 2", "foo Digest Users 3"},
},
Forward: &types.Forward{
Address: "foo Address",
TLS: &types.ClientTLS{
CA: "foo CA",
Cert: "foo Cert",
Key: "foo Key",
InsecureSkipVerify: true,
},
TrustForwardHeader: true,
},
},
WhitelistSourceRange: []string{"foo WhitelistSourceRange 1", "foo WhitelistSourceRange 2", "foo WhitelistSourceRange 3"},
Compress: true,
ProxyProtocol: &configuration.ProxyProtocol{
TrustedIPs: []string{"127.0.0.1/32", "192.168.0.1"},
},
},
"fii": {
Address: "fii Address",
TLS: &traefiktls.TLS{
MinVersion: "fii MinVersion",
CipherSuites: []string{"fii CipherSuites 1", "fii CipherSuites 2", "fii CipherSuites 3"},
Certificates: traefiktls.Certificates{
{CertFile: "CertFile 1", KeyFile: "KeyFile 1"},
{CertFile: "CertFile 2", KeyFile: "KeyFile 2"},
},
ClientCA: traefiktls.ClientCA{
Files: traefiktls.FilesOrContents{"fii ClientCAFiles 1", "fii ClientCAFiles 2", "fii ClientCAFiles 3"},
Optional: false,
},
},
Redirect: &types.Redirect{
Replacement: "fii Replacement",
Regex: "fii Regex",
EntryPoint: "fii EntryPoint",
},
Auth: &types.Auth{
Basic: &types.Basic{
UsersFile: "fii Basic UsersFile",
Users: types.Users{"fii Basic Users 1", "fii Basic Users 2", "fii Basic Users 3"},
},
Digest: &types.Digest{
UsersFile: "fii Digest UsersFile",
Users: types.Users{"fii Digest Users 1", "fii Digest Users 2", "fii Digest Users 3"},
},
Forward: &types.Forward{
Address: "fii Address",
TLS: &types.ClientTLS{
CA: "fii CA",
Cert: "fii Cert",
Key: "fii Key",
InsecureSkipVerify: true,
},
TrustForwardHeader: true,
},
},
WhitelistSourceRange: []string{"fii WhitelistSourceRange 1", "fii WhitelistSourceRange 2", "fii WhitelistSourceRange 3"},
Compress: true,
ProxyProtocol: &configuration.ProxyProtocol{
TrustedIPs: []string{"127.0.0.1/32", "192.168.0.1"},
},
},
}
config.Cluster = &types.Cluster{
Node: "Cluster Node",
Store: &types.Store{
Prefix: "Cluster Store Prefix",
// ...
},
}
config.Constraints = types.Constraints{
{
Key: "Constraints Key 1",
Regex: "Constraints Regex 2",
MustMatch: true,
},
{
Key: "Constraints Key 1",
Regex: "Constraints Regex 2",
MustMatch: true,
},
}
config.ACME = &acme.ACME{
Email: "acme Email",
Domains: []types.Domain{
{
Main: "Domains Main",
SANs: []string{"Domains acme SANs 1", "Domains acme SANs 2", "Domains acme SANs 3"},
},
},
Storage: "Storage",
StorageFile: "StorageFile",
OnDemand: true,
OnHostRule: true,
CAServer: "CAServer",
EntryPoint: "EntryPoint",
DNSChallenge: &acmeprovider.DNSChallenge{Provider: "DNSProvider"},
DelayDontCheckDNS: 666,
ACMELogging: true,
TLSConfig: &tls.Config{
InsecureSkipVerify: true,
// ...
},
}
config.DefaultEntryPoints = configuration.DefaultEntryPoints{"DefaultEntryPoints 1", "DefaultEntryPoints 2", "DefaultEntryPoints 3"}
config.ProvidersThrottleDuration = flaeg.Duration(666 * time.Second)
config.MaxIdleConnsPerHost = 666
config.IdleTimeout = flaeg.Duration(666 * time.Second)
config.InsecureSkipVerify = true
config.RootCAs = traefiktls.FilesOrContents{"RootCAs 1", "RootCAs 2", "RootCAs 3"}
config.Retry = &configuration.Retry{
Attempts: 666,
}
config.HealthCheck = &configuration.HealthCheckConfig{
Interval: flaeg.Duration(666 * time.Second),
}
config.API = &api.Handler{
EntryPoint: "traefik",
Dashboard: true,
Debug: true,
CurrentConfigurations: &safe.Safe{},
Statistics: &types.Statistics{
RecentErrors: 666,
},
Stats: &stats.Stats{
Uptime: time.Now(),
Pid: 666,
ResponseCounts: map[string]int{"foo": 1},
TotalResponseCounts: map[string]int{"bar": 1},
TotalResponseTime: time.Now(),
},
StatsRecorder: &middlewares.StatsRecorder{},
DashboardAssets: &assetfs.AssetFS{
Asset: func(path string) ([]byte, error) {
return nil, nil
},
AssetDir: func(path string) ([]string, error) {
return nil, nil
},
AssetInfo: func(path string) (os.FileInfo, error) {
return nil, nil
},
Prefix: "fii",
},
}
config.RespondingTimeouts = &configuration.RespondingTimeouts{
ReadTimeout: flaeg.Duration(666 * time.Second),
WriteTimeout: flaeg.Duration(666 * time.Second),
IdleTimeout: flaeg.Duration(666 * time.Second),
}
config.ForwardingTimeouts = &configuration.ForwardingTimeouts{
DialTimeout: flaeg.Duration(666 * time.Second),
ResponseHeaderTimeout: flaeg.Duration(666 * time.Second),
}
config.Docker = &docker.Provider{
BaseProvider: provider.BaseProvider{
Watch: true,
Filename: "docker Filename",
Constraints: types.Constraints{
{
Key: "docker Constraints Key 1",
Regex: "docker Constraints Regex 2",
MustMatch: true,
},
{
Key: "docker Constraints Key 1",
Regex: "docker Constraints Regex 2",
MustMatch: true,
},
},
Trace: true,
DebugLogGeneratedTemplate: true,
},
Endpoint: "docker Endpoint",
Domain: "docker Domain",
TLS: &types.ClientTLS{
CA: "docker CA",
Cert: "docker Cert",
Key: "docker Key",
InsecureSkipVerify: true,
},
ExposedByDefault: true,
UseBindPortIP: true,
SwarmMode: true,
}
config.File = &file.Provider{
BaseProvider: provider.BaseProvider{
Watch: true,
Filename: "file Filename",
Constraints: types.Constraints{
{
Key: "file Constraints Key 1",
Regex: "file Constraints Regex 2",
MustMatch: true,
},
{
Key: "file Constraints Key 1",
Regex: "file Constraints Regex 2",
MustMatch: true,
},
},
Trace: true,
DebugLogGeneratedTemplate: true,
},
Directory: "file Directory",
}
config.Web = &configuration.WebCompatibility{
Address: "web Address",
CertFile: "web CertFile",
KeyFile: "web KeyFile",
ReadOnly: true,
Statistics: &types.Statistics{
RecentErrors: 666,
},
Metrics: &types.Metrics{
Prometheus: &types.Prometheus{
Buckets: types.Buckets{6.5, 6.6, 6.7},
},
Datadog: &types.Datadog{
Address: "Datadog Address",
PushInterval: "Datadog PushInterval",
},
StatsD: &types.Statsd{
Address: "StatsD Address",
PushInterval: "StatsD PushInterval",
},
},
Path: "web Path",
Auth: &types.Auth{
Basic: &types.Basic{
UsersFile: "web Basic UsersFile",
Users: types.Users{"web Basic Users 1", "web Basic Users 2", "web Basic Users 3"},
},
Digest: &types.Digest{
UsersFile: "web Digest UsersFile",
Users: types.Users{"web Digest Users 1", "web Digest Users 2", "web Digest Users 3"},
},
Forward: &types.Forward{
Address: "web Address",
TLS: &types.ClientTLS{
CA: "web CA",
Cert: "web Cert",
Key: "web Key",
InsecureSkipVerify: true,
},
TrustForwardHeader: true,
},
},
Debug: true,
}
config.Marathon = &marathon.Provider{
BaseProvider: provider.BaseProvider{
Watch: true,
Filename: "marathon Filename",
Constraints: types.Constraints{
{
Key: "marathon Constraints Key 1",
Regex: "marathon Constraints Regex 2",
MustMatch: true,
},
{
Key: "marathon Constraints Key 1",
Regex: "marathon Constraints Regex 2",
MustMatch: true,
},
},
Trace: true,
DebugLogGeneratedTemplate: true,
},
Endpoint: "",
Domain: "",
ExposedByDefault: true,
GroupsAsSubDomains: true,
DCOSToken: "",
MarathonLBCompatibility: true,
TLS: &types.ClientTLS{
CA: "marathon CA",
Cert: "marathon Cert",
Key: "marathon Key",
InsecureSkipVerify: true,
},
DialerTimeout: flaeg.Duration(666 * time.Second),
KeepAlive: flaeg.Duration(666 * time.Second),
ForceTaskHostname: true,
Basic: &marathon.Basic{
HTTPBasicAuthUser: "marathon HTTPBasicAuthUser",
HTTPBasicPassword: "marathon HTTPBasicPassword",
},
RespectReadinessChecks: true,
}
config.ConsulCatalog = &consulcatalog.Provider{
BaseProvider: provider.BaseProvider{
Watch: true,
Filename: "ConsulCatalog Filename",
Constraints: types.Constraints{
{
Key: "ConsulCatalog Constraints Key 1",
Regex: "ConsulCatalog Constraints Regex 2",
MustMatch: true,
},
{
Key: "ConsulCatalog Constraints Key 1",
Regex: "ConsulCatalog Constraints Regex 2",
MustMatch: true,
},
},
Trace: true,
DebugLogGeneratedTemplate: true,
},
Endpoint: "ConsulCatalog Endpoint",
Domain: "ConsulCatalog Domain",
ExposedByDefault: true,
Prefix: "ConsulCatalog Prefix",
FrontEndRule: "ConsulCatalog FrontEndRule",
}
config.Kubernetes = &kubernetes.Provider{
BaseProvider: provider.BaseProvider{
Watch: true,
Filename: "k8s Filename",
Constraints: types.Constraints{
{
Key: "k8s Constraints Key 1",
Regex: "k8s Constraints Regex 2",
MustMatch: true,
},
{
Key: "k8s Constraints Key 1",
Regex: "k8s Constraints Regex 2",
MustMatch: true,
},
},
Trace: true,
DebugLogGeneratedTemplate: true,
},
Endpoint: "k8s Endpoint",
Token: "k8s Token",
CertAuthFilePath: "k8s CertAuthFilePath",
DisablePassHostHeaders: true,
Namespaces: kubernetes.Namespaces{"k8s Namespaces 1", "k8s Namespaces 2", "k8s Namespaces 3"},
LabelSelector: "k8s LabelSelector",
}
config.Mesos = &mesos.Provider{
BaseProvider: provider.BaseProvider{
Watch: true,
Filename: "mesos Filename",
Constraints: types.Constraints{
{
Key: "mesos Constraints Key 1",
Regex: "mesos Constraints Regex 2",
MustMatch: true,
},
{
Key: "mesos Constraints Key 1",
Regex: "mesos Constraints Regex 2",
MustMatch: true,
},
},
Trace: true,
DebugLogGeneratedTemplate: true,
},
Endpoint: "mesos Endpoint",
Domain: "mesos Domain",
ExposedByDefault: true,
GroupsAsSubDomains: true,
ZkDetectionTimeout: 666,
RefreshSeconds: 666,
IPSources: "mesos IPSources",
StateTimeoutSecond: 666,
Masters: []string{"mesos Masters 1", "mesos Masters 2", "mesos Masters 3"},
}
config.Eureka = &eureka.Provider{
BaseProvider: provider.BaseProvider{
Watch: true,
Filename: "eureka Filename",
Constraints: types.Constraints{
{
Key: "eureka Constraints Key 1",
Regex: "eureka Constraints Regex 2",
MustMatch: true,
},
{
Key: "eureka Constraints Key 1",
Regex: "eureka Constraints Regex 2",
MustMatch: true,
},
},
Trace: true,
DebugLogGeneratedTemplate: true,
},
Endpoint: "eureka Endpoint",
Delay: flaeg.Duration(30 * time.Second),
RefreshSeconds: flaeg.Duration(30 * time.Second),
}
config.ECS = &ecs.Provider{
BaseProvider: provider.BaseProvider{
Watch: true,
Filename: "ecs Filename",
Constraints: types.Constraints{
{
Key: "ecs Constraints Key 1",
Regex: "ecs Constraints Regex 2",
MustMatch: true,
},
{
Key: "ecs Constraints Key 1",
Regex: "ecs Constraints Regex 2",
MustMatch: true,
},
},
Trace: true,
DebugLogGeneratedTemplate: true,
},
Domain: "ecs Domain",
ExposedByDefault: true,
RefreshSeconds: 666,
Clusters: ecs.Clusters{"ecs Clusters 1", "ecs Clusters 2", "ecs Clusters 3"},
Cluster: "ecs Cluster",
AutoDiscoverClusters: true,
Region: "ecs Region",
AccessKeyID: "ecs AccessKeyID",
SecretAccessKey: "ecs SecretAccessKey",
}
config.Rancher = &rancher.Provider{
BaseProvider: provider.BaseProvider{
Watch: true,
Filename: "rancher Filename",
Constraints: types.Constraints{
{
Key: "rancher Constraints Key 1",
Regex: "rancher Constraints Regex 2",
MustMatch: true,
},
{
Key: "rancher Constraints Key 1",
Regex: "rancher Constraints Regex 2",
MustMatch: true,
},
},
Trace: true,
DebugLogGeneratedTemplate: true,
},
APIConfiguration: rancher.APIConfiguration{
Endpoint: "rancher Endpoint",
AccessKey: "rancher AccessKey",
SecretKey: "rancher SecretKey",
},
API: &rancher.APIConfiguration{
Endpoint: "rancher Endpoint",
AccessKey: "rancher AccessKey",
SecretKey: "rancher SecretKey",
},
Metadata: &rancher.MetadataConfiguration{
IntervalPoll: true,
Prefix: "rancher Metadata Prefix",
},
Domain: "rancher Domain",
RefreshSeconds: 666,
ExposedByDefault: true,
EnableServiceHealthFilter: true,
}
config.DynamoDB = &dynamodb.Provider{
BaseProvider: provider.BaseProvider{
Watch: true,
Filename: "dynamodb Filename",
Constraints: types.Constraints{
{
Key: "dynamodb Constraints Key 1",
Regex: "dynamodb Constraints Regex 2",
MustMatch: true,
},
{
Key: "dynamodb Constraints Key 1",
Regex: "dynamodb Constraints Regex 2",
MustMatch: true,
},
},
Trace: true,
DebugLogGeneratedTemplate: true,
},
AccessKeyID: "dynamodb AccessKeyID",
RefreshSeconds: 666,
Region: "dynamodb Region",
SecretAccessKey: "dynamodb SecretAccessKey",
TableName: "dynamodb TableName",
Endpoint: "dynamodb Endpoint",
}
config.Etcd = &etcd.Provider{
Provider: kv.Provider{
BaseProvider: provider.BaseProvider{
Watch: true,
Filename: "etcd Filename",
Constraints: types.Constraints{
{
Key: "etcd Constraints Key 1",
Regex: "etcd Constraints Regex 2",
MustMatch: true,
},
{
Key: "etcd Constraints Key 1",
Regex: "etcd Constraints Regex 2",
MustMatch: true,
},
},
Trace: true,
DebugLogGeneratedTemplate: true,
},
Endpoint: "etcd Endpoint",
Prefix: "etcd Prefix",
TLS: &types.ClientTLS{
CA: "etcd CA",
Cert: "etcd Cert",
Key: "etcd Key",
InsecureSkipVerify: true,
},
Username: "etcd Username",
Password: "etcd Password",
},
}
config.Zookeeper = &zk.Provider{
Provider: kv.Provider{
BaseProvider: provider.BaseProvider{
Watch: true,
Filename: "zk Filename",
Constraints: types.Constraints{
{
Key: "zk Constraints Key 1",
Regex: "zk Constraints Regex 2",
MustMatch: true,
},
{
Key: "zk Constraints Key 1",
Regex: "zk Constraints Regex 2",
MustMatch: true,
},
},
Trace: true,
DebugLogGeneratedTemplate: true,
},
Endpoint: "zk Endpoint",
Prefix: "zk Prefix",
TLS: &types.ClientTLS{
CA: "zk CA",
Cert: "zk Cert",
Key: "zk Key",
InsecureSkipVerify: true,
},
Username: "zk Username",
Password: "zk Password",
},
}
config.Boltdb = &boltdb.Provider{
Provider: kv.Provider{
BaseProvider: provider.BaseProvider{
Watch: true,
Filename: "boltdb Filename",
Constraints: types.Constraints{
{
Key: "boltdb Constraints Key 1",
Regex: "boltdb Constraints Regex 2",
MustMatch: true,
},
{
Key: "boltdb Constraints Key 1",
Regex: "boltdb Constraints Regex 2",
MustMatch: true,
},
},
Trace: true,
DebugLogGeneratedTemplate: true,
},
Endpoint: "boltdb Endpoint",
Prefix: "boltdb Prefix",
TLS: &types.ClientTLS{
CA: "boltdb CA",
Cert: "boltdb Cert",
Key: "boltdb Key",
InsecureSkipVerify: true,
},
Username: "boltdb Username",
Password: "boltdb Password",
},
}
config.Consul = &consul.Provider{
Provider: kv.Provider{
BaseProvider: provider.BaseProvider{
Watch: true,
Filename: "consul Filename",
Constraints: types.Constraints{
{
Key: "consul Constraints Key 1",
Regex: "consul Constraints Regex 2",
MustMatch: true,
},
{
Key: "consul Constraints Key 1",
Regex: "consul Constraints Regex 2",
MustMatch: true,
},
},
Trace: true,
DebugLogGeneratedTemplate: true,
},
Endpoint: "consul Endpoint",
Prefix: "consul Prefix",
TLS: &types.ClientTLS{
CA: "consul CA",
Cert: "consul Cert",
Key: "consul Key",
InsecureSkipVerify: true,
},
Username: "consul Username",
Password: "consul Password",
},
}
cleanJSON, err := Do(config, true)
if err != nil {
t.Fatal(err, cleanJSON)
}
}

View File

@@ -0,0 +1,237 @@
package anonymize
import (
"testing"
"github.com/stretchr/testify/assert"
)
func Test_doOnJSON(t *testing.T) {
baseConfiguration := `
{
"GraceTimeOut": 10000000000,
"Debug": false,
"CheckNewVersion": true,
"AccessLogsFile": "",
"TraefikLogsFile": "",
"LogLevel": "ERROR",
"EntryPoints": {
"http": {
"Network": "",
"Address": ":80",
"TLS": null,
"Redirect": {
"EntryPoint": "https",
"Regex": "",
"Replacement": ""
},
"Auth": null,
"Compress": false
},
"https": {
"Address": ":443",
"TLS": {
"MinVersion": "",
"CipherSuites": null,
"Certificates": null,
"ClientCAFiles": null
},
"Redirect": null,
"Auth": null,
"Compress": false
}
},
"Cluster": null,
"Constraints": [],
"ACME": {
"Email": "foo@bar.com",
"Domains": [
{
"Main": "foo@bar.com",
"SANs": null
},
{
"Main": "foo@bar.com",
"SANs": null
}
],
"Storage": "",
"StorageFile": "/acme/acme.json",
"OnDemand": true,
"OnHostRule": true,
"CAServer": "",
"EntryPoint": "https",
"DNSProvider": "",
"DelayDontCheckDNS": 0,
"ACMELogging": false,
"TLSConfig": null
},
"DefaultEntryPoints": [
"https",
"http"
],
"ProvidersThrottleDuration": 2000000000,
"MaxIdleConnsPerHost": 200,
"IdleTimeout": 180000000000,
"InsecureSkipVerify": false,
"Retry": null,
"HealthCheck": {
"Interval": 30000000000
},
"Docker": null,
"File": null,
"Web": null,
"Marathon": null,
"Consul": null,
"ConsulCatalog": null,
"Etcd": null,
"Zookeeper": null,
"Boltdb": null,
"Kubernetes": null,
"Mesos": null,
"Eureka": null,
"ECS": null,
"Rancher": null,
"DynamoDB": null,
"ConfigFile": "/etc/traefik/traefik.toml"
}
`
expectedConfiguration := `
{
"GraceTimeOut": 10000000000,
"Debug": false,
"CheckNewVersion": true,
"AccessLogsFile": "",
"TraefikLogsFile": "",
"LogLevel": "ERROR",
"EntryPoints": {
"http": {
"Network": "",
"Address": ":80",
"TLS": null,
"Redirect": {
"EntryPoint": "https",
"Regex": "",
"Replacement": ""
},
"Auth": null,
"Compress": false
},
"https": {
"Address": ":443",
"TLS": {
"MinVersion": "",
"CipherSuites": null,
"Certificates": null,
"ClientCAFiles": null
},
"Redirect": null,
"Auth": null,
"Compress": false
}
},
"Cluster": null,
"Constraints": [],
"ACME": {
"Email": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"Domains": [
{
"Main": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"SANs": null
},
{
"Main": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"SANs": null
}
],
"Storage": "",
"StorageFile": "/acme/acme.json",
"OnDemand": true,
"OnHostRule": true,
"CAServer": "",
"EntryPoint": "https",
"DNSProvider": "",
"DelayDontCheckDNS": 0,
"ACMELogging": false,
"TLSConfig": null
},
"DefaultEntryPoints": [
"https",
"http"
],
"ProvidersThrottleDuration": 2000000000,
"MaxIdleConnsPerHost": 200,
"IdleTimeout": 180000000000,
"InsecureSkipVerify": false,
"Retry": null,
"HealthCheck": {
"Interval": 30000000000
},
"Docker": null,
"File": null,
"Web": null,
"Marathon": null,
"Consul": null,
"ConsulCatalog": null,
"Etcd": null,
"Zookeeper": null,
"Boltdb": null,
"Kubernetes": null,
"Mesos": null,
"Eureka": null,
"ECS": null,
"Rancher": null,
"DynamoDB": null,
"ConfigFile": "/etc/traefik/traefik.toml"
}
`
anomConfiguration := doOnJSON(baseConfiguration)
if anomConfiguration != expectedConfiguration {
t.Errorf("Got %s, want %s.", anomConfiguration, expectedConfiguration)
}
}
func Test_doOnJSON_simple(t *testing.T) {
testCases := []struct {
name string
input string
expectedOutput string
}{
{
name: "email",
input: `{
"email1": "goo@example.com",
"email2": "foo.bargoo@example.com",
"email3": "foo.bargoo@example.com.us"
}`,
expectedOutput: `{
"email1": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"email2": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"email3": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}`,
},
{
name: "url",
input: `{
"URL": "foo domain.com foo",
"URL": "foo sub.domain.com foo",
"URL": "foo sub.sub.domain.com foo",
"URL": "foo sub.sub.sub.domain.com.us foo"
}`,
expectedOutput: `{
"URL": "foo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx foo",
"URL": "foo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx foo",
"URL": "foo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx foo",
"URL": "foo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx foo"
}`,
},
}
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
output := doOnJSON(test.input)
assert.Equal(t, test.expectedOutput, output)
})
}
}

View File

@@ -0,0 +1,176 @@
package anonymize
import (
"reflect"
"testing"
"github.com/stretchr/testify/assert"
)
type Courgette struct {
Ji string
Ho string
}
type Tomate struct {
Ji string
Ho string
}
type Carotte struct {
Name string
Value int
Courgette Courgette
ECourgette Courgette `export:"true"`
Pourgette *Courgette
EPourgette *Courgette `export:"true"`
Aubergine map[string]string
EAubergine map[string]string `export:"true"`
SAubergine map[string]Tomate
ESAubergine map[string]Tomate `export:"true"`
PSAubergine map[string]*Tomate
EPAubergine map[string]*Tomate `export:"true"`
}
func Test_doOnStruct(t *testing.T) {
testCase := []struct {
name string
base *Carotte
expected *Carotte
hasError bool
}{
{
name: "primitive",
base: &Carotte{
Name: "koko",
Value: 666,
},
expected: &Carotte{
Name: "xxxx",
},
},
{
name: "struct",
base: &Carotte{
Name: "koko",
Courgette: Courgette{
Ji: "huu",
},
},
expected: &Carotte{
Name: "xxxx",
},
},
{
name: "pointer",
base: &Carotte{
Name: "koko",
Pourgette: &Courgette{
Ji: "hoo",
},
},
expected: &Carotte{
Name: "xxxx",
Pourgette: nil,
},
},
{
name: "export struct",
base: &Carotte{
Name: "koko",
ECourgette: Courgette{
Ji: "huu",
},
},
expected: &Carotte{
Name: "xxxx",
ECourgette: Courgette{
Ji: "xxxx",
},
},
},
{
name: "export pointer struct",
base: &Carotte{
Name: "koko",
ECourgette: Courgette{
Ji: "huu",
},
},
expected: &Carotte{
Name: "xxxx",
ECourgette: Courgette{
Ji: "xxxx",
},
},
},
{
name: "export map string/string",
base: &Carotte{
Name: "koko",
EAubergine: map[string]string{
"foo": "bar",
},
},
expected: &Carotte{
Name: "xxxx",
EAubergine: map[string]string{
"foo": "bar",
},
},
},
{
name: "export map string/pointer",
base: &Carotte{
Name: "koko",
EPAubergine: map[string]*Tomate{
"foo": {
Ji: "fdskljf",
},
},
},
expected: &Carotte{
Name: "xxxx",
EPAubergine: map[string]*Tomate{
"foo": {
Ji: "xxxx",
},
},
},
},
{
name: "export map string/struct (UNSAFE)",
base: &Carotte{
Name: "koko",
ESAubergine: map[string]Tomate{
"foo": {
Ji: "JiJiJi",
},
},
},
expected: &Carotte{
Name: "xxxx",
ESAubergine: map[string]Tomate{
"foo": {
Ji: "JiJiJi",
},
},
},
hasError: true,
},
}
for _, test := range testCase {
t.Run(test.name, func(t *testing.T) {
val := reflect.ValueOf(test.base).Elem()
err := doOnStruct(val)
if !test.hasError && err != nil {
t.Fatal(err)
}
if test.hasError && err == nil {
t.Fatal("Got no error but want an error.")
}
assert.EqualValues(t, test.expected, test.base)
})
}
}

58
api/dashboard.go Normal file
View File

@@ -0,0 +1,58 @@
package api
import (
"net/http"
"net/url"
"github.com/containous/mux"
assetfs "github.com/elazarl/go-bindata-assetfs"
"github.com/traefik/traefik/log"
)
// DashboardHandler expose dashboard routes
type DashboardHandler struct {
Assets *assetfs.AssetFS
}
// AddRoutes add dashboard routes on a router
func (g DashboardHandler) AddRoutes(router *mux.Router) {
if g.Assets == nil {
log.Error("No assets for dashboard")
return
}
// Expose dashboard
router.Methods(http.MethodGet).
Path("/").
HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
http.Redirect(resp, req, safePrefix(req)+"/dashboard/", 302)
})
router.Methods(http.MethodGet).
Path("/dashboard/status").
HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
http.Redirect(resp, req, "/dashboard/", 302)
})
router.Methods(http.MethodGet).
PathPrefix("/dashboard/").
Handler(http.StripPrefix("/dashboard/", http.FileServer(g.Assets)))
}
func safePrefix(req *http.Request) string {
prefix := req.Header.Get("X-Forwarded-Prefix")
if prefix == "" {
return ""
}
parse, err := url.Parse(prefix)
if err != nil {
return ""
}
if parse.Host != "" {
return ""
}
return parse.Path
}

54
api/dashboard_test.go Normal file
View File

@@ -0,0 +1,54 @@
package api
import (
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_safePrefix(t *testing.T) {
testCases := []struct {
desc string
value string
expected string
}{
{
desc: "host",
value: "https://example.com",
expected: "",
},
{
desc: "host with path",
value: "https://example.com/foo/bar?test",
expected: "",
},
{
desc: "path",
value: "/foo/bar",
expected: "/foo/bar",
},
{
desc: "path without leading slash",
value: "foo/bar",
expected: "foo/bar",
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
req, err := http.NewRequest(http.MethodGet, "http://localhost", nil)
require.NoError(t, err)
req.Header.Set("X-Forwarded-Prefix", test.value)
prefix := safePrefix(req)
assert.Equal(t, test.expected, prefix)
})
}
}

48
api/debug.go Normal file
View File

@@ -0,0 +1,48 @@
package api
import (
"expvar"
"fmt"
"net/http"
"net/http/pprof"
"runtime"
"github.com/containous/mux"
)
func init() {
expvar.Publish("Goroutines", expvar.Func(goroutines))
}
func goroutines() interface{} {
return runtime.NumGoroutine()
}
// DebugHandler expose debug routes
type DebugHandler struct{}
// AddRoutes add debug routes on a router
func (g DebugHandler) AddRoutes(router *mux.Router) {
router.Methods(http.MethodGet).Path("/debug/vars").
HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
fmt.Fprint(w, "{\n")
first := true
expvar.Do(func(kv expvar.KeyValue) {
if !first {
fmt.Fprint(w, ",\n")
}
first = false
fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
})
fmt.Fprint(w, "\n}\n")
})
runtime.SetBlockProfileRate(1)
runtime.SetMutexProfileFraction(5)
router.Methods(http.MethodGet).PathPrefix("/debug/pprof/cmdline").HandlerFunc(pprof.Cmdline)
router.Methods(http.MethodGet).PathPrefix("/debug/pprof/profile").HandlerFunc(pprof.Profile)
router.Methods(http.MethodGet).PathPrefix("/debug/pprof/symbol").HandlerFunc(pprof.Symbol)
router.Methods(http.MethodGet).PathPrefix("/debug/pprof/trace").HandlerFunc(pprof.Trace)
router.Methods(http.MethodGet).PathPrefix("/debug/pprof/").HandlerFunc(pprof.Index)
}

252
api/handler.go Normal file
View File

@@ -0,0 +1,252 @@
package api
import (
"net/http"
"github.com/containous/mux"
assetfs "github.com/elazarl/go-bindata-assetfs"
thoas_stats "github.com/thoas/stats"
"github.com/traefik/traefik/log"
"github.com/traefik/traefik/middlewares"
"github.com/traefik/traefik/safe"
"github.com/traefik/traefik/types"
"github.com/traefik/traefik/version"
"github.com/unrolled/render"
)
// Handler expose api routes
type Handler struct {
EntryPoint string `description:"EntryPoint" export:"true"`
Dashboard bool `description:"Activate dashboard" export:"true"`
Debug bool `export:"true"`
CurrentConfigurations *safe.Safe
Statistics *types.Statistics `description:"Enable more detailed statistics" export:"true"`
Stats *thoas_stats.Stats `json:"-" hash:"-"`
StatsRecorder *middlewares.StatsRecorder `json:"-" hash:"-"`
DashboardAssets *assetfs.AssetFS `json:"-" hash:"-"`
}
var (
templatesRenderer = render.New(render.Options{
Directory: "nowhere",
})
)
// AddRoutes add api routes on a router
func (p Handler) AddRoutes(router *mux.Router) {
if p.Debug {
DebugHandler{}.AddRoutes(router)
}
router.Methods(http.MethodGet).Path("/api").HandlerFunc(p.getConfigHandler)
router.Methods(http.MethodGet).Path("/api/providers").HandlerFunc(p.getConfigHandler)
router.Methods(http.MethodGet).Path("/api/providers/{provider}").HandlerFunc(p.getProviderHandler)
router.Methods(http.MethodGet).Path("/api/providers/{provider}/backends").HandlerFunc(p.getBackendsHandler)
router.Methods(http.MethodGet).Path("/api/providers/{provider}/backends/{backend}").HandlerFunc(p.getBackendHandler)
router.Methods(http.MethodGet).Path("/api/providers/{provider}/backends/{backend}/servers").HandlerFunc(p.getServersHandler)
router.Methods(http.MethodGet).Path("/api/providers/{provider}/backends/{backend}/servers/{server}").HandlerFunc(p.getServerHandler)
router.Methods(http.MethodGet).Path("/api/providers/{provider}/frontends").HandlerFunc(p.getFrontendsHandler)
router.Methods(http.MethodGet).Path("/api/providers/{provider}/frontends/{frontend}").HandlerFunc(p.getFrontendHandler)
router.Methods(http.MethodGet).Path("/api/providers/{provider}/frontends/{frontend}/routes").HandlerFunc(p.getRoutesHandler)
router.Methods(http.MethodGet).Path("/api/providers/{provider}/frontends/{frontend}/routes/{route}").HandlerFunc(p.getRouteHandler)
// health route
router.Methods(http.MethodGet).Path("/health").HandlerFunc(p.getHealthHandler)
version.Handler{}.AddRoutes(router)
if p.Dashboard {
DashboardHandler{Assets: p.DashboardAssets}.AddRoutes(router)
}
}
func getProviderIDFromVars(vars map[string]string) string {
providerID := vars["provider"]
// TODO: Deprecated
if providerID == "rest" {
providerID = "web"
}
return providerID
}
func (p Handler) getConfigHandler(response http.ResponseWriter, request *http.Request) {
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
err := templatesRenderer.JSON(response, http.StatusOK, currentConfigurations)
if err != nil {
log.Error(err)
}
}
func (p Handler) getProviderHandler(response http.ResponseWriter, request *http.Request) {
providerID := getProviderIDFromVars(mux.Vars(request))
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
if provider, ok := currentConfigurations[providerID]; ok {
err := templatesRenderer.JSON(response, http.StatusOK, provider)
if err != nil {
log.Error(err)
}
} else {
http.NotFound(response, request)
}
}
func (p Handler) getBackendsHandler(response http.ResponseWriter, request *http.Request) {
providerID := getProviderIDFromVars(mux.Vars(request))
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
if provider, ok := currentConfigurations[providerID]; ok {
err := templatesRenderer.JSON(response, http.StatusOK, provider.Backends)
if err != nil {
log.Error(err)
}
} else {
http.NotFound(response, request)
}
}
func (p Handler) getBackendHandler(response http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request)
providerID := getProviderIDFromVars(vars)
backendID := vars["backend"]
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
if provider, ok := currentConfigurations[providerID]; ok {
if backend, ok := provider.Backends[backendID]; ok {
err := templatesRenderer.JSON(response, http.StatusOK, backend)
if err != nil {
log.Error(err)
}
return
}
}
http.NotFound(response, request)
}
func (p Handler) getServersHandler(response http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request)
providerID := getProviderIDFromVars(vars)
backendID := vars["backend"]
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
if provider, ok := currentConfigurations[providerID]; ok {
if backend, ok := provider.Backends[backendID]; ok {
err := templatesRenderer.JSON(response, http.StatusOK, backend.Servers)
if err != nil {
log.Error(err)
}
return
}
}
http.NotFound(response, request)
}
func (p Handler) getServerHandler(response http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request)
providerID := getProviderIDFromVars(vars)
backendID := vars["backend"]
serverID := vars["server"]
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
if provider, ok := currentConfigurations[providerID]; ok {
if backend, ok := provider.Backends[backendID]; ok {
if server, ok := backend.Servers[serverID]; ok {
err := templatesRenderer.JSON(response, http.StatusOK, server)
if err != nil {
log.Error(err)
}
return
}
}
}
http.NotFound(response, request)
}
func (p Handler) getFrontendsHandler(response http.ResponseWriter, request *http.Request) {
providerID := getProviderIDFromVars(mux.Vars(request))
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
if provider, ok := currentConfigurations[providerID]; ok {
err := templatesRenderer.JSON(response, http.StatusOK, provider.Frontends)
if err != nil {
log.Error(err)
}
} else {
http.NotFound(response, request)
}
}
func (p Handler) getFrontendHandler(response http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request)
providerID := getProviderIDFromVars(vars)
frontendID := vars["frontend"]
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
if provider, ok := currentConfigurations[providerID]; ok {
if frontend, ok := provider.Frontends[frontendID]; ok {
err := templatesRenderer.JSON(response, http.StatusOK, frontend)
if err != nil {
log.Error(err)
}
return
}
}
http.NotFound(response, request)
}
func (p Handler) getRoutesHandler(response http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request)
providerID := getProviderIDFromVars(vars)
frontendID := vars["frontend"]
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
if provider, ok := currentConfigurations[providerID]; ok {
if frontend, ok := provider.Frontends[frontendID]; ok {
err := templatesRenderer.JSON(response, http.StatusOK, frontend.Routes)
if err != nil {
log.Error(err)
}
return
}
}
http.NotFound(response, request)
}
func (p Handler) getRouteHandler(response http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request)
providerID := getProviderIDFromVars(vars)
frontendID := vars["frontend"]
routeID := vars["route"]
currentConfigurations := p.CurrentConfigurations.Get().(types.Configurations)
if provider, ok := currentConfigurations[providerID]; ok {
if frontend, ok := provider.Frontends[frontendID]; ok {
if route, ok := frontend.Routes[routeID]; ok {
err := templatesRenderer.JSON(response, http.StatusOK, route)
if err != nil {
log.Error(err)
}
return
}
}
}
http.NotFound(response, request)
}
// healthResponse combines data returned by thoas/stats with statistics (if
// they are enabled).
type healthResponse struct {
*thoas_stats.Data
*middlewares.Stats
}
func (p *Handler) getHealthHandler(response http.ResponseWriter, request *http.Request) {
health := &healthResponse{Data: p.Stats.Data()}
if p.StatsRecorder != nil {
health.Stats = p.StatsRecorder.Data()
}
err := templatesRenderer.JSON(response, http.StatusOK, health)
if err != nil {
log.Error(err)
}
}

View File

3151
autogen/gentemplates/gen.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,35 +1,34 @@
FROM golang:1.8
FROM golang:1.16-alpine
# Install a more recent version of mercurial to avoid mismatching results
# between glide run on a decently updated host system and the build container.
RUN awk '$1 ~ "^deb" { $3 = $3 "-backports"; print; exit }' /etc/apt/sources.list > /etc/apt/sources.list.d/backports.list && \
DEBIAN_FRONTEND=noninteractive apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -t jessie-backports --yes --no-install-recommends mercurial=3.9.1-1~bpo8+1 && \
rm -fr /var/lib/apt/lists/
RUN apk --update upgrade \
&& apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar ca-certificates tzdata \
&& update-ca-certificates \
&& rm -rf /var/cache/apk/*
RUN go get github.com/jteeuwen/go-bindata/... \
&& go get github.com/golang/lint/golint \
&& go get github.com/kisielk/errcheck \
&& go get github.com/client9/misspell/cmd/misspell \
&& go get github.com/mattfarina/glide-hash \
&& go get github.com/sgotti/glide-vc
RUN go get golang.org/x/lint/golint \
&& go get github.com/client9/misspell/cmd/misspell
# Which docker version to test on
ARG DOCKER_VERSION=17.03.1
# Which glide version to test on
ARG GLIDE_VERSION=v0.12.3
# Download glide
RUN mkdir -p /usr/local/bin \
&& curl -fL https://github.com/Masterminds/glide/releases/download/${GLIDE_VERSION}/glide-${GLIDE_VERSION}-linux-amd64.tar.gz \
| tar -xzC /usr/local/bin --transform 's#^.+/##x'
ARG DOCKER_VERSION=18.09.7
# Download docker
RUN mkdir -p /usr/local/bin \
&& curl -fL https://get.docker.com/builds/Linux/x86_64/docker-${DOCKER_VERSION}-ce.tgz \
&& curl -fL https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}.tgz \
| tar -xzC /usr/local/bin --transform 's#^.+/##x'
WORKDIR /go/src/github.com/containous/traefik
COPY . /go/src/github.com/containous/traefik
# Download go-bindata binary to bin folder in $GOPATH
RUN mkdir -p /usr/local/bin \
&& curl -fsSL -o /usr/local/bin/go-bindata https://github.com/containous/go-bindata/releases/download/v1.0.0/go-bindata \
&& chmod +x /usr/local/bin/go-bindata
# Download misspell binary to bin folder in $GOPATH
RUN curl -sfL https://raw.githubusercontent.com/client9/misspell/master/install-misspell.sh | bash -s -- -b $GOPATH/bin v0.3.4
WORKDIR /go/src/github.com/traefik/traefik
# 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

View File

@@ -7,13 +7,13 @@ import (
"sync"
"time"
"github.com/abronan/valkeyrie/store"
"github.com/cenk/backoff"
"github.com/containous/staert"
"github.com/containous/traefik/job"
"github.com/containous/traefik/log"
"github.com/containous/traefik/safe"
"github.com/docker/libkv/store"
"github.com/satori/go.uuid"
"github.com/google/uuid"
"github.com/traefik/traefik/job"
"github.com/traefik/traefik/log"
"github.com/traefik/traefik/safe"
)
// Metadata stores Object plus metadata
@@ -76,11 +76,11 @@ func NewDataStore(ctx context.Context, kvSource staert.KvSource, object Object,
func (d *Datastore) watchChanges() error {
stopCh := make(chan struct{})
kvCh, err := d.kv.Watch(d.lockKey, stopCh)
kvCh, err := d.kv.Watch(d.lockKey, stopCh, nil)
if err != nil {
return err
return fmt.Errorf("error while watching key %s: %v", d.lockKey, err)
}
go func() {
safe.Go(func() {
ctx, cancel := context.WithCancel(d.ctx)
operation := func() error {
for {
@@ -97,7 +97,6 @@ func (d *Datastore) watchChanges() error {
if err != nil {
return err
}
// log.Debugf("Datastore object change received: %+v", d.meta)
if d.listener != nil {
err := d.listener(d.meta.object)
if err != nil {
@@ -114,30 +113,19 @@ func (d *Datastore) watchChanges() error {
if err != nil {
log.Errorf("Error in watch datastore: %v", err)
}
}()
})
return nil
}
func (d *Datastore) reload() error {
log.Debugf("Datastore reload")
d.localLock.Lock()
err := d.kv.LoadConfig(d.meta)
if err != nil {
d.localLock.Unlock()
return err
}
err = d.meta.unmarshall()
if err != nil {
d.localLock.Unlock()
return err
}
d.localLock.Unlock()
return nil
log.Debug("Datastore reload")
_, err := d.Load()
return err
}
// Begin creates a transaction with the KV store.
func (d *Datastore) Begin() (Transaction, Object, error) {
id := uuid.NewV4().String()
id := uuid.New().String()
log.Debugf("Transaction %s begins", id)
remoteLock, err := d.kv.NewLock(d.lockKey, &store.LockOptions{TTL: 20 * time.Second, Value: []byte(id)})
if err != nil {
@@ -164,7 +152,7 @@ func (d *Datastore) Begin() (Transaction, Object, error) {
operation := func() error {
meta := d.get()
if meta.Lock != id {
return fmt.Errorf("Object lock value: expected %s, got %s", id, meta.Lock)
return fmt.Errorf("object lock value: expected %s, got %s", id, meta.Lock)
}
return nil
}
@@ -179,7 +167,7 @@ func (d *Datastore) Begin() (Transaction, Object, error) {
ebo.MaxElapsedTime = 60 * time.Second
err = backoff.RetryNotify(safe.OperationWithRecover(operation), ebo, notify)
if err != nil {
return nil, nil, fmt.Errorf("Datastore cannot sync: %v", err)
return nil, nil, fmt.Errorf("datastore cannot sync: %v", err)
}
// we synced with KV store, we can now return Setter
@@ -200,6 +188,10 @@ func (d *Datastore) get() *Metadata {
func (d *Datastore) Load() (Object, error) {
d.localLock.Lock()
defer d.localLock.Unlock()
// clear Object first, as mapstructure's decoder doesn't have ZeroFields set to true for merging purposes
d.meta.Object = d.meta.Object[:0]
err := d.kv.LoadConfig(d.meta)
if err != nil {
return nil, err
@@ -232,12 +224,12 @@ func (s *datastoreTransaction) Commit(object Object) error {
s.localLock.Lock()
defer s.localLock.Unlock()
if s.dirty {
return fmt.Errorf("Transaction already used, please begin a new one")
return fmt.Errorf("transaction already used, please begin a new one")
}
s.Datastore.meta.object = object
err := s.Datastore.meta.Marshall()
if err != nil {
return fmt.Errorf("Marshall error: %s", err)
return fmt.Errorf("marshall error: %s", err)
}
err = s.kv.StoreConfig(s.Datastore.meta)
if err != nil {
@@ -246,7 +238,7 @@ func (s *datastoreTransaction) Commit(object Object) error {
err = s.remoteLock.Unlock()
if err != nil {
return fmt.Errorf("Unlock error: %s", err)
return fmt.Errorf("unlock error: %s", err)
}
s.dirty = true

View File

@@ -2,15 +2,24 @@ package cluster
import (
"context"
"net/http"
"time"
"github.com/cenk/backoff"
"github.com/containous/traefik/log"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/types"
"github.com/containous/mux"
"github.com/docker/leadership"
"github.com/traefik/traefik/log"
"github.com/traefik/traefik/safe"
"github.com/traefik/traefik/types"
"github.com/unrolled/render"
)
const clusterLeaderKeySuffix = "/leader"
var templatesRenderer = render.New(render.Options{
Directory: "nowhere",
})
// Leadership allows leadership election using a KV store
type Leadership struct {
*safe.Pool
@@ -25,7 +34,7 @@ func NewLeadership(ctx context.Context, cluster *types.Cluster) *Leadership {
return &Leadership{
Pool: safe.NewPool(ctx),
Cluster: cluster,
candidate: leadership.NewCandidate(cluster.Store, cluster.Store.Prefix+"/leader", cluster.Node, 20*time.Second),
candidate: leadership.NewCandidate(cluster.Store, cluster.Store.Prefix+clusterLeaderKeySuffix, cluster.Node, 20*time.Second),
listeners: []LeaderListener{},
leader: safe.New(false),
}
@@ -54,7 +63,7 @@ func (l *Leadership) Participate(pool *safe.Pool) {
})
}
// AddListener adds a leadership listerner
// AddListener adds a leadership listener
func (l *Leadership) AddListener(listener LeaderListener) {
l.listeners = append(l.listeners, listener)
}
@@ -86,7 +95,7 @@ func (l *Leadership) onElection(elected bool) {
l.leader.Set(true)
l.Start()
} else {
log.Infof("Node %s elected slave ♝", l.Cluster.Node)
log.Infof("Node %s elected worker ♝", l.Cluster.Node)
l.leader.Set(false)
l.Stop()
}
@@ -98,7 +107,40 @@ func (l *Leadership) onElection(elected bool) {
}
}
type leaderResponse struct {
Leader bool `json:"leader"`
LeaderNode string `json:"leader_node"`
}
func (l *Leadership) getLeaderHandler(response http.ResponseWriter, request *http.Request) {
leaderNode := ""
leaderKv, err := l.Cluster.Store.Get(l.Cluster.Store.Prefix+clusterLeaderKeySuffix, nil)
if err != nil {
log.Error(err)
} else {
leaderNode = string(leaderKv.Value)
}
leader := &leaderResponse{Leader: l.IsLeader(), LeaderNode: leaderNode}
status := http.StatusOK
if !leader.Leader {
// Set status to be `429`, as this will typically cause load balancers to stop sending requests to the instance without removing them from rotation.
status = http.StatusTooManyRequests
}
err = templatesRenderer.JSON(response, status, leader)
if err != nil {
log.Error(err)
}
}
// IsLeader returns true if current node is leader
func (l *Leadership) IsLeader() bool {
return l.leader.Get().(bool)
}
// AddRoutes add dashboard routes on a router
func (l *Leadership) AddRoutes(router *mux.Router) {
// Expose cluster leader
router.Methods(http.MethodGet).Path("/api/cluster/leader").HandlerFunc(l.getLeaderHandler)
}

171
cmd/bug/bug.go Normal file
View File

@@ -0,0 +1,171 @@
package bug
import (
"bytes"
"fmt"
"net/url"
"os/exec"
"runtime"
"text/template"
"github.com/containous/flaeg"
"github.com/traefik/traefik/anonymize"
"github.com/traefik/traefik/cmd"
"github.com/traefik/traefik/cmd/version"
)
const (
bugTracker = "https://github.com/traefik/traefik/issues/new"
bugTemplate = `<!--
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
The issue tracker is for reporting bugs and feature requests only.
For end-user related support questions, refer to one of the following:
- Stack Overflow (using the "traefik" tag): https://stackoverflow.com/questions/tagged/traefik
- the Traefik community Slack channel: https://slack.traefik.io
-->
### Do you want to request a *feature* or report a *bug*?
(If you intend to ask a support question: **DO NOT FILE AN ISSUE**.
Use [Stack Overflow](https://stackoverflow.com/questions/tagged/traefik)
or [Slack](https://slack.traefik.io) instead.)
### What did you do?
<!--
HOW TO WRITE A GOOD ISSUE?
- Respect the issue template as more as possible.
- If it's possible use the command ` + "`" + "traefik bug" + "`" + `. See https://www.youtube.com/watch?v=Lyz62L8m93I.
- The title must be short and descriptive.
- Explain the conditions which led you to write this issue: the context.
- The context should lead to something, an idea or a problem that youre facing.
- Remain clear and concise.
- Format your messages to help the reader focus on what matters and understand the structure of your message, use Markdown syntax https://help.github.com/articles/github-flavored-markdown
-->
### What did you expect to see?
### What did you see instead?
### Output of ` + "`" + `traefik version` + "`" + `: (_What version of Traefik are you using?_)
` + "```" + `
{{.Version}}
` + "```" + `
### What is your environment & configuration (arguments, toml, provider, platform, ...)?
` + "```" + `json
{{.Configuration}}
` + "```" + `
<!--
Add more configuration information here.
-->
### If applicable, please paste the log output at DEBUG level (` + "`" + `--logLevel=DEBUG` + "`" + ` switch)
` + "```" + `
(paste your output here)
` + "```" + `
`
)
// NewCmd builds a new Bug command
func NewCmd(traefikConfiguration *cmd.TraefikConfiguration, traefikPointersConfiguration *cmd.TraefikConfiguration) *flaeg.Command {
// version Command init
return &flaeg.Command{
Name: "bug",
Description: `Report an issue on Traefik bugtracker`,
Config: traefikConfiguration,
DefaultPointersConfig: traefikPointersConfiguration,
Run: runCmd(traefikConfiguration),
Metadata: map[string]string{
"parseAllSources": "true",
},
}
}
func runCmd(traefikConfiguration *cmd.TraefikConfiguration) func() error {
return func() error {
body, err := createReport(traefikConfiguration)
if err != nil {
return err
}
sendReport(body)
return nil
}
}
func createReport(traefikConfiguration *cmd.TraefikConfiguration) (string, error) {
var versionPrint bytes.Buffer
if err := version.GetPrint(&versionPrint); err != nil {
return "", err
}
tmpl, err := template.New("bug").Parse(bugTemplate)
if err != nil {
return "", err
}
config, err := anonymize.Do(traefikConfiguration, true)
if err != nil {
return "", err
}
v := struct {
Version string
Configuration string
}{
Version: versionPrint.String(),
Configuration: config,
}
var bug bytes.Buffer
if err := tmpl.Execute(&bug, v); err != nil {
return "", err
}
return bug.String(), nil
}
func sendReport(body string) {
URL := bugTracker + "?body=" + url.QueryEscape(body)
if err := openBrowser(URL); err != nil {
fmt.Printf("Please file a new issue at %s using this template:\n\n", bugTracker)
fmt.Print(body)
}
}
func openBrowser(URL string) error {
var err error
switch runtime.GOOS {
case "linux":
err = exec.Command("xdg-open", URL).Start()
case "windows":
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", URL).Start()
case "darwin":
err = exec.Command("open", URL).Start()
default:
err = fmt.Errorf("unsupported platform")
}
return err
}

67
cmd/bug/bug_test.go Normal file
View File

@@ -0,0 +1,67 @@
package bug
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/traefik/traefik/anonymize"
"github.com/traefik/traefik/cmd"
"github.com/traefik/traefik/configuration"
"github.com/traefik/traefik/provider/file"
"github.com/traefik/traefik/tls"
"github.com/traefik/traefik/types"
)
func Test_createReport(t *testing.T) {
traefikConfiguration := &cmd.TraefikConfiguration{
ConfigFile: "FOO",
GlobalConfiguration: configuration.GlobalConfiguration{
EntryPoints: configuration.EntryPoints{
"goo": &configuration.EntryPoint{
Address: "hoo.bar",
Auth: &types.Auth{
Basic: &types.Basic{
UsersFile: "foo Basic UsersFile",
Users: types.Users{"foo Basic Users 1", "foo Basic Users 2", "foo Basic Users 3"},
},
Digest: &types.Digest{
UsersFile: "foo Digest UsersFile",
Users: types.Users{"foo Digest Users 1", "foo Digest Users 2", "foo Digest Users 3"},
},
},
},
},
File: &file.Provider{
Directory: "BAR",
},
RootCAs: tls.FilesOrContents{"fllf"},
},
}
report, err := createReport(traefikConfiguration)
assert.NoError(t, err, report)
// exported anonymous configuration
assert.NotContains(t, "web Basic Users ", report)
assert.NotContains(t, "foo Digest Users ", report)
assert.NotContains(t, "hoo.bar", report)
}
func Test_anonymize_traefikConfiguration(t *testing.T) {
traefikConfiguration := &cmd.TraefikConfiguration{
ConfigFile: "FOO",
GlobalConfiguration: configuration.GlobalConfiguration{
EntryPoints: configuration.EntryPoints{
"goo": &configuration.EntryPoint{
Address: "hoo.bar",
},
},
File: &file.Provider{
Directory: "BAR",
},
},
}
_, err := anonymize.Do(traefikConfiguration, true)
assert.NoError(t, err)
assert.Equal(t, "hoo.bar", traefikConfiguration.GlobalConfiguration.EntryPoints["goo"].Address)
}

347
cmd/configuration.go Normal file
View File

@@ -0,0 +1,347 @@
package cmd
import (
"time"
"github.com/containous/flaeg"
servicefabric "github.com/containous/traefik-extra-service-fabric"
sf "github.com/jjcollinge/servicefabric"
"github.com/traefik/traefik/api"
"github.com/traefik/traefik/configuration"
"github.com/traefik/traefik/middlewares/accesslog"
"github.com/traefik/traefik/middlewares/tracing"
"github.com/traefik/traefik/middlewares/tracing/datadog"
"github.com/traefik/traefik/middlewares/tracing/jaeger"
"github.com/traefik/traefik/middlewares/tracing/zipkin"
"github.com/traefik/traefik/ping"
"github.com/traefik/traefik/provider/boltdb"
"github.com/traefik/traefik/provider/consul"
"github.com/traefik/traefik/provider/consulcatalog"
"github.com/traefik/traefik/provider/docker"
"github.com/traefik/traefik/provider/dynamodb"
"github.com/traefik/traefik/provider/ecs"
"github.com/traefik/traefik/provider/etcd"
"github.com/traefik/traefik/provider/eureka"
"github.com/traefik/traefik/provider/file"
"github.com/traefik/traefik/provider/kubernetes"
"github.com/traefik/traefik/provider/marathon"
"github.com/traefik/traefik/provider/mesos"
"github.com/traefik/traefik/provider/rancher"
"github.com/traefik/traefik/provider/rest"
"github.com/traefik/traefik/provider/zk"
"github.com/traefik/traefik/types"
)
// TraefikConfiguration holds GlobalConfiguration and other stuff
type TraefikConfiguration struct {
configuration.GlobalConfiguration `mapstructure:",squash" export:"true"`
ConfigFile string `short:"c" description:"Configuration file to use (TOML)." export:"true"`
}
// NewTraefikDefaultPointersConfiguration creates a TraefikConfiguration with pointers default values
func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
// default Docker
var defaultDocker docker.Provider
defaultDocker.Watch = true
defaultDocker.ExposedByDefault = true
defaultDocker.Endpoint = "unix:///var/run/docker.sock"
defaultDocker.SwarmMode = false
defaultDocker.SwarmModeRefreshSeconds = 15
// default File
var defaultFile file.Provider
defaultFile.Watch = true
defaultFile.Filename = "" // needs equivalent to viper.ConfigFileUsed()
// default Rest
var defaultRest rest.Provider
defaultRest.EntryPoint = configuration.DefaultInternalEntryPointName
// TODO: Deprecated - Web provider, use REST provider instead
var defaultWeb configuration.WebCompatibility
defaultWeb.Address = ":8080"
defaultWeb.Statistics = &types.Statistics{
RecentErrors: 10,
}
// TODO: Deprecated - default Metrics
defaultWeb.Metrics = &types.Metrics{
Prometheus: &types.Prometheus{
Buckets: types.Buckets{0.1, 0.3, 1.2, 5},
EntryPoint: configuration.DefaultInternalEntryPointName,
},
Datadog: &types.Datadog{
Address: "localhost:8125",
PushInterval: "10s",
},
StatsD: &types.Statsd{
Address: "localhost:8125",
PushInterval: "10s",
},
InfluxDB: &types.InfluxDB{
Address: "localhost:8089",
Protocol: "udp",
PushInterval: "10s",
},
}
// default Marathon
var defaultMarathon marathon.Provider
defaultMarathon.Watch = true
defaultMarathon.Endpoint = "http://127.0.0.1:8080"
defaultMarathon.ExposedByDefault = true
defaultMarathon.Constraints = types.Constraints{}
defaultMarathon.DialerTimeout = flaeg.Duration(5 * time.Second)
defaultMarathon.ResponseHeaderTimeout = flaeg.Duration(60 * time.Second)
defaultMarathon.TLSHandshakeTimeout = flaeg.Duration(5 * time.Second)
defaultMarathon.KeepAlive = flaeg.Duration(10 * time.Second)
// default Consul
var defaultConsul consul.Provider
defaultConsul.Watch = true
defaultConsul.Endpoint = "127.0.0.1:8500"
defaultConsul.Prefix = "traefik"
defaultConsul.Constraints = types.Constraints{}
// default CatalogProvider
var defaultConsulCatalog consulcatalog.Provider
defaultConsulCatalog.Endpoint = "127.0.0.1:8500"
defaultConsulCatalog.ExposedByDefault = true
defaultConsulCatalog.Constraints = types.Constraints{}
defaultConsulCatalog.Prefix = "traefik"
defaultConsulCatalog.FrontEndRule = "Host:{{.ServiceName}}.{{.Domain}}"
defaultConsulCatalog.Stale = false
defaultConsulCatalog.StrictChecks = true
// default Etcd
var defaultEtcd etcd.Provider
defaultEtcd.Watch = true
defaultEtcd.Endpoint = "127.0.0.1:2379"
defaultEtcd.Prefix = "/traefik"
defaultEtcd.Constraints = types.Constraints{}
// default Zookeeper
var defaultZookeeper zk.Provider
defaultZookeeper.Watch = true
defaultZookeeper.Endpoint = "127.0.0.1:2181"
defaultZookeeper.Prefix = "traefik"
defaultZookeeper.Constraints = types.Constraints{}
// default Boltdb
var defaultBoltDb boltdb.Provider
defaultBoltDb.Watch = true
defaultBoltDb.Endpoint = "127.0.0.1:4001"
defaultBoltDb.Prefix = "/traefik"
defaultBoltDb.Constraints = types.Constraints{}
// default Kubernetes
var defaultKubernetes kubernetes.Provider
defaultKubernetes.Watch = true
defaultKubernetes.Constraints = types.Constraints{}
// default Mesos
var defaultMesos mesos.Provider
defaultMesos.Watch = true
defaultMesos.Endpoint = "http://127.0.0.1:5050"
defaultMesos.ExposedByDefault = true
defaultMesos.Constraints = types.Constraints{}
defaultMesos.RefreshSeconds = 30
defaultMesos.ZkDetectionTimeout = 30
defaultMesos.StateTimeoutSecond = 30
// default ECS
var defaultECS ecs.Provider
defaultECS.Watch = true
defaultECS.ExposedByDefault = true
defaultECS.AutoDiscoverClusters = false
defaultECS.Clusters = ecs.Clusters{"default"}
defaultECS.RefreshSeconds = 15
defaultECS.Constraints = types.Constraints{}
// default Rancher
var defaultRancher rancher.Provider
defaultRancher.Watch = true
defaultRancher.ExposedByDefault = true
defaultRancher.RefreshSeconds = 15
// default DynamoDB
var defaultDynamoDB dynamodb.Provider
defaultDynamoDB.Constraints = types.Constraints{}
defaultDynamoDB.RefreshSeconds = 15
defaultDynamoDB.TableName = "traefik"
defaultDynamoDB.Watch = true
// default Eureka
var defaultEureka eureka.Provider
defaultEureka.RefreshSeconds = flaeg.Duration(30 * time.Second)
// default ServiceFabric
var defaultServiceFabric servicefabric.Provider
defaultServiceFabric.APIVersion = sf.DefaultAPIVersion
defaultServiceFabric.RefreshSeconds = 10
// default Ping
var defaultPing = ping.Handler{
EntryPoint: "traefik",
}
// default TraefikLog
defaultTraefikLog := types.TraefikLog{
Format: "common",
FilePath: "",
}
// default AccessLog
defaultAccessLog := types.AccessLog{
Format: accesslog.CommonFormat,
FilePath: "",
Filters: &types.AccessLogFilters{},
Fields: &types.AccessLogFields{
DefaultMode: types.AccessLogKeep,
Headers: &types.FieldHeaders{
DefaultMode: types.AccessLogKeep,
},
},
}
// default HealthCheckConfig
healthCheck := configuration.HealthCheckConfig{
Interval: flaeg.Duration(configuration.DefaultHealthCheckInterval),
}
// default RespondingTimeouts
respondingTimeouts := configuration.RespondingTimeouts{
IdleTimeout: flaeg.Duration(configuration.DefaultIdleTimeout),
}
// default ForwardingTimeouts
forwardingTimeouts := configuration.ForwardingTimeouts{
DialTimeout: flaeg.Duration(configuration.DefaultDialTimeout),
}
// default Tracing
defaultTracing := tracing.Tracing{
Backend: "jaeger",
ServiceName: "traefik",
SpanNameLimit: 0,
Jaeger: &jaeger.Config{
SamplingServerURL: "http://localhost:5778/sampling",
SamplingType: "const",
SamplingParam: 1.0,
LocalAgentHostPort: "127.0.0.1:6831",
TraceContextHeaderName: "uber-trace-id",
},
Zipkin: &zipkin.Config{
HTTPEndpoint: "http://localhost:9411/api/v1/spans",
SameSpan: false,
ID128Bit: true,
Debug: false,
},
DataDog: &datadog.Config{
LocalAgentHostPort: "localhost:8126",
GlobalTag: "",
Debug: false,
PrioritySampling: false,
},
}
// default LifeCycle
defaultLifeCycle := configuration.LifeCycle{
GraceTimeOut: flaeg.Duration(configuration.DefaultGraceTimeout),
}
// default ApiConfiguration
defaultAPI := api.Handler{
EntryPoint: "traefik",
Dashboard: true,
}
defaultAPI.Statistics = &types.Statistics{
RecentErrors: 10,
}
// default Metrics
defaultMetrics := types.Metrics{
Prometheus: &types.Prometheus{
Buckets: types.Buckets{0.1, 0.3, 1.2, 5},
EntryPoint: configuration.DefaultInternalEntryPointName,
},
Datadog: &types.Datadog{
Address: "localhost:8125",
PushInterval: "10s",
},
StatsD: &types.Statsd{
Address: "localhost:8125",
PushInterval: "10s",
},
InfluxDB: &types.InfluxDB{
Address: "localhost:8089",
Protocol: "udp",
PushInterval: "10s",
},
}
defaultResolver := configuration.HostResolverConfig{
CnameFlattening: false,
ResolvConfig: "/etc/resolv.conf",
ResolvDepth: 5,
}
defaultConfiguration := configuration.GlobalConfiguration{
Docker: &defaultDocker,
File: &defaultFile,
Web: &defaultWeb,
Rest: &defaultRest,
Marathon: &defaultMarathon,
Consul: &defaultConsul,
ConsulCatalog: &defaultConsulCatalog,
Etcd: &defaultEtcd,
Zookeeper: &defaultZookeeper,
Boltdb: &defaultBoltDb,
Kubernetes: &defaultKubernetes,
Mesos: &defaultMesos,
ECS: &defaultECS,
Rancher: &defaultRancher,
Eureka: &defaultEureka,
DynamoDB: &defaultDynamoDB,
Retry: &configuration.Retry{},
HealthCheck: &healthCheck,
RespondingTimeouts: &respondingTimeouts,
ForwardingTimeouts: &forwardingTimeouts,
TraefikLog: &defaultTraefikLog,
AccessLog: &defaultAccessLog,
LifeCycle: &defaultLifeCycle,
Ping: &defaultPing,
API: &defaultAPI,
Metrics: &defaultMetrics,
Tracing: &defaultTracing,
HostResolver: &defaultResolver,
}
return &TraefikConfiguration{
GlobalConfiguration: defaultConfiguration,
}
}
// NewTraefikConfiguration creates a TraefikConfiguration with default values
func NewTraefikConfiguration() *TraefikConfiguration {
return &TraefikConfiguration{
GlobalConfiguration: configuration.GlobalConfiguration{
AccessLogsFile: "",
TraefikLogsFile: "",
EntryPoints: map[string]*configuration.EntryPoint{},
Constraints: types.Constraints{},
DefaultEntryPoints: []string{"http"},
ProvidersThrottleDuration: flaeg.Duration(2 * time.Second),
MaxIdleConnsPerHost: 200,
IdleTimeout: flaeg.Duration(0),
HealthCheck: &configuration.HealthCheckConfig{
Interval: flaeg.Duration(configuration.DefaultHealthCheckInterval),
},
LifeCycle: &configuration.LifeCycle{
GraceTimeOut: flaeg.Duration(configuration.DefaultGraceTimeout),
},
CheckNewVersion: true,
},
ConfigFile: "",
}
}

22
cmd/context.go Normal file
View File

@@ -0,0 +1,22 @@
package cmd
import (
"context"
"os"
"os/signal"
"syscall"
)
// ContextWithSignal create a context cancelled when SIGINT or SIGTERM are notified
func ContextWithSignal(ctx context.Context) context.Context {
newCtx, cancel := context.WithCancel(ctx)
signals := make(chan os.Signal)
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
go func() {
select {
case <-signals:
cancel()
}
}()
return newCtx
}

View File

@@ -0,0 +1,73 @@
package healthcheck
import (
"crypto/tls"
"errors"
"fmt"
"net/http"
"os"
"time"
"github.com/containous/flaeg"
"github.com/traefik/traefik/cmd"
"github.com/traefik/traefik/configuration"
)
// NewCmd builds a new HealthCheck command
func NewCmd(traefikConfiguration *cmd.TraefikConfiguration, traefikPointersConfiguration *cmd.TraefikConfiguration) *flaeg.Command {
return &flaeg.Command{
Name: "healthcheck",
Description: `Calls traefik /ping to check health (web provider must be enabled)`,
Config: traefikConfiguration,
DefaultPointersConfig: traefikPointersConfiguration,
Run: runCmd(traefikConfiguration),
Metadata: map[string]string{
"parseAllSources": "true",
},
}
}
func runCmd(traefikConfiguration *cmd.TraefikConfiguration) func() error {
return func() error {
traefikConfiguration.GlobalConfiguration.SetEffectiveConfiguration(traefikConfiguration.ConfigFile)
resp, errPing := Do(traefikConfiguration.GlobalConfiguration)
if errPing != nil {
fmt.Printf("Error calling healthcheck: %s\n", errPing)
os.Exit(1)
}
if resp.StatusCode != http.StatusOK {
fmt.Printf("Bad healthcheck status: %s\n", resp.Status)
os.Exit(1)
}
fmt.Printf("OK: %s\n", resp.Request.URL)
os.Exit(0)
return nil
}
}
// Do try to do a healthcheck
func Do(globalConfiguration configuration.GlobalConfiguration) (*http.Response, error) {
if globalConfiguration.Ping == nil {
return nil, errors.New("please enable `ping` to use health check")
}
pingEntryPoint, ok := globalConfiguration.EntryPoints[globalConfiguration.Ping.EntryPoint]
if !ok {
return nil, errors.New("missing `ping` entrypoint")
}
client := &http.Client{Timeout: 5 * time.Second}
protocol := "http"
if pingEntryPoint.TLS != nil {
protocol = "https"
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client.Transport = tr
}
path := "/"
if globalConfiguration.Web != nil {
path = globalConfiguration.Web.Path
}
return client.Head(protocol + "://" + pingEntryPoint.Address + path + "ping")
}

View File

@@ -0,0 +1,211 @@
package storeconfig
import (
"encoding/json"
"fmt"
"io/ioutil"
stdlog "log"
"os"
"github.com/abronan/valkeyrie/store"
"github.com/containous/flaeg"
"github.com/containous/staert"
"github.com/traefik/traefik/acme"
"github.com/traefik/traefik/cluster"
"github.com/traefik/traefik/cmd"
"github.com/traefik/traefik/log"
)
// NewCmd builds a new StoreConfig command
func NewCmd(traefikConfiguration *cmd.TraefikConfiguration, traefikPointersConfiguration *cmd.TraefikConfiguration) *flaeg.Command {
return &flaeg.Command{
Name: "storeconfig",
Description: `Store the static traefik configuration into a Key-value stores. Traefik will not start.`,
Config: traefikConfiguration,
DefaultPointersConfig: traefikPointersConfiguration,
Metadata: map[string]string{
"parseAllSources": "true",
},
}
}
// Run store config in KV
func Run(kv *staert.KvSource, traefikConfiguration *cmd.TraefikConfiguration) func() error {
return func() error {
if kv == nil {
return fmt.Errorf("error using command storeconfig, no Key-value store defined")
}
fileConfig := traefikConfiguration.GlobalConfiguration.File
if fileConfig != nil {
traefikConfiguration.GlobalConfiguration.File = nil
if len(fileConfig.Filename) == 0 && len(fileConfig.Directory) == 0 {
fileConfig.Filename = traefikConfiguration.ConfigFile
}
}
jsonConf, err := json.Marshal(traefikConfiguration.GlobalConfiguration)
if err != nil {
return err
}
stdlog.Printf("Storing configuration: %s\n", jsonConf)
err = kv.StoreConfig(traefikConfiguration.GlobalConfiguration)
if err != nil {
return err
}
if fileConfig != nil {
jsonConf, err = json.Marshal(fileConfig)
if err != nil {
return err
}
stdlog.Printf("Storing file configuration: %s\n", jsonConf)
config, err := fileConfig.BuildConfiguration()
if err != nil {
return err
}
stdlog.Print("Writing config to KV")
err = kv.StoreConfig(config)
if err != nil {
return err
}
}
if traefikConfiguration.GlobalConfiguration.ACME != nil {
account := &acme.Account{}
// Migrate ACME data from file to KV store if needed
if len(traefikConfiguration.GlobalConfiguration.ACME.StorageFile) > 0 {
account, err = migrateACMEData(traefikConfiguration.GlobalConfiguration.ACME.StorageFile)
if err != nil {
return err
}
}
accountInitialized, err := keyExists(kv, traefikConfiguration.GlobalConfiguration.ACME.Storage)
if err != nil && err != store.ErrKeyNotFound {
return err
}
// Check to see if ACME account object is already in kv store
if traefikConfiguration.GlobalConfiguration.ACME.OverrideCertificates || !accountInitialized {
// Store the ACME Account into the KV Store
// Certificates in KV Store will be overridden
meta := cluster.NewMetadata(account)
err = meta.Marshall()
if err != nil {
return err
}
source := staert.KvSource{
Store: kv,
Prefix: traefikConfiguration.GlobalConfiguration.ACME.Storage,
}
err = source.StoreConfig(meta)
if err != nil {
return err
}
}
// Force to delete storagefile
return kv.Delete(kv.Prefix + "/acme/storagefile")
}
return nil
}
}
func keyExists(source *staert.KvSource, key string) (bool, error) {
list, err := source.List(key, nil)
if err != nil {
return false, err
}
return len(list) > 0, nil
}
// migrateACMEData allows migrating data from acme.json file to KV store in function of the file format
func migrateACMEData(fileName string) (*acme.Account, error) {
f, err := os.Open(fileName)
if err != nil {
return nil, err
}
defer f.Close()
file, err := ioutil.ReadAll(f)
if err != nil {
return nil, err
}
// Check if the storage file is not empty before to get data
account := &acme.Account{}
if len(file) > 0 {
accountFromNewFormat, err := acme.FromNewToOldFormat(fileName)
if err != nil {
return nil, err
}
if accountFromNewFormat == nil {
// convert ACME json file to KV store (used for backward compatibility)
localStore := acme.NewLocalStore(fileName)
account, err = localStore.Get()
if err != nil {
return nil, err
}
err = account.RemoveAccountV1Values()
if err != nil {
return nil, err
}
} else {
account = accountFromNewFormat
}
} else {
log.Warnf("No data will be imported from the storageFile %q because it is empty.", fileName)
}
err = account.Init()
return account, err
}
// CreateKvSource creates KvSource
// TLS support is enable for Consul and Etcd backends
func CreateKvSource(traefikConfiguration *cmd.TraefikConfiguration) (*staert.KvSource, error) {
var kv *staert.KvSource
var kvStore store.Store
var err error
switch {
case traefikConfiguration.Consul != nil:
kvStore, err = traefikConfiguration.Consul.CreateStore()
kv = &staert.KvSource{
Store: kvStore,
Prefix: traefikConfiguration.Consul.Prefix,
}
case traefikConfiguration.Etcd != nil:
kvStore, err = traefikConfiguration.Etcd.CreateStore()
kv = &staert.KvSource{
Store: kvStore,
Prefix: traefikConfiguration.Etcd.Prefix,
}
case traefikConfiguration.Zookeeper != nil:
kvStore, err = traefikConfiguration.Zookeeper.CreateStore()
kv = &staert.KvSource{
Store: kvStore,
Prefix: traefikConfiguration.Zookeeper.Prefix,
}
case traefikConfiguration.Boltdb != nil:
kvStore, err = traefikConfiguration.Boltdb.CreateStore()
kv = &staert.KvSource{
Store: kvStore,
Prefix: traefikConfiguration.Boltdb.Prefix,
}
}
return kv, err
}

View File

@@ -1,152 +0,0 @@
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/url"
"os/exec"
"regexp"
"runtime"
"text/template"
"github.com/containous/flaeg"
"github.com/mvdan/xurls"
)
var (
bugtracker = "https://github.com/containous/traefik/issues/new"
bugTemplate = `<!--
PLEASE READ THIS MESSAGE.
Please keep in mind that the GitHub issue tracker is not intended as a general support forum, but for reporting bugs and feature requests.
For other type of questions, consider using one of:
- the Traefik community Slack channel: https://traefik.herokuapp.com
- StackOverflow: https://stackoverflow.com/questions/tagged/traefik
HOW TO WRITE A GOOD ISSUE?
- if it's possible use the command` + "`" + `traefik bug` + "`" + `. See https://www.youtube.com/watch?v=Lyz62L8m93I.
- The title must be short and descriptive.
- Explain the conditions which led you to write this issue: the context.
- The context should lead to something, an idea or a problem that youre facing.
- Remain clear and concise.
- Format your messages to help the reader focus on what matters and understand the structure of your message, use Markdown syntax https://help.github.com/articles/github-flavored-markdown
-->
### Do you want to request a *feature* or report a *bug*?
### What did you do?
### What did you expect to see?
### What did you see instead?
### Output of ` + "`" + `traefik version` + "`" + `: (_What version of Traefik are you using?_)
` + "```" + `
{{.Version}}
` + "```" + `
### What is your environment & configuration (arguments, toml, provider, platform, ...)?
` + "```" + `toml
{{.Configuration}}
` + "```" + `
<!--
Add more configuration information here.
-->
### If applicable, please paste the log output in debug mode (` + "`" + `--debug` + "`" + ` switch)
` + "```" + `
(paste your output here)
` + "```" + `
`
)
// newBugCmd builds a new Bug command
func newBugCmd(traefikConfiguration interface{}, traefikPointersConfiguration interface{}) *flaeg.Command {
//version Command init
return &flaeg.Command{
Name: "bug",
Description: `Report an issue on Traefik bugtracker`,
Config: traefikConfiguration,
DefaultPointersConfig: traefikPointersConfiguration,
Run: func() error {
var version bytes.Buffer
if err := getVersionPrint(&version); err != nil {
return err
}
tmpl, err := template.New("").Parse(bugTemplate)
if err != nil {
return err
}
configJSON, err := json.MarshalIndent(traefikConfiguration, "", " ")
if err != nil {
return err
}
v := struct {
Version string
Configuration string
}{
Version: version.String(),
Configuration: anonymize(string(configJSON)),
}
var bug bytes.Buffer
if err := tmpl.Execute(&bug, v); err != nil {
return err
}
body := bug.String()
URL := bugtracker + "?body=" + url.QueryEscape(body)
if err := openBrowser(URL); err != nil {
fmt.Print("Please file a new issue at " + bugtracker + " using this template:\n\n")
fmt.Print(body)
}
return nil
},
Metadata: map[string]string{
"parseAllSources": "true",
},
}
}
func openBrowser(URL string) error {
var err error
switch runtime.GOOS {
case "linux":
err = exec.Command("xdg-open", URL).Start()
case "windows":
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", URL).Start()
case "darwin":
err = exec.Command("open", URL).Start()
default:
err = fmt.Errorf("unsupported platform")
}
return err
}
func anonymize(input string) string {
replace := "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
mailExp := regexp.MustCompile(`\w[-._\w]*\w@\w[-._\w]*\w\.\w{2,3}"`)
return xurls.Relaxed.ReplaceAllString(mailExp.ReplaceAllString(input, replace), replace)
}

View File

@@ -1,42 +1,51 @@
package main
import (
"crypto/tls"
"context"
"encoding/json"
"fmt"
fmtlog "log"
"net/http"
"os"
"path/filepath"
"reflect"
"runtime"
"strings"
"time"
"github.com/Sirupsen/logrus"
"github.com/cenk/backoff"
"github.com/containous/flaeg"
"github.com/containous/staert"
"github.com/containous/traefik/acme"
"github.com/containous/traefik/cluster"
"github.com/containous/traefik/log"
"github.com/containous/traefik/middlewares"
"github.com/containous/traefik/provider/kubernetes"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/server"
"github.com/containous/traefik/types"
"github.com/containous/traefik/version"
"github.com/coreos/go-systemd/daemon"
"github.com/docker/libkv/store"
"github.com/satori/go.uuid"
assetfs "github.com/elazarl/go-bindata-assetfs"
"github.com/ogier/pflag"
"github.com/sirupsen/logrus"
"github.com/traefik/traefik/autogen/genstatic"
"github.com/traefik/traefik/cmd"
"github.com/traefik/traefik/cmd/bug"
"github.com/traefik/traefik/cmd/healthcheck"
"github.com/traefik/traefik/cmd/storeconfig"
cmdVersion "github.com/traefik/traefik/cmd/version"
"github.com/traefik/traefik/collector"
"github.com/traefik/traefik/configuration"
"github.com/traefik/traefik/configuration/router"
"github.com/traefik/traefik/job"
"github.com/traefik/traefik/log"
"github.com/traefik/traefik/provider/ecs"
"github.com/traefik/traefik/provider/kubernetes"
"github.com/traefik/traefik/safe"
"github.com/traefik/traefik/server"
"github.com/traefik/traefik/server/uuid"
traefiktls "github.com/traefik/traefik/tls"
"github.com/traefik/traefik/types"
"github.com/traefik/traefik/version"
"github.com/vulcand/oxy/roundrobin"
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
// traefik config inits
traefikConfiguration := cmd.NewTraefikConfiguration()
traefikPointersConfiguration := cmd.NewTraefikDefaultPointersConfiguration()
//traefik config inits
traefikConfiguration := server.NewTraefikConfiguration()
traefikPointersConfiguration := server.NewTraefikDefaultPointersConfiguration()
//traefik Command init
// traefik Command init
traefikCmd := &flaeg.Command{
Name: "traefik",
Description: `traefik is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
@@ -44,218 +53,204 @@ Complete documentation is available at https://traefik.io`,
Config: traefikConfiguration,
DefaultPointersConfig: traefikPointersConfiguration,
Run: func() error {
run(traefikConfiguration)
runCmd(&traefikConfiguration.GlobalConfiguration, traefikConfiguration.ConfigFile)
return nil
},
}
//storeconfig Command init
var kv *staert.KvSource
var err error
// storeconfig Command init
storeConfigCmd := storeconfig.NewCmd(traefikConfiguration, traefikPointersConfiguration)
storeconfigCmd := &flaeg.Command{
Name: "storeconfig",
Description: `Store the static traefik configuration into a Key-value stores. Traefik will not start.`,
Config: traefikConfiguration,
DefaultPointersConfig: traefikPointersConfiguration,
Run: func() error {
if kv == nil {
return fmt.Errorf("Error using command storeconfig, no Key-value store defined")
}
jsonConf, err := json.Marshal(traefikConfiguration.GlobalConfiguration)
if err != nil {
return err
}
fmtlog.Printf("Storing configuration: %s\n", jsonConf)
err = kv.StoreConfig(traefikConfiguration.GlobalConfiguration)
if err != nil {
return err
}
if traefikConfiguration.GlobalConfiguration.ACME != nil && len(traefikConfiguration.GlobalConfiguration.ACME.StorageFile) > 0 {
// convert ACME json file to KV store
store := acme.NewLocalStore(traefikConfiguration.GlobalConfiguration.ACME.StorageFile)
object, err := store.Load()
if err != nil {
return err
}
meta := cluster.NewMetadata(object)
err = meta.Marshall()
if err != nil {
return err
}
source := staert.KvSource{
Store: kv,
Prefix: traefikConfiguration.GlobalConfiguration.ACME.Storage,
}
err = source.StoreConfig(meta)
if err != nil {
return err
}
}
return nil
},
Metadata: map[string]string{
"parseAllSources": "true",
},
}
//init flaeg source
// init flaeg source
f := flaeg.New(traefikCmd, os.Args[1:])
//add custom parsers
f.AddParser(reflect.TypeOf(server.EntryPoints{}), &server.EntryPoints{})
f.AddParser(reflect.TypeOf(server.DefaultEntryPoints{}), &server.DefaultEntryPoints{})
// add custom parsers
f.AddParser(reflect.TypeOf(configuration.EntryPoints{}), &configuration.EntryPoints{})
f.AddParser(reflect.TypeOf(configuration.DefaultEntryPoints{}), &configuration.DefaultEntryPoints{})
f.AddParser(reflect.TypeOf(traefiktls.FilesOrContents{}), &traefiktls.FilesOrContents{})
f.AddParser(reflect.TypeOf(types.Constraints{}), &types.Constraints{})
f.AddParser(reflect.TypeOf(kubernetes.Namespaces{}), &kubernetes.Namespaces{})
f.AddParser(reflect.TypeOf([]acme.Domain{}), &acme.Domains{})
f.AddParser(reflect.TypeOf(ecs.Clusters{}), &ecs.Clusters{})
f.AddParser(reflect.TypeOf([]types.Domain{}), &types.Domains{})
f.AddParser(reflect.TypeOf(types.DNSResolvers{}), &types.DNSResolvers{})
f.AddParser(reflect.TypeOf(types.Buckets{}), &types.Buckets{})
f.AddParser(reflect.TypeOf(types.StatusCodes{}), &types.StatusCodes{})
f.AddParser(reflect.TypeOf(types.FieldNames{}), &types.FieldNames{})
f.AddParser(reflect.TypeOf(types.FieldHeaderNames{}), &types.FieldHeaderNames{})
//add commands
f.AddCommand(newVersionCmd())
f.AddCommand(newBugCmd(traefikConfiguration, traefikPointersConfiguration))
f.AddCommand(storeconfigCmd)
// add commands
f.AddCommand(cmdVersion.NewCmd())
f.AddCommand(bug.NewCmd(traefikConfiguration, traefikPointersConfiguration))
f.AddCommand(storeConfigCmd)
f.AddCommand(healthcheck.NewCmd(traefikConfiguration, traefikPointersConfiguration))
usedCmd, err := f.GetCommand()
if err != nil {
fmtlog.Println(err)
os.Exit(-1)
os.Exit(1)
}
if _, err := f.Parse(usedCmd); err != nil {
if err == pflag.ErrHelp {
os.Exit(0)
}
fmtlog.Printf("Error parsing command: %s\n", err)
os.Exit(-1)
os.Exit(1)
}
//staert init
// staert init
s := staert.NewStaert(traefikCmd)
//init toml source
// init TOML source
toml := staert.NewTomlSource("traefik", []string{traefikConfiguration.ConfigFile, "/etc/traefik/", "$HOME/.traefik/", "."})
//add sources to staert
// add sources to staert
s.AddSource(toml)
s.AddSource(f)
if _, err := s.LoadConfig(); err != nil {
fmtlog.Println(fmt.Errorf("Error reading TOML config file %s : %s", toml.ConfigFileUsed(), err))
os.Exit(-1)
fmtlog.Printf("Error reading TOML config file %s : %s\n", toml.ConfigFileUsed(), err)
os.Exit(1)
}
traefikConfiguration.ConfigFile = toml.ConfigFileUsed()
kv, err = CreateKvSource(traefikConfiguration)
kv, err := storeconfig.CreateKvSource(traefikConfiguration)
if err != nil {
fmtlog.Printf("Error creating kv store: %s\n", err)
os.Exit(-1)
os.Exit(1)
}
storeConfigCmd.Run = storeconfig.Run(kv, traefikConfiguration)
// IF a KV Store is enable and no sub-command called in args
// if a KV Store is enable and no sub-command called in args
if kv != nil && usedCmd == traefikCmd {
if traefikConfiguration.Cluster == nil {
traefikConfiguration.Cluster = &types.Cluster{Node: uuid.NewV4().String()}
traefikConfiguration.Cluster = &types.Cluster{Node: uuid.Get()}
}
if traefikConfiguration.Cluster.Store == nil {
traefikConfiguration.Cluster.Store = &types.Store{Prefix: kv.Prefix, Store: kv.Store}
}
s.AddSource(kv)
if _, err := s.LoadConfig(); err != nil {
operation := func() error {
_, err := s.LoadConfig()
return err
}
notify := func(err error, time time.Duration) {
log.Errorf("Load config error: %+v, retrying in %s", err, time)
}
err := backoff.RetryNotify(safe.OperationWithRecover(operation), job.NewBackOff(backoff.NewExponentialBackOff()), notify)
if err != nil {
fmtlog.Printf("Error loading configuration: %s\n", err)
os.Exit(-1)
os.Exit(1)
}
}
if err := s.Run(); err != nil {
fmtlog.Printf("Error running traefik: %s\n", err)
os.Exit(-1)
os.Exit(1)
}
os.Exit(0)
}
func run(traefikConfiguration *server.TraefikConfiguration) {
fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags)
func runCmd(globalConfiguration *configuration.GlobalConfiguration, configFile string) {
configureLogging(globalConfiguration)
// load global configuration
globalConfiguration := traefikConfiguration.GlobalConfiguration
http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = globalConfiguration.MaxIdleConnsPerHost
if globalConfiguration.InsecureSkipVerify {
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}
loggerMiddleware := middlewares.NewLogger(globalConfiguration.AccessLogsFile)
defer loggerMiddleware.Close()
if globalConfiguration.File != nil && len(globalConfiguration.File.Filename) == 0 {
// no filename, setting to global config file
if len(traefikConfiguration.ConfigFile) != 0 {
globalConfiguration.File.Filename = traefikConfiguration.ConfigFile
} else {
log.Errorln("Error using file configuration backend, no filename defined")
}
if len(configFile) > 0 {
log.Infof("Using TOML configuration file %s", configFile)
}
if len(globalConfiguration.EntryPoints) == 0 {
globalConfiguration.EntryPoints = map[string]*server.EntryPoint{"http": {Address: ":80"}}
globalConfiguration.DefaultEntryPoints = []string{"http"}
http.DefaultTransport.(*http.Transport).Proxy = http.ProxyFromEnvironment
if globalConfiguration.AllowMinWeightZero {
roundrobin.SetDefaultWeight(0)
}
if globalConfiguration.Debug {
globalConfiguration.LogLevel = "DEBUG"
}
globalConfiguration.SetEffectiveConfiguration(configFile)
globalConfiguration.ValidateConfiguration()
// logging
level, err := logrus.ParseLevel(strings.ToLower(globalConfiguration.LogLevel))
if err != nil {
log.Error("Error getting level", err)
}
log.SetLevel(level)
if len(globalConfiguration.TraefikLogsFile) > 0 {
dir := filepath.Dir(globalConfiguration.TraefikLogsFile)
err := os.MkdirAll(dir, 0755)
if err != nil {
log.Errorf("Failed to create log path %s: %s", dir, err)
}
fi, err := os.OpenFile(globalConfiguration.TraefikLogsFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
defer func() {
if err := fi.Close(); err != nil {
log.Error("Error closing file", err)
}
}()
if err != nil {
log.Error("Error opening file", err)
} else {
log.SetOutput(fi)
log.SetFormatter(&logrus.TextFormatter{DisableColors: true, FullTimestamp: true, DisableSorting: true})
}
} else {
log.SetFormatter(&logrus.TextFormatter{FullTimestamp: true, DisableSorting: true})
}
jsonConf, _ := json.Marshal(globalConfiguration)
log.Infof("Traefik version %s built on %s", version.Version, version.BuildDate)
if globalConfiguration.CheckNewVersion {
ticker := time.NewTicker(24 * time.Hour)
safe.Go(func() {
version.CheckNewVersion()
for {
select {
case <-ticker.C:
version.CheckNewVersion()
}
}
})
jsonConf, err := json.Marshal(globalConfiguration)
if err != nil {
log.Error(err)
log.Debugf("Global configuration loaded [struct] %#v", globalConfiguration)
} else {
log.Debugf("Global configuration loaded %s", string(jsonConf))
}
if len(traefikConfiguration.ConfigFile) != 0 {
log.Infof("Using TOML configuration file %s", traefikConfiguration.ConfigFile)
if globalConfiguration.API != nil && globalConfiguration.API.Dashboard {
globalConfiguration.API.DashboardAssets = &assetfs.AssetFS{Asset: genstatic.Asset, AssetInfo: genstatic.AssetInfo, AssetDir: genstatic.AssetDir, Prefix: "static"}
}
log.Debugf("Global configuration loaded %s", string(jsonConf))
svr := server.NewServer(globalConfiguration)
svr.Start()
if globalConfiguration.CheckNewVersion {
checkNewVersion()
}
stats(globalConfiguration)
providerAggregator := configuration.NewProviderAggregator(globalConfiguration)
acmeprovider, err := globalConfiguration.InitACMEProvider()
if err != nil {
log.Errorf("Unable to initialize ACME provider: %v", err)
} else if acmeprovider != nil {
err = providerAggregator.AddProvider(acmeprovider)
if err != nil {
log.Errorf("Unable to add ACME provider to the providers list: %v", err)
acmeprovider = nil
}
}
entryPoints := map[string]server.EntryPoint{}
for entryPointName, config := range globalConfiguration.EntryPoints {
entryPoint := server.EntryPoint{
Configuration: config,
}
internalRouter := router.NewInternalRouterAggregator(*globalConfiguration, entryPointName)
if acmeprovider != nil {
if acmeprovider.HTTPChallenge != nil && entryPointName == acmeprovider.HTTPChallenge.EntryPoint {
internalRouter.AddRouter(acmeprovider)
}
// TLS ALPN 01
if acmeprovider.TLSChallenge != nil && acmeprovider.HTTPChallenge == nil && acmeprovider.DNSChallenge == nil {
entryPoint.TLSALPNGetter = acmeprovider.GetTLSALPNCertificate
}
if acmeprovider.OnDemand && entryPointName == acmeprovider.EntryPoint {
entryPoint.OnDemandListener = acmeprovider.ListenRequest
}
if entryPointName == acmeprovider.EntryPoint {
entryPoint.CertificateStore = traefiktls.NewCertificateStore()
acmeprovider.SetCertificateStore(entryPoint.CertificateStore)
log.Debugf("Setting Acme Certificate store from Entrypoint: %s", entryPointName)
}
}
entryPoint.InternalRouter = internalRouter
entryPoints[entryPointName] = entryPoint
}
svr := server.NewServer(*globalConfiguration, providerAggregator, entryPoints)
if acmeprovider != nil && acmeprovider.OnHostRule {
acmeprovider.SetConfigListenerChan(make(chan types.Configuration))
svr.AddListener(acmeprovider.ListenConfiguration)
}
ctx := cmd.ContextWithSignal(context.Background())
if globalConfiguration.Ping != nil {
globalConfiguration.Ping.WithContext(ctx)
}
svr.StartWithContext(ctx)
defer svr.Close()
sent, err := daemon.SdNotify(false, "READY=1")
if !sent && err != nil {
log.Error("Fail to notify", err)
}
t, err := daemon.SdWatchdogEnabled(false)
if err != nil {
log.Error("Problem with watchdog", err)
@@ -266,48 +261,116 @@ func run(traefikConfiguration *server.TraefikConfiguration) {
safe.Go(func() {
tick := time.Tick(t)
for range tick {
if ok, _ := daemon.SdNotify(false, "WATCHDOG=1"); !ok {
log.Error("Fail to tick watchdog")
_, errHealthCheck := healthcheck.Do(*globalConfiguration)
if globalConfiguration.Ping == nil || errHealthCheck == nil {
if ok, _ := daemon.SdNotify(false, "WATCHDOG=1"); !ok {
log.Error("Fail to tick watchdog")
}
} else {
log.Error(errHealthCheck)
}
}
})
}
svr.Wait()
log.Info("Shutting down")
logrus.Exit(0)
}
// CreateKvSource creates KvSource
// TLS support is enable for Consul and Etcd backends
func CreateKvSource(traefikConfiguration *server.TraefikConfiguration) (*staert.KvSource, error) {
var kv *staert.KvSource
var store store.Store
var err error
func configureLogging(globalConfiguration *configuration.GlobalConfiguration) {
// configure default log flags
fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags)
switch {
case traefikConfiguration.Consul != nil:
store, err = traefikConfiguration.Consul.CreateStore()
kv = &staert.KvSource{
Store: store,
Prefix: traefikConfiguration.Consul.Prefix,
}
case traefikConfiguration.Etcd != nil:
store, err = traefikConfiguration.Etcd.CreateStore()
kv = &staert.KvSource{
Store: store,
Prefix: traefikConfiguration.Etcd.Prefix,
}
case traefikConfiguration.Zookeeper != nil:
store, err = traefikConfiguration.Zookeeper.CreateStore()
kv = &staert.KvSource{
Store: store,
Prefix: traefikConfiguration.Zookeeper.Prefix,
}
case traefikConfiguration.Boltdb != nil:
store, err = traefikConfiguration.Boltdb.CreateStore()
kv = &staert.KvSource{
Store: store,
Prefix: traefikConfiguration.Boltdb.Prefix,
// configure log level
// an explicitly defined log level always has precedence. if none is
// given and debug mode is disabled, the default is ERROR, and DEBUG
// otherwise.
levelStr := strings.ToLower(globalConfiguration.LogLevel)
if levelStr == "" {
levelStr = "error"
if globalConfiguration.Debug {
levelStr = "debug"
}
}
level, err := logrus.ParseLevel(levelStr)
if err != nil {
log.Error("Error getting level", err)
}
log.SetLevel(level)
// configure log output file
logFile := globalConfiguration.TraefikLogsFile
if len(logFile) > 0 {
log.Warn("top-level traefikLogsFile has been deprecated -- please use traefiklog.filepath")
}
if globalConfiguration.TraefikLog != nil && len(globalConfiguration.TraefikLog.FilePath) > 0 {
logFile = globalConfiguration.TraefikLog.FilePath
}
// configure log format
var formatter logrus.Formatter
if globalConfiguration.TraefikLog != nil && globalConfiguration.TraefikLog.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, 0755); err != nil {
log.Errorf("Failed to create log path %s: %s", dir, err)
}
err = log.OpenFile(logFile)
logrus.RegisterExitHandler(func() {
if err := log.CloseFile(); err != nil {
log.Error("Error closing log", err)
}
})
if err != nil {
log.Error("Error opening file", err)
}
}
return kv, err
}
func checkNewVersion() {
ticker := time.Tick(24 * time.Hour)
safe.Go(func() {
for time.Sleep(10 * time.Minute); ; <-ticker {
version.CheckNewVersion()
}
})
}
func stats(globalConfiguration *configuration.GlobalConfiguration) {
if globalConfiguration.SendAnonymousUsage {
log.Info(`
Stats collection is enabled.
Many thanks for contributing to Traefik's improvement by allowing us to receive anonymous information from your configuration.
Help us improve Traefik by leaving this feature on :)
More details on: https://doc.traefik.io/traefik/v1.7/basics/#collected-data
`)
collect(globalConfiguration)
} else {
log.Info(`
Stats collection is disabled.
Help us improve Traefik by turning this feature on :)
More details on: https://doc.traefik.io/traefik/v1.7/basics/#collected-data
`)
}
}
func collect(globalConfiguration *configuration.GlobalConfiguration) {
ticker := time.Tick(24 * time.Hour)
safe.Go(func() {
for time.Sleep(10 * time.Minute); ; <-ticker {
if err := collector.Collect(globalConfiguration); err != nil {
log.Debug(err)
}
}
})
}

View File

@@ -1,63 +0,0 @@
package main
import (
"fmt"
"io"
"os"
"runtime"
"text/template"
"github.com/containous/flaeg"
"github.com/containous/traefik/version"
)
var versionTemplate = `Version: {{.Version}}
Codename: {{.Codename}}
Go version: {{.GoVersion}}
Built: {{.BuildTime}}
OS/Arch: {{.Os}}/{{.Arch}}`
// newVersionCmd builds a new Version command
func newVersionCmd() *flaeg.Command {
//version Command init
return &flaeg.Command{
Name: "version",
Description: `Print version`,
Config: struct{}{},
DefaultPointersConfig: struct{}{},
Run: func() error {
if err := getVersionPrint(os.Stdout); err != nil {
return err
}
fmt.Printf("\n")
return nil
},
}
}
func getVersionPrint(wr io.Writer) error {
tmpl, err := template.New("").Parse(versionTemplate)
if err != nil {
return err
}
v := struct {
Version string
Codename string
GoVersion string
BuildTime string
Os string
Arch string
}{
Version: version.Version,
Codename: version.Codename,
GoVersion: runtime.Version(),
BuildTime: version.BuildDate,
Os: runtime.GOOS,
Arch: runtime.GOARCH,
}
return tmpl.Execute(wr, v)
}

62
cmd/version/version.go Normal file
View File

@@ -0,0 +1,62 @@
package version
import (
"fmt"
"io"
"os"
"runtime"
"text/template"
"github.com/containous/flaeg"
"github.com/traefik/traefik/version"
)
var versionTemplate = `Version: {{.Version}}
Codename: {{.Codename}}
Go version: {{.GoVersion}}
Built: {{.BuildTime}}
OS/Arch: {{.Os}}/{{.Arch}}`
// NewCmd builds a new Version command
func NewCmd() *flaeg.Command {
return &flaeg.Command{
Name: "version",
Description: `Print version`,
Config: struct{}{},
DefaultPointersConfig: struct{}{},
Run: func() error {
if err := GetPrint(os.Stdout); err != nil {
return err
}
fmt.Print("\n")
return nil
},
}
}
// GetPrint write Printable version
func GetPrint(wr io.Writer) error {
tmpl, err := template.New("").Parse(versionTemplate)
if err != nil {
return err
}
v := struct {
Version string
Codename string
GoVersion string
BuildTime string
Os string
Arch string
}{
Version: version.Version,
Codename: version.Codename,
GoVersion: runtime.Version(),
BuildTime: version.BuildDate,
Os: runtime.GOOS,
Arch: runtime.GOARCH,
}
return tmpl.Execute(wr, v)
}

79
collector/collector.go Normal file
View File

@@ -0,0 +1,79 @@
package collector
import (
"bytes"
"encoding/base64"
"encoding/json"
"net"
"net/http"
"strconv"
"time"
"github.com/mitchellh/hashstructure"
"github.com/traefik/traefik/anonymize"
"github.com/traefik/traefik/configuration"
"github.com/traefik/traefik/log"
"github.com/traefik/traefik/version"
)
// collectorURL URL where the stats are send
const collectorURL = "https://collect.traefik.io/619df80498b60f985d766ce62f912b7c"
// Collected data
type data struct {
Version string
Codename string
BuildDate string
Configuration string
Hash string
}
// Collect anonymous data.
func Collect(globalConfiguration *configuration.GlobalConfiguration) error {
anonConfig, err := anonymize.Do(globalConfiguration, false)
if err != nil {
return err
}
log.Infof("Anonymous stats sent to %s: %s", collectorURL, anonConfig)
hashConf, err := hashstructure.Hash(globalConfiguration, nil)
if err != nil {
return err
}
data := &data{
Version: version.Version,
Codename: version.Codename,
BuildDate: version.BuildDate,
Hash: strconv.FormatUint(hashConf, 10),
Configuration: base64.StdEncoding.EncodeToString([]byte(anonConfig)),
}
buf := new(bytes.Buffer)
err = json.NewEncoder(buf).Encode(data)
if err != nil {
return err
}
_, err = makeHTTPClient().Post(collectorURL, "application/json; charset=utf-8", buf)
return err
}
func makeHTTPClient() *http.Client {
dialer := &net.Dialer{
Timeout: configuration.DefaultDialTimeout,
KeepAlive: 30 * time.Second,
DualStack: true,
}
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: dialer.DialContext,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
return &http.Client{Transport: transport}
}

View File

@@ -0,0 +1,586 @@
package configuration
import (
"errors"
"fmt"
"strings"
"time"
"github.com/containous/flaeg"
servicefabric "github.com/containous/traefik-extra-service-fabric"
"github.com/go-acme/lego/v4/challenge/dns01"
"github.com/traefik/traefik/acme"
"github.com/traefik/traefik/api"
"github.com/traefik/traefik/log"
"github.com/traefik/traefik/middlewares/tracing"
"github.com/traefik/traefik/middlewares/tracing/datadog"
"github.com/traefik/traefik/middlewares/tracing/jaeger"
"github.com/traefik/traefik/middlewares/tracing/zipkin"
"github.com/traefik/traefik/ping"
acmeprovider "github.com/traefik/traefik/provider/acme"
"github.com/traefik/traefik/provider/boltdb"
"github.com/traefik/traefik/provider/consul"
"github.com/traefik/traefik/provider/consulcatalog"
"github.com/traefik/traefik/provider/docker"
"github.com/traefik/traefik/provider/dynamodb"
"github.com/traefik/traefik/provider/ecs"
"github.com/traefik/traefik/provider/etcd"
"github.com/traefik/traefik/provider/eureka"
"github.com/traefik/traefik/provider/file"
"github.com/traefik/traefik/provider/kubernetes"
"github.com/traefik/traefik/provider/marathon"
"github.com/traefik/traefik/provider/mesos"
"github.com/traefik/traefik/provider/rancher"
"github.com/traefik/traefik/provider/rest"
"github.com/traefik/traefik/provider/zk"
"github.com/traefik/traefik/tls"
"github.com/traefik/traefik/types"
jaegercli "github.com/uber/jaeger-client-go"
)
const (
// DefaultInternalEntryPointName the name of the default internal entry point
DefaultInternalEntryPointName = "traefik"
// DefaultHealthCheckInterval is the default health check interval.
DefaultHealthCheckInterval = 30 * time.Second
// DefaultDialTimeout when connecting to a backend server.
DefaultDialTimeout = 30 * time.Second
// DefaultIdleTimeout before closing an idle connection.
DefaultIdleTimeout = 180 * time.Second
// DefaultGraceTimeout controls how long Traefik serves pending requests
// prior to shutting down.
DefaultGraceTimeout = 10 * time.Second
// DefaultAcmeCAServer is the default ACME API endpoint
DefaultAcmeCAServer = "https://acme-v02.api.letsencrypt.org/directory"
)
// GlobalConfiguration holds global configuration (with providers, etc.).
// It's populated from the traefik configuration file passed as an argument to the binary.
type GlobalConfiguration struct {
LifeCycle *LifeCycle `description:"Timeouts influencing the server life cycle" export:"true"`
GraceTimeOut flaeg.Duration `short:"g" description:"(Deprecated) Duration to give active requests a chance to finish before Traefik stops" export:"true"` // Deprecated
Debug bool `short:"d" description:"Enable debug mode" export:"true"`
CheckNewVersion bool `description:"Periodically check if a new version has been released" export:"true"`
SendAnonymousUsage bool `description:"send periodically anonymous usage statistics" export:"true"`
AccessLogsFile string `description:"(Deprecated) Access logs file" export:"true"` // Deprecated
AccessLog *types.AccessLog `description:"Access log settings" export:"true"`
TraefikLogsFile string `description:"(Deprecated) Traefik logs file. Stdout is used when omitted or empty" export:"true"` // Deprecated
TraefikLog *types.TraefikLog `description:"Traefik log settings" export:"true"`
Tracing *tracing.Tracing `description:"OpenTracing configuration" export:"true"`
LogLevel string `short:"l" description:"Log level" export:"true"`
EntryPoints EntryPoints `description:"Entrypoints definition using format: --entryPoints='Name:http Address::8000 Redirect.EntryPoint:https' --entryPoints='Name:https Address::4442 TLS:tests/traefik.crt,tests/traefik.key;prod/traefik.crt,prod/traefik.key'" export:"true"`
Cluster *types.Cluster
Constraints types.Constraints `description:"Filter services by constraint, matching with service tags" export:"true"`
ACME *acme.ACME `description:"Enable ACME (Let's Encrypt): automatic SSL" export:"true"`
DefaultEntryPoints DefaultEntryPoints `description:"Entrypoints to be used by frontends that do not specify any entrypoint" export:"true"`
ProvidersThrottleDuration flaeg.Duration `description:"Backends throttle duration: minimum duration between 2 events from providers before applying a new configuration. It avoids unnecessary reloads if multiples events are sent in a short amount of time." export:"true"`
MaxIdleConnsPerHost int `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used" export:"true"`
IdleTimeout flaeg.Duration `description:"(Deprecated) maximum amount of time an idle (keep-alive) connection will remain idle before closing itself." export:"true"` // Deprecated
InsecureSkipVerify bool `description:"Disable SSL certificate verification" export:"true"`
RootCAs tls.FilesOrContents `description:"Add cert file for self-signed certificate"`
Retry *Retry `description:"Enable retry sending request if network error" export:"true"`
HealthCheck *HealthCheckConfig `description:"Health check parameters" export:"true"`
RespondingTimeouts *RespondingTimeouts `description:"Timeouts for incoming requests to the Traefik instance" export:"true"`
ForwardingTimeouts *ForwardingTimeouts `description:"Timeouts for requests forwarded to the backend servers" export:"true"`
AllowMinWeightZero bool `description:"Allow weight to take 0 as minimum real value." export:"true"` // Deprecated
KeepTrailingSlash bool `description:"Do not remove trailing slash." export:"true"` // Deprecated
Web *WebCompatibility `description:"(Deprecated) Enable Web backend with default settings" export:"true"` // Deprecated
Docker *docker.Provider `description:"Enable Docker backend with default settings" export:"true"`
File *file.Provider `description:"Enable File backend with default settings" export:"true"`
Marathon *marathon.Provider `description:"Enable Marathon backend with default settings" export:"true"`
Consul *consul.Provider `description:"Enable Consul backend with default settings" export:"true"`
ConsulCatalog *consulcatalog.Provider `description:"Enable Consul catalog backend with default settings" export:"true"`
Etcd *etcd.Provider `description:"Enable Etcd backend with default settings" export:"true"`
Zookeeper *zk.Provider `description:"Enable Zookeeper backend with default settings" export:"true"`
Boltdb *boltdb.Provider `description:"Enable Boltdb backend with default settings" export:"true"`
Kubernetes *kubernetes.Provider `description:"Enable Kubernetes backend with default settings" export:"true"`
Mesos *mesos.Provider `description:"Enable Mesos backend with default settings" export:"true"`
Eureka *eureka.Provider `description:"Enable Eureka backend with default settings" export:"true"`
ECS *ecs.Provider `description:"Enable ECS backend with default settings" export:"true"`
Rancher *rancher.Provider `description:"Enable Rancher backend with default settings" export:"true"`
DynamoDB *dynamodb.Provider `description:"Enable DynamoDB backend with default settings" export:"true"`
ServiceFabric *servicefabric.Provider `description:"Enable Service Fabric backend with default settings" export:"true"`
Rest *rest.Provider `description:"Enable Rest backend with default settings" export:"true"`
API *api.Handler `description:"Enable api/dashboard" export:"true"`
Metrics *types.Metrics `description:"Enable a metrics exporter" export:"true"`
Ping *ping.Handler `description:"Enable ping" export:"true"`
HostResolver *HostResolverConfig `description:"Enable CNAME Flattening" export:"true"`
}
// WebCompatibility is a configuration to handle compatibility with deprecated web provider options
type WebCompatibility struct {
Address string `description:"(Deprecated) Web administration port" export:"true"`
CertFile string `description:"(Deprecated) SSL certificate" export:"true"`
KeyFile string `description:"(Deprecated) SSL certificate" export:"true"`
ReadOnly bool `description:"(Deprecated) Enable read only API" export:"true"`
Statistics *types.Statistics `description:"(Deprecated) Enable more detailed statistics" export:"true"`
Metrics *types.Metrics `description:"(Deprecated) Enable a metrics exporter" export:"true"`
Path string `description:"(Deprecated) Root path for dashboard and API" export:"true"`
Auth *types.Auth `export:"true"`
Debug bool `export:"true"`
}
func (gc *GlobalConfiguration) handleWebDeprecation() {
if gc.Web != nil {
log.Warn("web provider configuration is deprecated, you should use these options : api, rest provider, ping and metrics")
if gc.API != nil || gc.Metrics != nil || gc.Ping != nil || gc.Rest != nil {
log.Warn("web option is ignored if you use it with one of these options : api, rest provider, ping or metrics")
return
}
gc.EntryPoints[DefaultInternalEntryPointName] = &EntryPoint{
Address: gc.Web.Address,
Auth: gc.Web.Auth,
}
if gc.Web.CertFile != "" {
gc.EntryPoints[DefaultInternalEntryPointName].TLS = &tls.TLS{
Certificates: []tls.Certificate{
{
CertFile: tls.FileOrContent(gc.Web.CertFile),
KeyFile: tls.FileOrContent(gc.Web.KeyFile),
},
},
}
}
if gc.API == nil {
gc.API = &api.Handler{
EntryPoint: DefaultInternalEntryPointName,
Statistics: gc.Web.Statistics,
Dashboard: true,
}
}
if gc.Ping == nil {
gc.Ping = &ping.Handler{
EntryPoint: DefaultInternalEntryPointName,
}
}
if gc.Metrics == nil {
gc.Metrics = gc.Web.Metrics
}
if !gc.Debug {
gc.Debug = gc.Web.Debug
}
}
}
// SetEffectiveConfiguration adds missing configuration parameters derived from existing ones.
// It also takes care of maintaining backwards compatibility.
func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
if len(gc.EntryPoints) == 0 {
gc.EntryPoints = map[string]*EntryPoint{"http": {
Address: ":80",
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
}}
gc.DefaultEntryPoints = []string{"http"}
}
gc.handleWebDeprecation()
if (gc.API != nil && gc.API.EntryPoint == DefaultInternalEntryPointName) ||
(gc.Ping != nil && gc.Ping.EntryPoint == DefaultInternalEntryPointName) ||
(gc.Metrics != nil && gc.Metrics.Prometheus != nil && gc.Metrics.Prometheus.EntryPoint == DefaultInternalEntryPointName) ||
(gc.Rest != nil && gc.Rest.EntryPoint == DefaultInternalEntryPointName) {
if _, ok := gc.EntryPoints[DefaultInternalEntryPointName]; !ok {
gc.EntryPoints[DefaultInternalEntryPointName] = &EntryPoint{Address: ":8080"}
}
}
for entryPointName := range gc.EntryPoints {
entryPoint := gc.EntryPoints[entryPointName]
// ForwardedHeaders must be remove in the next breaking version
if entryPoint.ForwardedHeaders == nil {
entryPoint.ForwardedHeaders = &ForwardedHeaders{Insecure: true}
}
if len(entryPoint.WhitelistSourceRange) > 0 {
log.Warnf("Deprecated configuration found: %s. Please use %s.", "whiteListSourceRange", "whiteList.sourceRange")
if entryPoint.WhiteList == nil {
entryPoint.WhiteList = &types.WhiteList{
SourceRange: entryPoint.WhitelistSourceRange,
}
entryPoint.WhitelistSourceRange = nil
}
}
if entryPoint.TLS != nil && entryPoint.TLS.DefaultCertificate == nil && len(entryPoint.TLS.Certificates) > 0 {
log.Infof("No tls.defaultCertificate given for %s: using the first item in tls.certificates as a fallback.", entryPointName)
entryPoint.TLS.DefaultCertificate = &entryPoint.TLS.Certificates[0]
}
}
// Make sure LifeCycle isn't nil to spare nil checks elsewhere.
if gc.LifeCycle == nil {
gc.LifeCycle = &LifeCycle{}
}
// Prefer legacy grace timeout parameter for backwards compatibility reasons.
if gc.GraceTimeOut > 0 {
log.Warn("top-level grace period configuration has been deprecated -- please use lifecycle grace period")
gc.LifeCycle.GraceTimeOut = gc.GraceTimeOut
}
if gc.Docker != nil {
if len(gc.Docker.Filename) != 0 && gc.Docker.TemplateVersion != 2 {
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
gc.Docker.TemplateVersion = 1
} else {
gc.Docker.TemplateVersion = 2
}
if gc.Docker.SwarmModeRefreshSeconds <= 0 {
gc.Docker.SwarmModeRefreshSeconds = 15
}
}
if gc.Marathon != nil {
if len(gc.Marathon.Filename) != 0 && gc.Marathon.TemplateVersion != 2 {
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
gc.Marathon.TemplateVersion = 1
} else {
gc.Marathon.TemplateVersion = 2
}
}
if gc.Mesos != nil {
if len(gc.Mesos.Filename) != 0 && gc.Mesos.TemplateVersion != 2 {
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
gc.Mesos.TemplateVersion = 1
} else {
gc.Mesos.TemplateVersion = 2
}
}
if gc.Eureka != nil {
if gc.Eureka.Delay != 0 {
log.Warn("Delay has been deprecated -- please use RefreshSeconds")
gc.Eureka.RefreshSeconds = gc.Eureka.Delay
}
}
if gc.ECS != nil {
if len(gc.ECS.Filename) != 0 && gc.ECS.TemplateVersion != 2 {
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
gc.ECS.TemplateVersion = 1
} else {
gc.ECS.TemplateVersion = 2
}
}
if gc.ConsulCatalog != nil {
if len(gc.ConsulCatalog.Filename) != 0 && gc.ConsulCatalog.TemplateVersion != 2 {
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
gc.ConsulCatalog.TemplateVersion = 1
} else {
gc.ConsulCatalog.TemplateVersion = 2
}
}
if gc.Rancher != nil {
if len(gc.Rancher.Filename) != 0 && gc.Rancher.TemplateVersion != 2 {
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
gc.Rancher.TemplateVersion = 1
} else {
gc.Rancher.TemplateVersion = 2
}
// Ensure backwards compatibility for now
if len(gc.Rancher.AccessKey) > 0 ||
len(gc.Rancher.Endpoint) > 0 ||
len(gc.Rancher.SecretKey) > 0 {
if gc.Rancher.API == nil {
gc.Rancher.API = &rancher.APIConfiguration{
AccessKey: gc.Rancher.AccessKey,
SecretKey: gc.Rancher.SecretKey,
Endpoint: gc.Rancher.Endpoint,
}
}
log.Warn("Deprecated configuration found: rancher.[accesskey|secretkey|endpoint]. " +
"Please use rancher.api.[accesskey|secretkey|endpoint] instead.")
}
if gc.Rancher.Metadata != nil && len(gc.Rancher.Metadata.Prefix) == 0 {
gc.Rancher.Metadata.Prefix = "latest"
}
}
if gc.API != nil {
gc.API.Debug = gc.Debug
}
if gc.Web != nil && (gc.Web.Path == "" || !strings.HasSuffix(gc.Web.Path, "/")) {
gc.Web.Path += "/"
}
if gc.File != nil {
gc.File.TraefikFile = configFile
}
gc.initACMEProvider()
gc.initTracing()
}
func (gc *GlobalConfiguration) initTracing() {
if gc.Tracing != nil {
switch gc.Tracing.Backend {
case jaeger.Name:
if gc.Tracing.Jaeger == nil {
gc.Tracing.Jaeger = &jaeger.Config{
SamplingServerURL: "http://localhost:5778/sampling",
SamplingType: "const",
SamplingParam: 1.0,
LocalAgentHostPort: "127.0.0.1:6831",
TraceContextHeaderName: jaegercli.TraceContextHeaderName,
}
}
if gc.Tracing.Zipkin != nil {
log.Warn("Zipkin configuration will be ignored")
gc.Tracing.Zipkin = nil
}
if gc.Tracing.DataDog != nil {
log.Warn("DataDog configuration will be ignored")
gc.Tracing.DataDog = nil
}
case zipkin.Name:
if gc.Tracing.Zipkin == nil {
gc.Tracing.Zipkin = &zipkin.Config{
HTTPEndpoint: "http://localhost:9411/api/v1/spans",
SameSpan: false,
ID128Bit: true,
Debug: false,
}
}
if gc.Tracing.Jaeger != nil {
log.Warn("Jaeger configuration will be ignored")
gc.Tracing.Jaeger = nil
}
if gc.Tracing.DataDog != nil {
log.Warn("DataDog configuration will be ignored")
gc.Tracing.DataDog = nil
}
case datadog.Name:
if gc.Tracing.DataDog == nil {
gc.Tracing.DataDog = &datadog.Config{
LocalAgentHostPort: "localhost:8126",
GlobalTag: "",
Debug: false,
PrioritySampling: false,
}
}
if gc.Tracing.Zipkin != nil {
log.Warn("Zipkin configuration will be ignored")
gc.Tracing.Zipkin = nil
}
if gc.Tracing.Jaeger != nil {
log.Warn("Jaeger configuration will be ignored")
gc.Tracing.Jaeger = nil
}
default:
log.Warnf("Unknown tracer %q", gc.Tracing.Backend)
return
}
}
}
func (gc *GlobalConfiguration) initACMEProvider() {
if gc.ACME != nil {
gc.ACME.CAServer = getSafeACMECAServer(gc.ACME.CAServer)
if gc.ACME.DNSChallenge != nil && gc.ACME.HTTPChallenge != nil {
log.Warn("Unable to use DNS challenge and HTTP challenge at the same time. Fallback to DNS challenge.")
gc.ACME.HTTPChallenge = nil
}
if gc.ACME.DNSChallenge != nil && gc.ACME.TLSChallenge != nil {
log.Warn("Unable to use DNS challenge and TLS challenge at the same time. Fallback to DNS challenge.")
gc.ACME.TLSChallenge = nil
}
if gc.ACME.HTTPChallenge != nil && gc.ACME.TLSChallenge != nil {
log.Warn("Unable to use HTTP challenge and TLS challenge at the same time. Fallback to TLS challenge.")
gc.ACME.HTTPChallenge = nil
}
for _, domain := range gc.ACME.Domains {
if domain.Main != dns01.UnFqdn(domain.Main) {
log.Warnf("FQDN detected, please remove the trailing dot: %s", domain.Main)
}
for _, san := range domain.SANs {
if san != dns01.UnFqdn(san) {
log.Warnf("FQDN detected, please remove the trailing dot: %s", san)
}
}
}
// TODO: to remove in the future
if len(gc.ACME.StorageFile) > 0 && len(gc.ACME.Storage) == 0 {
log.Warn("ACME.StorageFile is deprecated, use ACME.Storage instead")
gc.ACME.Storage = gc.ACME.StorageFile
}
if len(gc.ACME.DNSProvider) > 0 {
log.Warn("ACME.DNSProvider is deprecated, use ACME.DNSChallenge instead")
gc.ACME.DNSChallenge = &acmeprovider.DNSChallenge{Provider: gc.ACME.DNSProvider, DelayBeforeCheck: gc.ACME.DelayDontCheckDNS}
}
if gc.ACME.OnDemand {
log.Warn("ACME.OnDemand is deprecated")
}
}
}
// InitACMEProvider create an acme provider from the ACME part of globalConfiguration
func (gc *GlobalConfiguration) InitACMEProvider() (*acmeprovider.Provider, error) {
if gc.ACME != nil {
if len(gc.ACME.Storage) == 0 {
// Delete the ACME configuration to avoid starting ACME in cluster mode
gc.ACME = nil
return nil, errors.New("unable to initialize ACME provider with no storage location for the certificates")
}
// TODO: Remove when Provider ACME will replace totally ACME
// If provider file, use Provider ACME instead of ACME
if gc.Cluster == nil {
provider := &acmeprovider.Provider{}
provider.Configuration = &acmeprovider.Configuration{
KeyType: gc.ACME.KeyType,
OnHostRule: gc.ACME.OnHostRule,
OnDemand: gc.ACME.OnDemand,
Email: gc.ACME.Email,
PreferredChain: gc.ACME.PreferredChain,
Storage: gc.ACME.Storage,
HTTPChallenge: gc.ACME.HTTPChallenge,
DNSChallenge: gc.ACME.DNSChallenge,
TLSChallenge: gc.ACME.TLSChallenge,
Domains: gc.ACME.Domains,
ACMELogging: gc.ACME.ACMELogging,
CAServer: gc.ACME.CAServer,
EntryPoint: gc.ACME.EntryPoint,
}
store := acmeprovider.NewLocalStore(provider.Storage)
provider.Store = store
acme.ConvertToNewFormat(provider.Storage)
gc.ACME = nil
return provider, nil
}
}
return nil, nil
}
func getSafeACMECAServer(caServerSrc string) string {
if len(caServerSrc) == 0 {
return DefaultAcmeCAServer
}
if strings.HasPrefix(caServerSrc, "https://acme-v01.api.letsencrypt.org") {
caServer := strings.Replace(caServerSrc, "v01", "v02", 1)
log.Warnf("The CA server %[1]q refers to a v01 endpoint of the ACME API, please change to %[2]q. Fallback to %[2]q.", caServerSrc, caServer)
return caServer
}
if strings.HasPrefix(caServerSrc, "https://acme-staging.api.letsencrypt.org") {
caServer := strings.Replace(caServerSrc, "https://acme-staging.api.letsencrypt.org", "https://acme-staging-v02.api.letsencrypt.org", 1)
log.Warnf("The CA server %[1]q refers to a v01 endpoint of the ACME API, please change to %[2]q. Fallback to %[2]q.", caServerSrc, caServer)
return caServer
}
return caServerSrc
}
// ValidateConfiguration validate that configuration is coherent
func (gc *GlobalConfiguration) ValidateConfiguration() {
if gc.ACME != nil {
if _, ok := gc.EntryPoints[gc.ACME.EntryPoint]; !ok {
log.Fatalf("Unknown entrypoint %q for ACME configuration", gc.ACME.EntryPoint)
} else {
if gc.EntryPoints[gc.ACME.EntryPoint].TLS == nil {
log.Fatalf("Entrypoint %q has no TLS configuration for ACME configuration", gc.ACME.EntryPoint)
}
}
}
}
// DefaultEntryPoints holds default entry points
type DefaultEntryPoints []string
// String is the method to format the flag's value, part of the flag.Value interface.
// The String method's output will be used in diagnostics.
func (dep *DefaultEntryPoints) String() string {
return strings.Join(*dep, ",")
}
// Set is the method to set the flag value, part of the flag.Value interface.
// Set's argument is a string to be parsed to set the flag.
// It's a comma-separated list, so we split it.
func (dep *DefaultEntryPoints) Set(value string) error {
entrypoints := strings.Split(value, ",")
if len(entrypoints) == 0 {
return fmt.Errorf("bad DefaultEntryPoints format: %s", value)
}
for _, entrypoint := range entrypoints {
*dep = append(*dep, entrypoint)
}
return nil
}
// Get return the EntryPoints map
func (dep *DefaultEntryPoints) Get() interface{} {
return *dep
}
// SetValue sets the EntryPoints map with val
func (dep *DefaultEntryPoints) SetValue(val interface{}) {
*dep = val.(DefaultEntryPoints)
}
// Type is type of the struct
func (dep *DefaultEntryPoints) Type() string {
return "defaultentrypoints"
}
// Retry contains request retry config
type Retry struct {
Attempts int `description:"Number of attempts" export:"true"`
}
// HealthCheckConfig contains health check configuration parameters.
type HealthCheckConfig struct {
Interval flaeg.Duration `description:"Default periodicity of enabled health checks" export:"true"`
}
// RespondingTimeouts contains timeout configurations for incoming requests to the Traefik instance.
type RespondingTimeouts struct {
ReadTimeout flaeg.Duration `description:"ReadTimeout is the maximum duration for reading the entire request, including the body. If zero, no timeout is set" export:"true"`
WriteTimeout flaeg.Duration `description:"WriteTimeout is the maximum duration before timing out writes of the response. If zero, no timeout is set" export:"true"`
IdleTimeout flaeg.Duration `description:"IdleTimeout is the maximum amount duration an idle (keep-alive) connection will remain idle before closing itself. Defaults to 180 seconds. If zero, no timeout is set" export:"true"`
}
// ForwardingTimeouts contains timeout configurations for forwarding requests to the backend servers.
type ForwardingTimeouts struct {
DialTimeout flaeg.Duration `description:"The amount of time to wait until a connection to a backend server can be established. Defaults to 30 seconds. If zero, no timeout exists" export:"true"`
ResponseHeaderTimeout flaeg.Duration `description:"The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists" export:"true"`
}
// LifeCycle contains configurations relevant to the lifecycle (such as the
// shutdown phase) of Traefik.
type LifeCycle struct {
RequestAcceptGraceTimeout flaeg.Duration `description:"Duration to keep accepting requests before Traefik initiates the graceful shutdown procedure"`
GraceTimeOut flaeg.Duration `description:"Duration to give active requests a chance to finish before Traefik stops"`
}
// HostResolverConfig contain configuration for CNAME Flattening
type HostResolverConfig struct {
CnameFlattening bool `description:"A flag to enable/disable CNAME flattening" export:"true"`
ResolvConfig string `description:"resolv.conf used for DNS resolving" export:"true"`
ResolvDepth int `description:"The maximal depth of DNS recursive resolving" export:"true"`
}

View File

@@ -0,0 +1,336 @@
package configuration
import (
"testing"
"time"
"github.com/containous/flaeg"
"github.com/stretchr/testify/assert"
"github.com/traefik/traefik/acme"
"github.com/traefik/traefik/middlewares/tracing"
"github.com/traefik/traefik/middlewares/tracing/jaeger"
"github.com/traefik/traefik/middlewares/tracing/zipkin"
"github.com/traefik/traefik/provider"
acmeprovider "github.com/traefik/traefik/provider/acme"
"github.com/traefik/traefik/provider/file"
"github.com/traefik/traefik/tls"
)
const defaultConfigFile = "traefik.toml"
func TestSetEffectiveConfigurationGraceTimeout(t *testing.T) {
testCases := []struct {
desc string
legacyGraceTimeout time.Duration
lifeCycleGraceTimeout time.Duration
wantGraceTimeout time.Duration
}{
{
desc: "legacy grace timeout given only",
legacyGraceTimeout: 5 * time.Second,
wantGraceTimeout: 5 * time.Second,
},
{
desc: "legacy and life cycle grace timeouts given",
legacyGraceTimeout: 5 * time.Second,
lifeCycleGraceTimeout: 12 * time.Second,
wantGraceTimeout: 5 * time.Second,
},
{
desc: "legacy grace timeout omitted",
legacyGraceTimeout: 0,
lifeCycleGraceTimeout: 12 * time.Second,
wantGraceTimeout: 12 * time.Second,
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
gc := &GlobalConfiguration{
GraceTimeOut: flaeg.Duration(test.legacyGraceTimeout),
}
if test.lifeCycleGraceTimeout > 0 {
gc.LifeCycle = &LifeCycle{
GraceTimeOut: flaeg.Duration(test.lifeCycleGraceTimeout),
}
}
gc.SetEffectiveConfiguration(defaultConfigFile)
assert.Equal(t, test.wantGraceTimeout, time.Duration(gc.LifeCycle.GraceTimeOut))
})
}
}
func TestSetEffectiveConfigurationFileProviderFilename(t *testing.T) {
testCases := []struct {
desc string
fileProvider *file.Provider
wantFileProviderFilename string
wantFileProviderTraefikFile string
}{
{
desc: "no filename for file provider given",
fileProvider: &file.Provider{},
wantFileProviderFilename: "",
wantFileProviderTraefikFile: defaultConfigFile,
},
{
desc: "filename for file provider given",
fileProvider: &file.Provider{BaseProvider: provider.BaseProvider{Filename: "other.toml"}},
wantFileProviderFilename: "other.toml",
wantFileProviderTraefikFile: defaultConfigFile,
},
{
desc: "directory for file provider given",
fileProvider: &file.Provider{Directory: "/"},
wantFileProviderFilename: "",
wantFileProviderTraefikFile: defaultConfigFile,
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
gc := &GlobalConfiguration{
File: test.fileProvider,
}
gc.SetEffectiveConfiguration(defaultConfigFile)
assert.Equal(t, test.wantFileProviderFilename, gc.File.Filename)
assert.Equal(t, test.wantFileProviderTraefikFile, gc.File.TraefikFile)
})
}
}
func TestSetEffectiveConfigurationTracing(t *testing.T) {
testCases := []struct {
desc string
tracing *tracing.Tracing
expected *tracing.Tracing
}{
{
desc: "no tracing configuration",
tracing: &tracing.Tracing{},
expected: &tracing.Tracing{},
},
{
desc: "tracing bad backend name",
tracing: &tracing.Tracing{
Backend: "powpow",
},
expected: &tracing.Tracing{
Backend: "powpow",
},
},
{
desc: "tracing jaeger backend name",
tracing: &tracing.Tracing{
Backend: "jaeger",
Zipkin: &zipkin.Config{
HTTPEndpoint: "http://localhost:9411/api/v1/spans",
SameSpan: false,
ID128Bit: true,
Debug: false,
},
},
expected: &tracing.Tracing{
Backend: "jaeger",
Jaeger: &jaeger.Config{
SamplingServerURL: "http://localhost:5778/sampling",
SamplingType: "const",
SamplingParam: 1.0,
LocalAgentHostPort: "127.0.0.1:6831",
TraceContextHeaderName: "uber-trace-id",
},
Zipkin: nil,
},
},
{
desc: "tracing zipkin backend name",
tracing: &tracing.Tracing{
Backend: "zipkin",
Jaeger: &jaeger.Config{
SamplingServerURL: "http://localhost:5778/sampling",
SamplingType: "const",
SamplingParam: 1.0,
LocalAgentHostPort: "127.0.0.1:6831",
TraceContextHeaderName: "uber-trace-id",
},
},
expected: &tracing.Tracing{
Backend: "zipkin",
Jaeger: nil,
Zipkin: &zipkin.Config{
HTTPEndpoint: "http://localhost:9411/api/v1/spans",
SameSpan: false,
ID128Bit: true,
Debug: false,
},
},
},
{
desc: "tracing zipkin backend name value override",
tracing: &tracing.Tracing{
Backend: "zipkin",
Jaeger: &jaeger.Config{
SamplingServerURL: "http://localhost:5778/sampling",
SamplingType: "const",
SamplingParam: 1.0,
LocalAgentHostPort: "127.0.0.1:6831",
TraceContextHeaderName: "uber-trace-id",
},
Zipkin: &zipkin.Config{
HTTPEndpoint: "http://powpow:9411/api/v1/spans",
SameSpan: true,
ID128Bit: true,
Debug: true,
},
},
expected: &tracing.Tracing{
Backend: "zipkin",
Jaeger: nil,
Zipkin: &zipkin.Config{
HTTPEndpoint: "http://powpow:9411/api/v1/spans",
SameSpan: true,
ID128Bit: true,
Debug: true,
},
},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
gc := &GlobalConfiguration{
Tracing: test.tracing,
}
gc.SetEffectiveConfiguration(defaultConfigFile)
assert.Equal(t, test.expected, gc.Tracing)
})
}
}
func TestInitACMEProvider(t *testing.T) {
testCases := []struct {
desc string
acmeConfiguration *acme.ACME
expectedConfiguration *acmeprovider.Provider
noError bool
}{
{
desc: "No ACME configuration",
acmeConfiguration: nil,
expectedConfiguration: nil,
noError: true,
},
{
desc: "ACME configuration with storage",
acmeConfiguration: &acme.ACME{Storage: "foo/acme.json"},
expectedConfiguration: &acmeprovider.Provider{Configuration: &acmeprovider.Configuration{Storage: "foo/acme.json"}},
noError: true,
},
{
desc: "ACME configuration with no storage",
acmeConfiguration: &acme.ACME{},
expectedConfiguration: nil,
noError: false,
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
gc := &GlobalConfiguration{
ACME: test.acmeConfiguration,
}
configuration, err := gc.InitACMEProvider()
assert.True(t, (err == nil) == test.noError)
if test.expectedConfiguration == nil {
assert.Nil(t, configuration)
} else {
assert.Equal(t, test.expectedConfiguration.Storage, configuration.Storage)
}
})
}
}
func TestSetEffectiveConfigurationTLSMinVersion(t *testing.T) {
testCases := []struct {
desc string
provided EntryPoint
expected EntryPoint
}{
{
desc: "Entrypoint with no TLS",
provided: EntryPoint{
Address: ":80",
},
expected: EntryPoint{
Address: ":80",
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
},
},
{
desc: "Entrypoint with TLS Specifying MinVersion",
provided: EntryPoint{
Address: ":443",
TLS: &tls.TLS{
MinVersion: "VersionTLS12",
},
},
expected: EntryPoint{
Address: ":443",
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
TLS: &tls.TLS{
MinVersion: "VersionTLS12",
},
},
},
{
desc: "Entrypoint with TLS without Specifying MinVersion",
provided: EntryPoint{
Address: ":443",
TLS: &tls.TLS{},
},
expected: EntryPoint{
Address: ":443",
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
TLS: &tls.TLS{},
},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
gc := &GlobalConfiguration{
EntryPoints: map[string]*EntryPoint{
"foo": &test.provided,
},
}
gc.SetEffectiveConfiguration(defaultConfigFile)
assert.Equal(t, &test.expected, gc.EntryPoints["foo"])
})
}
}

View File

@@ -0,0 +1,296 @@
package configuration
import (
"fmt"
"strings"
"github.com/traefik/traefik/log"
"github.com/traefik/traefik/tls"
"github.com/traefik/traefik/types"
)
// EntryPoint holds an entry point configuration of the reverse proxy (ip, port, TLS...)
type EntryPoint struct {
Address string
TLS *tls.TLS `export:"true"`
Redirect *types.Redirect `export:"true"`
Auth *types.Auth `export:"true"`
WhitelistSourceRange []string // Deprecated
WhiteList *types.WhiteList `export:"true"`
Compress bool `export:"true"`
ProxyProtocol *ProxyProtocol `export:"true"`
ForwardedHeaders *ForwardedHeaders `export:"true"`
}
// ProxyProtocol contains Proxy-Protocol configuration
type ProxyProtocol struct {
Insecure bool `export:"true"`
TrustedIPs []string
}
// ForwardedHeaders Trust client forwarding headers
type ForwardedHeaders struct {
Insecure bool `export:"true"`
TrustedIPs []string
}
// EntryPoints holds entry points configuration of the reverse proxy (ip, port, TLS...)
type EntryPoints map[string]*EntryPoint
// String is the method to format the flag's value, part of the flag.Value interface.
// The String method's output will be used in diagnostics.
func (ep EntryPoints) String() string {
return fmt.Sprintf("%+v", map[string]*EntryPoint(ep))
}
// Get return the EntryPoints map
func (ep *EntryPoints) Get() interface{} {
return *ep
}
// SetValue sets the EntryPoints map with val
func (ep *EntryPoints) SetValue(val interface{}) {
*ep = val.(EntryPoints)
}
// Type is type of the struct
func (ep *EntryPoints) Type() string {
return "entrypoints"
}
// Set is the method to set the flag value, part of the flag.Value interface.
// Set's argument is a string to be parsed to set the flag.
// It's a comma-separated list, so we split it.
func (ep *EntryPoints) Set(value string) error {
result := parseEntryPointsConfiguration(value)
var whiteListSourceRange []string
if len(result["whitelistsourcerange"]) > 0 {
whiteListSourceRange = strings.Split(result["whitelistsourcerange"], ",")
}
compress := toBool(result, "compress")
configTLS, err := makeEntryPointTLS(result)
if err != nil {
return err
}
(*ep)[result["name"]] = &EntryPoint{
Address: result["address"],
TLS: configTLS,
Auth: makeEntryPointAuth(result),
Redirect: makeEntryPointRedirect(result),
Compress: compress,
WhitelistSourceRange: whiteListSourceRange,
WhiteList: makeWhiteList(result),
ProxyProtocol: makeEntryPointProxyProtocol(result),
ForwardedHeaders: makeEntryPointForwardedHeaders(result),
}
return nil
}
func makeWhiteList(result map[string]string) *types.WhiteList {
var wl *types.WhiteList
if rawRange, ok := result["whitelist_sourcerange"]; ok {
wl = &types.WhiteList{
SourceRange: strings.Split(rawRange, ","),
UseXForwardedFor: toBool(result, "whitelist_usexforwardedfor"),
}
}
return wl
}
func makeEntryPointAuth(result map[string]string) *types.Auth {
var basic *types.Basic
if v, ok := result["auth_basic_users"]; ok {
basic = &types.Basic{
Users: strings.Split(v, ","),
RemoveHeader: toBool(result, "auth_basic_removeheader"),
}
}
var digest *types.Digest
if v, ok := result["auth_digest_users"]; ok {
digest = &types.Digest{
Users: strings.Split(v, ","),
RemoveHeader: toBool(result, "auth_digest_removeheader"),
}
}
var forward *types.Forward
if address, ok := result["auth_forward_address"]; ok {
var clientTLS *types.ClientTLS
cert := result["auth_forward_tls_cert"]
key := result["auth_forward_tls_key"]
insecureSkipVerify := toBool(result, "auth_forward_tls_insecureskipverify")
if len(cert) > 0 && len(key) > 0 || insecureSkipVerify {
clientTLS = &types.ClientTLS{
CA: result["auth_forward_tls_ca"],
CAOptional: toBool(result, "auth_forward_tls_caoptional"),
Cert: cert,
Key: key,
InsecureSkipVerify: insecureSkipVerify,
}
}
var authResponseHeaders []string
if v, ok := result["auth_forward_authresponseheaders"]; ok {
authResponseHeaders = strings.Split(v, ",")
}
forward = &types.Forward{
Address: address,
TLS: clientTLS,
TrustForwardHeader: toBool(result, "auth_forward_trustforwardheader"),
AuthResponseHeaders: authResponseHeaders,
}
}
var auth *types.Auth
if basic != nil || digest != nil || forward != nil {
auth = &types.Auth{
Basic: basic,
Digest: digest,
Forward: forward,
HeaderField: result["auth_headerfield"],
}
}
return auth
}
func makeEntryPointProxyProtocol(result map[string]string) *ProxyProtocol {
var proxyProtocol *ProxyProtocol
ppTrustedIPs := result["proxyprotocol_trustedips"]
if len(result["proxyprotocol_insecure"]) > 0 || len(ppTrustedIPs) > 0 {
proxyProtocol = &ProxyProtocol{
Insecure: toBool(result, "proxyprotocol_insecure"),
}
if len(ppTrustedIPs) > 0 {
proxyProtocol.TrustedIPs = strings.Split(ppTrustedIPs, ",")
}
}
if proxyProtocol != nil && proxyProtocol.Insecure {
log.Warn("ProxyProtocol.Insecure:true is dangerous. Please use 'ProxyProtocol.TrustedIPs:IPs' and remove 'ProxyProtocol.Insecure:true'")
}
return proxyProtocol
}
func makeEntryPointForwardedHeaders(result map[string]string) *ForwardedHeaders {
// TODO must be changed to false by default in the next breaking version.
forwardedHeaders := &ForwardedHeaders{Insecure: true}
if _, ok := result["forwardedheaders_insecure"]; ok {
forwardedHeaders.Insecure = toBool(result, "forwardedheaders_insecure")
}
fhTrustedIPs := result["forwardedheaders_trustedips"]
if len(fhTrustedIPs) > 0 {
// TODO must be removed in the next breaking version.
forwardedHeaders.Insecure = toBool(result, "forwardedheaders_insecure")
forwardedHeaders.TrustedIPs = strings.Split(fhTrustedIPs, ",")
}
return forwardedHeaders
}
func makeEntryPointRedirect(result map[string]string) *types.Redirect {
var redirect *types.Redirect
if len(result["redirect_entrypoint"]) > 0 || len(result["redirect_regex"]) > 0 || len(result["redirect_replacement"]) > 0 {
redirect = &types.Redirect{
EntryPoint: result["redirect_entrypoint"],
Regex: result["redirect_regex"],
Replacement: result["redirect_replacement"],
Permanent: toBool(result, "redirect_permanent"),
}
}
return redirect
}
func makeEntryPointTLS(result map[string]string) (*tls.TLS, error) {
var configTLS *tls.TLS
if len(result["tls"]) > 0 {
certs := tls.Certificates{}
if err := certs.Set(result["tls"]); err != nil {
return nil, err
}
configTLS = &tls.TLS{
Certificates: certs,
}
} else if len(result["tls_acme"]) > 0 {
configTLS = &tls.TLS{
Certificates: tls.Certificates{},
}
}
if configTLS != nil {
if len(result["ca"]) > 0 {
files := tls.FilesOrContents{}
files.Set(result["ca"])
optional := toBool(result, "ca_optional")
configTLS.ClientCA = tls.ClientCA{
Files: files,
Optional: optional,
}
}
if len(result["tls_minversion"]) > 0 {
configTLS.MinVersion = result["tls_minversion"]
}
if len(result["tls_ciphersuites"]) > 0 {
configTLS.CipherSuites = strings.Split(result["tls_ciphersuites"], ",")
}
if len(result["tls_snistrict"]) > 0 {
configTLS.SniStrict = toBool(result, "tls_snistrict")
}
if len(result["tls_defaultcertificate_cert"]) > 0 && len(result["tls_defaultcertificate_key"]) > 0 {
configTLS.DefaultCertificate = &tls.Certificate{
CertFile: tls.FileOrContent(result["tls_defaultcertificate_cert"]),
KeyFile: tls.FileOrContent(result["tls_defaultcertificate_key"]),
}
}
}
return configTLS, nil
}
func parseEntryPointsConfiguration(raw string) map[string]string {
sections := strings.Fields(raw)
config := make(map[string]string)
for _, part := range sections {
field := strings.SplitN(part, ":", 2)
name := strings.ToLower(strings.Replace(field[0], ".", "_", -1))
if len(field) > 1 {
config[name] = field[1]
} else {
if strings.EqualFold(name, "TLS") {
config["tls_acme"] = "TLS"
} else {
config[name] = ""
}
}
}
return config
}
func toBool(conf map[string]string, key string) bool {
if val, ok := conf[key]; ok {
return strings.EqualFold(val, "true") ||
strings.EqualFold(val, "enable") ||
strings.EqualFold(val, "on")
}
return false
}

View File

@@ -0,0 +1,493 @@
package configuration
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/traefik/traefik/tls"
"github.com/traefik/traefik/types"
)
func Test_parseEntryPointsConfiguration(t *testing.T) {
testCases := []struct {
name string
value string
expectedResult map[string]string
}{
{
name: "all parameters",
value: "Name:foo " +
"Address::8000 " +
"TLS:goo,gii " +
"TLS " +
"TLS.MinVersion:VersionTLS11 " +
"TLS.CipherSuites:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA " +
"CA:car " +
"CA.Optional:true " +
"Redirect.EntryPoint:https " +
"Redirect.Regex:http://localhost/(.*) " +
"Redirect.Replacement:http://mydomain/$1 " +
"Redirect.Permanent:true " +
"Compress:true " +
"ProxyProtocol.TrustedIPs:192.168.0.1 " +
"ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24 " +
"Auth.Basic.Users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0 " +
"Auth.Basic.RemoveHeader:true " +
"Auth.Digest.Users:test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e " +
"Auth.Digest.RemoveHeader:true " +
"Auth.HeaderField:X-WebAuth-User " +
"Auth.Forward.Address:https://authserver.com/auth " +
"Auth.Forward.AuthResponseHeaders:X-Auth,X-Test,X-Secret " +
"Auth.Forward.TrustForwardHeader:true " +
"Auth.Forward.TLS.CA:path/to/local.crt " +
"Auth.Forward.TLS.CAOptional:true " +
"Auth.Forward.TLS.Cert:path/to/foo.cert " +
"Auth.Forward.TLS.Key:path/to/foo.key " +
"Auth.Forward.TLS.InsecureSkipVerify:true " +
"WhiteListSourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " +
"whiteList.sourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " +
"whiteList.useXForwardedFor:true ",
expectedResult: map[string]string{
"address": ":8000",
"auth_basic_users": "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
"auth_basic_removeheader": "true",
"auth_digest_users": "test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e",
"auth_digest_removeheader": "true",
"auth_forward_address": "https://authserver.com/auth",
"auth_forward_authresponseheaders": "X-Auth,X-Test,X-Secret",
"auth_forward_tls_ca": "path/to/local.crt",
"auth_forward_tls_caoptional": "true",
"auth_forward_tls_cert": "path/to/foo.cert",
"auth_forward_tls_insecureskipverify": "true",
"auth_forward_tls_key": "path/to/foo.key",
"auth_forward_trustforwardheader": "true",
"auth_headerfield": "X-WebAuth-User",
"ca": "car",
"ca_optional": "true",
"compress": "true",
"forwardedheaders_trustedips": "10.0.0.3/24,20.0.0.3/24",
"name": "foo",
"proxyprotocol_trustedips": "192.168.0.1",
"redirect_entrypoint": "https",
"redirect_permanent": "true",
"redirect_regex": "http://localhost/(.*)",
"redirect_replacement": "http://mydomain/$1",
"tls": "goo,gii",
"tls_acme": "TLS",
"tls_ciphersuites": "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"tls_minversion": "VersionTLS11",
"whitelistsourcerange": "10.42.0.0/16,152.89.1.33/32,afed:be44::/16",
"whitelist_sourcerange": "10.42.0.0/16,152.89.1.33/32,afed:be44::/16",
"whitelist_usexforwardedfor": "true",
},
},
{
name: "compress on",
value: "name:foo Compress:on",
expectedResult: map[string]string{
"name": "foo",
"compress": "on",
},
},
{
name: "TLS",
value: "Name:foo TLS:goo TLS",
expectedResult: map[string]string{
"name": "foo",
"tls": "goo",
"tls_acme": "TLS",
},
},
}
for _, test := range testCases {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
conf := parseEntryPointsConfiguration(test.value)
assert.Len(t, conf, len(test.expectedResult))
assert.Equal(t, test.expectedResult, conf)
})
}
}
func Test_toBool(t *testing.T) {
testCases := []struct {
name string
value string
key string
expectedBool bool
}{
{
name: "on",
value: "on",
key: "foo",
expectedBool: true,
},
{
name: "true",
value: "true",
key: "foo",
expectedBool: true,
},
{
name: "enable",
value: "enable",
key: "foo",
expectedBool: true,
},
{
name: "arbitrary string",
value: "bar",
key: "foo",
expectedBool: false,
},
{
name: "no existing entry",
value: "bar",
key: "fii",
expectedBool: false,
},
}
for _, test := range testCases {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
conf := map[string]string{
"foo": test.value,
}
result := toBool(conf, test.key)
assert.Equal(t, test.expectedBool, result)
})
}
}
func TestEntryPoints_Set(t *testing.T) {
testCases := []struct {
name string
expression string
expectedEntryPointName string
expectedEntryPoint *EntryPoint
}{
{
name: "all parameters camelcase",
expression: "Name:foo " +
"Address::8000 " +
"TLS:goo,gii;foo,fii " +
"TLS " +
"TLS.MinVersion:VersionTLS11 " +
"TLS.CipherSuites:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA " +
"CA:car " +
"CA.Optional:true " +
"Redirect.EntryPoint:https " +
"Redirect.Regex:http://localhost/(.*) " +
"Redirect.Replacement:http://mydomain/$1 " +
"Redirect.Permanent:true " +
"Compress:true " +
"ProxyProtocol.TrustedIPs:192.168.0.1 " +
"ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24 " +
"Auth.Basic.Users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0 " +
"Auth.Basic.RemoveHeader:true " +
"Auth.Digest.Users:test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e " +
"Auth.Digest.RemoveHeader:true " +
"Auth.HeaderField:X-WebAuth-User " +
"Auth.Forward.Address:https://authserver.com/auth " +
"Auth.Forward.AuthResponseHeaders:X-Auth,X-Test,X-Secret " +
"Auth.Forward.TrustForwardHeader:true " +
"Auth.Forward.TLS.CA:path/to/local.crt " +
"Auth.Forward.TLS.CAOptional:true " +
"Auth.Forward.TLS.Cert:path/to/foo.cert " +
"Auth.Forward.TLS.Key:path/to/foo.key " +
"Auth.Forward.TLS.InsecureSkipVerify:true " +
"WhiteListSourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " +
"whiteList.sourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " +
"whiteList.useXForwardedFor:true ",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
Address: ":8000",
TLS: &tls.TLS{
MinVersion: "VersionTLS11",
CipherSuites: []string{"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA384", "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"},
Certificates: tls.Certificates{
{
CertFile: tls.FileOrContent("goo"),
KeyFile: tls.FileOrContent("gii"),
},
{
CertFile: tls.FileOrContent("foo"),
KeyFile: tls.FileOrContent("fii"),
},
},
ClientCA: tls.ClientCA{
Files: tls.FilesOrContents{"car"},
Optional: true,
},
},
Redirect: &types.Redirect{
EntryPoint: "https",
Regex: "http://localhost/(.*)",
Replacement: "http://mydomain/$1",
Permanent: true,
},
Auth: &types.Auth{
Basic: &types.Basic{
RemoveHeader: true,
Users: types.Users{
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
},
},
Digest: &types.Digest{
RemoveHeader: true,
Users: types.Users{
"test:traefik:a2688e031edb4be6a3797f3882655c05",
"test2:traefik:518845800f9e2bfb1f1f740ec24f074e",
},
},
Forward: &types.Forward{
Address: "https://authserver.com/auth",
AuthResponseHeaders: []string{"X-Auth", "X-Test", "X-Secret"},
TLS: &types.ClientTLS{
CA: "path/to/local.crt",
CAOptional: true,
Cert: "path/to/foo.cert",
Key: "path/to/foo.key",
InsecureSkipVerify: true,
},
TrustForwardHeader: true,
},
HeaderField: "X-WebAuth-User",
},
WhitelistSourceRange: []string{
"10.42.0.0/16",
"152.89.1.33/32",
"afed:be44::/16",
},
WhiteList: &types.WhiteList{
SourceRange: []string{
"10.42.0.0/16",
"152.89.1.33/32",
"afed:be44::/16",
},
UseXForwardedFor: true,
},
Compress: true,
ProxyProtocol: &ProxyProtocol{
Insecure: false,
TrustedIPs: []string{"192.168.0.1"},
},
ForwardedHeaders: &ForwardedHeaders{
Insecure: false,
TrustedIPs: []string{
"10.0.0.3/24",
"20.0.0.3/24",
},
},
},
},
{
name: "all parameters lowercase",
expression: "Name:foo " +
"address::8000 " +
"tls:goo,gii;foo,fii " +
"tls " +
"tls.minversion:VersionTLS11 " +
"tls.ciphersuites:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA " +
"ca:car " +
"ca.Optional:true " +
"redirect.entryPoint:https " +
"redirect.regex:http://localhost/(.*) " +
"redirect.replacement:http://mydomain/$1 " +
"redirect.permanent:true " +
"compress:true " +
"whiteListSourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16 " +
"proxyProtocol.TrustedIPs:192.168.0.1 " +
"forwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24 " +
"auth.basic.users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0 " +
"auth.digest.users:test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e " +
"auth.headerField:X-WebAuth-User " +
"auth.forward.address:https://authserver.com/auth " +
"auth.forward.authResponseHeaders:X-Auth,X-Test,X-Secret " +
"auth.forward.trustForwardHeader:true " +
"auth.forward.tls.ca:path/to/local.crt " +
"auth.forward.tls.caOptional:true " +
"auth.forward.tls.cert:path/to/foo.cert " +
"auth.forward.tls.key:path/to/foo.key " +
"auth.forward.tls.insecureSkipVerify:true ",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
Address: ":8000",
TLS: &tls.TLS{
MinVersion: "VersionTLS11",
CipherSuites: []string{"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA384", "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"},
Certificates: tls.Certificates{
{
CertFile: tls.FileOrContent("goo"),
KeyFile: tls.FileOrContent("gii"),
},
{
CertFile: tls.FileOrContent("foo"),
KeyFile: tls.FileOrContent("fii"),
},
},
ClientCA: tls.ClientCA{
Files: tls.FilesOrContents{"car"},
Optional: true,
},
},
Redirect: &types.Redirect{
EntryPoint: "https",
Regex: "http://localhost/(.*)",
Replacement: "http://mydomain/$1",
Permanent: true,
},
Auth: &types.Auth{
Basic: &types.Basic{
Users: types.Users{
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
},
},
Digest: &types.Digest{
Users: types.Users{
"test:traefik:a2688e031edb4be6a3797f3882655c05",
"test2:traefik:518845800f9e2bfb1f1f740ec24f074e",
},
},
Forward: &types.Forward{
Address: "https://authserver.com/auth",
AuthResponseHeaders: []string{"X-Auth", "X-Test", "X-Secret"},
TLS: &types.ClientTLS{
CA: "path/to/local.crt",
CAOptional: true,
Cert: "path/to/foo.cert",
Key: "path/to/foo.key",
InsecureSkipVerify: true,
},
TrustForwardHeader: true,
},
HeaderField: "X-WebAuth-User",
},
WhitelistSourceRange: []string{
"10.42.0.0/16",
"152.89.1.33/32",
"afed:be44::/16",
},
Compress: true,
ProxyProtocol: &ProxyProtocol{
Insecure: false,
TrustedIPs: []string{"192.168.0.1"},
},
ForwardedHeaders: &ForwardedHeaders{
Insecure: false,
TrustedIPs: []string{
"10.0.0.3/24",
"20.0.0.3/24",
},
},
},
},
{
name: "default",
expression: "Name:foo",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
},
},
{
name: "ForwardedHeaders insecure true",
expression: "Name:foo ForwardedHeaders.Insecure:true",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
},
},
{
name: "ForwardedHeaders insecure false",
expression: "Name:foo ForwardedHeaders.Insecure:false",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
ForwardedHeaders: &ForwardedHeaders{Insecure: false},
},
},
{
name: "ForwardedHeaders TrustedIPs",
expression: "Name:foo ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
ForwardedHeaders: &ForwardedHeaders{
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
},
},
},
{
name: "ProxyProtocol insecure true",
expression: "Name:foo ProxyProtocol.Insecure:true",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
ProxyProtocol: &ProxyProtocol{Insecure: true},
},
},
{
name: "ProxyProtocol insecure false",
expression: "Name:foo ProxyProtocol.Insecure:false",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
ProxyProtocol: &ProxyProtocol{},
},
},
{
name: "ProxyProtocol TrustedIPs",
expression: "Name:foo ProxyProtocol.TrustedIPs:10.0.0.3/24,20.0.0.3/24",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
ProxyProtocol: &ProxyProtocol{
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
},
},
},
{
name: "compress on",
expression: "Name:foo Compress:on",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
Compress: true,
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
},
},
{
name: "compress true",
expression: "Name:foo Compress:true",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
Compress: true,
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
},
},
}
for _, test := range testCases {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
eps := EntryPoints{}
err := eps.Set(test.expression)
require.NoError(t, err)
ep := eps[test.expectedEntryPointName]
assert.EqualValues(t, test.expectedEntryPoint, ep)
})
}
}

View File

@@ -0,0 +1,113 @@
package configuration
import (
"encoding/json"
"github.com/traefik/traefik/log"
"github.com/traefik/traefik/provider"
"github.com/traefik/traefik/safe"
"github.com/traefik/traefik/types"
)
// ProviderAggregator aggregate providers
type ProviderAggregator struct {
providers []provider.Provider
constraints types.Constraints
}
// NewProviderAggregator return an aggregate of all the providers configured in GlobalConfiguration
func NewProviderAggregator(gc *GlobalConfiguration) ProviderAggregator {
provider := ProviderAggregator{
constraints: gc.Constraints,
}
if gc.Docker != nil {
provider.quietAddProvider(gc.Docker)
}
if gc.Marathon != nil {
provider.quietAddProvider(gc.Marathon)
}
if gc.File != nil {
provider.quietAddProvider(gc.File)
}
if gc.Rest != nil {
provider.quietAddProvider(gc.Rest)
}
if gc.Consul != nil {
provider.quietAddProvider(gc.Consul)
}
if gc.ConsulCatalog != nil {
provider.quietAddProvider(gc.ConsulCatalog)
}
if gc.Etcd != nil {
provider.quietAddProvider(gc.Etcd)
}
if gc.Zookeeper != nil {
provider.quietAddProvider(gc.Zookeeper)
}
if gc.Boltdb != nil {
provider.quietAddProvider(gc.Boltdb)
}
if gc.Kubernetes != nil {
provider.quietAddProvider(gc.Kubernetes)
}
if gc.Mesos != nil {
provider.quietAddProvider(gc.Mesos)
}
if gc.Eureka != nil {
provider.quietAddProvider(gc.Eureka)
}
if gc.ECS != nil {
provider.quietAddProvider(gc.ECS)
}
if gc.Rancher != nil {
provider.quietAddProvider(gc.Rancher)
}
if gc.DynamoDB != nil {
provider.quietAddProvider(gc.DynamoDB)
}
if gc.ServiceFabric != nil {
provider.quietAddProvider(gc.ServiceFabric)
}
return provider
}
func (p *ProviderAggregator) quietAddProvider(provider provider.Provider) {
err := p.AddProvider(provider)
if err != nil {
log.Errorf("Error initializing provider %T: %v", provider, err)
}
}
// AddProvider add a provider in the providers map
func (p *ProviderAggregator) AddProvider(provider provider.Provider) error {
err := provider.Init(p.constraints)
if err != nil {
return err
}
p.providers = append(p.providers, provider)
return nil
}
// Init the provider
func (p ProviderAggregator) Init(_ types.Constraints) error {
return nil
}
// Provide call the provide method of every providers
func (p ProviderAggregator) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool) error {
for _, p := range p.providers {
jsonConf, err := json.Marshal(p)
if err != nil {
log.Debugf("Unable to marshal provider conf %T with error: %v", p, err)
}
log.Infof("Starting provider %T %s", p, jsonConf)
currentProvider := p
safe.Go(func() {
err := currentProvider.Provide(configurationChan, pool)
if err != nil {
log.Errorf("Error starting provider %T: %v", p, err)
}
})
}
return nil
}

View File

@@ -0,0 +1,132 @@
package router
import (
"github.com/containous/mux"
"github.com/traefik/traefik/configuration"
"github.com/traefik/traefik/log"
"github.com/traefik/traefik/metrics"
"github.com/traefik/traefik/middlewares"
mauth "github.com/traefik/traefik/middlewares/auth"
"github.com/traefik/traefik/types"
"github.com/urfave/negroni"
)
// NewInternalRouterAggregator Create a new internalRouterAggregator
func NewInternalRouterAggregator(globalConfiguration configuration.GlobalConfiguration, entryPointName string) *InternalRouterAggregator {
var serverMiddlewares []negroni.Handler
if globalConfiguration.EntryPoints[entryPointName].WhiteList != nil {
ipWhitelistMiddleware, err := middlewares.NewIPWhiteLister(
globalConfiguration.EntryPoints[entryPointName].WhiteList.SourceRange,
globalConfiguration.EntryPoints[entryPointName].WhiteList.UseXForwardedFor)
if err != nil {
log.Fatalf("Error creating whitelist middleware: %s", err)
}
if ipWhitelistMiddleware != nil {
serverMiddlewares = append(serverMiddlewares, ipWhitelistMiddleware)
}
}
if globalConfiguration.EntryPoints[entryPointName].Auth != nil {
authMiddleware, err := mauth.NewAuthenticator(globalConfiguration.EntryPoints[entryPointName].Auth, nil)
if err != nil {
log.Fatalf("Error creating authenticator middleware: %s", err)
}
serverMiddlewares = append(serverMiddlewares, authMiddleware)
}
router := InternalRouterAggregator{}
routerWithPrefix := InternalRouterAggregator{}
routerWithPrefixAndMiddleware := InternalRouterAggregator{}
if globalConfiguration.Metrics != nil && globalConfiguration.Metrics.Prometheus != nil && globalConfiguration.Metrics.Prometheus.EntryPoint == entryPointName {
routerWithPrefixAndMiddleware.AddRouter(metrics.PrometheusHandler{})
}
if globalConfiguration.Rest != nil && globalConfiguration.Rest.EntryPoint == entryPointName {
routerWithPrefixAndMiddleware.AddRouter(globalConfiguration.Rest)
}
if globalConfiguration.API != nil && globalConfiguration.API.EntryPoint == entryPointName {
routerWithPrefixAndMiddleware.AddRouter(globalConfiguration.API)
}
if globalConfiguration.Ping != nil && globalConfiguration.Ping.EntryPoint == entryPointName {
routerWithPrefix.AddRouter(globalConfiguration.Ping)
}
if globalConfiguration.ACME != nil && globalConfiguration.ACME.HTTPChallenge != nil && globalConfiguration.ACME.HTTPChallenge.EntryPoint == entryPointName {
router.AddRouter(globalConfiguration.ACME)
}
realRouterWithMiddleware := WithMiddleware{router: &routerWithPrefixAndMiddleware, routerMiddlewares: serverMiddlewares}
if globalConfiguration.Web != nil && globalConfiguration.Web.Path != "" {
router.AddRouter(&WithPrefix{PathPrefix: globalConfiguration.Web.Path, Router: &routerWithPrefix})
router.AddRouter(&WithPrefix{PathPrefix: globalConfiguration.Web.Path, Router: &realRouterWithMiddleware})
} else {
router.AddRouter(&routerWithPrefix)
router.AddRouter(&realRouterWithMiddleware)
}
return &router
}
// WithMiddleware router with internal middleware
type WithMiddleware struct {
router types.InternalRouter
routerMiddlewares []negroni.Handler
}
// AddRoutes Add routes to the router
func (wm *WithMiddleware) AddRoutes(systemRouter *mux.Router) {
realRouter := systemRouter.PathPrefix("/").Subrouter()
wm.router.AddRoutes(realRouter)
if len(wm.routerMiddlewares) > 0 {
realRouter.Walk(wrapRoute(wm.routerMiddlewares))
}
}
// WithPrefix router which add a prefix
type WithPrefix struct {
Router types.InternalRouter
PathPrefix string
}
// AddRoutes Add routes to the router
func (wp *WithPrefix) AddRoutes(systemRouter *mux.Router) {
realRouter := systemRouter.PathPrefix("/").Subrouter()
if wp.PathPrefix != "" {
realRouter = systemRouter.PathPrefix(wp.PathPrefix).Subrouter()
realRouter.StrictSlash(true)
realRouter.SkipClean(true)
}
wp.Router.AddRoutes(realRouter)
}
// InternalRouterAggregator InternalRouter that aggregate other internalRouter
type InternalRouterAggregator struct {
internalRouters []types.InternalRouter
}
// AddRouter add a router in the aggregator
func (r *InternalRouterAggregator) AddRouter(router types.InternalRouter) {
r.internalRouters = append(r.internalRouters, router)
}
// AddRoutes Add routes to the router
func (r *InternalRouterAggregator) AddRoutes(systemRouter *mux.Router) {
for _, router := range r.internalRouters {
router.AddRoutes(systemRouter)
}
}
// wrapRoute with middlewares
func wrapRoute(middlewares []negroni.Handler) func(*mux.Route, *mux.Router, []*mux.Route) error {
return func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
middles := append(middlewares, negroni.Wrap(route.GetHandler()))
route.Handler(negroni.New(middles...))
return nil
}
}

View File

@@ -0,0 +1,346 @@
package router
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/containous/mux"
"github.com/stretchr/testify/assert"
"github.com/traefik/traefik/acme"
"github.com/traefik/traefik/api"
"github.com/traefik/traefik/configuration"
"github.com/traefik/traefik/ping"
acmeprovider "github.com/traefik/traefik/provider/acme"
"github.com/traefik/traefik/safe"
"github.com/traefik/traefik/types"
"github.com/urfave/negroni"
)
func TestNewInternalRouterAggregatorWithWebPath(t *testing.T) {
currentConfiguration := &safe.Safe{}
currentConfiguration.Set(types.Configurations{})
globalConfiguration := configuration.GlobalConfiguration{
Web: &configuration.WebCompatibility{
Path: "/prefix",
},
API: &api.Handler{
EntryPoint: "traefik",
CurrentConfigurations: currentConfiguration,
},
Ping: &ping.Handler{
EntryPoint: "traefik",
},
ACME: &acme.ACME{
HTTPChallenge: &acmeprovider.HTTPChallenge{
EntryPoint: "traefik",
},
},
EntryPoints: configuration.EntryPoints{
"traefik": &configuration.EntryPoint{},
},
}
testCases := []struct {
desc string
testedURL string
expectedStatusCode int
}{
{
desc: "Ping without prefix",
testedURL: "/ping",
expectedStatusCode: 502,
},
{
desc: "Ping with prefix",
testedURL: "/prefix/ping",
expectedStatusCode: 200,
},
{
desc: "acme without prefix",
testedURL: "/.well-known/acme-challenge/token",
expectedStatusCode: 404,
},
{
desc: "api without prefix",
testedURL: "/api",
expectedStatusCode: 502,
},
{
desc: "api with prefix",
testedURL: "/prefix/api",
expectedStatusCode: 200,
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
router := NewInternalRouterAggregator(globalConfiguration, "traefik")
internalMuxRouter := mux.NewRouter()
router.AddRoutes(internalMuxRouter)
internalMuxRouter.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusBadGateway)
})
recorder := httptest.NewRecorder()
request := httptest.NewRequest(http.MethodGet, test.testedURL, nil)
internalMuxRouter.ServeHTTP(recorder, request)
assert.Equal(t, test.expectedStatusCode, recorder.Code)
})
}
}
func TestNewInternalRouterAggregatorWithAuth(t *testing.T) {
currentConfiguration := &safe.Safe{}
currentConfiguration.Set(types.Configurations{})
globalConfiguration := configuration.GlobalConfiguration{
API: &api.Handler{
EntryPoint: "traefik",
CurrentConfigurations: currentConfiguration,
},
Ping: &ping.Handler{
EntryPoint: "traefik",
},
ACME: &acme.ACME{
HTTPChallenge: &acmeprovider.HTTPChallenge{
EntryPoint: "traefik",
},
},
EntryPoints: configuration.EntryPoints{
"traefik": &configuration.EntryPoint{
Auth: &types.Auth{
Basic: &types.Basic{
Users: types.Users{"test:test"},
},
},
},
},
}
testCases := []struct {
desc string
testedURL string
expectedStatusCode int
}{
{
desc: "Wrong url",
testedURL: "/wrong",
expectedStatusCode: 502,
},
{
desc: "Ping without auth",
testedURL: "/ping",
expectedStatusCode: 200,
},
{
desc: "acme without auth",
testedURL: "/.well-known/acme-challenge/token",
expectedStatusCode: 404,
},
{
desc: "api with auth",
testedURL: "/api",
expectedStatusCode: 401,
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
router := NewInternalRouterAggregator(globalConfiguration, "traefik")
internalMuxRouter := mux.NewRouter()
router.AddRoutes(internalMuxRouter)
internalMuxRouter.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusBadGateway)
})
recorder := httptest.NewRecorder()
request := httptest.NewRequest(http.MethodGet, test.testedURL, nil)
internalMuxRouter.ServeHTTP(recorder, request)
assert.Equal(t, test.expectedStatusCode, recorder.Code)
})
}
}
func TestNewInternalRouterAggregatorWithAuthAndPrefix(t *testing.T) {
currentConfiguration := &safe.Safe{}
currentConfiguration.Set(types.Configurations{})
globalConfiguration := configuration.GlobalConfiguration{
Web: &configuration.WebCompatibility{
Path: "/prefix",
},
API: &api.Handler{
EntryPoint: "traefik",
CurrentConfigurations: currentConfiguration,
},
Ping: &ping.Handler{
EntryPoint: "traefik",
},
ACME: &acme.ACME{
HTTPChallenge: &acmeprovider.HTTPChallenge{
EntryPoint: "traefik",
},
},
EntryPoints: configuration.EntryPoints{
"traefik": &configuration.EntryPoint{
Auth: &types.Auth{
Basic: &types.Basic{
Users: types.Users{"test:test"},
},
},
},
},
}
testCases := []struct {
desc string
testedURL string
expectedStatusCode int
}{
{
desc: "Ping without prefix",
testedURL: "/ping",
expectedStatusCode: 502,
},
{
desc: "Ping without auth and with prefix",
testedURL: "/prefix/ping",
expectedStatusCode: 200,
},
{
desc: "acme without auth and without prefix",
testedURL: "/.well-known/acme-challenge/token",
expectedStatusCode: 404,
},
{
desc: "api with auth and prefix",
testedURL: "/prefix/api",
expectedStatusCode: 401,
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
router := NewInternalRouterAggregator(globalConfiguration, "traefik")
internalMuxRouter := mux.NewRouter()
router.AddRoutes(internalMuxRouter)
internalMuxRouter.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusBadGateway)
})
recorder := httptest.NewRecorder()
request := httptest.NewRequest(http.MethodGet, test.testedURL, nil)
internalMuxRouter.ServeHTTP(recorder, request)
assert.Equal(t, test.expectedStatusCode, recorder.Code)
})
}
}
type MockInternalRouterFunc func(systemRouter *mux.Router)
func (m MockInternalRouterFunc) AddRoutes(systemRouter *mux.Router) {
m(systemRouter)
}
func TestWithMiddleware(t *testing.T) {
router := WithMiddleware{
router: MockInternalRouterFunc(func(systemRouter *mux.Router) {
systemRouter.Handle("/test", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("router"))
}))
}),
routerMiddlewares: []negroni.Handler{
negroni.HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
rw.Write([]byte("before middleware1|"))
next.ServeHTTP(rw, r)
rw.Write([]byte("|after middleware1"))
}),
negroni.HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
rw.Write([]byte("before middleware2|"))
next.ServeHTTP(rw, r)
rw.Write([]byte("|after middleware2"))
}),
},
}
internalMuxRouter := mux.NewRouter()
router.AddRoutes(internalMuxRouter)
recorder := httptest.NewRecorder()
request := httptest.NewRequest(http.MethodGet, "/test", nil)
internalMuxRouter.ServeHTTP(recorder, request)
obtained := recorder.Body.String()
assert.Equal(t, "before middleware1|before middleware2|router|after middleware2|after middleware1", obtained)
}
func TestWithPrefix(t *testing.T) {
testCases := []struct {
desc string
prefix string
testedURL string
expectedStatusCode int
}{
{
desc: "No prefix",
testedURL: "/test",
expectedStatusCode: 200,
},
{
desc: "With prefix and wrong url",
prefix: "/prefix",
testedURL: "/test",
expectedStatusCode: 404,
},
{
desc: "With prefix",
prefix: "/prefix",
testedURL: "/prefix/test",
expectedStatusCode: 200,
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
router := WithPrefix{
Router: MockInternalRouterFunc(func(systemRouter *mux.Router) {
systemRouter.Handle("/test", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
}))
}),
PathPrefix: test.prefix,
}
internalMuxRouter := mux.NewRouter()
router.AddRoutes(internalMuxRouter)
recorder := httptest.NewRecorder()
request := httptest.NewRequest(http.MethodGet, test.testedURL, nil)
internalMuxRouter.ServeHTTP(recorder, request)
assert.Equal(t, test.expectedStatusCode, recorder.Code)
})
}
}

File diff suppressed because it is too large Load Diff

1055
contrib/grafana/traefik.json Normal file

File diff suppressed because it is too large Load Diff

171
contrib/scripts/dumpcerts.sh Executable file
View File

@@ -0,0 +1,171 @@
#!/usr/bin/env bash
# Copyright (c) 2017 Brian 'redbeard' Harrington <redbeard@dead-city.org>
#
# dumpcerts.sh - A simple utility to explode a Traefik acme.json file into a
# directory of certificates and a private key
#
# Usage - dumpcerts.sh /etc/traefik/acme.json /etc/ssl/
#
# Dependencies -
# util-linux
# openssl
# jq
# The MIT License (MIT)
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
# Exit codes:
# 1 - A component is missing or could not be read
# 2 - There was a problem reading acme.json
# 4 - The destination certificate directory does not exist
# 8 - Missing private key
set -o errexit
set -o pipefail
set -o nounset
USAGE="$(basename "$0") <path to acme> <destination cert directory>"
# Platform variations
case "$(uname)" in
'Linux')
# On Linux, -d should always work. --decode does not work with Alpine's busybox-binary
CMD_DECODE_BASE64="base64 -d"
;;
*)
# Max OS-X supports --decode and -D, but --decode may be supported by other platforms as well.
CMD_DECODE_BASE64="base64 --decode"
;;
esac
# Allow us to exit on a missing jq binary
exit_jq() {
echo "
You must have the binary 'jq' to use this.
jq is available at: https://stedolan.github.io/jq/download/
${USAGE}" >&2
exit 1
}
bad_acme() {
echo "
There was a problem parsing your acme.json file.
${USAGE}" >&2
exit 2
}
if [ $# -ne 2 ]; then
echo "
Insufficient number of parameters.
${USAGE}" >&2
exit 1
fi
readonly acmefile="${1}"
readonly certdir="${2%/}"
if [ ! -r "${acmefile}" ]; then
echo "
There was a problem reading from '${acmefile}'
We need to read this file to explode the JSON bundle... exiting.
${USAGE}" >&2
exit 2
fi
if [ ! -d "${certdir}" ]; then
echo "
Path ${certdir} does not seem to be a directory
We need a directory in which to explode the JSON bundle... exiting.
${USAGE}" >&2
exit 4
fi
jq=$(command -v jq) || exit_jq
priv=$(${jq} -e -r '.Account.PrivateKey' "${acmefile}") || bad_acme
if [ ! -n "${priv}" ]; then
echo "
There didn't seem to be a private key in ${acmefile}.
Please ensure that there is a key in this file and try again." >&2
exit 8
fi
# If they do not exist, create the needed subdirectories for our assets
# and place each in a variable for later use, normalizing the path
mkdir -p "${certdir}"/{certs,private}
pdir="${certdir}/private/"
cdir="${certdir}/certs/"
# Save the existing umask, change the default mode to 600, then
# after writing the private key switch it back to the default
oldumask=$(umask)
umask 177
trap 'umask ${oldumask}' EXIT
# traefik stores the private key in stripped base64 format but the certificates
# bundled as a base64 object without stripping headers. This normalizes the
# headers and formatting.
#
# In testing this out it was a balance between the following mechanisms:
# gawk:
# echo ${priv} | awk 'BEGIN {print "-----BEGIN RSA PRIVATE KEY-----"}
# {gsub(/.{64}/,"&\n")}1
# END {print "-----END RSA PRIVATE KEY-----"}' > "${pdir}/letsencrypt.key"
#
# openssl:
# echo -e "-----BEGIN RSA PRIVATE KEY-----\n${priv}\n-----END RSA PRIVATE KEY-----" \
# | openssl rsa -inform pem -out "${pdir}/letsencrypt.key"
#
# and sed:
# echo "-----BEGIN RSA PRIVATE KEY-----" > "${pdir}/letsencrypt.key"
# echo ${priv} | sed -E 's/(.{64})/\1\n/g' >> "${pdir}/letsencrypt.key"
# sed -i '$ d' "${pdir}/letsencrypt.key"
# echo "-----END RSA PRIVATE KEY-----" >> "${pdir}/letsencrypt.key"
# openssl rsa -noout -in "${pdir}/letsencrypt.key" -check # To check if the key is valid
# In the end, openssl was chosen because most users will need this script
# *because* of openssl combined with the fact that it will refuse to write the
# key if it does not parse out correctly. The other mechanisms were left as
# comments so that the user can choose the mechanism most appropriate to them.
echo -e "-----BEGIN RSA PRIVATE KEY-----\n${priv}\n-----END RSA PRIVATE KEY-----" \
| openssl rsa -inform pem -out "${pdir}/letsencrypt.key"
# Process the certificates for each of the domains in acme.json
domains=$(jq -r '.Certificates[].Domain.Main' ${acmefile}) || bad_acme
for domain in $domains; do
# Traefik stores a cert bundle for each domain. Within this cert
# bundle there is both proper the certificate and the Let's Encrypt CA
echo "Extracting cert bundle for ${domain}"
cert=$(jq -e -r --arg domain "$domain" '.Certificates[] |
select (.Domain.Main == $domain )| .Certificate' ${acmefile}) || bad_acme
echo "${cert}" | ${CMD_DECODE_BASE64} > "${cdir}/${domain}.crt"
echo "Extracting private key for ${domain}"
key=$(jq -e -r --arg domain "$domain" '.Certificates[] |
select (.Domain.Main == $domain )| .Key' ${acmefile}) || bad_acme
echo "${key}" | ${CMD_DECODE_BASE64} > "${pdir}/${domain}.key"
done

View File

@@ -1,11 +1,41 @@
[Unit]
Description=Traefik
Documentation=https://doc.traefik.io/traefik/v1.7
#After=network-online.target
#AssertFileIsExecutable=/usr/bin/traefik
#AssertPathExists=/etc/traefik/traefik.toml
[Service]
# Run traefik as its own user (create new user with: useradd -r -s /bin/false -U -M traefik)
#User=traefik
#AmbientCapabilities=CAP_NET_BIND_SERVICE
# configure service behavior
Type=notify
ExecStart=/usr/bin/traefik --configFile=/etc/traefik.toml
#ExecStart=/usr/bin/traefik --configFile=/etc/traefik/traefik.toml
Restart=always
WatchdogSec=1s
# lock down system access
# prohibit any operating system and configuration modification
#ProtectSystem=strict
# create separate, new (and empty) /tmp and /var/tmp filesystems
#PrivateTmp=true
# make /home directories inaccessible
#ProtectHome=true
# turns off access to physical devices (/dev/...)
#PrivateDevices=true
# make kernel settings (procfs and sysfs) read-only
#ProtectKernelTunables=true
# make cgroups /sys/fs/cgroup read-only
#ProtectControlGroups=true
# allow writing of acme.json
#ReadWritePaths=/etc/traefik/acme.json
# depending on log and entrypoint configuration, you may need to allow writing to other paths, too
# limit number of processes in this unit
#LimitNPROC=1
[Install]
WantedBy=multi-user.target

10
docs.Dockerfile Normal file
View File

@@ -0,0 +1,10 @@
FROM alpine:3.14
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.local/bin
COPY requirements.txt /mkdocs/
WORKDIR /mkdocs
VOLUME /mkdocs
RUN apk --no-cache --no-progress add py3-pip gcc musl-dev python3-dev \
&& pip3 install --user -r requirements.txt

View File

@@ -1 +0,0 @@
docs.traefik.io

View File

@@ -1,7 +1,8 @@
# Basics
# Concepts
## Concepts
Let's take our example from the [overview](https://docs.traefik.io/#overview) again:
Let's take our example from the [overview](/#overview) again:
> Imagine that you have deployed a bunch of microservices on your infrastructure. You probably used a service registry (like etcd or consul) and/or an orchestrator (swarm, Mesos/Marathon) to manage all these services.
@@ -13,20 +14,20 @@ Let's take our example from the [overview](https://docs.traefik.io/#overview) ag
> ![Architecture](img/architecture.png)
Let's zoom on Træfik and have an overview of its internal architecture:
Let's zoom on Traefik and have an overview of its internal architecture:
![Architecture](img/internal.png)
- Incoming requests end on [entrypoints](#entrypoints), as the name suggests, they are the network entry points into Træfik (listening port, SSL, traffic redirection...).
- Incoming requests end on [entrypoints](#entrypoints), as the name suggests, they are the network entry points into Traefik (listening port, SSL, traffic redirection...).
- Traffic is then forwarded to a matching [frontend](#frontends). A frontend defines routes from [entrypoints](#entrypoints) to [backends](#backends).
Routes are created using requests fields (`Host`, `Path`, `Headers`...) and can match or not a request.
- The [frontend](#frontends) will then send the request to a [backend](#backends). A backend can be composed by one or more [servers](#servers), and by a load-balancing strategy.
- Finally, the [server](#servers) will forward the request to the corresponding microservice in the private network.
## Entrypoints
### Entrypoints
Entrypoints are the network entry points into Træfik.
Entrypoints are the network entry points into Traefik.
They can be defined using:
- a port (80, 443...)
@@ -61,7 +62,9 @@ And here is another example with client certificate authentication:
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]
clientCAFiles = ["tests/clientca1.crt", "tests/clientca2.crt"]
[entryPoints.https.tls.ClientCA]
files = ["tests/clientca1.crt", "tests/clientca2.crt"]
optional = false
[[entryPoints.https.tls.certificates]]
certFile = "tests/traefik.crt"
keyFile = "tests/traefik.key"
@@ -69,15 +72,15 @@ And here is another example with client certificate authentication:
- We enable SSL on `https` by giving a certificate and a key.
- One or several files containing Certificate Authorities in PEM format are added.
- It is possible to have multiple CA:s in the same file or keep them in separate files.
- It is possible to have multiple CA's in the same file or keep them in separate files.
## Frontends
### Frontends
A frontend consists of a set of rules that determine how incoming requests are forwarded from an entrypoint to a backend.
Rules may be classified in one of two groups: Modifiers and matchers.
### Modifiers
#### Modifiers
Modifier rules only modify the request. They do not have any impact on routing decisions being made.
@@ -85,49 +88,66 @@ Following is the list of existing modifier rules:
- `AddPrefix: /products`: Add path prefix to the existing request path prior to forwarding the request to the backend.
- `ReplacePath: /serverless-path`: Replaces the path and adds the old path to the `X-Replaced-Path` header. Useful for mapping to AWS Lambda or Google Cloud Functions.
- `ReplacePathRegex: ^/api/v2/(.*) /api/$1`: Replaces the path with a regular expression and adds the old path to the `X-Replaced-Path` header. Separate the regular expression and the replacement by a space.
### Matchers
#### Matchers
Matcher rules determine if a particular request should be forwarded to a backend.
Separate multiple rule values by `,` (comma) in order to enable ANY semantics (i.e., forward a request if any rule matches). Does not work for `Headers` and `HeadersRegexp`.
The associativity rule is the following:
Separate multiple rule values by `;` (semicolon) in order to enable ALL semantics (i.e., forward a request if all rules match).
You can optionally enable `passHostHeader` to forward client `Host` header to the backend.
- `,` is the `OR` operator (works **only inside a matcher**, ex: `Host:foo.com,bar.com`).
- i.e., forward a request if any rule matches.
- Does not work for `Headers` and `HeadersRegexp`.
- `;` is the `AND` operator (works **only between matchers**, ex: `Host:foo.com;Path:/bar`)
- i.e., forward a request if all rules match
Following is the list of existing matcher rules along with examples:
- `Headers: Content-Type, application/json`: Match HTTP header. It accepts a comma-separated key/value pair where both key and value must be literals.
- `HeadersRegexp: Content-Type, application/(text|json)`: Match HTTP header. It accepts a comma-separated key/value pair where the key must be a literal and the value may be a literal or a regular expression.
- `Host: traefik.io, www.traefik.io`: Match request host. It accepts a sequence of literal hosts.
- `HostRegexp: traefik.io, {subdomain:[a-z]+}.traefik.io`: Match request host. It accepts a sequence of literal and regular expression hosts.
- `Method: GET, POST, PUT`: Match request HTTP method. It accepts a sequence of HTTP methods.
- `Path: /products/, /articles/{category}/{id:[0-9]+}`: Match exact request path. It accepts a sequence of literal and regular expression paths.
- `PathStrip: /products/`: Match exact path and strip off the path prior to forwarding the request to the backend. It accepts a sequence of literal paths.
- `PathStripRegex: /articles/{category}/{id:[0-9]+}`: Match exact path and strip off the path prior to forwarding the request to the backend. It accepts a sequence of literal and regular expression paths.
- `PathPrefix: /products/, /articles/{category}/{id:[0-9]+}`: Match request prefix path. It accepts a sequence of literal and regular expression prefix paths.
- `PathPrefixStrip: /products/`: Match request prefix path and strip off the path prefix prior to forwarding the request to the backend. It accepts a sequence of literal prefix paths. Starting with Traefik 1.3, the stripped prefix path will be available in the `X-Forwarded-Prefix` header.
- `PathPrefixStripRegex: /articles/{category}/{id:[0-9]+}`: Match request prefix path and strip off the path prefix prior to forwarding the request to the backend. It accepts a sequence of literal and regular expression prefix paths. Starting with Traefik 1.3, the stripped prefix path will be available in the `X-Forwarded-Prefix` header.
| Matcher | Description |
|------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `Headers: Content-Type, application/json` | Match HTTP header. It accepts a comma-separated key/value pair where both key and value must be literals. |
| `HeadersRegexp: Content-Type, application/(text/json)` | Match HTTP header. It accepts a comma-separated key/value pair where the key must be a literal and the value may be a literal or a regular expression. |
| `Host: traefik.io, www.traefik.io` | Match request host. It accepts a sequence of literal hosts. |
| `HostRegexp: traefik.io, {subdomain:[a-z]+}.traefik.io` | Match request host. It accepts a sequence of literal and regular expression hosts. |
| `Method: GET, POST, PUT` | Match request HTTP method. It accepts a sequence of HTTP methods. |
| `Path: /products/, /articles/{category}/{id:[0-9]+}` | Match exact request path. It accepts a sequence of literal and regular expression paths. |
| `PathStrip: /products/` | Match exact path and strip off the path prior to forwarding the request to the backend. It accepts a sequence of literal paths. |
| `PathStripRegex: /articles/{category}/{id:[0-9]+}` | Match exact path and strip off the path prior to forwarding the request to the backend. It accepts a sequence of literal and regular expression paths. |
| `PathPrefix: /products/, /articles/{category}/{id:[0-9]+}` | Match request prefix path. It accepts a sequence of literal and regular expression prefix paths. |
| `PathPrefixStrip: /products/` | Match request prefix path and strip off the path prefix prior to forwarding the request to the backend. It accepts a sequence of literal prefix paths. Starting with Traefik 1.3, the stripped prefix path will be available in the `X-Forwarded-Prefix` header. |
| `PathPrefixStripRegex: /articles/{category}/{id:[0-9]+}` | Match request prefix path and strip off the path prefix prior to forwarding the request to the backend. It accepts a sequence of literal and regular expression prefix paths. Starting with Traefik 1.3, the stripped prefix path will be available in the `X-Forwarded-Prefix` header. |
| `Query: foo=bar, bar=baz` | Match Query String parameters. It accepts a sequence of key=value pairs. |
In order to use regular expressions with Host and Path matchers, you must declare an arbitrarily named variable followed by the colon-separated regular expression, all enclosed in curly braces. Any pattern supported by [Go's regexp package](https://golang.org/pkg/regexp/) may be used. Example: `/posts/{id:[0-9]+}`.
In order to use regular expressions with Host and Path matchers, you must declare an arbitrarily named variable followed by the colon-separated regular expression, all enclosed in curly braces. Any pattern supported by [Go's regexp package](https://golang.org/pkg/regexp/) may be used (example: `/posts/{id:[0-9]+}`).
(Note that the variable has no special meaning; however, it is required by the gorilla/mux dependency which embeds the regular expression and defines the syntax.)
!!! note
The variable has no special meaning; however, it is required by the [gorilla/mux](https://github.com/gorilla/mux) dependency which embeds the regular expression and defines the syntax.
#### Path Matcher Usage Guidelines
You can optionally enable `passHostHeader` to forward client `Host` header to the backend.
You can also optionally configure the `passTLSClientCert` option to pass the Client certificates to the backend in a specific header.
##### Path Matcher Usage Guidelines
This section explains when to use the various path matchers.
Use `Path` if your backend listens on the exact path only. For instance, `Path: /products` would match `/products` but not `/products/shoes`.
Use a `*Prefix*` matcher if your backend listens on a particular base path but also serves requests on sub-paths. For instance, `PathPrefix: /products` would match `/products` but also `/products/shoes` and `/products/shirts`. Since the path is forwarded as-is, your backend is expected to listen on `/products`.
Use a `*Prefix*` matcher if your backend listens on a particular base path but also serves requests on sub-paths.
For instance, `PathPrefix: /products` would match `/products` but also `/products/shoes` and `/products/shirts`.
Since the path is forwarded as-is, your backend is expected to listen on `/products`.
Use a `*Strip` matcher if your backend listens on the root path (`/`) but should be routeable on a specific prefix. For instance, `PathPrefixStrip: /products` would match `/products` but also `/products/shoes` and `/products/shirts`. Since the path is stripped prior to forwarding, your backend is expected to listen on `/`.
If your backend is serving assets (e.g., images or Javascript files), chances are it must return properly constructed relative URLs. Continuing on the example, the backend should return `/products/shoes/image.png` (and not `/images.png` which Traefik would likely not be able to associate with the same backend). The `X-Forwarded-Prefix` header (available since Traefik 1.3) can be queried to build such URLs dynamically.
Use a `*Strip` matcher if your backend listens on the root path (`/`) but should be routeable on a specific prefix.
For instance, `PathPrefixStrip: /products` would match `/products` but also `/products/shoes` and `/products/shirts`.
Since the path is stripped prior to forwarding, your backend is expected to listen on `/`.
If your backend is serving assets (e.g., images or Javascript files), chances are it must return properly constructed relative URLs.
Continuing on the example, the backend should return `/products/shoes/image.png` (and not `/images.png` which Traefik would likely not be able to associate with the same backend).
The `X-Forwarded-Prefix` header (available since Traefik 1.3) can be queried to build such URLs dynamically.
Instead of distinguishing your backends by path only, you can add a Host matcher to the mix. That way, namespacing of your backends happens on the basis of hosts in addition to paths.
Instead of distinguishing your backends by path only, you can add a Host matcher to the mix.
That way, namespacing of your backends happens on the basis of hosts in addition to paths.
### Examples
#### Examples
Here is an example of frontends definition:
@@ -140,6 +160,8 @@ Here is an example of frontends definition:
[frontends.frontend2]
backend = "backend1"
passHostHeader = true
[frontends.frontend2.passTLSClientCert]
pem = true
priority = 10
entrypoints = ["https"] # overrides defaultEntryPoints
[frontends.frontend2.routes.test_1]
@@ -152,48 +174,50 @@ Here is an example of frontends definition:
- Three frontends are defined: `frontend1`, `frontend2` and `frontend3`
- `frontend1` will forward the traffic to the `backend2` if the rule `Host:test.localhost,test2.localhost` is matched
- `frontend2` will forward the traffic to the `backend1` if the rule `Host:localhost,{subdomain:[a-z]+}.localhost` is matched (forwarding client `Host` header to the backend)
- `frontend2` will forward the traffic to the `backend1` if the rule `HostRegexp:localhost,{subdomain:[a-z]+}.localhost` is matched (forwarding client `Host` header to the backend)
- `frontend3` will forward the traffic to the `backend2` if the rules `Host:test3.localhost` **AND** `Path:/test` are matched
### Combining multiple rules
#### Combining multiple rules
As seen in the previous example, you can combine multiple rules.
In TOML file, you can use multiple routes:
```toml
[frontends.frontend3]
backend = "backend2"
[frontends.frontend3.routes.test_1]
rule = "Host:test3.localhost"
[frontends.frontend3.routes.test_2]
rule = "Path:/test"
[frontends.frontend3]
backend = "backend2"
[frontends.frontend3.routes.test_1]
rule = "Host:test3.localhost"
[frontends.frontend3.routes.test_2]
rule = "Path:/test"
```
Here `frontend3` will forward the traffic to the `backend2` if the rules `Host:test3.localhost` **AND** `Path:/test` are matched.
You can also use the notation using a `;` separator, same result:
```toml
[frontends.frontend3]
backend = "backend2"
[frontends.frontend3.routes.test_1]
rule = "Host:test3.localhost;Path:/test"
[frontends.frontend3]
backend = "backend2"
[frontends.frontend3.routes.test_1]
rule = "Host:test3.localhost;Path:/test"
```
Finally, you can create a rule to bind multiple domains or Path to a frontend, using the `,` separator:
```toml
[frontends.frontend2]
[frontends.frontend2.routes.test_1]
rule = "Host:test1.localhost,test2.localhost"
[frontends.frontend3]
backend = "backend2"
[frontends.frontend3.routes.test_1]
rule = "Path:/test1,/test2"
[frontends.frontend2]
[frontends.frontend2.routes.test_1]
rule = "Host:test1.localhost,test2.localhost"
[frontends.frontend3]
backend = "backend2"
[frontends.frontend3.routes.test_1]
rule = "Path:/test1,/test2"
```
### Rules Order
#### Rules Order
When combining `Modifier` rules with `Matcher` rules, it is important to remember that `Modifier` rules **ALWAYS** apply after the `Matcher` rules.
When combining `Modifier` rules with `Matcher` rules, it is important to remember that `Modifier` rules **ALWAYS** apply after the `Matcher` rules.
The following rules are both `Matchers` and `Modifiers`, so the `Matcher` portion of the rule will apply first, and the `Modifier` will apply later.
- `PathStrip`
@@ -208,40 +232,150 @@ The following rules are both `Matchers` and `Modifiers`, so the `Matcher` portio
3. `PathStripRegex`
4. `PathPrefixStripRegex`
5. `AddPrefix`
6. `ReplacePath`
6. `ReplacePath`
### Priorities
#### Priorities
By default, routes will be sorted (in descending order) using rules length (to avoid path overlap):
`PathPrefix:/12345` will be matched before `PathPrefix:/1234` that will be matched before `PathPrefix:/1`.
- `PathPrefix:/foo;Host:foo.com` (length == 28) will be matched before `PathPrefixStrip:/foobar` (length == 23) will be matched before `PathPrefix:/foo,/bar` (length == 20).
- A priority value of 0 will be ignored, so the default value will be calculated (rules length).
You can customize priority by frontend:
You can customize priority by frontend. The priority value override the rule length during sorting:
```toml
[frontends]
[frontends.frontend1]
backend = "backend1"
priority = 20
passHostHeader = true
[frontends.frontend1.routes.test_1]
rule = "PathPrefix:/to"
[frontends.frontend2]
backend = "backend2"
passHostHeader = true
[frontends.frontend2.routes.test_1]
rule = "PathPrefix:/toto"
```
Here, `frontend1` will be matched before `frontend2` (`20 > 16`).
#### Custom headers
Custom headers can be configured through the frontends, to add headers to either requests or responses that match the frontend's rules.
This allows for setting headers such as `X-Script-Name` to be added to the request, or custom headers to be added to the response.
!!! warning
If the custom header name is the same as one header name of the request or response, it will be replaced.
In this example, all matches to the path `/cheese` will have the `X-Script-Name` header added to the proxied request and the `X-Custom-Response-Header` header added to the response.
```toml
[frontends]
[frontends.frontend1]
backend = "backend1"
priority = 10
passHostHeader = true
[frontends.frontend1.headers.customresponseheaders]
X-Custom-Response-Header = "True"
[frontends.frontend1.headers.customrequestheaders]
X-Script-Name = "test"
[frontends.frontend1.routes.test_1]
rule = "PathPrefix:/to"
[frontends.frontend2]
priority = 5
backend = "backend2"
passHostHeader = true
[frontends.frontend2.routes.test_1]
rule = "PathPrefix:/toto"
rule = "PathPrefixStrip:/cheese"
```
Here, `frontend1` will be matched before `frontend2` (`10 > 5`).
In this second example, all matches to the path `/cheese` will have the `X-Script-Name` header added to the proxied request, the `X-Custom-Request-Header` header removed from the request, and the `X-Custom-Response-Header` header removed from the response.
## Backends
```toml
[frontends]
[frontends.frontend1]
backend = "backend1"
[frontends.frontend1.headers.customresponseheaders]
X-Custom-Response-Header = ""
[frontends.frontend1.headers.customrequestheaders]
X-Script-Name = "test"
X-Custom-Request-Header = ""
[frontends.frontend1.routes.test_1]
rule = "PathPrefixStrip:/cheese"
```
#### Security headers
Security related headers (HSTS headers, SSL redirection, Browser XSS filter, etc) can be added and configured per frontend in a similar manner to the custom headers above.
This functionality allows for some easy security features to quickly be set.
An example of some of the security headers:
```toml
[frontends]
[frontends.frontend1]
backend = "backend1"
[frontends.frontend1.headers]
FrameDeny = true
[frontends.frontend1.routes.test_1]
rule = "PathPrefixStrip:/cheddar"
[frontends.frontend2]
backend = "backend2"
[frontends.frontend2.headers]
SSLRedirect = true
[frontends.frontend2.routes.test_1]
rule = "PathPrefixStrip:/stilton"
```
In this example, traffic routed through the first frontend will have the `X-Frame-Options` header set to `DENY`, and the second will only allow HTTPS request through, otherwise will return a 301 HTTPS redirect.
!!! note
The detailed documentation for those security headers can be found in [unrolled/secure](https://github.com/unrolled/secure#available-options).
### Backends
A backend is responsible to load-balance the traffic coming from one or more frontends to a set of http servers.
#### Servers
Servers are simply defined using a `url`. You can also apply a custom `weight` to each server (this will be used by load-balancing).
!!! note
Paths in `url` are ignored. Use `Modifier` to specify paths instead.
Here is an example of backends and servers definition:
```toml
[backends]
[backends.backend1]
# ...
[backends.backend1.servers.server1]
url = "http://172.17.0.2:80"
weight = 10
[backends.backend1.servers.server2]
url = "http://172.17.0.3:80"
weight = 1
[backends.backend2]
# ...
[backends.backend2.servers.server1]
url = "https://172.17.0.4:443"
weight = 1
[backends.backend2.servers.server2]
url = "https://172.17.0.5:443"
weight = 2
[backends.backend3]
# ...
[backends.backend3.servers.server1]
url = "h2c://172.17.0.6:80"
weight = 1
```
- Two backends are defined: `backend1` and `backend2`
- `backend1` will forward the traffic to two servers: `172.17.0.2:80` with weight `10` and `172.17.0.3:80` with weight `1`.
- `backend2` will forward the traffic to two servers: `172.17.0.4:443` with weight `1` and `172.17.0.5:443` with weight `2` both using TLS.
- `backend3` will forward the traffic to: `172.17.0.6:80` with weight `1` using HTTP2 without TLS.
#### Load-balancing
Various methods of load-balancing are supported:
- `wrr`: Weighted Round Robin
- `drr`: Dynamic Round Robin: increases weights on servers that perform better than others. It also rolls back to original weights if the servers have changed.
- `wrr`: Weighted Round Robin.
- `drr`: Dynamic Round Robin: increases weights on servers that perform better than others.
It also rolls back to original weights if the servers have changed.
#### Circuit breakers
A circuit breaker can also be applied to a backend, preventing high loads on failing servers.
Initial state is Standby. CB observes the statistics and does not modify the request.
@@ -256,16 +390,33 @@ It can be configured using:
For example:
- `NetworkErrorRatio() > 0.5`: watch error ratio over 10 second sliding window for a frontend
- `NetworkErrorRatio() > 0.5`: watch error ratio over 10 second sliding window for a frontend.
- `LatencyAtQuantileMS(50.0) > 50`: watch latency at quantile in milliseconds.
- `ResponseCodeRatio(500, 600, 0, 600) > 0.5`: ratio of response codes in range [500-600) to [0-600)
- `ResponseCodeRatio(500, 600, 0, 600) > 0.5`: ratio of response codes in ranges [500-600) and [0-600).
To proactively prevent backends from being overwhelmed with high load, a maximum connection limit can
also be applied to each backend.
Here is an example of backends and servers definition:
Maximum connections can be configured by specifying an integer value for `maxconn.amount` and
`maxconn.extractorfunc` which is a strategy used to determine how to categorize requests in order to
evaluate the maximum connections.
```toml
[backends]
[backends.backend1]
[backends.backend1.circuitbreaker]
expression = "NetworkErrorRatio() > 0.5"
[backends.backend1.servers.server1]
url = "http://172.17.0.2:80"
weight = 10
[backends.backend1.servers.server2]
url = "http://172.17.0.3:80"
weight = 1
```
- `backend1` will forward the traffic to two servers: `http://172.17.0.2:80"` with weight `10` and `http://172.17.0.3:80` with weight `1` using default `wrr` load-balancing strategy.
- a circuit breaker is added on `backend1` using the expression `NetworkErrorRatio() > 0.5`: watch error ratio over 10 second sliding window
#### Maximum connections
To proactively prevent backends from being overwhelmed with high load, a maximum connection limit can also be applied to each backend.
Maximum connections can be configured by specifying an integer value for `maxconn.amount` and `maxconn.extractorfunc` which is a strategy used to determine how to categorize requests in order to evaluate the maximum connections.
For example:
```toml
@@ -274,17 +425,58 @@ For example:
[backends.backend1.maxconn]
amount = 10
extractorfunc = "request.host"
# ...
```
- `backend1` will return `HTTP code 429 Too Many Requests` if there are already 10 requests in progress for the same Host header.
- Another possible value for `extractorfunc` is `client.ip` which will categorize requests based on client source ip.
- Lastly `extractorfunc` can take the value of `request.header.ANY_HEADER` which will categorize requests based on `ANY_HEADER` that you provide.
Sticky sessions are supported with both load balancers. When sticky sessions are enabled, a cookie called `_TRAEFIK_BACKEND` is set on the initial
request. On subsequent requests, the client will be directed to the backend stored in the cookie if it is still healthy. If not, a new backend
will be assigned.
#### Sticky sessions
Sticky sessions are supported with both load balancers.
When sticky sessions are enabled, a cookie is set on the initial request.
The default cookie name is an abbreviation of a sha1 (ex: `_1d52e`).
On subsequent requests, the client will be directed to the backend stored in the cookie if it is still healthy.
If not, a new backend will be assigned.
```toml
[backends]
[backends.backend1]
# Enable sticky session
[backends.backend1.loadbalancer.stickiness]
# Customize the cookie name
#
# Optional
# Default: a sha1 (6 chars)
#
# cookieName = "my_cookie"
# Customize secure option
#
# Optional
# Default: false
#
# secure = true
# Customize http only option
#
# Optional
# Default: false
#
# httpOnly = true
# Customize same site option.
# Can be: "none", "lax", "strict"
#
# Optional
#
# sameSite = "none"
```
The deprecated way:
For example:
```toml
[backends]
[backends.backend1]
@@ -292,151 +484,310 @@ For example:
sticky = true
```
A health check can be configured in order to remove a backend from LB rotation
as long as it keeps returning HTTP status codes other than 200 OK to HTTP GET
requests periodically carried out by Traefik. The check is defined by a path
appended to the backend URL and an interval (given in a format understood by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration)) specifying how
often the health check should be executed (the default being 30 seconds). Each
backend must respond to the health check within 5 seconds.
#### Health Check
A recovering backend returning 200 OK responses again is being returned to the
LB rotation pool.
A health check can be configured in order to remove a backend from LB rotation as long as it keeps returning HTTP status codes other than `2xx` or `3xx` to HTTP GET requests periodically carried out by Traefik.
The check is defined by a path appended to the backend URL and an interval (given in a format understood by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration)) specifying how often the health check should be executed (the default being 30 seconds).
Each backend must respond to the health check within 5 seconds.
By default, the port of the backend server is used, however, this may be overridden.
A recovering backend returning `2xx` or `3xx` responses again is being returned to the LB rotation pool.
For example:
```toml
[backends]
[backends.backend1]
[backends.backend1.healthcheck]
path = "/health"
interval = "10s"
path = "/health"
interval = "10s"
```
## Servers
Servers are simply defined using a `URL`. You can also apply a custom `weight` to each server (this will be used by load-balancing).
Here is an example of backends and servers definition:
To use a different port for the health check:
```toml
[backends]
[backends.backend1]
[backends.backend1.circuitbreaker]
expression = "NetworkErrorRatio() > 0.5"
[backends.backend1.servers.server1]
url = "http://172.17.0.2:80"
weight = 10
[backends.backend1.servers.server2]
url = "http://172.17.0.3:80"
weight = 1
[backends.backend2]
[backends.backend2.LoadBalancer]
method = "drr"
[backends.backend2.servers.server1]
url = "http://172.17.0.4:80"
weight = 1
[backends.backend2.servers.server2]
url = "http://172.17.0.5:80"
weight = 2
[backends.backend1.healthcheck]
path = "/health"
interval = "10s"
port = 8080
```
- Two backends are defined: `backend1` and `backend2`
- `backend1` will forward the traffic to two servers: `http://172.17.0.2:80"` with weight `10` and `http://172.17.0.3:80` with weight `1` using default `wrr` load-balancing strategy.
- `backend2` will forward the traffic to two servers: `http://172.17.0.4:80"` with weight `1` and `http://172.17.0.5:80` with weight `2` using `drr` load-balancing strategy.
- a circuit breaker is added on `backend1` using the expression `NetworkErrorRatio() > 0.5`: watch error ratio over 10 second sliding window
# Configuration
To use a different scheme for the health check:
```toml
[backends]
[backends.backend1]
[backends.backend1.healthcheck]
path = "/health"
interval = "10s"
scheme = "http"
```
Træfik's configuration has two parts:
Additional http headers and hostname to health check request can be specified, for instance:
```toml
[backends]
[backends.backend1]
[backends.backend1.healthcheck]
path = "/health"
interval = "10s"
hostname = "myhost.com"
port = 8080
[backends.backend1.healthcheck.headers]
My-Custom-Header = "foo"
My-Header = "bar"
```
- The [static Træfik configuration](/basics#static-trfk-configuration) which is loaded only at the beginning.
- The [dynamic Træfik configuration](/basics#dynamic-trfk-configuration) which can be hot-reloaded (no need to restart the process).
## Configuration
Traefik's configuration has two parts:
## Static Træfik configuration
- The [static Traefik configuration](/basics#static-traefik-configuration) which is loaded only at the beginning.
- The [dynamic Traefik configuration](/basics#dynamic-traefik-configuration) which can be hot-reloaded (no need to restart the process).
The static configuration is the global configuration which is setting up connections to configuration backends and entrypoints.
### Static Traefik configuration
Træfik can be configured using many configuration sources with the following precedence order.
The static configuration is the global configuration which is setting up connections to configuration backends and entrypoints.
Traefik can be configured using many configuration sources with the following precedence order.
Each item takes precedence over the item below it:
- [Key-value Store](/basics/#key-value-stores)
- [Key-value store](/basics/#key-value-stores)
- [Arguments](/basics/#arguments)
- [Configuration file](/basics/#configuration-file)
- Default
It means that arguments override configuration file, and Key-value Store overrides arguments.
It means that arguments override configuration file, and key-value store overrides arguments.
### Configuration file
!!! note
the provider-enabling argument parameters (e.g., `--docker`) set all default values for the specific provider.
It must not be used if a configuration source with less precedence wants to set a non-default provider value.
By default, Træfik will try to find a `traefik.toml` in the following places:
#### Configuration file
By default, Traefik will try to find a `traefik.toml` in the following places:
- `/etc/traefik/`
- `$HOME/.traefik/`
- `.` *the working directory*
- `.` _the working directory_
You can override this by setting a `configFile` argument:
```bash
$ traefik --configFile=foo/bar/myconfigfile.toml
traefik --configFile=foo/bar/myconfigfile.toml
```
Please refer to the [global configuration](/toml/#global-configuration) section to get documentation on it.
Please refer to the [global configuration](/configuration/commons) section to get documentation on it.
### Arguments
#### Arguments
Each argument (and command) is described in the help section:
```bash
$ traefik --help
traefik --help
```
Note that all default values will be displayed as well.
### Key-value stores
#### Key-value stores
Træfik supports several Key-value stores:
Traefik supports several Key-value stores:
- [Consul](https://consul.io)
- [etcd](https://coreos.com/etcd/)
- [ZooKeeper](https://zookeeper.apache.org/)
- [ZooKeeper](https://zookeeper.apache.org/)
- [boltdb](https://github.com/boltdb/bolt)
Please refer to the [User Guide Key-value store configuration](/user-guide/kv-config/) section to get documentation on it.
## Dynamic Træfik configuration
### Dynamic Traefik configuration
The dynamic configuration concerns :
The dynamic configuration concerns :
- [Frontends](/basics/#frontends)
- [Backends](/basics/#backends)
- [Servers](/basics/#servers)
- [Backends](/basics/#backends)
- [Servers](/basics/#servers)
- HTTPS Certificates
Træfik can hot-reload those rules which could be provided by [multiple configuration backends](/toml/#configuration-backends).
Traefik can hot-reload those rules which could be provided by [multiple configuration backends](/configuration/commons).
We only need to enable `watch` option to make Træfik watch configuration backend changes and generate its configuration automatically.
We only need to enable `watch` option to make Traefik watch configuration backend changes and generate its configuration automatically.
Routes to services will be created and updated instantly at any changes.
Please refer to the [configuration backends](/toml/#configuration-backends) section to get documentation on it.
Please refer to the [configuration backends](/configuration/commons) section to get documentation on it.
# Commands
## Commands
Usage: `traefik [command] [--flag=flag_argument]`
### traefik
List of Træfik available commands with description :                                                             
Usage:
```bash
traefik [command] [--flag=flag_argument]
```
- `version` : Print version 
- `storeconfig` : Store the static traefik configuration into a Key-value stores. Please refer to the [Store Træfik configuration](/user-guide/kv-config/#store-trfk-configuration) section to get documentation on it.
List of Traefik available commands with description :
- `version` : Print version
- `storeconfig` : Store the static Traefik configuration into a Key-value stores. Please refer to the [Store Traefik configuration](/user-guide/kv-config/#store-configuration-in-key-value-store) section to get documentation on it.
- `bug`: The easiest way to submit a pre-filled issue.
- `healthcheck`: Calls Traefik `/ping` to check health.
Each command may have related flags.
Each command may have related flags.
All those related flags will be displayed with :
```bash
$ traefik [command] --help
traefik [command] --help
```
Note that each command is described at the beginning of the help section:
Each command is described at the beginning of the help section:
```bash
$ traefik --help
traefik --help
# or
docker run traefik[:version] --help
# ex: docker run traefik:1.5 --help
```
### Command: bug
Here is the easiest way to submit a pre-filled issue on [Traefik GitHub](https://github.com/traefik/traefik).
```bash
traefik bug
```
Watch [this demo](https://www.youtube.com/watch?v=Lyz62L8m93I).
### Command: healthcheck
This command allows to check the health of Traefik. Its exit status is `0` if Traefik is healthy and `1` if it is unhealthy.
This can be used with Docker [HEALTHCHECK](https://docs.docker.com/engine/reference/builder/#healthcheck) instruction or any other health check orchestration mechanism.
!!! note
The [`ping`](/configuration/ping) must be enabled to allow the `healthcheck` command to call `/ping`.
```bash
traefik healthcheck
```
```bash
OK: http://:8082/ping
```
## Collected Data
**This feature is disabled by default.**
You can read the public proposal on this topic [here](https://github.com/traefik/traefik/issues/2369).
### Why ?
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 more important (for example, which configuration backend is used and which is not used).
### What ?
Once a day (the first call begins 10 minutes after the start of Traefik), we collect:
- the Traefik version
- a hash of the configuration
- an **anonymous version** of the static configuration:
- token, user name, password, URL, IP, domain, email, etc, are removed
!!! note
We do not collect the dynamic configuration (frontends & backends).
!!! note
We do not collect data behind the scenes to run advertising programs or to sell such data to third-party.
#### Here is an example
- Source configuration:
```toml
[entryPoints]
[entryPoints.http]
address = ":80"
[api]
[Docker]
endpoint = "tcp://10.10.10.10:2375"
domain = "foo.bir"
exposedByDefault = true
swarmMode = true
[Docker.TLS]
ca = "dockerCA"
cert = "dockerCert"
key = "dockerKey"
insecureSkipVerify = true
[ECS]
domain = "foo.bar"
exposedByDefault = true
clusters = ["foo-bar"]
region = "us-west-2"
accessKeyID = "AccessKeyID"
secretAccessKey = "SecretAccessKey"
```
- Obfuscated and anonymous configuration:
```toml
[entryPoints]
[entryPoints.http]
address = ":80"
[api]
[Docker]
endpoint = "xxxx"
domain = "xxxx"
exposedByDefault = true
swarmMode = true
[Docker.TLS]
ca = "xxxx"
cert = "xxxx"
key = "xxxx"
insecureSkipVerify = false
[ECS]
domain = "xxxx"
exposedByDefault = true
clusters = []
region = "us-west-2"
accessKeyID = "xxxx"
secretAccessKey = "xxxx"
```
### Show me the code !
If you want to dig into more details, here is the source code of the collecting system: [collector.go](https://github.com/traefik/traefik/blob/v1.7/collector/collector.go)
By default we anonymize all configuration fields, except fields tagged with `export=true`.
### How to enable this ?
You can enable the collecting system by:
- adding this line in the configuration TOML file:
```toml
# Send anonymous usage data
#
# Optional
# Default: false
#
sendAnonymousUsage = true
```
- adding this flag in the CLI:
```bash
./traefik --sendAnonymousUsage=true
```

View File

@@ -14,8 +14,8 @@ I used 4 VMs for the tests with the following configuration:
## Setup
1. One VM used to launch the benchmarking tool [wrk](https://github.com/wg/wrk)
2. One VM for traefik (v1.0.0-beta.416) / nginx (v1.4.6)
3. Two VMs for 2 backend servers in go [whoami](https://github.com/emilevauge/whoamI/)
2. One VM for Traefik (v1.0.0-beta.416) / nginx (v1.4.6)
3. Two VMs for 2 backend servers in go [whoami](https://github.com/traefik/whoami/)
Each VM has been tuned using the following limits:
@@ -65,8 +65,8 @@ http {
keepalive_requests 10000;
types_hash_max_size 2048;
open_file_cache max=200000 inactive=300s;
open_file_cache_valid 300s;
open_file_cache max=200000 inactive=300s;
open_file_cache_valid 300s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
@@ -118,7 +118,7 @@ server {
Here is the `traefik.toml` file used:
```toml
MaxIdleConnsPerHost = 100000
maxIdleConnsPerHost = 100000
defaultEntryPoints = ["http"]
[entryPoints]
@@ -182,7 +182,8 @@ Requests/sec: 33591.67
Transfer/sec: 4.97MB
```
### traefik:
### Traefik:
```shell
wrk -t20 -c1000 -d60s -H "Host: test.traefik" --latency http://IP-traefik:8000/bench
Running 1m test @ http://IP-traefik:8000/bench
@@ -209,5 +210,5 @@ Not bad for young project :) !
Some areas of possible improvements:
- Use [GO_REUSEPORT](https://github.com/kavu/go_reuseport) listener
- Run a separate server instance per CPU core with `GOMAXPROCS=1` (it appears during benchmarks that there is a lot more context switches with traefik than with nginx)
- Run a separate server instance per CPU core with `GOMAXPROCS=1` (it appears during benchmarks that there is a lot more context switches with Traefik than with nginx)

547
docs/configuration/acme.md Normal file
View File

@@ -0,0 +1,547 @@
# ACME (Let's Encrypt) Configuration
See [Let's Encrypt examples](/user-guide/examples/#lets-encrypt-support) and [Docker & Let's Encrypt user guide](/user-guide/docker-and-lets-encrypt) as well.
## Configuration
```toml
# Sample entrypoint configuration when using ACME.
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]
```
```toml
# Enable ACME (Let's Encrypt): automatic SSL.
[acme]
# Email address used for registration.
#
# Required
#
email = "test@traefik.io"
# File used for certificates storage.
#
# Optional (Deprecated)
#
#storageFile = "acme.json"
# File or key used for certificates storage.
#
# Required
#
storage = "acme.json"
# or `storage = "traefik/acme/account"` if using KV store.
# Entrypoint to proxy acme apply certificates to.
#
# Required
#
entryPoint = "https"
# Deprecated, replaced by [acme.dnsChallenge].
#
# Optional.
#
# dnsProvider = "digitalocean"
# Deprecated, replaced by [acme.dnsChallenge.delayBeforeCheck].
#
# Optional
# Default: 0
#
# delayDontCheckDNS = 0
# If true, display debug log messages from the acme client library.
#
# Optional
# Default: false
#
# acmeLogging = true
# If true, override certificates in key-value store when using storeconfig.
#
# Optional
# Default: false
#
# overrideCertificates = true
# Deprecated. Enable on demand certificate generation.
#
# Optional
# Default: false
#
# onDemand = true
# Enable certificate generation on frontends host rules.
#
# Optional
# Default: false
#
# onHostRule = true
# CA server to use.
# Uncomment the line to use Let's Encrypt's staging server,
# leave commented to go to prod.
#
# Optional
# Default: "https://acme-v02.api.letsencrypt.org/directory"
#
# caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
# Preferred chain to use.
#
# Optional
# Default: empty
#
preferredChain = "ISRG Root X1"
# KeyType to use.
#
# Optional
# Default: "RSA4096"
#
# Available values : "EC256", "EC384", "RSA2048", "RSA4096", "RSA8192"
#
# KeyType = "RSA4096"
# Use a TLS-ALPN-01 ACME challenge.
#
# Optional (but recommended)
#
[acme.tlsChallenge]
# Use a HTTP-01 ACME challenge.
#
# Optional
#
# [acme.httpChallenge]
# EntryPoint to use for the HTTP-01 challenges.
#
# Required
#
# entryPoint = "http"
# Use a DNS-01 ACME challenge rather than HTTP-01 challenge.
# Note: mandatory for wildcard certificate generation.
#
# Optional
#
# [acme.dnsChallenge]
# DNS provider used.
#
# Required
#
# provider = "digitalocean"
# By default, the provider will verify the TXT DNS challenge record before letting ACME verify.
# If delayBeforeCheck is greater than zero, this check is delayed for the configured duration in seconds.
# Useful if internal networks block external DNS queries.
#
# Optional
# Default: 0
#
# delayBeforeCheck = 0
# Use following DNS servers to resolve the FQDN authority.
#
# Optional
# Default: empty
#
# resolvers = ["1.1.1.1:53", "8.8.8.8:53"]
# Disable the DNS propagation checks before notifying ACME that the DNS challenge is ready.
#
# NOT RECOMMENDED:
# Increase the risk of reaching Let's Encrypt's rate limits.
#
# Optional
# Default: false
#
# disablePropagationCheck = true
# Domains list.
# Only domains defined here can generate wildcard certificates.
# The certificates for these domains are negotiated at traefik startup only.
#
# [[acme.domains]]
# main = "local1.com"
# sans = ["test1.local1.com", "test2.local1.com"]
# [[acme.domains]]
# main = "local2.com"
# [[acme.domains]]
# main = "*.local3.com"
# sans = ["local3.com", "test1.test1.local3.com"]
```
### `caServer`
The CA server to use.
This example shows the usage of Let's Encrypt's staging server:
```toml
[acme]
# ...
caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
# ...
```
### `preferredChain`
Preferred chain to use.
```toml
[acme]
# ...
preferredChain = "ISRG Root X1"
# ...
```
### ACME Challenge
#### `tlsChallenge`
Use the `TLS-ALPN-01` challenge to generate and renew ACME certificates by provisioning a TLS certificate.
```toml
[acme]
# ...
entryPoint = "https"
[acme.tlsChallenge]
```
!!! note
If the `TLS-ALPN-01` challenge is used, `acme.entryPoint` has to be reachable by Let's Encrypt through port 443.
This is a Let's Encrypt limitation as described on the [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72).
#### `httpChallenge`
Use the `HTTP-01` challenge to generate and renew ACME certificates by provisioning a HTTP resource under a well-known URI.
Redirection is fully compatible with the `HTTP-01` challenge.
```toml
[acme]
# ...
entryPoint = "https"
[acme.httpChallenge]
entryPoint = "http"
```
!!! note
If the `HTTP-01` challenge is used, `acme.httpChallenge.entryPoint` has to be defined and reachable by Let's Encrypt through port 80.
This is a Let's Encrypt limitation as described on the [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72).
##### `entryPoint`
Specify the entryPoint to use during the challenges.
```toml
defaultEntryPoints = ["http", "https"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]
# ...
[acme]
# ...
entryPoint = "https"
[acme.httpChallenge]
entryPoint = "http"
```
!!! note
`acme.httpChallenge.entryPoint` has to be reachable through port 80. It's a Let's Encrypt limitation as described on the [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72).
#### `dnsChallenge`
Use the `DNS-01` challenge to generate and renew ACME certificates by provisioning a DNS record.
```toml
[acme]
# ...
[acme.dnsChallenge]
provider = "digitalocean"
delayBeforeCheck = 0
# ...
```
##### `delayBeforeCheck`
By default, the `provider` will verify the TXT DNS challenge record before letting ACME verify.
If `delayBeforeCheck` is greater than zero, this check is delayed for the configured duration in seconds.
Useful if internal networks block external DNS queries.
!!! note
A `provider` is mandatory.
##### `provider`
Here is a list of supported `provider`s, that can automate the DNS verification, along with the required environment variables and their [wildcard & root domain support](/configuration/acme/#wildcard-domains) for each.
Do not hesitate to complete it.
Every lego environment variable can be overridden by their respective `_FILE` counterpart, which should have a filepath to a file that contains the secret as its value.
For example, `CF_API_EMAIL_FILE=/run/secrets/traefik_cf-api-email` could be used to provide a Cloudflare API email address as a Docker secret named `traefik_cf-api-email`.
| Provider Name | Provider Code | Environment Variables | Wildcard & Root Domain Support |
|-------------------------------------------------------------|----------------|---------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------|
| [ACME DNS](https://github.com/joohoi/acme-dns) | `acme-dns` | `ACME_DNS_API_BASE`, `ACME_DNS_STORAGE_PATH` | Not tested yet |
| [Alibaba Cloud](https://www.alibabacloud.com) | `alidns` | `ALICLOUD_ACCESS_KEY`, `ALICLOUD_SECRET_KEY`, `ALICLOUD_REGION_ID` | Not tested yet |
| [Auroradns](https://www.pcextreme.com/aurora/dns) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` | Not tested yet |
| [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP`, `[AZURE_METADATA_ENDPOINT]` | Not tested yet |
| [Bindman](https://github.com/labbsr0x/bindman-dns-webhook) | `bindman` | `BINDMAN_MANAGER_ADDRESS` | YES |
| [Blue Cat](https://www.bluecatnetworks.com/) | `bluecat` | `BLUECAT_SERVER_URL`, `BLUECAT_USER_NAME`, `BLUECAT_PASSWORD`, `BLUECAT_CONFIG_NAME`, `BLUECAT_DNS_VIEW` | Not tested yet |
| [ClouDNS](https://www.cloudns.net/) | `cloudns` | `CLOUDNS_AUTH_ID`, `CLOUDNS_AUTH_PASSWORD` | YES |
| [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CF_API_EMAIL`, `CF_API_KEY` - The `Global API Key` needs to be used, not the `Origin CA Key` | YES |
| [CloudXNS](https://www.cloudxns.net) | `cloudxns` | `CLOUDXNS_API_KEY`, `CLOUDXNS_SECRET_KEY` | Not tested yet |
| [ConoHa](https://www.conoha.jp) | `conoha` | `CONOHA_TENANT_ID`, `CONOHA_API_USERNAME`, `CONOHA_API_PASSWORD` | YES |
| [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` | YES |
| [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` | YES |
| [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` | Not tested yet |
| [DNSPod](https://www.dnspod.com/) | `dnspod` | `DNSPOD_API_KEY` | Not tested yet |
| [Domain Offensive (do.de)](https://www.do.de/) | `dode` | `DODE_TOKEN` | YES |
| [DreamHost](https://www.dreamhost.com/) | `dreamhost` | `DREAMHOST_API_KEY` | YES |
| [Duck DNS](https://www.duckdns.org/) | `duckdns` | `DUCKDNS_TOKEN` | YES |
| [Dyn](https://dyn.com) | `dyn` | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` | Not tested yet |
| [EasyDNS](https://easydns.com/) | `easydns` | `EASYDNS_TOKEN`, `EASYDNS_KEY` | YES |
| External Program | `exec` | `EXEC_PATH` | YES |
| [Exoscale](https://www.exoscale.com) | `exoscale` | `EXOSCALE_API_KEY`, `EXOSCALE_API_SECRET`, `EXOSCALE_ENDPOINT` | YES |
| [Fast DNS](https://www.akamai.com/) | `fastdns` | `AKAMAI_CLIENT_TOKEN`, `AKAMAI_CLIENT_SECRET`, `AKAMAI_ACCESS_TOKEN` | YES |
| [Gandi](https://www.gandi.net) | `gandi` | `GANDI_API_KEY` | Not tested yet |
| [Gandi v5](http://doc.livedns.gandi.net) | `gandiv5` | `GANDIV5_API_KEY` | YES |
| [Glesys](https://glesys.com/) | `glesys` | `GLESYS_API_USER`, `GLESYS_API_KEY`, `GLESYS_DOMAIN` | Not tested yet |
| [GoDaddy](https://godaddy.com/domains) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` | Not tested yet |
| [Google Cloud DNS](https://cloud.google.com/dns/docs/) | `gcloud` | `GCE_PROJECT`, Application Default Credentials (2) (3), [`GCE_SERVICE_ACCOUNT_FILE`] | YES |
| [hosting.de](https://www.hosting.de) | `hostingde` | `HOSTINGDE_API_KEY`, `HOSTINGDE_ZONE_NAME` | YES |
| HTTP request | `httpreq` | `HTTPREQ_ENDPOINT`, `HTTPREQ_MODE`, `HTTPREQ_USERNAME`, `HTTPREQ_PASSWORD` (1) | YES |
| [IIJ](https://www.iij.ad.jp/) | `iij` | `IIJ_API_ACCESS_KEY`, `IIJ_API_SECRET_KEY`, `IIJ_DO_SERVICE_CODE` | Not tested yet |
| [INWX](https://www.inwx.de/en) | `inwx` | `INWX_USERNAME`, `INWX_PASSWORD` | YES |
| [Joker.com](https://joker.com) | `joker` | `JOKER_API_KEY` or `JOKER_USERNAME`, `JOKER_PASSWORD` | YES |
| [Lightsail](https://aws.amazon.com/lightsail/) | `lightsail` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `DNS_ZONE` | Not tested yet |
| [Linode](https://www.linode.com) | `linode` | `LINODE_API_KEY` | Not tested yet |
| [Linode v4](https://www.linode.com) | `linodev4` | `LINODE_TOKEN` | Not tested yet |
| manual | `manual` | none, but you need to run Traefik interactively, turn on `acmeLogging` to see instructions and press <kbd>Enter</kbd>. | YES |
| [MyDNS.jp](https://www.mydns.jp/) | `mydnsjp` | `MYDNSJP_MASTER_ID`, `MYDNSJP_PASSWORD` | YES |
| [Namecheap](https://www.namecheap.com) | `namecheap` | `NAMECHEAP_API_USER`, `NAMECHEAP_API_KEY` | YES |
| [Namesilo](https://www.namesilo.com/) | `namesilo` | `NAMESILO_API_KEY` | YES |
| [name.com](https://www.name.com/) | `namedotcom` | `NAMECOM_USERNAME`, `NAMECOM_API_TOKEN`, `NAMECOM_SERVER` | Not tested yet |
| [Netcup](https://www.netcup.eu/) | `netcup` | `NETCUP_CUSTOMER_NUMBER`, `NETCUP_API_KEY`, `NETCUP_API_PASSWORD` | Not tested yet |
| [NIFCloud](https://cloud.nifty.com/service/dns.htm) | `nifcloud` | `NIFCLOUD_ACCESS_KEY_ID`, `NIFCLOUD_SECRET_ACCESS_KEY` | Not tested yet |
| [Ns1](https://ns1.com/) | `ns1` | `NS1_API_KEY` | Not tested yet |
| [Open Telekom Cloud](https://cloud.telekom.de) | `otc` | `OTC_DOMAIN_NAME`, `OTC_USER_NAME`, `OTC_PASSWORD`, `OTC_PROJECT_NAME`, `OTC_IDENTITY_ENDPOINT` | Not tested yet |
| [OVH](https://www.ovh.com) | `ovh` | `OVH_ENDPOINT`, `OVH_APPLICATION_KEY`, `OVH_APPLICATION_SECRET`, `OVH_CONSUMER_KEY` | YES |
| [Openstack Designate](https://docs.openstack.org/designate) | `designate` | `OS_AUTH_URL`, `OS_USERNAME`, `OS_PASSWORD`, `OS_TENANT_NAME`, `OS_REGION_NAME` | YES |
| [Oracle Cloud](https://cloud.oracle.com/home) | `oraclecloud` | `OCI_COMPARTMENT_OCID`, `OCI_PRIVKEY_FILE`, `OCI_PRIVKEY_PASS`, `OCI_PUBKEY_FINGERPRINT`, `OCI_REGION`, `OCI_TENANCY_OCID`, `OCI_USER_OCID` | YES |
| [PowerDNS](https://www.powerdns.com) | `pdns` | `PDNS_API_KEY`, `PDNS_API_URL` | Not tested yet |
| [Rackspace](https://www.rackspace.com/cloud/dns) | `rackspace` | `RACKSPACE_USER`, `RACKSPACE_API_KEY` | Not tested yet |
| [RFC2136](https://tools.ietf.org/html/rfc2136) | `rfc2136` | `RFC2136_TSIG_KEY`, `RFC2136_TSIG_SECRET`, `RFC2136_TSIG_ALGORITHM`, `RFC2136_NAMESERVER` | Not tested yet |
| [Route 53](https://aws.amazon.com/route53/) | `route53` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `[AWS_REGION]`, `[AWS_HOSTED_ZONE_ID]` or a configured user/instance IAM profile. | YES |
| [Sakura Cloud](https://cloud.sakura.ad.jp/) | `sakuracloud` | `SAKURACLOUD_ACCESS_TOKEN`, `SAKURACLOUD_ACCESS_TOKEN_SECRET` | Not tested yet |
| [Selectel](https://selectel.ru/en/) | `selectel` | `SELECTEL_API_TOKEN` | YES |
| [Stackpath](https://www.stackpath.com/) | `stackpath` | `STACKPATH_CLIENT_ID`, `STACKPATH_CLIENT_SECRET`, `STACKPATH_STACK_ID` | Not tested yet |
| [TransIP](https://www.transip.nl/) | `transip` | `TRANSIP_ACCOUNT_NAME`, `TRANSIP_PRIVATE_KEY_PATH` | YES |
| [VegaDNS](https://github.com/shupp/VegaDNS-API) | `vegadns` | `SECRET_VEGADNS_KEY`, `SECRET_VEGADNS_SECRET`, `VEGADNS_URL` | Not tested yet |
| [Versio](https://www.versio.nl/domeinnamen) | `versio` | `VERSIO_USERNAME`, `VERSIO_PASSWORD` | YES |
| [Vscale](https://vscale.io/) | `vscale` | `VSCALE_API_TOKEN` | YES |
| [VULTR](https://www.vultr.com) | `vultr` | `VULTR_API_KEY` | YES |
| [Zone.ee](https://www.zone.ee) | `zoneee` | `ZONEEE_API_USER`, `ZONEEE_API_KEY` | YES |
- (1): more information about the HTTP message format can be found [here](https://go-acme.github.io/lego/dns/httpreq/)
- (2): https://cloud.google.com/docs/authentication/production#providing_credentials_to_your_application
- (3): https://github.com/golang/oauth2/blob/36a7019397c4c86cf59eeab3bc0d188bac444277/google/default.go#L61-L76
#### `resolvers`
Use custom DNS servers to resolve the FQDN authority.
```toml
[acme]
# ...
[acme.dnsChallenge]
# ...
resolvers = ["1.1.1.1:53", "8.8.8.8:53"]
```
### `domains`
You can provide SANs (alternative domains) to each main domain.
All domains must have A/AAAA records pointing to Traefik.
Each domain & SAN will lead to a certificate request.
!!! note
The certificates for the domains listed in `acme.domains` are negotiated at traefik startup only.
```toml
[acme]
# ...
[[acme.domains]]
main = "local1.com"
sans = ["test1.local1.com", "test2.local1.com"]
[[acme.domains]]
main = "local2.com"
[[acme.domains]]
main = "*.local3.com"
sans = ["local3.com", "test1.test1.local3.com"]
# ...
```
!!! warning
Take note that Let's Encrypt applies [rate limiting](https://letsencrypt.org/docs/rate-limits).
!!! note
Wildcard certificates can only be verified through a `DNS-01` challenge.
#### Wildcard Domains
[ACME V2](https://community.letsencrypt.org/t/acme-v2-and-wildcard-certificate-support-is-live/55579) allows wildcard certificate support.
As described in [Let's Encrypt's post](https://community.letsencrypt.org/t/staging-endpoint-for-acme-v2/49605) wildcard certificates can only be generated through a [`DNS-01` challenge](/configuration/acme/#dnschallenge).
```toml
[acme]
# ...
[[acme.domains]]
main = "*.local1.com"
sans = ["local1.com"]
# ...
```
It is not possible to request a double wildcard certificate for a domain (for example `*.*.local.com`).
Most likely the root domain should receive a certificate too, so it needs to be specified as SAN and 2 `DNS-01` challenges are executed.
In this case the generated DNS TXT record for both domains is the same.
Even though this behaviour is [DNS RFC](https://community.letsencrypt.org/t/wildcard-issuance-two-txt-records-for-the-same-name/54528/2) compliant, it can lead to problems as all DNS providers keep DNS records cached for a certain time (TTL) and this TTL can be superior to the challenge timeout making the `DNS-01` challenge fail.
The Traefik ACME client library [LEGO](https://github.com/go-acme/lego) supports some but not all DNS providers to work around this issue.
The [`provider` table](/configuration/acme/#provider) indicates if they allow generating certificates for a wildcard domain and its root domain.
### `onDemand` (Deprecated)
!!! danger "DEPRECATED"
This option is deprecated.
```toml
[acme]
# ...
onDemand = true
# ...
```
Enable on demand certificate generation.
This will request certificates from Let's Encrypt during the first TLS handshake for host names that do not yet have certificates.
!!! warning
TLS handshakes are slow when requesting a host name certificate for the first time. This can lead to DoS attacks!
!!! warning
Take note that Let's Encrypt applies [rate limiting](https://letsencrypt.org/docs/rate-limits).
### `onHostRule`
```toml
[acme]
# ...
onHostRule = true
# ...
```
Enable certificate generation on frontend `Host` rules (for frontends wired to the `acme.entryPoint`).
This will request a certificate from Let's Encrypt for each frontend with a Host rule.
For example, the rule `Host:test1.traefik.io,test2.traefik.io` will request a certificate with main domain `test1.traefik.io` and SAN `test2.traefik.io`.
!!! warning
`onHostRule` option can not be used to generate wildcard certificates.
Refer to [wildcard generation](/configuration/acme/#wildcard-domains) for further information.
### `storage`
The `storage` option sets the location where your ACME certificates are saved to.
```toml
[acme]
# ...
storage = "acme.json"
# ...
```
The value can refer to two kinds of storage:
- a JSON file
- a KV store entry
!!! danger "DEPRECATED"
`storage` replaces `storageFile` which is deprecated.
!!! note
During migration to a KV store use both `storageFile` and `storage` to migrate ACME certificates too. See [`storeconfig` subcommand](/user-guide/kv-config/#store-configuration-in-key-value-store) for further information.
#### As a File
ACME certificates can be stored in a JSON file that needs to have file mode `600`.
In Docker you can either mount the JSON file or the folder containing it:
```bash
docker run -v "/my/host/acme.json:acme.json" traefik
```
```bash
docker run -v "/my/host/acme:/etc/traefik/acme" traefik
```
!!! warning
This file cannot be shared across multiple instances of Traefik at the same time. Please use a [KV Store entry](/configuration/acme/#as-a-key-value-store-entry) instead.
#### As a Key Value Store Entry
ACME certificates can be stored in a KV Store entry. This kind of storage is **mandatory in cluster mode**.
```toml
storage = "traefik/acme/account"
```
Because KV stores (like Consul) have limited entry size the certificates list is compressed before it is saved as KV store entry.
!!! note
It is possible to store up to approximately 100 ACME certificates in Consul.
#### ACME v2 Migration
During migration from ACME v1 to ACME v2, using a storage file, a backup of the original file is created in the same place as the latter (with a `.bak` extension).
For example: if `acme.storage`'s value is `/etc/traefik/acme/acme.json`, the backup file will be `/etc/traefik/acme/acme.json.bak`.
!!! note
When Traefik is launched in a container, the storage file's parent directory needs to be mounted to be able to access the backup file on the host.
Otherwise the backup file will be deleted when the container is stopped. Traefik will only generate it once!
### `dnsProvider` (Deprecated)
!!! danger "DEPRECATED"
This option is deprecated. Please use [dnsChallenge.provider](/configuration/acme/#provider) instead.
### `delayDontCheckDNS` (Deprecated)
!!! danger "DEPRECATED"
This option is deprecated. Please use [dnsChallenge.delayBeforeCheck](/configuration/acme/#dnschallenge) instead.
## Fallbacks
If Let's Encrypt is not reachable, these certificates will be used:
1. ACME certificates already generated before downtime
1. Expired ACME certificates
1. Provided certificates
!!! note
For new (sub)domains which need Let's Encrypt authentification, the default Traefik certificate will be used until Traefik is restarted.

350
docs/configuration/api.md Normal file
View File

@@ -0,0 +1,350 @@
# API Definition
## Configuration
```toml
# API definition
# Warning: Enabling API will expose Traefik's configuration.
# It is not recommended in production,
# unless secured by authentication and authorizations
[api]
# Name of the related entry point
#
# Optional
# Default: "traefik"
#
entryPoint = "traefik"
# Enable Dashboard
#
# Optional
# Default: true
#
dashboard = true
# Enable debug mode.
# This will install HTTP handlers to expose Go expvars under /debug/vars and
# pprof profiling data under /debug/pprof/.
# Additionally, the log level will be set to DEBUG.
#
# Optional
# Default: false
#
debug = true
```
For more customization, see [entry points](/configuration/entrypoints/) documentation and the examples below.
## Dashboard (Web UI)
![Web UI Providers](/img/web.frontend.png)
![Web UI Health](/img/traefik-health.png)
## Security
Enabling the API will expose all configuration elements,
including sensitive data.
It is not recommended in production,
unless secured by authentication and authorizations.
A good sane default (but not exhaustive) set of recommendations
would be to apply the following protection mechanism:
* _At application level:_ enabling HTTP [Basic Authentication](#authentication)
* _At transport level:_ NOT exposing publicly the API's port,
keeping it restricted over internal networks
(restricted networks as in https://en.wikipedia.org/wiki/Principle_of_least_privilege).
## API
| Path | Method | Description |
|-----------------------------------------------------------------|------------------|-------------------------------------------|
| `/` | `GET` | Provides a simple HTML frontend of Traefik |
| `/cluster/leader` | `GET` | JSON leader true/false response |
| `/health` | `GET` | JSON health metrics |
| `/api` | `GET` | Configuration for all providers |
| `/api/providers` | `GET` | Providers |
| `/api/providers/{provider}` | `GET`, `PUT` | Get or update provider (1) |
| `/api/providers/{provider}/backends` | `GET` | List backends |
| `/api/providers/{provider}/backends/{backend}` | `GET` | Get backend |
| `/api/providers/{provider}/backends/{backend}/servers` | `GET` | List servers in backend |
| `/api/providers/{provider}/backends/{backend}/servers/{server}` | `GET` | Get a server in a backend |
| `/api/providers/{provider}/frontends` | `GET` | List frontends |
| `/api/providers/{provider}/frontends/{frontend}` | `GET` | Get a frontend |
| `/api/providers/{provider}/frontends/{frontend}/routes` | `GET` | List routes in a frontend |
| `/api/providers/{provider}/frontends/{frontend}/routes/{route}` | `GET` | Get a route in a frontend |
<1> See [Rest](/configuration/backends/rest/#api) for more information.
!!! warning
For compatibility reason, when you activate the rest provider, you can use `web` or `rest` as `provider` value.
But be careful, in the configuration for all providers the key is still `web`.
### Address / Port
You can define a custom address/port like this:
```toml
defaultEntryPoints = ["http"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.foo]
address = ":8082"
[entryPoints.bar]
address = ":8083"
[ping]
entryPoint = "foo"
[api]
entryPoint = "bar"
```
In the above example, you would access a regular path, dashboard, and health-check as follows:
* Regular path: `http://hostname:80/path`
* Dashboard: `http://hostname:8083/`
* Ping URL: `http://hostname:8082/ping`
In the above example, it is _very_ important to create a named dedicated entry point, and do **not** include it in `defaultEntryPoints`.
Otherwise, you are likely to expose _all_ services via that entry point.
### Custom Path
You can define a custom path like this:
```toml
defaultEntryPoints = ["http"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.foo]
address = ":8080"
[entryPoints.bar]
address = ":8081"
# Activate API and Dashboard
[api]
entryPoint = "bar"
dashboard = true
[file]
[backends]
[backends.backend1]
[backends.backend1.servers.server1]
url = "http://127.0.0.1:8081"
[frontends]
[frontends.frontend1]
entryPoints = ["foo"]
backend = "backend1"
[frontends.frontend1.routes.test_1]
rule = "PathPrefixStrip:/yourprefix;PathPrefix:/yourprefix"
```
### Authentication
You can define the authentication like this:
```toml
defaultEntryPoints = ["http"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.foo]
address=":8080"
[entryPoints.foo.auth]
[entryPoints.foo.auth.basic]
users = [
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
]
[api]
entrypoint="foo"
```
For more information, see [entry points](/configuration/entrypoints/) .
### Provider call example
```shell
curl -s "http://localhost:8080/api" | jq .
```
```json
{
"file": {
"frontends": {
"frontend2": {
"routes": {
"test_2": {
"rule": "Path:/test"
}
},
"backend": "backend1"
},
"frontend1": {
"routes": {
"test_1": {
"rule": "Host:test.localhost"
}
},
"backend": "backend2"
}
},
"backends": {
"backend2": {
"loadBalancer": {
"method": "drr"
},
"servers": {
"server2": {
"weight": 2,
"URL": "http://172.17.0.5:80"
},
"server1": {
"weight": 1,
"url": "http://172.17.0.4:80"
}
}
},
"backend1": {
"loadBalancer": {
"method": "wrr"
},
"circuitBreaker": {
"expression": "NetworkErrorRatio() > 0.5"
},
"servers": {
"server2": {
"weight": 1,
"url": "http://172.17.0.3:80"
},
"server1": {
"weight": 10,
"url": "http://172.17.0.2:80"
}
}
}
}
}
}
```
### Cluster Leadership
```shell
curl -s "http://localhost:8080/cluster/leader" | jq .
```
```shell
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=UTF-8
< Date: xxx
< Content-Length: 15
```
If the given node is not a cluster leader, an HTTP status of `429-Too-Many-Requests` will be returned.
```json
{
// current leadership status of the queried node
"leader": true
}
```
### Health
```shell
curl -s "http://localhost:8080/health" | jq .
```
```json
{
// Traefik PID
"pid": 2458,
// Traefik server uptime (formated time)
"uptime": "39m6.885931127s",
// Traefik server uptime in seconds
"uptime_sec": 2346.885931127,
// current server date
"time": "2015-10-07 18:32:24.362238909 +0200 CEST",
// current server date in seconds
"unixtime": 1444235544,
// count HTTP response status code in realtime
"status_code_count": {
"502": 1
},
// count HTTP response status code since Traefik started
"total_status_code_count": {
"200": 7,
"404": 21,
"502": 13
},
// count HTTP response
"count": 1,
// count HTTP response
"total_count": 41,
// sum of all response time (formated time)
"total_response_time": "35.456865605s",
// sum of all response time in seconds
"total_response_time_sec": 35.456865605,
// average response time (formated time)
"average_response_time": "864.8016ms",
// average response time in seconds
"average_response_time_sec": 0.8648016000000001,
// request statistics [requires --api.statistics to be set]
// ten most recent requests with 4xx and 5xx status codes
"recent_errors": [
{
// status code
"status_code": 500,
// description of status code
"status": "Internal Server Error",
// request HTTP method
"method": "GET",
// request hostname
"host": "localhost",
// request path
"path": "/path",
// RFC 3339 formatted date/time
"time": "2016-10-21T16:59:15.418495872-07:00"
}
]
}
```
## Dashboard Statistics
You can control how the Traefik's internal metrics are shown in the Dashboard.
If you want to export internal metrics to different monitoring systems,
please check the page [Metrics](./metrics.md).
```toml
[api]
# ...
# Enable more detailed statistics.
[api.statistics]
# Number of recent errors logged.
#
# Default: 10
#
recentErrors = 10
# ...
```
| Path | Method | Description |
|------------|---------------|-------------------------|
| `/metrics` | `GET` | Export internal metrics |

View File

@@ -0,0 +1,59 @@
# BoltDB Provider
Traefik can be configured to use BoltDB as a provider.
```toml
################################################################
# BoltDB Provider
################################################################
# Enable BoltDB Provider.
[boltdb]
# BoltDB file.
#
# Required
# Default: "127.0.0.1:4001"
#
endpoint = "/my.db"
# Enable watch BoltDB changes.
#
# Optional
# Default: true
#
watch = true
# Prefix used for KV store.
#
# Optional
# Default: "/traefik"
#
prefix = "/traefik"
# Override default configuration template.
# For advanced users :)
#
# Optional
#
filename = "boltdb.tmpl"
# Use BoltDB user/pass authentication.
#
# Optional
#
# username = foo
# password = bar
# Enable BoltDB TLS connection.
#
# Optional
#
# [boltdb.tls]
# ca = "/etc/ssl/ca.crt"
# cert = "/etc/ssl/boltdb.crt"
# key = "/etc/ssl/boltdb.key"
# insecureSkipVerify = true
```
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).

View File

@@ -0,0 +1,61 @@
# Consul Key-Value Provider
Traefik can be configured to use Consul as a provider.
```toml
################################################################
# Consul KV Provider
################################################################
# Enable Consul KV Provider.
[consul]
# Consul server endpoint.
#
# Required
# Default: "127.0.0.1:8500"
#
endpoint = "127.0.0.1:8500"
# Enable watch Consul changes.
#
# Optional
# Default: true
#
watch = true
# Prefix used for KV store.
#
# Optional
# Default: traefik
#
prefix = "traefik"
# Override default configuration template.
# For advanced users :)
#
# Optional
#
# filename = "consul.tmpl"
# Use Consul user/pass authentication.
#
# Optional
#
# username = foo
# password = bar
# Enable Consul TLS connection.
#
# Optional
#
# [consul.tls]
# ca = "/etc/ssl/ca.crt"
# cert = "/etc/ssl/consul.crt"
# key = "/etc/ssl/consul.key"
# insecureSkipVerify = true
```
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
Please refer to the [Key Value storage structure](/user-guide/kv-config/#key-value-storage-structure) section to get documentation on Traefik KV structure.

View File

@@ -0,0 +1,252 @@
# Consul Catalog Provider
Traefik can be configured to use service discovery catalog of Consul as a provider.
```toml
################################################################
# Consul Catalog Provider
################################################################
# Enable Consul Catalog Provider.
[consulCatalog]
# Consul server endpoint.
#
# Required
# Default: "127.0.0.1:8500"
#
endpoint = "127.0.0.1:8500"
# Expose Consul catalog services by default in Traefik.
#
# Optional
# Default: true
#
exposedByDefault = false
# Allow Consul server to serve the catalog reads regardless of whether it is the leader.
#
# Optional
# Default: false
#
stale = false
# Default base domain used for the frontend rules.
#
# Optional
#
domain = "consul.localhost"
# Keep a Consul node only if all checks status are passing
# If true, only the Consul nodes with checks status 'passing' will be kept.
# if false, only the Consul nodes with checks status 'passing' or 'warning' will be kept.
#
# Optional
# Default: true
#
strictChecks = true
# Prefix for Consul catalog tags.
#
# Optional
# Default: "traefik"
#
prefix = "traefik"
# Default frontEnd Rule for Consul services.
#
# The format is a Go Template with:
# - ".ServiceName", ".Domain" and ".Attributes" available
# - "getTag(name, tags, defaultValue)", "hasTag(name, tags)" and "getAttribute(name, tags, defaultValue)" functions are available
# - "getAttribute(...)" function uses prefixed tag names based on "prefix" value
#
# Optional
# Default: "Host:{{.ServiceName}}.{{.Domain}}"
#
#frontEndRule = "Host:{{.ServiceName}}.{{.Domain}}"
# Enable Consul catalog TLS connection.
#
# Optional
#
# [consulCatalog.tls]
# ca = "/etc/ssl/ca.crt"
# cert = "/etc/ssl/consul.crt"
# key = "/etc/ssl/consul.key"
# insecureSkipVerify = true
# Override default configuration template.
# For advanced users :)
#
# Optional
#
# filename = "consulcatalog.tmpl"
# Override template version
# For advanced users :)
#
# Optional
# - "1": previous template version (must be used only with older custom templates, see "filename")
# - "2": current template version (must be used to force template version when "filename" is used)
#
# templateVersion = 2
```
This provider will create routes matching on hostname based on the service name used in Consul.
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
## Tags
Additional settings can be defined using Consul Catalog tags.
!!! note
The default prefix is `traefik`.
| Label | Description |
|--------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `<prefix>.enable=false` | Disables this container in Traefik. |
| `<prefix>.protocol=https` | Overrides the default `http` protocol. |
| `<prefix>.weight=10` | Assigns this weight to the container. |
| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
| `<prefix>.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend. ex: `NetworkErrorRatio() > 0.` |
| `<prefix>.backend.responseForwarding.flushInterval=10ms` | Defines the interval between two flushes when forwarding response from backend to client. |
| `<prefix>.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. |
| `<prefix>.backend.healthcheck.interval=1s` | Defines the health check interval. |
| `<prefix>.backend.healthcheck.port=8080` | Sets a different port for the health check. |
| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. |
| `<prefix>.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
| `<prefix>.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `<prefix>.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm. |
| `<prefix>.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions. |
| `<prefix>.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions. |
| `<prefix>.backend.loadbalancer.stickiness.secure=true` | Sets secure cookie option for sticky sessions. |
| `<prefix>.backend.loadbalancer.stickiness.httpOnly=true` | Sets http only cookie option for sticky sessions. |
| `<prefix>.backend.loadbalancer.stickiness.sameSite=none` | Sets same site cookie option for sticky sessions. (`none`, `lax`, `strict`) |
| `<prefix>.backend.loadbalancer.sticky=true` | Enables backend sticky sessions. (DEPRECATED) |
| `<prefix>.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
| `<prefix>.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
| `<prefix>.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). |
| `<prefix>.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
| `<prefix>.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. |
| `<prefix>.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
| `<prefix>.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
| `<prefix>.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. |
| `<prefix>.frontend.auth.digest.usersFile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
| `<prefix>.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. |
| `<prefix>.frontend.auth.forward.authResponseHeaders=EXPR` | Sets the forward authentication authResponseHeaders in CSV format: `X-Auth-User,X-Auth-Header` |
| `<prefix>.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. |
| `<prefix>.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). |
| `<prefix>.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. |
| `<prefix>.frontend.auth.forward.tls.insecureSkipVerify=true` | If set to true invalid SSL certificates are accepted. |
| `<prefix>.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. |
| `<prefix>.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. |
| `<prefix>.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. |
| `<prefix>.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
| `<prefix>.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `<prefix>.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `<prefix>.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `<prefix>.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. |
| `<prefix>.frontend.passTLSClientCert.infos.issuer.commonName=true` | Add the issuer.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `<prefix>.frontend.passTLSClientCert.infos.issuer.country=true` | Add the issuer.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `<prefix>.frontend.passTLSClientCert.infos.issuer.domainComponent=true` | Add the issuer.domainComponent field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `<prefix>.frontend.passTLSClientCert.infos.issuer.locality=true` | Add the issuer.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `<prefix>.frontend.passTLSClientCert.infos.issuer.organization=true` | Add the issuer.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `<prefix>.frontend.passTLSClientCert.infos.issuer.province=true` | Add the issuer.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `<prefix>.frontend.passTLSClientCert.infos.issuer.serialNumber=true` | Add the subject.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `<prefix>.frontend.passTLSClientCert.infos.notAfter=true` | Add the noAfter field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `<prefix>.frontend.passTLSClientCert.infos.notBefore=true` | Add the noBefore field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `<prefix>.frontend.passTLSClientCert.infos.sans=true` | Add the sans field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `<prefix>.frontend.passTLSClientCert.infos.subject.commonName=true` | Add the subject.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `<prefix>.frontend.passTLSClientCert.infos.subject.country=true` | Add the subject.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `<prefix>.frontend.passTLSClientCert.infos.subject.domainComponent=true` | Add the subject.domainComponent field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `<prefix>.frontend.passTLSClientCert.infos.subject.locality=true` | Add the subject.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `<prefix>.frontend.passTLSClientCert.infos.subject.organization=true` | Add the subject.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `<prefix>.frontend.passTLSClientCert.infos.subject.province=true` | Add the subject.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `<prefix>.frontend.passTLSClientCert.infos.subject.serialNumber=true` | Add the subject.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `<prefix>.frontend.passTLSClientCert.pem=true` | Pass the escaped pem in the `X-Forwarded-Ssl-Client-Cert` header. |
| `<prefix>.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
| `<prefix>.frontend.priority=10` | Overrides default frontend priority. |
| `<prefix>.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `<prefix>.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `<prefix>.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `<prefix>.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `<prefix>.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS). |
| `<prefix>.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
| `<prefix>.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
| `<prefix>.frontend.redirect.permanent=true` | Returns 301 instead of 302. |
| `<prefix>.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{{.ServiceName}}.{{.Domain}}`. |
| `<prefix>.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
| `<prefix>.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. |
### Multiple frontends for a single service
If you need to support multiple frontends for a service, for example when having multiple `rules` that can't be combined, specify them as follows:
```
<prefix>.frontends.A.rule=Host:A:PathPrefix:/A
<prefix>.frontends.B.rule=Host:B:PathPrefix:/
```
`A` and `B` here are just arbitrary names, they can be anything. You can use any setting that applies to `<prefix>.frontend` from the table above.
### Custom Headers
!!! note
The default prefix is `traefik`.
| Label | Description |
|--------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `<prefix>.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `<prefix>.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
### Security Headers
!!! note
The default prefix is `traefik`.
| Label | Description |
|-----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `<prefix>.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
| `<prefix>.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `<prefix>.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `<prefix>.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `<prefix>.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `<prefix>.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `<prefix>.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
| `<prefix>.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `<prefix>.frontend.headers.hostsProxyHeaders=EXPR` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
| `<prefix>.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
| `<prefix>.frontend.headers.publicKey=VALUE` | Adds HPKP header. |
| `<prefix>.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
| `<prefix>.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
| `<prefix>.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
| `<prefix>.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
| `<prefix>.frontend.headers.SSLForceHost=true` | If `SSLForceHost` is `true` and `SSLHost` is set, requests will be forced to use `SSLHost` even the ones that are already using SSL. Default is false. |
| `<prefix>.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-Proto:https`).<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `<prefix>.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
| `<prefix>.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
| `<prefix>.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
### Examples
If you want that Traefik uses Consul tags correctly you need to defined them like that:
```js
traefik.enable=true
traefik.tags=api
traefik.tags=external
```
If the prefix defined in Traefik configuration is `bla`, tags need to be defined like that:
```js
bla.enable=true
bla.tags=api
bla.tags=external
```

View File

@@ -0,0 +1,555 @@
# Docker Provider
Traefik can be configured to use Docker as a provider.
## Docker
```toml
################################################################
# Docker Provider
################################################################
# Enable Docker Provider.
[docker]
# Docker server endpoint. Can be a tcp or a unix socket endpoint.
#
# Required
#
endpoint = "unix:///var/run/docker.sock"
# Default base domain used for the frontend rules.
# Can be overridden by setting the "traefik.domain" label on a container.
#
# Optional
#
domain = "docker.localhost"
# Enable watch docker changes.
#
# Optional
#
watch = true
# Override default configuration template.
# For advanced users :)
#
# Optional
#
# filename = "docker.tmpl"
# Override template version
# For advanced users :)
#
# Optional
# - "1": previous template version (must be used only with older custom templates, see "filename")
# - "2": current template version (must be used to force template version when "filename" is used)
#
# templateVersion = 2
# Expose containers by default in Traefik.
# If set to false, containers that don't have `traefik.enable=true` will be ignored.
#
# Optional
# Default: true
#
exposedByDefault = true
# Use the IP address from the binded port instead of the inner network one.
#
# In case no IP address is attached to the binded port (or in case
# there is no bind), the inner network one will be used as a fallback.
#
# Optional
# Default: false
#
usebindportip = true
# Use Swarm Mode services as data provider.
#
# Optional
# Default: false
#
swarmMode = false
# Polling interval (in seconds) for Swarm Mode.
#
# Optional
# Default: 15
#
swarmModeRefreshSeconds = 15
# Define a default docker network to use for connections to all containers.
# Can be overridden by the traefik.docker.network label.
#
# Optional
#
network = "web"
# Enable docker TLS connection.
#
# Optional
#
# [docker.tls]
# ca = "/etc/ssl/ca.crt"
# cert = "/etc/ssl/docker.crt"
# key = "/etc/ssl/docker.key"
# insecureSkipVerify = true
```
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
## Docker Swarm Mode
```toml
################################################################
# Docker Swarm Mode Provider
################################################################
# Enable Docker Provider.
[docker]
# Docker server endpoint.
# Can be a tcp or a unix socket endpoint.
#
# Required
# Default: "unix:///var/run/docker.sock"
#
# swarm classic (1.12-)
# endpoint = "tcp://127.0.0.1:2375"
# docker swarm mode (1.12+)
endpoint = "tcp://127.0.0.1:2377"
# Default base domain used for the frontend rules.
# Can be overridden by setting the "traefik.domain" label on a services.
#
# Optional
# Default: ""
#
domain = "docker.localhost"
# Enable watch docker changes.
#
# Optional
# Default: true
#
watch = true
# Use Docker Swarm Mode as data provider.
#
# Optional
# Default: false
#
swarmMode = true
# Define a default docker network to use for connections to all containers.
# Can be overridden by the traefik.docker.network label.
#
# Optional
#
network = "web"
# Override default configuration template.
# For advanced users :)
#
# Optional
#
# filename = "docker.tmpl"
# Override template version
# For advanced users :)
#
# Optional
# - "1": previous template version (must be used only with older custom templates, see "filename")
# - "2": current template version (must be used to force template version when "filename" is used)
#
# templateVersion = 2
# Expose services by default in Traefik.
#
# Optional
# Default: true
#
exposedByDefault = false
# Enable docker TLS connection.
#
# Optional
#
# [docker.tls]
# ca = "/etc/ssl/ca.crt"
# cert = "/etc/ssl/docker.crt"
# key = "/etc/ssl/docker.key"
# insecureSkipVerify = true
```
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
## Security Considerations
### Security Challenge with the Docker Socket
Traefik requires access to the docker socket to get its dynamic configuration,
by watching the Docker API through this socket.
!!! important
Depending on your context and your usage, accessing the Docker API without any restriction might be a security concern.
As explained on the Docker documentation: ([Docker Daemon Attack Surface page](https://docs.docker.com/engine/security/#docker-daemon-attack-surface)):
`[...] only **trusted** users should be allowed to control your Docker daemon [...]`
If the Traefik processes (handling requests from the outside world) is attacked,
then the attacker can access the Docker (or Swarm Mode) backend.
Also, when using Swarm Mode, it is mandatory to schedule Traefik's containers on the Swarm manager nodes,
to let Traefik accessing the Docker Socket of the Swarm manager node.
More information about Docker's security:
- [KubeCon EU 2018 Keynote, Running with Scissors, from Liz Rice](https://www.youtube.com/watch?v=ltrV-Qmh3oY)
- [Don't expose the Docker socket (not even to a container)](https://www.lvh.io/posts/dont-expose-the-docker-socket-not-even-to-a-container/)
- [A thread on Stack Overflow about sharing the `/var/run/docker.sock` file](https://news.ycombinator.com/item?id=17983623)
- [To Dind or not to DinD](https://blog.loof.fr/2018/01/to-dind-or-not-do-dind.html)
### Workarounds
!!! note "Improved Security"
[Traefik Enterprise](https://traefik.io/traefik-enterprise/) solves this problem by separating the control plane (connected to Docker) and the data plane (handling the requests).
Another possible workaround is to expose the Docker socket over TCP, instead of the default Unix socket file.
It allows different implementation levels of the [AAA (Authentication, Authorization, Accounting) concepts](https://en.wikipedia.org/wiki/AAA_(computer_security)), depending on your security assessment:
- Authentication with Client Certificates as described in [the "Protect the Docker daemon socket" page of Docker's documentation](https://docs.docker.com/engine/security/https/)
- Authorization with the [Docker Authorization Plugin Mechanism](https://docs.docker.com/engine/extend/plugins_authorization/)
- Accounting at networking level, by exposing the socket only inside a Docker private network, only available for Traefik.
- Accounting at container level, by exposing the socket on a another container than Traefik's.
With Swarm mode, it allows scheduling of Traefik on worker nodes, with only the "socket exposer" container on the manager nodes.
- Accounting at kernel level, by enforcing kernel calls with mechanisms like [SELinux](https://en.wikipedia.org/wiki/Security-Enhanced_Linux),
to only allows an identified set of actions for Traefik's process (or the "socket exposer" process).
Use the following ressources to get started:
- [Traefik issue GH-4174 about security with Docker socket](https://github.com/traefik/traefik/issues/4174)
- [Inspecting Docker Activity with Socat](https://developers.redhat.com/blog/2015/02/25/inspecting-docker-activity-with-socat/)
- [Letting Traefik run on Worker Nodes](https://blog.mikesir87.io/2018/07/letting-traefik-run-on-worker-nodes/)
- [Docker Socket Proxy from Tecnativa](https://github.com/Tecnativa/docker-socket-proxy)
## Labels: overriding default behavior
### Using Docker with Swarm Mode
If you use a compose file with the Swarm mode, labels should be defined in the `deploy` part of your service.
This behavior is only enabled for docker-compose version 3+ ([Compose file reference](https://docs.docker.com/compose/compose-file/compose-file-v3/#labels-2)).
```yaml
version: "3"
services:
whoami:
deploy:
labels:
traefik.docker.network: traefik
```
Required labels:
- `traefik.frontend.rule`
- `traefik.port` - Without this the debug logs will show this service is deliberately filtered out.
- `traefik.docker.network` - Without this a 504 may occur.
#### Troubleshooting
If service doesn't show up in the dashboard, check the debug logs to see if the port is missing:
`Filtering container without port, <SERVICE_NAME>: port label is missing, ...')`
If `504 Gateway Timeout` occurs and there are networks used, ensure that `traefik.docker.network` is defined.
The complete name is required, meaning if the network is internal the name needs to be `<project_name>_<network_name>`.
### Using Docker Compose
If you are intending to use only Docker Compose commands (e.g. `docker-compose up --scale whoami=2 -d`), labels should be under your service, otherwise they will be ignored.
```yaml
version: "3"
services:
whoami:
labels:
traefik.docker.network: traefik
```
### On Containers
Labels can be used on containers to override default behavior.
| Label | Description |
|-------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.docker.network` | Overrides the default docker network to use for connections to the container. [1] |
| `traefik.domain` | Sets the default base domain for the frontend rules. For more information, check the [Container Labels section's of the user guide "Let's Encrypt & Docker"](/user-guide/docker-and-lets-encrypt/#container-labels) |
| `traefik.enable=false` | Disables this container in Traefik. |
| `traefik.port=80` | Registers this port. Useful when the container exposes multiples ports. |
| `traefik.tags=foo,bar,myTag` | Adds Traefik tags to the Docker container/service to be used in [constraints](/configuration/commons/#constraints). |
| `traefik.protocol=https` | Overrides the default `http` protocol |
| `traefik.weight=10` | Assigns this weight to the container |
| `traefik.backend=foo` | Overrides the container name by `foo` in the generated name of the backend. |
| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend |
| `traefik.backend.responseForwarding.flushInterval=10ms` | Defines the interval between two flushes when forwarding response from backend to client. |
| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. |
| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. |
| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. |
| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. |
| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions |
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions |
| `traefik.backend.loadbalancer.stickiness.secure=true` | Sets secure cookie option for sticky sessions. |
| `traefik.backend.loadbalancer.stickiness.httpOnly=true` | Sets http only cookie option for sticky sessions. |
| `traefik.backend.loadbalancer.stickiness.sameSite=none` | Sets same site cookie option for sticky sessions. (`none`, `lax`, `strict`) |
| `traefik.backend.loadbalancer.sticky=true` | Enables backend sticky sessions (DEPRECATED) |
| `traefik.backend.loadbalancer.swarm=true` | Uses Swarm's inbuilt load balancer (only relevant under Swarm Mode) [3]. |
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
| `traefik.frontend.auth.basic=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` [2] (DEPRECATED). |
| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
| `traefik.frontend.auth.basic.users=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` [2]. |
| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets the basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
| `traefik.frontend.auth.digest.users=EXPR` | Sets the digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. |
| `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets the digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
| `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. |
| `traefik.frontend.auth.forward.authResponseHeaders=EXPR` | Sets the forward authentication authResponseHeaders in CSV format: `X-Auth-User,X-Auth-Header` |
| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. |
| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). |
| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. |
| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true` | If set to true invalid SSL certificates are accepted. |
| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. |
| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. |
| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header user to pass the authenticated user to the application. |
| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. |
| `traefik.frontend.passTLSClientCert.infos.issuer.commonName=true` | Add the issuer.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.country=true` | Add the issuer.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.domainComponent=true` | Add the issuer.domainComponent field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.locality=true` | Add the issuer.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.organization=true` | Add the issuer.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.province=true` | Add the issuer.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.serialNumber=true` | Add the issuer.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.notAfter=true` | Add the noAfter field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.notBefore=true` | Add the noBefore field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.sans=true` | Add the sans field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.commonName=true` | Add the subject.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.country=true` | Add the subject.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.domainComponent=true` | Add the subject.domainComponent field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.locality=true` | Add the subject.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.organization=true` | Add the subject.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.province=true` | Add the subject.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.serialNumber=true` | Add the subject.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.pem=true` | Pass the escaped pem in the `X-Forwarded-Ssl-Client-Cert` header. |
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend (DEPRECATED). |
| `traefik.frontend.priority=10` | Overrides default frontend priority |
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) |
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. |
| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{containerName}.{domain}` or `Host:{service}.{project_name}.{domain}` if you are using `docker-compose`. |
| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access.<br>If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. |
[1] `traefik.docker.network`:
If a container is linked to several networks, be sure to set the proper network name (you can check with `docker inspect <container_id>`) otherwise it will randomly pick one (depending on how docker is returning them).
For instance when deploying docker `stack` from compose files, the compose defined networks will be prefixed with the `stack` name.
Or if your service references external network use it's name instead.
[2] `traefik.frontend.auth.basic.users=EXPR`:
To create `user:password` pair, it's possible to use this command:
`echo $(htpasswd -nb user password) | sed -e s/\\$/\\$\\$/g`.
The result will be `user:$$apr1$$9Cv/OMGj$$ZomWQzuQbL.3TRCS81A1g/`, note additional symbol `$` makes escaping.
[3] `traefik.backend.loadbalancer.swarm`:
If you enable this option, Traefik will use the virtual IP provided by docker swarm instead of the containers IPs.
Which means that Traefik will not perform any kind of load balancing and will delegate this task to swarm.
It also means that Traefik will manipulate only one backend, not one backend per container.
#### Custom Headers
| Label | Description |
|-------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.frontend.headers.customRequestHeaders=EXPR` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
#### Security Headers
| Label | Description |
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `traefik.frontend.headers.hostsProxyHeaders=EXPR` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
| `traefik.frontend.headers.publicKey=VALUE` | Adds HPKP header. |
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
| `traefik.frontend.headers.SSLForceHost=true` | If `SSLForceHost` is `true` and `SSLHost` is set, requests will be forced to use `SSLHost` even the ones that are already using SSL. Default is false. |
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-Proto:https`).<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
### On containers with Multiple Ports (segment labels)
Segment labels are used to define routes to a container exposing multiple ports.
A segment is a group of labels that apply to a port exposed by a container.
You can define as many segments as ports exposed in a container.
Segment labels override the default behavior.
| Label | Description |
|----------------------------------------------------------------------------------------|----------------------------------------------------------------------------|
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
| `traefik.<segment_name>.frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` |
| `traefik.<segment_name>.frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` |
| `traefik.<segment_name>.frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` |
| `traefik.<segment_name>.frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` |
| `traefik.<segment_name>.frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` |
| `traefik.<segment_name>.frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` |
| `traefik.<segment_name>.frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` |
| `traefik.<segment_name>.frontend.auth.forward.authResponseHeaders=EXPR` | Same as `traefik.frontend.auth.forward.authResponseHeaders` |
| `traefik.<segment_name>.frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` |
| `traefik.<segment_name>.frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` |
| `traefik.<segment_name>.frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` |
| `traefik.<segment_name>.frontend.auth.forward.tls.insecureSkipVerify=true` | Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` |
| `traefik.<segment_name>.frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` |
| `traefik.<segment_name>.frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` |
| `traefik.<segment_name>.frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` |
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.commonName=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.commonName` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.country=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.country` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.domainComponent=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.domainComponent` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.locality=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.locality` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.organization=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.organization` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.province=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.province` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.serialNumber=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.serialNumber` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notAfter=true` | Same as `traefik.frontend.passTLSClientCert.infos.notAfter` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notBefore=true` | Same as `traefik.frontend.passTLSClientCert.infos.notBefore` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.sans=true` | Same as `traefik.frontend.passTLSClientCert.infos.sans` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.commonName=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.commonName` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.country=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.country` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.domainComponent=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.domainComponent` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.locality=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.locality` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.organization=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.organization` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.province=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.province` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.serialNumber=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.serialNumber` |
| `traefik.<segment_name>.frontend.passTLSClientCert.pem=true` | Same as `traefik.frontend.passTLSClientCert.infos.pem` |
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
#### Custom Headers
| Label | Description |
|----------------------------------------------------------------------|----------------------------------------------------------|
| `traefik.<segment_name>.frontend.headers.customRequestHeaders=EXPR` | Same as `traefik.frontend.headers.customRequestHeaders` |
| `traefik.<segment_name>.frontend.headers.customResponseHeaders=EXPR` | Same as `traefik.frontend.headers.customResponseHeaders` |
#### Security Headers
| Label | Description |
|-------------------------------------------------------------------------|--------------------------------------------------------------|
| `traefik.<segment_name>.frontend.headers.allowedHosts=EXPR` | Same as `traefik.frontend.headers.allowedHosts` |
| `traefik.<segment_name>.frontend.headers.browserXSSFilter=true` | Same as `traefik.frontend.headers.browserXSSFilter` |
| `traefik.<segment_name>.frontend.headers.contentSecurityPolicy=VALUE` | Same as `traefik.frontend.headers.contentSecurityPolicy` |
| `traefik.<segment_name>.frontend.headers.contentTypeNosniff=true` | Same as `traefik.frontend.headers.contentTypeNosniff` |
| `traefik.<segment_name>.frontend.headers.customBrowserXSSValue=VALUE` | Same as `traefik.frontend.headers.customBrowserXSSValue` |
| `traefik.<segment_name>.frontend.headers.customFrameOptionsValue=VALUE` | Same as `traefik.frontend.headers.customFrameOptionsValue` |
| `traefik.<segment_name>.frontend.headers.forceSTSHeader=false` | Same as `traefik.frontend.headers.forceSTSHeader` |
| `traefik.<segment_name>.frontend.headers.frameDeny=false` | Same as `traefik.frontend.headers.frameDeny` |
| `traefik.<segment_name>.frontend.headers.hostsProxyHeaders=EXPR` | Same as `traefik.frontend.headers.hostsProxyHeaders` |
| `traefik.<segment_name>.frontend.headers.isDevelopment=false` | Same as `traefik.frontend.headers.isDevelopment` |
| `traefik.<segment_name>.frontend.headers.publicKey=VALUE` | Same as `traefik.frontend.headers.publicKey` |
| `traefik.<segment_name>.frontend.headers.referrerPolicy=VALUE` | Same as `traefik.frontend.headers.referrerPolicy` |
| `traefik.<segment_name>.frontend.headers.SSLRedirect=true` | Same as `traefik.frontend.headers.SSLRedirect` |
| `traefik.<segment_name>.frontend.headers.SSLTemporaryRedirect=true` | Same as `traefik.frontend.headers.SSLTemporaryRedirect` |
| `traefik.<segment_name>.frontend.headers.SSLHost=HOST` | Same as `traefik.frontend.headers.SSLHost` |
| `traefik.<segment_name>.frontend.headers.SSLForceHost=true` | Same as `traefik.frontend.headers.SSLForceHost` |
| `traefik.<segment_name>.frontend.headers.SSLProxyHeaders=EXPR` | Same as `traefik.frontend.headers.SSLProxyHeaders=EXPR` |
| `traefik.<segment_name>.frontend.headers.STSSeconds=315360000` | Same as `traefik.frontend.headers.STSSeconds=315360000` |
| `traefik.<segment_name>.frontend.headers.STSIncludeSubdomains=true` | Same as `traefik.frontend.headers.STSIncludeSubdomains=true` |
| `traefik.<segment_name>.frontend.headers.STSPreload=true` | Same as `traefik.frontend.headers.STSPreload=true` |
!!! note
If a label is defined both as a `container label` and a `segment label` (for example `traefik.<segment_name>.port=PORT` and `traefik.port=PORT` ), the `segment label` is used to defined the `<segment_name>` property (`port` in the example).
It's possible to mix `container labels` and `segment labels`, in this case `container labels` are used as default value for missing `segment labels` but no frontends are going to be created with the `container labels`.
More details in this [example](/user-guide/docker-and-lets-encrypt/#labels).
!!! warning
When running inside a container, Traefik will need network access through:
`docker network connect <network> <traefik-container>`
## usebindportip
The default behavior of Traefik is to route requests to the IP/Port of the matching container.
When setting `usebindportip` to true, you tell Traefik to use the IP/Port attached to the container's binding instead of the inner network IP/Port.
When used in conjunction with the `traefik.port` label (that tells Traefik to route requests to a specific port), Traefik tries to find a binding with `traefik.port` port to select the container. If it can't find such a binding, Traefik falls back on the internal network IP of the container, but still uses the `traefik.port` that is set in the label.
Below is a recap of the behavior of `usebindportip` in different situations.
| traefik.port label | Container's binding | Routes to |
|--------------------|----------------------------------------------------|----------------|
| - | - | IntIP:IntPort |
| - | ExtPort:IntPort | IntIP:IntPort |
| - | ExtIp:ExtPort:IntPort | ExtIp:ExtPort |
| LblPort | - | IntIp:LblPort |
| LblPort | ExtIp:ExtPort:LblPort | ExtIp:ExtPort |
| LblPort | ExtIp:ExtPort:OtherPort | IntIp:LblPort |
| LblPort | ExtIp1:ExtPort1:IntPort1 & ExtIp2:LblPort:IntPort2 | ExtIp2:LblPort |
!!! note
In the above table, ExtIp stands for "external IP found in the binding", IntIp stands for "internal network container's IP", ExtPort stands for "external Port found in the binding", and IntPort stands for "internal network container's port."

View File

@@ -0,0 +1,70 @@
# DynamoDB Provider
Traefik can be configured to use Amazon DynamoDB as a provider.
## Configuration
```toml
################################################################
# DynamoDB Provider
################################################################
# Enable DynamoDB Provider.
[dynamodb]
# Region to use when connecting to AWS.
#
# Required
#
region = "us-west-1"
# DyanmoDB Table Name.
#
# Optional
# Default: "traefik"
#
tableName = "traefik"
# Enable watch DynamoDB changes.
#
# Optional
# Default: true
#
watch = true
# Polling interval (in seconds).
#
# Optional
# Default: 15
#
refreshSeconds = 15
# Access Key ID to use when connecting to AWS.
#
# Optional
#
accessKeyID = "abc"
# Secret Access Key to use when connecting to AWS.
#
# Optional
#
secretAccessKey = "123"
# Endpoint of local dynamodb instance for testing?
#
# Optional
#
endpoint = "http://localhost:8080"
```
## Table Items
Items in the `dynamodb` table must have three attributes:
- `id` (string): The id is the primary key.
- `name`(string): The name is used as the name of the frontend or backend.
- `frontend` or `backend` (map): This attribute's structure matches exactly the structure of a Frontend or Backend type in Traefik.
See `types/types.go` for details.
The presence or absence of this attribute determines its type.
So an item should never have both a `frontend` and a `backend` attribute.

View File

@@ -0,0 +1,354 @@
# ECS Provider
Traefik can be configured to use Amazon ECS as a provider.
## Configuration
```toml
################################################################
# ECS Provider
################################################################
# Enable ECS Provider.
[ecs]
# ECS Cluster Name.
#
# DEPRECATED - Please use `clusters`.
#
cluster = "default"
# ECS Clusters Name.
#
# Optional
# Default: ["default"]
#
clusters = ["default"]
# Enable watch ECS changes.
#
# Optional
# Default: true
#
watch = true
# Default base domain used for the frontend rules.
# Can be overridden by setting the "traefik.domain" label.
#
# Optional
# Default: ""
#
domain = "ecs.localhost"
# Enable auto discover ECS clusters.
#
# Optional
# Default: false
#
autoDiscoverClusters = false
# Polling interval (in seconds).
#
# Optional
# Default: 15
#
refreshSeconds = 15
# Expose ECS services by default in Traefik.
#
# Optional
# Default: true
#
exposedByDefault = false
# Region to use when connecting to AWS.
#
# Optional
#
region = "us-east-1"
# Access Key ID to use when connecting to AWS.
#
# Optional
#
accessKeyID = "abc"
# Secret Access Key to use when connecting to AWS.
#
# Optional
#
secretAccessKey = "123"
# Override default configuration template.
# For advanced users :)
#
# Optional
#
# filename = "ecs.tmpl"
# Override template version
# For advanced users :)
#
# Optional
# - "1": previous template version (must be used only with older custom templates, see "filename")
# - "2": current template version (must be used to force template version when "filename" is used)
#
# templateVersion = 2
```
If `accessKeyID`/`secretAccessKey` is not given credentials will be resolved in the following order:
- From environment variables; `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, and `AWS_SESSION_TOKEN`.
- Shared credentials, determined by `AWS_PROFILE` and `AWS_SHARED_CREDENTIALS_FILE`, defaults to `default` and `~/.aws/credentials`.
- EC2 instance role or ECS task role
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
## Policy
Traefik needs the following policy to read ECS information:
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "TraefikECSReadAccess",
"Effect": "Allow",
"Action": [
"ecs:ListClusters",
"ecs:DescribeClusters",
"ecs:ListTasks",
"ecs:DescribeTasks",
"ecs:DescribeContainerInstances",
"ecs:DescribeTaskDefinition",
"ec2:DescribeInstances"
],
"Resource": [
"*"
]
}
]
}
```
## Labels: overriding default behaviour
Labels can be used on task containers to override default behaviour:
| Label | Description |
|-------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.domain` | Sets the default base domain for frontend rules. |
| `traefik.enable=false` | Disables this container in Traefik. |
| `traefik.port=80` | Overrides the default `port` value. Overrides `NetworkBindings` from Docker Container |
| `traefik.protocol=https` | Overrides the default `http` protocol |
| `traefik.weight=10` | Assigns this weight to the container |
| `traefik.backend=foo` | Overrides the service name by `foo` in the generated name of the backend. |
| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend |
| `traefik.backend.responseForwarding.flushInterval=10ms` | Defines the interval between two flushes when forwarding response from backend to client. |
| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. |
| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. (Default: 30s) |
| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. |
| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. |
| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions |
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie manually name for sticky sessions |
| `traefik.backend.loadbalancer.stickiness.secure=true` | Sets secure cookie option for sticky sessions. |
| `traefik.backend.loadbalancer.stickiness.httpOnly=true` | Sets http only cookie option for sticky sessions. |
| `traefik.backend.loadbalancer.stickiness.sameSite=none` | Sets same site cookie option for sticky sessions. (`none`, `lax`, `strict`) |
| `traefik.backend.loadbalancer.sticky=true` | Enables backend sticky sessions (DEPRECATED) |
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). |
| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
| `traefik.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. |
| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
| `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. |
| `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
| `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. |
| `traefik.frontend.auth.forward.authResponseHeaders=EXPR` | Sets the forward authentication authResponseHeaders in CSV format: `X-Auth-User,X-Auth-Header` |
| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. |
| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). |
| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. |
| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true` | If set to true invalid SSL certificates are accepted. |
| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. |
| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. |
| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. |
| `traefik.frontend.auth.removeHeader=true` | If set to true, removes the Authorization header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.commonName=true` | Add the issuer.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.country=true` | Add the issuer.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.domainComponent=true` | Add the issuer.domainComponent field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.locality=true` | Add the issuer.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.organization=true` | Add the issuer.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.province=true` | Add the issuer.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.serialNumber=true` | Add the issuer.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.notAfter=true` | Add the noAfter field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.notBefore=true` | Add the noBefore field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.sans=true` | Add the sans field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.commonName=true` | Add the subject.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.country=true` | Add the subject.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.domainComponent=true` | Add the subject.domainComponent field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.locality=true` | Add the subject.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.organization=true` | Add the subject.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.province=true` | Add the subject.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.serialNumber=true` | Add the subject.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.pem=true` | Pass the escaped pem in the `X-Forwarded-Ssl-Client-Cert` header. |
| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. |
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
| `traefik.frontend.priority=10` | Overrides default frontend priority |
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) |
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. |
| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{instance_name}.{domain}`. |
| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. |
### Custom Headers
| Label | Description |
|-------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
### Security Headers
| Label | Description |
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
| `traefik.frontend.headers.publicKey=VALUE` | Adds HPKP header. |
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
| `traefik.frontend.headers.SSLForceHost=true` | If `SSLForceHost` is `true` and `SSLHost` is set, requests will be forced to use `SSLHost` even the ones that are already using SSL. Default is false. |
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
### Containers with Multiple Ports (segment labels)
Segment labels are used to define routes to an application exposing multiple ports.
A segment is a group of labels that apply to a port exposed by an application.
You can define as many segments as ports exposed in an application.
Segment labels override the default behavior.
| Label | Description |
|----------------------------------------------------------------------------------------|----------------------------------------------------------------------------|
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
| `traefik.<segment_name>.frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` |
| `traefik.<segment_name>.frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` |
| `traefik.<segment_name>.frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` |
| `traefik.<segment_name>.frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` |
| `traefik.<segment_name>.frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` |
| `traefik.<segment_name>.frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` |
| `traefik.<segment_name>.frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` |
| `traefik.<segment_name>.frontend.auth.forward.authResponseHeaders=EXPR` | Same as `traefik.frontend.auth.forward.authResponseHeaders` |
| `traefik.<segment_name>.frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` |
| `traefik.<segment_name>.frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` |
| `traefik.<segment_name>.frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` |
| `traefik.<segment_name>.frontend.auth.forward.tls.insecureSkipVerify=true` | Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` |
| `traefik.<segment_name>.frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` |
| `traefik.<segment_name>.frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` |
| `traefik.<segment_name>.frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` |
| `traefik.<segment_name>.frontend.auth.removeHeader=true` | Same as `traefik.frontend.auth.removeHeader` |
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.commonName=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.commonName` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.country=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.country` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.domainComponent=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.domainComponent` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.locality=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.locality` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.organization=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.organization` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.province=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.province` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.serialNumber=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.serialNumber` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notAfter=true` | Same as `traefik.frontend.passTLSClientCert.infos.notAfter` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notBefore=true` | Same as `traefik.frontend.passTLSClientCert.infos.notBefore` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.sans=true` | Same as `traefik.frontend.passTLSClientCert.infos.sans` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.commonName=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.commonName` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.country=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.country` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.domainComponent=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.domainComponent` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.locality=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.locality` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.organization=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.organization` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.province=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.province` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.serialNumber=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.serialNumber` |
| `traefik.<segment_name>.frontend.passTLSClientCert.pem=true` | Same as `traefik.frontend.passTLSClientCert.infos.pem` |
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
#### Custom Headers
| Label | Description |
|----------------------------------------------------------------------|----------------------------------------------------------|
| `traefik.<segment_name>.frontend.headers.customRequestHeaders=EXPR ` | Same as `traefik.frontend.headers.customRequestHeaders` |
| `traefik.<segment_name>.frontend.headers.customResponseHeaders=EXPR` | Same as `traefik.frontend.headers.customResponseHeaders` |
#### Security Headers
| Label | Description |
|-------------------------------------------------------------------------|--------------------------------------------------------------|
| `traefik.<segment_name>.frontend.headers.allowedHosts=EXPR` | Same as `traefik.frontend.headers.allowedHosts` |
| `traefik.<segment_name>.frontend.headers.browserXSSFilter=true` | Same as `traefik.frontend.headers.browserXSSFilter` |
| `traefik.<segment_name>.frontend.headers.contentSecurityPolicy=VALUE` | Same as `traefik.frontend.headers.contentSecurityPolicy` |
| `traefik.<segment_name>.frontend.headers.contentTypeNosniff=true` | Same as `traefik.frontend.headers.contentTypeNosniff` |
| `traefik.<segment_name>.frontend.headers.customBrowserXSSValue=VALUE` | Same as `traefik.frontend.headers.customBrowserXSSValue` |
| `traefik.<segment_name>.frontend.headers.customFrameOptionsValue=VALUE` | Same as `traefik.frontend.headers.customFrameOptionsValue` |
| `traefik.<segment_name>.frontend.headers.forceSTSHeader=false` | Same as `traefik.frontend.headers.forceSTSHeader` |
| `traefik.<segment_name>.frontend.headers.frameDeny=false` | Same as `traefik.frontend.headers.frameDeny` |
| `traefik.<segment_name>.frontend.headers.hostsProxyHeaders=EXPR` | Same as `traefik.frontend.headers.hostsProxyHeaders` |
| `traefik.<segment_name>.frontend.headers.isDevelopment=false` | Same as `traefik.frontend.headers.isDevelopment` |
| `traefik.<segment_name>.frontend.headers.publicKey=VALUE` | Same as `traefik.frontend.headers.publicKey` |
| `traefik.<segment_name>.frontend.headers.referrerPolicy=VALUE` | Same as `traefik.frontend.headers.referrerPolicy` |
| `traefik.<segment_name>.frontend.headers.SSLRedirect=true` | Same as `traefik.frontend.headers.SSLRedirect` |
| `traefik.<segment_name>.frontend.headers.SSLTemporaryRedirect=true` | Same as `traefik.frontend.headers.SSLTemporaryRedirect` |
| `traefik.<segment_name>.frontend.headers.SSLHost=HOST` | Same as `traefik.frontend.headers.SSLHost` |
| `traefik.<segment_name>.frontend.headers.SSLForceHost=true` | Same as `traefik.frontend.headers.SSLForceHost` |
| `traefik.<segment_name>.frontend.headers.SSLProxyHeaders=EXPR` | Same as `traefik.frontend.headers.SSLProxyHeaders=EXPR` |
| `traefik.<segment_name>.frontend.headers.STSSeconds=315360000` | Same as `traefik.frontend.headers.STSSeconds=315360000` |
| `traefik.<segment_name>.frontend.headers.STSIncludeSubdomains=true` | Same as `traefik.frontend.headers.STSIncludeSubdomains=true` |
| `traefik.<segment_name>.frontend.headers.STSPreload=true` | Same as `traefik.frontend.headers.STSPreload=true` |

View File

@@ -0,0 +1,75 @@
# Etcd Provider
Traefik can be configured to use Etcd as a provider.
```toml
################################################################
# Etcd Provider
################################################################
# Enable Etcd Provider.
[etcd]
# Etcd server endpoint.
#
# Required
# Default: "127.0.0.1:2379"
#
endpoint = "127.0.0.1:2379"
# Enable watch Etcd changes.
#
# Optional
# Default: true
#
watch = true
# Prefix used for KV store.
#
# Optional
# Default: "/traefik"
#
prefix = "/traefik"
# Force to use API V3 (otherwise still use API V2)
#
# Deprecated
#
# Optional
# Default: false
#
useAPIV3 = true
# Override default configuration template.
# For advanced users :)
#
# Optional
#
# filename = "etcd.tmpl"
# Use etcd user/pass authentication.
#
# Optional
#
# username = foo
# password = bar
# Enable etcd TLS connection.
#
# Optional
#
# [etcd.tls]
# ca = "/etc/ssl/ca.crt"
# cert = "/etc/ssl/etcd.crt"
# key = "/etc/ssl/etcd.key"
# insecureSkipVerify = true
```
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
Please refer to the [Key Value storage structure](/user-guide/kv-config/#key-value-storage-structure) section to get documentation on Traefik KV structure.
!!! note
The option `useAPIV3` allows using Etcd API V3 only if it's set to true.
This option is **deprecated** and API V2 won't be supported in the future.

View File

@@ -0,0 +1,32 @@
# Eureka Provider
Traefik can be configured to use Eureka as a provider.
```toml
################################################################
# Eureka Provider
################################################################
# Enable Eureka Provider.
[eureka]
# Eureka server endpoint.
#
# Required
#
endpoint = "http://my.eureka.server/eureka"
# Override default configuration time between refresh.
#
# Optional
# Default: 30s
#
refreshSeconds = "1m"
# Override default configuration template.
# For advanced users :)
#
# Optional
#
# filename = "eureka.tmpl"
```

View File

@@ -0,0 +1,362 @@
# File Provider
Traefik can be configured with a file.
## Reference
```toml
[file]
# Backends
[backends]
[backends.backend1]
[backends.backend1.servers]
[backends.backend1.servers.server0]
url = "http://10.10.10.1:80"
weight = 1
[backends.backend1.servers.server1]
url = "http://10.10.10.2:80"
weight = 2
# ...
[backends.backend1.circuitBreaker]
expression = "NetworkErrorRatio() > 0.5"
[backends.backend1.responseForwarding]
flushInterval = "10ms"
[backends.backend1.loadBalancer]
method = "drr"
[backends.backend1.loadBalancer.stickiness]
cookieName = "foobar"
secure = true
httpOnly = true
sameSite = "foobar"
[backends.backend1.maxConn]
amount = 10
extractorfunc = "request.host"
[backends.backend1.healthCheck]
path = "/health"
port = 88
interval = "30s"
scheme = "http"
hostname = "myhost.com"
[backends.backend1.healthcheck.headers]
My-Custom-Header = "foo"
My-Header = "bar"
[backends.backend2]
# ...
# Frontends
[frontends]
[frontends.frontend1]
entryPoints = ["http", "https"]
backend = "backend1"
passHostHeader = true
priority = 42
# Use frontends.frontend1.auth.basic below instead
basicAuth = [
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
]
[frontends.frontend1.passTLSClientCert]
pem = true
[frontends.frontend1.passTLSClientCert.infos]
notBefore = true
notAfter = true
[frontends.frontend1.passTLSClientCert.infos.subject]
country = true
domainComponent = true
province = true
locality = true
organization = true
commonName = true
serialNumber = true
[frontends.frontend1.passTLSClientCert.infos.issuer]
country = true
domainComponent = true
province = true
locality = true
organization = true
commonName = true
serialNumber = true
[frontends.frontend1.auth]
headerField = "X-WebAuth-User"
[frontends.frontend1.auth.basic]
removeHeader = true
users = [
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
]
usersFile = "/path/to/.htpasswd"
[frontends.frontend1.auth.digest]
removeHeader = true
users = [
"test:traefik:a2688e031edb4be6a3797f3882655c05",
"test2:traefik:518845800f9e2bfb1f1f740ec24f074e",
]
usersFile = "/path/to/.htdigest"
[frontends.frontend1.auth.forward]
address = "https://authserver.com/auth"
trustForwardHeader = true
authResponseHeaders = ["X-Auth-User"]
[frontends.frontend1.auth.forward.tls]
ca = "path/to/local.crt"
caOptional = true
cert = "path/to/foo.cert"
key = "path/to/foo.key"
insecureSkipVerify = true
[frontends.frontend1.whiteList]
sourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
useXForwardedFor = true
[frontends.frontend1.routes]
[frontends.frontend1.routes.route0]
rule = "Host:test.localhost"
[frontends.frontend1.routes.Route1]
rule = "Method:GET"
# ...
[frontends.frontend1.headers]
allowedHosts = ["foobar", "foobar"]
hostsProxyHeaders = ["foobar", "foobar"]
SSLRedirect = true
SSLTemporaryRedirect = true
SSLHost = "foobar"
STSSeconds = 42
STSIncludeSubdomains = true
STSPreload = true
forceSTSHeader = true
frameDeny = true
customFrameOptionsValue = "foobar"
contentTypeNosniff = true
browserXSSFilter = true
contentSecurityPolicy = "foobar"
publicKey = "foobar"
referrerPolicy = "foobar"
isDevelopment = true
[frontends.frontend1.headers.customRequestHeaders]
X-Foo-Bar-01 = "foobar"
X-Foo-Bar-02 = "foobar"
# ...
[frontends.frontend1.headers.customResponseHeaders]
X-Foo-Bar-03 = "foobar"
X-Foo-Bar-04 = "foobar"
# ...
[frontends.frontend1.headers.SSLProxyHeaders]
X-Foo-Bar-05 = "foobar"
X-Foo-Bar-06 = "foobar"
# ...
[frontends.frontend1.errors]
[frontends.frontend1.errors.errorPage0]
status = ["500-599"]
backend = "error"
query = "/{status}.html"
[frontends.frontend1.errors.errorPage1]
status = ["404", "403"]
backend = "error"
query = "/{status}.html"
# ...
[frontends.frontend1.ratelimit]
extractorfunc = "client.ip"
[frontends.frontend1.ratelimit.rateset.rateset1]
period = "10s"
average = 100
burst = 200
[frontends.frontend1.ratelimit.rateset.rateset2]
period = "3s"
average = 5
burst = 10
# ...
[frontends.frontend1.redirect]
entryPoint = "https"
regex = "^http://localhost/(.*)"
replacement = "http://mydomain/$1"
permanent = true
[frontends.frontend2]
# ...
# HTTPS certificates
[[tls]]
entryPoints = ["https"]
[tls.certificate]
certFile = "path/to/my.cert"
keyFile = "path/to/my.key"
[[tls]]
# ...
```
## Configuration Mode
You have two choices:
- [Rules in Traefik configuration file](/configuration/backends/file/#rules-in-traefik-configuration-file)
- [Rules in dedicated files](/configuration/backends/file/#rules-in-dedicated-files)
To enable the file backend, you must either pass the `--file` option to the Traefik binary or put the `[file]` section (with or without inner settings) in the configuration file.
The configuration file allows managing both backends/frontends and HTTPS certificates (which are not [Let's Encrypt](https://letsencrypt.org) certificates generated through Traefik).
TOML templating can be used if rules are not defined in the Traefik configuration file.
### Rules in Traefik Configuration File
Add your configuration at the end of the global configuration file `traefik.toml`:
```toml
defaultEntryPoints = ["http", "https"]
[entryPoints]
[entryPoints.http]
# ...
[entryPoints.https]
# ...
[file]
# rules
[backends]
[backends.backend1]
# ...
[backends.backend2]
# ...
[frontends]
[frontends.frontend1]
# ...
[frontends.frontend2]
# ...
[frontends.frontend3]
# ...
# HTTPS certificate
[[tls]]
# ...
[[tls]]
# ...
```
!!! note
If `tls.entryPoints` is not defined, the certificate is attached to all the `defaultEntryPoints` with a TLS configuration.
!!! note
Adding certificates directly to the entryPoint is still maintained but certificates declared in this way cannot be managed dynamically.
It's recommended to use the file provider to declare certificates.
!!! warning
TOML templating cannot be used if rules are defined in the Traefik configuration file.
### Rules in Dedicated Files
Traefik allows defining rules in one or more separate files.
#### One Separate File
You have to specify the file path in the `file.filename` option.
```toml
# traefik.toml
defaultEntryPoints = ["http", "https"]
[entryPoints]
[entryPoints.http]
# ...
[entryPoints.https]
# ...
[file]
filename = "rules.toml"
watch = true
```
The option `file.watch` allows Traefik to watch file changes automatically.
#### Multiple Separated Files
You could have multiple `.toml` files in a directory (and recursively in its sub-directories):
```toml
[file]
directory = "/path/to/config/"
watch = true
```
The option `file.watch` allows Traefik to watch file changes automatically.
#### Separate Files Content
If you are defining rules in one or more separate files, you can use two formats.
##### Simple Format
Backends, Frontends and TLS certificates are defined one at time, as described in the file `rules.toml`:
```toml
# rules.toml
[backends]
[backends.backend1]
# ...
[backends.backend2]
# ...
[frontends]
[frontends.frontend1]
# ...
[frontends.frontend2]
# ...
[frontends.frontend3]
# ...
# HTTPS certificate
[[tls]]
# ...
[[tls]]
# ...
```
##### TOML Templating
!!! warning
TOML templating can only be used **if rules are defined in one or more separate files**.
Templating will not work in the Traefik configuration file.
Traefik allows using TOML templating.
Thus, it's possible to define easily lot of Backends, Frontends and TLS certificates as described in the file `template-rules.toml` :
```toml
# template-rules.toml
[backends]
{{ range $i, $e := until 100 }}
[backends.backend{{ $e }}]
#...
{{ end }}
[frontends]
{{ range $i, $e := until 100 }}
[frontends.frontend{{ $e }}]
#...
{{ end }}
# HTTPS certificate
{{ range $i, $e := until 100 }}
[[tls]]
#...
{{ end }}
```

View File

@@ -0,0 +1,399 @@
# Kubernetes Ingress Provider
Traefik can be configured to use Kubernetes Ingress as a provider.
See also [Kubernetes user guide](/user-guide/kubernetes).
## Configuration
```toml
################################################################
# Kubernetes Ingress Provider
################################################################
# Enable Kubernetes Ingress Provider.
[kubernetes]
# Kubernetes server endpoint.
#
# Optional for in-cluster configuration, required otherwise.
# Default: empty
#
# endpoint = "http://localhost:8080"
# Bearer token used for the Kubernetes client configuration.
#
# Optional
# Default: empty
#
# token = "my token"
# Path to the certificate authority file.
# Used for the Kubernetes client configuration.
#
# Optional
# Default: empty
#
# certAuthFilePath = "/my/ca.crt"
# Array of namespaces to watch.
#
# Optional
# Default: all namespaces (empty array).
#
# namespaces = ["default", "production"]
# Ingress label selector to filter Ingress objects that should be processed.
#
# Optional
# Default: empty (process all Ingresses)
#
# labelselector = "A and not B"
# Value of `kubernetes.io/ingress.class` annotation that identifies Ingress objects to be processed.
# If the parameter is non-empty, only Ingresses containing an annotation with the same value are processed.
# Otherwise, Ingresses missing the annotation, having an empty value, or the value `traefik` are processed.
#
# Optional
# Default: empty
#
# ingressClass = "traefik-internal"
# Disable PassHost Headers.
#
# Optional
# Default: false
#
# disablePassHostHeaders = true
# Enable PassTLSCert Headers.
#
# Optional
# Default: false
#
# enablePassTLSCert = true
# Throttle how frequently we refresh our configuration from Ingresses when there
# are frequent changes.
#
# Optional
# Default: 0 (no throttling)
#
# throttleDuration = 10s
# Override default configuration template.
#
# Optional
# Default: <built-in template>
#
# filename = "kubernetes.tmpl"
# Enable IngressEndpoint configuration.
# This will allow Traefik to update the status section of ingress objects, if desired.
#
# Optional
#
# [kubernetes.ingressEndpoint]
#
# At least one must be configured.
# `publishedservice` will override the `hostname` and `ip` settings if configured.
#
# hostname = "localhost"
# ip = "127.0.0.1"
# publishedService = "namespace/servicename"
```
### `endpoint`
The Kubernetes server endpoint as URL.
When deployed into Kubernetes, Traefik will read the environment variables `KUBERNETES_SERVICE_HOST` and `KUBERNETES_SERVICE_PORT` to construct the endpoint.
The access token will be looked up in `/var/run/secrets/kubernetes.io/serviceaccount/token` and the SSL CA certificate in `/var/run/secrets/kubernetes.io/serviceaccount/ca.crt`.
Both are provided mounted automatically when deployed inside Kubernetes.
The endpoint may be specified to override the environment variable values inside a cluster.
When the environment variables are not found, Traefik will try to connect to the Kubernetes API server with an external-cluster client.
In this case, the endpoint is required.
Specifically, it may be set to the URL used by `kubectl proxy` to connect to a Kubernetes cluster using the granted authentication and authorization of the associated kubeconfig.
### `labelselector`
By default, Traefik processes all Ingress objects in the configured namespaces.
A label selector can be defined to filter on specific Ingress objects only.
See [label-selectors](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors) for details.
### `ingressEndpoint`
You can configure a static hostname or IP address that Traefik will add to the status section of Ingress objects that it manages.
If you prefer, you can provide a service, which traefik will copy the status spec from.
This will give more flexibility in cloud/dynamic environments.
### TLS communication between Traefik and backend pods
Traefik automatically requests endpoint information based on the service provided in the ingress spec.
Although traefik will connect directly to the endpoints (pods), it still checks the service port to see if TLS communication is required.
There are 3 ways to configure Traefik to use https to communicate with backend pods:
1. If the service port defined in the ingress spec is 443 (note that you can still use `targetPort` to use a different port on your pod).
2. If the service port defined in the ingress spec has a name that starts with `https` (such as `https-api`, `https-web` or just `https`).
3. If the ingress spec includes the annotation `ingress.kubernetes.io/protocol: https`.
If either of those configuration options exist, then the backend communication protocol is assumed to be TLS, and will connect via TLS automatically.
!!! note
Please note that by enabling TLS communication between traefik and your pods, you will have to have trusted certificates that have the proper trust chain and IP subject name.
If this is not an option, you may need to skip TLS certificate verification.
See the [insecureSkipVerify](/configuration/commons/#main-section) setting for more details.
## Annotations
### General annotations
The following general annotations are applicable on the Ingress object:
| Annotation | Description |
|---------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.ingress.kubernetes.io/app-root: "/index.html"` | Redirects all requests for `/` to the defined path. (1) |
| `traefik.ingress.kubernetes.io/error-pages: <YML>` | See [custom error pages](/configuration/commons/#custom-error-pages) section. (2) |
| `traefik.ingress.kubernetes.io/frontend-entry-points: http,https` | Override the default frontend endpoints. |
| `traefik.ingress.kubernetes.io/pass-client-tls-cert: <YML>` | Forward the client certificate following the configuration in YAML. (3) |
| `traefik.ingress.kubernetes.io/pass-tls-cert: "true"` | Override the default frontend PassTLSCert value. Default: `false`.(DEPRECATED) |
| `traefik.ingress.kubernetes.io/preserve-host: "true"` | Forward client `Host` header to the backend. |
| `traefik.ingress.kubernetes.io/priority: "3"` | Override the default frontend rule priority. |
| `traefik.ingress.kubernetes.io/rate-limit: <YML>` | See [rate limiting](/configuration/commons/#rate-limiting) section. (4) |
| `traefik.ingress.kubernetes.io/redirect-entry-point: https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS). |
| `traefik.ingress.kubernetes.io/redirect-permanent: "true"` | Return 301 instead of 302. |
| `traefik.ingress.kubernetes.io/redirect-regex: ^http://localhost/(.*)` | Redirect to another URL for that frontend. Must be set with `traefik.ingress.kubernetes.io/redirect-replacement`. |
| `traefik.ingress.kubernetes.io/redirect-replacement: http://mydomain/$1` | Redirect to another URL for that frontend. Must be set with `traefik.ingress.kubernetes.io/redirect-regex`. |
| `traefik.ingress.kubernetes.io/request-modifier: AddPrefix: /users` | Adds a [request modifier](/basics/#modifiers) to the backend request. |
| `traefik.ingress.kubernetes.io/rewrite-target: /users` | Replaces each matched Ingress path with the specified one, and adds the old path to the `X-Replaced-Path` header. |
| `traefik.ingress.kubernetes.io/rule-type: PathPrefixStrip` | Overrides the default frontend rule type. Only path-related matchers can be specified [(`Path`, `PathPrefix`, `PathStrip`, `PathPrefixStrip`)](/basics/#path-matcher-usage-guidelines).(5) |
| `traefik.ingress.kubernetes.io/service-weights: <YML>` | Set ingress backend weights specified as percentage or decimal numbers in YAML. (6) |
| `traefik.ingress.kubernetes.io/whitelist-source-range: "1.2.3.0/24, fe80::/16"` | A comma-separated list of IP ranges permitted for access (7). |
| `ingress.kubernetes.io/whitelist-x-forwarded-for: "true"` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
| `ingress.kubernetes.io/protocol:<NAME>` | Set the protocol Traefik will use to communicate with pods. Acceptable protocols: http,https,h2c |
<1> `traefik.ingress.kubernetes.io/app-root`:
Non-root paths will not be affected by this annotation and handled normally.
This annotation may not be combined with other redirect annotations.
Trying to do so will result in the other redirects being ignored.
This annotation can be used in combination with `traefik.ingress.kubernetes.io/redirect-permanent` to configure whether the `app-root` redirect is a 301 or a 302.
<2> `traefik.ingress.kubernetes.io/error-pages` example:
```yaml
foo:
status:
- "404"
backend: bar
query: /bar
fii:
status:
- "503"
- "500"
backend: bar
query: /bir
```
<3> `traefik.ingress.kubernetes.io/pass-client-tls-cert` example:
```yaml
# add escaped pem in the `X-Forwarded-Tls-Client-Cert` header
pem: true
# add escaped certificate following infos in the `X-Forwarded-Tls-Client-Cert-Infos` header
infos:
notafter: true
notbefore: true
sans: true
subject:
country: true
province: true
locality: true
organization: true
commonname: true
serialnumber: true
```
If `pem` is set, it will add a `X-Forwarded-Tls-Client-Cert` header that contains the escaped pem as value.
If at least one flag of the `infos` part is set, it will add a `X-Forwarded-Tls-Client-Cert-Infos` header that contains an escaped string composed of the client certificate data selected by the infos flags.
This infos part is composed like the following example (not escaped):
```
Subject="C=FR,ST=SomeState,L=Lyon,O=Cheese,CN=*.cheese.org",NB=1531900816,NA=1563436816,SAN=*.cheese.org,*.cheese.net,cheese.in,test@cheese.org,test@cheese.net,10.0.1.0,10.0.1.2
```
Note these options work only with certificates issued by CAs included in the applicable [EntryPoint ClientCA section](/configuration/entrypoints/#tls-mutual-authentication); certificates from other CAs are not parsed or passed through as-is.
<4> `traefik.ingress.kubernetes.io/rate-limit` example:
```yaml
extractorfunc: client.ip
rateset:
bar:
period: 3s
average: 6
burst: 9
foo:
period: 6s
average: 12
burst: 18
```
<5> `traefik.ingress.kubernetes.io/rule-type`
Note: `ReplacePath` is deprecated in this annotation, use the `traefik.ingress.kubernetes.io/request-modifier` annotation instead. Default: `PathPrefix`.
<6> `traefik.ingress.kubernetes.io/service-weights`:
Service weights enable to split traffic across multiple backing services in a fine-grained manner.
Example:
```yaml
service_backend1: 12.50%
service_backend2: 12.50%
service_backend3: 75 # Same as 75%, the percentage sign is optional
```
A single service backend definition may be omitted; in this case, Traefik auto-completes that service backend to 100% automatically.
Conveniently, users need not bother to compute the percentage remainder for a main service backend.
For instance, in the example above `service_backend3` does not need to be specified to be assigned 75%.
!!! note
For each service weight given, the Ingress specification must include a backend item with the corresponding `serviceName` and (if given) matching path.
Currently, 3 decimal places for the weight are supported.
An attempt to exceed the precision should be avoided as it may lead to percentage computation flaws and, in consequence, Ingress parsing errors.
For each path definition, this annotation will fail if:
- the sum of backend weights exceeds 100% or
- the sum of backend weights is less than 100% without one or more omitted backends
See also the [user guide section traffic splitting](/user-guide/kubernetes/#traffic-splitting).
<7> `traefik.ingress.kubernetes.io/whitelist-source-range`:
All source IPs are permitted if the list is empty or a single range is ill-formatted.
Please note, you may have to set `service.spec.externalTrafficPolicy` to the value `Local` to preserve the source IP of the request for filtering.
Please see [this link](https://kubernetes.io/docs/tutorials/services/source-ip/) for more information.
!!! note
Please note that `traefik.ingress.kubernetes.io/redirect-regex` and `traefik.ingress.kubernetes.io/redirect-replacement` do not have to be set if `traefik.ingress.kubernetes.io/redirect-entry-point` is defined for the redirection (they will not be used in this case).
The following annotations are applicable on the Service object associated with a particular Ingress object:
| Annotation | Description |
|--------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.ingress.kubernetes.io/buffering: <YML>` | (1) See the [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.loadbalancer.sticky: "true"` | Enable backend sticky sessions (DEPRECATED). |
| `traefik.ingress.kubernetes.io/affinity: "true"` | Enable backend sticky sessions. |
| `traefik.ingress.kubernetes.io/circuit-breaker-expression: <expression>` | Set the circuit breaker expression for the backend. |
| `traefik.ingress.kubernetes.io/responseforwarding-flushinterval: "10ms` | Defines the interval between two flushes when forwarding response from backend to client. |
| `traefik.ingress.kubernetes.io/load-balancer-method: drr` | Override the default `wrr` load balancer algorithm. |
| `traefik.ingress.kubernetes.io/max-conn-amount: "10"` | Sets the maximum number of simultaneous connections to the backend.<br>Must be used in conjunction with the label below to take effect. |
| `traefik.ingress.kubernetes.io/max-conn-extractor-func: client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
| `traefik.ingress.kubernetes.io/session-cookie-name: <NAME>` | Manually set the cookie name for sticky sessions. |
<1> `traefik.ingress.kubernetes.io/buffering` example:
```yaml
maxrequestbodybytes: 10485760
memrequestbodybytes: 2097153
maxresponsebodybytes: 10485761
memresponsebodybytes: 2097152
retryexpression: IsNetworkError() && Attempts() <= 2
```
!!! note
`traefik.ingress.kubernetes.io/` and `ingress.kubernetes.io/` are supported prefixes.
### Custom Headers Annotations
| Annotation | Description |
| ------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `ingress.kubernetes.io/custom-request-headers: EXPR` | Provides the container with custom request headers that will be appended to each request forwarded to the container. Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `ingress.kubernetes.io/custom-response-headers: EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client. Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
### Security Headers Annotations
The following security annotations are applicable on the Ingress object:
| Annotation | Description |
| ----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `ingress.kubernetes.io/allowed-hosts: EXPR` | Provides a list of allowed hosts that requests will be processed. Format: `Host1,Host2` |
| `ingress.kubernetes.io/browser-xss-filter: "true"` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `ingress.kubernetes.io/content-security-policy: VALUE` | Adds CSP Header with the custom value. |
| `ingress.kubernetes.io/content-type-nosniff: "true"` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `ingress.kubernetes.io/custom-browser-xss-value: VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `ingress.kubernetes.io/custom-frame-options-value: VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `ingress.kubernetes.io/force-hsts: "false"` | Adds the STS header to non-SSL requests. |
| `ingress.kubernetes.io/frame-deny: "true"` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `ingress.kubernetes.io/hsts-max-age: "315360000"` | Sets the max-age of the HSTS header. |
| `ingress.kubernetes.io/hsts-include-subdomains: "true"` | Adds the IncludeSubdomains section of the STS header. |
| `ingress.kubernetes.io/hsts-preload: "true"` | Adds the preload flag to the HSTS header. |
| `ingress.kubernetes.io/is-development: "false"` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
| `ingress.kubernetes.io/proxy-headers: EXPR` | Provides a list of headers that the proxied hostname may be stored. Format: `HEADER1,HEADER2` |
| `ingress.kubernetes.io/public-key: VALUE` | Adds HPKP header. |
| `ingress.kubernetes.io/referrer-policy: VALUE` | Adds referrer policy header. |
| `ingress.kubernetes.io/ssl-redirect: "true"` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
| `ingress.kubernetes.io/ssl-temporary-redirect: "true"` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
| `ingress.kubernetes.io/ssl-host: HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
| `ingress.kubernetes.io/ssl-force-host: "true"` | If `SSLForceHost` is `true` and `SSLHost` is set, requests will be forced to use `SSLHost` even the ones that are already using SSL. Default is false. |
| `ingress.kubernetes.io/ssl-proxy-headers: EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-Proto:https`). Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
### Authentication
Additional authentication annotations can be added to the Ingress object.
The source of the authentication is a Secret object that contains the credentials.
| Annotation | basic | digest | forward | Description |
|----------------------------------------------------------------------|-------|--------|---------|-------------------------------------------------------------------------------------------------------------|
| `ingress.kubernetes.io/auth-type: basic` | x | x | x | Contains the authentication type: `basic`, `digest`, `forward`. |
| `ingress.kubernetes.io/auth-secret: mysecret` | x | x | | Name of Secret containing the username and password with access to the paths defined in the Ingress object. |
| `ingress.kubernetes.io/auth-remove-header: true` | x | x | | If set to `true` removes the `Authorization` header. |
| `ingress.kubernetes.io/auth-header-field: X-WebAuth-User` | x | x | | Pass Authenticated user to application via headers. |
| `ingress.kubernetes.io/auth-url: https://example.com` | | | x | [The URL of the authentication server](/configuration/entrypoints/#forward-authentication). |
| `ingress.kubernetes.io/auth-trust-headers: false` | | | x | Trust `X-Forwarded-*` headers. |
| `ingress.kubernetes.io/auth-response-headers: X-Auth-User, X-Secret` | | | x | Copy headers from the authentication server to the request. |
| `ingress.kubernetes.io/auth-tls-secret: secret` | | | x | Name of Secret containing the certificate and key for the forward auth. |
| `ingress.kubernetes.io/auth-tls-insecure` | | | x | If set to `true` invalid SSL certificates are accepted. |
The secret must be created in the same namespace as the Ingress object.
The following limitations hold for basic/digest auth:
- The realm is not configurable; the only supported (and default) value is `traefik`.
- The Secret must contain a single file only.
### TLS certificates management
TLS certificates can be managed in Secrets objects.
More information are available in the [User Guide](/user-guide/kubernetes/#add-a-tls-certificate-to-the-ingress).
!!! note
Only TLS certificates provided by users can be stored in Kubernetes Secrets.
[Let's Encrypt](https://letsencrypt.org) certificates cannot be managed in Kubernets Secrets yet.
### Global Default Backend Ingresses
Ingresses can be created that look like the following:
```yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: cheese
spec:
backend:
serviceName: stilton
servicePort: 80
```
This ingress follows the [Global Default Backend](https://kubernetes.io/docs/concepts/services-networking/ingress/#the-ingress-resource) property of ingresses.
This will allow users to create a "default backend" that will match all unmatched requests.
!!! note
Due to Traefik's use of priorities, you may have to set this ingress priority lower than other ingresses in your environment, to avoid this global ingress from satisfying requests that _could_ match other ingresses.
To do this, use the `traefik.ingress.kubernetes.io/priority` annotation (as seen in [General Annotations](/configuration/backends/kubernetes/#general-annotations)) on your ingresses accordingly.

View File

@@ -0,0 +1,414 @@
# Marathon Provider
Traefik can be configured to use Marathon as a provider.
See also [Marathon user guide](/user-guide/marathon).
## Configuration
```toml
################################################################
# Mesos/Marathon Provider
################################################################
# Enable Marathon Provider.
[marathon]
# Marathon server endpoint.
# You can also specify multiple endpoint for Marathon:
# endpoint = "http://10.241.1.71:8080,10.241.1.72:8080,10.241.1.73:8080"
#
# Required
# Default: "http://127.0.0.1:8080"
#
endpoint = "http://127.0.0.1:8080"
# Enable watch Marathon changes.
#
# Optional
# Default: true
#
watch = true
# Default base domain used for the frontend rules.
# Can be overridden by setting the "traefik.domain" label on an application.
#
# Required
#
domain = "marathon.localhost"
# Override default configuration template.
# For advanced users :)
#
# Optional
#
# filename = "marathon.tmpl"
# Override template version
# For advanced users :)
#
# Optional
# - "1": previous template version (must be used only with older custom templates, see "filename")
# - "2": current template version (must be used to force template version when "filename" is used)
#
# templateVersion = 2
# Expose Marathon apps by default in Traefik.
#
# Optional
# Default: true
#
# exposedByDefault = false
# Convert Marathon groups to subdomains.
# Default behavior: /foo/bar/myapp => foo-bar-myapp.{defaultDomain}
# with groupsAsSubDomains enabled: /foo/bar/myapp => myapp.bar.foo.{defaultDomain}
#
# Optional
# Default: false
#
# groupsAsSubDomains = true
# Enable compatibility with marathon-lb labels.
#
# Optional
# Default: false
#
# marathonLBCompatibility = true
# Enable filtering using Marathon constraints..
# If enabled, Traefik will read Marathon constraints, as defined in https://mesosphere.github.io/marathon/docs/constraints.html
# Each individual constraint will be treated as a verbatim compounded tag.
# i.e. "rack_id:CLUSTER:rack-1", with all constraint groups concatenated together using ":"
#
# Optional
# Default: false
#
# filterMarathonConstraints = true
# Enable Marathon basic authentication.
#
# Optional
#
# [marathon.basic]
# httpBasicAuthUser = "foo"
# httpBasicPassword = "bar"
# TLS client configuration. https://golang.org/pkg/crypto/tls/#Config
#
# Optional
#
# [marathon.TLS]
# CA = "/etc/ssl/ca.crt"
# Cert = "/etc/ssl/marathon.cert"
# Key = "/etc/ssl/marathon.key"
# insecureSkipVerify = true
# DCOSToken for DCOS environment.
# This will override the Authorization header.
#
# Optional
#
# dcosToken = "xxxxxx"
# Override DialerTimeout.
# Amount of time to allow the Marathon provider to wait to open a TCP connection
# to a Marathon master.
# Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw
# values (digits).
# If no units are provided, the value is parsed assuming seconds.
#
# Optional
# Default: "5s"
#
# dialerTimeout = "5s"
# Override ResponseHeaderTimeout.
# Amount of time to allow the Marathon provider to wait until the first response
# header from the Marathon master is received.
# Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw
# values (digits).
# If no units are provided, the value is parsed assuming seconds.
#
# Optional
# Default: "60s"
#
# responseHeaderTimeout = "60s"
# Override TLSHandshakeTimeout.
# Amount of time to allow the Marathon provider to wait until the TLS
# handshake completes.
# Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw
# values (digits).
# If no units are provided, the value is parsed assuming seconds.
#
# Optional
# Default: "5s"
#
# TLSHandshakeTimeout = "5s"
# Set the TCP Keep Alive interval for the Marathon HTTP Client.
# Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw
# values (digits).
# If no units are provided, the value is parsed assuming seconds.
#
# Optional
# Default: "10s"
#
# keepAlive = "10s"
# By default, a task's IP address (as returned by the Marathon API) is used as
# backend server if an IP-per-task configuration can be found; otherwise, the
# name of the host running the task is used.
# The latter behavior can be enforced by enabling this switch.
#
# Optional
# Default: false
#
# forceTaskHostname = true
# Applications may define readiness checks which are probed by Marathon during
# deployments periodically and the results exposed via the API.
# Enabling the following parameter causes Traefik to filter out tasks
# whose readiness checks have not succeeded.
# Note that the checks are only valid at deployment times.
# See the Marathon guide for details.
#
# Optional
# Default: false
#
# respectReadinessChecks = true
```
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
## Labels: overriding default behavior
Marathon labels may be used to dynamically change the routing and forwarding behavior.
They may be specified on one of two levels: Application or service.
### Application Level
The following labels can be defined on Marathon applications. They adjust the behavior for the entire application.
| Label | Description |
|-------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.domain` | Sets the default base domain used for the frontend rules. |
| `traefik.enable=false` | Disables this container in Traefik. |
| `traefik.port=80` | Registers this port. Useful when the container exposes multiples ports. |
| `traefik.portIndex=1` | Registers port by index in the application's ports array. Useful when the application exposes multiple ports. |
| `traefik.protocol=https` | Overrides the default `http` protocol. |
| `traefik.weight=10` | Assigns this weight to the container. |
| `traefik.backend=foo` | Overrides the application name by `foo` in the generated name of the backend. |
| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend |
| `traefik.backend.responseForwarding.flushInterval=10ms` | Defines the interval between two flushes when forwarding response from backend to client. |
| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. |
| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. (Default: 30s) |
| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. |
| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. |
| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions |
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions |
| `traefik.backend.loadbalancer.stickiness.secure=true` | Sets secure cookie option for sticky sessions. |
| `traefik.backend.loadbalancer.stickiness.httpOnly=true` | Sets http only cookie option for sticky sessions. |
| `traefik.backend.loadbalancer.stickiness.sameSite=none` | Sets same site cookie option for sticky sessions. (`none`, `lax`, `strict`) |
| `traefik.backend.loadbalancer.sticky=true` | Enables backend sticky sessions (DEPRECATED) |
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). |
| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
| `traefik.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. |
| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
| `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. |
| `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
| `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. |
| `traefik.frontend.auth.forward.authResponseHeaders=EXPR` | Sets the forward authentication authResponseHeaders in CSV format: `X-Auth-User,X-Auth-Header` |
| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. |
| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). |
| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. |
| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true` | If set to true invalid SSL certificates are accepted. |
| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. |
| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. |
| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. |
| `traefik.frontend.auth.removeHeader=true` | If set to true, removes the Authorization header. |
| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. |
| `traefik.frontend.passTLSClientCert.infos.issuer.commonName=true` | Add the issuer.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.country=true` | Add the issuer.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.domainComponent=true` | Add the issuer.domainComponent field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.locality=true` | Add the issuer.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.organization=true` | Add the issuer.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.province=true` | Add the issuer.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.serialNumber=true` | Add the issuer.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.notAfter=true` | Add the noAfter field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.notBefore=true` | Add the noBefore field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.sans=true` | Add the sans field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.commonName=true` | Add the subject.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.country=true` | Add the subject.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.domainComponent=true` | Add the subject.domainComponent field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.locality=true` | Add the subject.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.organization=true` | Add the subject.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.province=true` | Add the subject.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.serialNumber=true` | Add the subject.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.pem=true` | Pass the escaped pem in the `X-Forwarded-Ssl-Client-Cert` header. |
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
| `traefik.frontend.priority=10` | Overrides default frontend priority |
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) |
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. |
| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{sub_domain}.{domain}`. |
| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. |
#### Custom Headers
| Label | Description |
|-------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
|
#### Security Headers
| Label | Description |
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
| `traefik.frontend.headers.publicKey=VALUE` | Adds HPKP header. |
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
| `traefik.frontend.headers.SSLForceHost=true` | If `SSLForceHost` is `true` and `SSLHost` is set, requests will be forced to use `SSLHost` even the ones that are already using SSL. Default is false. |
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-Proto:https`).<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
### Applications with Multiple Ports (segment labels)
Segment labels are used to define routes to an application exposing multiple ports.
A segment is a group of labels that apply to a port exposed by an application.
You can define as many segments as ports exposed in an application.
Segment labels override the default behavior.
| Label | Description |
|----------------------------------------------------------------------------------------|----------------------------------------------------------------------------|
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
| `traefik.<segment_name>.portIndex=1` | Same as `traefik.portIndex` |
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
| `traefik.<segment_name>.frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` |
| `traefik.<segment_name>.frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` |
| `traefik.<segment_name>.frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` |
| `traefik.<segment_name>.frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` |
| `traefik.<segment_name>.frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` |
| `traefik.<segment_name>.frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` |
| `traefik.<segment_name>.frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` |
| `traefik.<segment_name>.frontend.auth.forward.authResponseHeaders=EXPR` | Same as `traefik.frontend.auth.forward.authResponseHeaders` |
| `traefik.<segment_name>.frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` |
| `traefik.<segment_name>.frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` |
| `traefik.<segment_name>.frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` |
| `traefik.<segment_name>.frontend.auth.forward.tls.insecureSkipVerify=true` | Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` |
| `traefik.<segment_name>.frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` |
| `traefik.<segment_name>.frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` |
| `traefik.<segment_name>.frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` |
| `traefik.<segment_name>.frontend.auth.removeHeader=true` | Same as `traefik.frontend.auth.removeHeader` |
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.commonName=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.commonName` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.domainComponent=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.domainComponent` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.country=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.country` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.locality=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.locality` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.organization=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.organization` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.province=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.province` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.serialNumber=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.serialNumber` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notAfter=true` | Same as `traefik.frontend.passTLSClientCert.infos.notAfter` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notBefore=true` | Same as `traefik.frontend.passTLSClientCert.infos.notBefore` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.sans=true` | Same as `traefik.frontend.passTLSClientCert.infos.sans` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.commonName=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.commonName` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.domainComponent=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.domainComponent` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.country=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.country` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.locality=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.locality` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.organization=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.organization` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.province=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.province` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.serialNumber=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.serialNumber` |
| `traefik.<segment_name>.frontend.passTLSClientCert.pem=true` | Same as `traefik.frontend.passTLSClientCert.infos.pem` |
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
#### Custom Headers
| Label | Description |
|----------------------------------------------------------------------|----------------------------------------------------------|
| `traefik.<segment_name>.frontend.headers.customRequestHeaders=EXPR ` | Same as `traefik.frontend.headers.customRequestHeaders` |
| `traefik.<segment_name>.frontend.headers.customResponseHeaders=EXPR` | Same as `traefik.frontend.headers.customResponseHeaders` |
#### Security Headers
| Label | Description |
|-------------------------------------------------------------------------|--------------------------------------------------------------|
| `traefik.<segment_name>.frontend.headers.allowedHosts=EXPR` | Same as `traefik.frontend.headers.allowedHosts` |
| `traefik.<segment_name>.frontend.headers.browserXSSFilter=true` | Same as `traefik.frontend.headers.browserXSSFilter` |
| `traefik.<segment_name>.frontend.headers.contentSecurityPolicy=VALUE` | Same as `traefik.frontend.headers.contentSecurityPolicy` |
| `traefik.<segment_name>.frontend.headers.contentTypeNosniff=true` | Same as `traefik.frontend.headers.contentTypeNosniff` |
| `traefik.<segment_name>.frontend.headers.customBrowserXSSValue=VALUE` | Same as `traefik.frontend.headers.customBrowserXSSValue` |
| `traefik.<segment_name>.frontend.headers.customFrameOptionsValue=VALUE` | Same as `traefik.frontend.headers.customFrameOptionsValue` |
| `traefik.<segment_name>.frontend.headers.forceSTSHeader=false` | Same as `traefik.frontend.headers.forceSTSHeader` |
| `traefik.<segment_name>.frontend.headers.frameDeny=false` | Same as `traefik.frontend.headers.frameDeny` |
| `traefik.<segment_name>.frontend.headers.hostsProxyHeaders=EXPR` | Same as `traefik.frontend.headers.hostsProxyHeaders` |
| `traefik.<segment_name>.frontend.headers.isDevelopment=false` | Same as `traefik.frontend.headers.isDevelopment` |
| `traefik.<segment_name>.frontend.headers.publicKey=VALUE` | Same as `traefik.frontend.headers.publicKey` |
| `traefik.<segment_name>.frontend.headers.referrerPolicy=VALUE` | Same as `traefik.frontend.headers.referrerPolicy` |
| `traefik.<segment_name>.frontend.headers.SSLRedirect=true` | Same as `traefik.frontend.headers.SSLRedirect` |
| `traefik.<segment_name>.frontend.headers.SSLTemporaryRedirect=true` | Same as `traefik.frontend.headers.SSLTemporaryRedirect` |
| `traefik.<segment_name>.frontend.headers.SSLHost=HOST` | Same as `traefik.frontend.headers.SSLHost` |
| `traefik.<segment_name>.frontend.headers.SSLForceHost=true` | Same as `traefik.frontend.headers.SSLForceHost` |
| `traefik.<segment_name>.frontend.headers.SSLProxyHeaders=EXPR` | Same as `traefik.frontend.headers.SSLProxyHeaders=EXPR` |
| `traefik.<segment_name>.frontend.headers.STSSeconds=315360000` | Same as `traefik.frontend.headers.STSSeconds=315360000` |
| `traefik.<segment_name>.frontend.headers.STSIncludeSubdomains=true` | Same as `traefik.frontend.headers.STSIncludeSubdomains=true` |
| `traefik.<segment_name>.frontend.headers.STSPreload=true` | Same as `traefik.frontend.headers.STSPreload=true` |

View File

@@ -0,0 +1,321 @@
# Mesos Generic Provider
Traefik can be configured to use Mesos as a provider.
```toml
################################################################
# Mesos Provider
################################################################
# Enable Mesos Provider.
[mesos]
# Mesos server endpoint.
# You can also specify multiple endpoint for Mesos:
# endpoint = "192.168.35.40:5050,192.168.35.41:5050,192.168.35.42:5050"
# endpoint = "zk://192.168.35.20:2181,192.168.35.21:2181,192.168.35.22:2181/mesos"
#
# Required
# Default: "http://127.0.0.1:5050"
#
endpoint = "http://127.0.0.1:8080"
# Enable watch Mesos changes.
#
# Optional
# Default: true
#
watch = true
# Default base domain used for the frontend rules.
# Can be overridden by setting the "traefik.domain" label on an application.
#
# Required
#
domain = "mesos.localhost"
# Expose Mesos apps by default in Traefik.
#
# Optional
# Default: true
#
# exposedByDefault = false
# Override default configuration template.
# For advanced users :)
#
# Optional
#
# filename = "mesos.tmpl"
# Override template version
# For advanced users :)
#
# Optional
# - "1": previous template version (must be used only with older custom templates, see "filename")
# - "2": current template version (must be used to force template version when "filename" is used)
#
# templateVersion = 2
# TLS client configuration. https://golang.org/pkg/crypto/tls/#Config
#
# Optional
#
# [mesos.TLS]
# insecureSkipVerify = true
# Zookeeper timeout (in seconds).
#
# Optional
# Default: 30
#
# zkDetectionTimeout = 30
# Polling interval (in seconds).
#
# Optional
# Default: 30
#
# refreshSeconds = 30
# IP sources (e.g. host, docker, mesos, netinfo).
#
# Optional
#
# ipSources = "host"
# HTTP Timeout (in seconds).
#
# Optional
# Default: 30
#
# stateTimeoutSecond = "30"
# Convert groups to subdomains.
# Default behavior: /foo/bar/myapp => foo-bar-myapp.{defaultDomain}
# with groupsAsSubDomains enabled: /foo/bar/myapp => myapp.bar.foo.{defaultDomain}
#
# Optional
# Default: false
#
# groupsAsSubDomains = true
```
## Labels: overriding default behavior
The following labels can be defined on Mesos tasks. They adjust the behavior for the entire application.
| Label | Description |
|-------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.domain` | Sets the default base domain for the frontend rules. |
| `traefik.enable=false` | Disables this container in Traefik. |
| `traefik.port=80` | Registers this port. Useful when the application exposes multiple ports. |
| `traefik.portName=web` | Registers port by name in the application's ports array. Useful when the application exposes multiple ports. |
| `traefik.portIndex=1` | Registers port by index in the application's ports array. Useful when the application exposes multiple ports. |
| `traefik.protocol=https` | Overrides the default `http` protocol |
| `traefik.weight=10` | Assigns this weight to the container |
| `traefik.backend=foo` | Overrides the task name by `foo` in the generated name of the backend. |
| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend |
| `traefik.backend.responseForwarding.flushInterval=10ms` | Defines the interval between two flushes when forwarding response from backend to client. |
| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. |
| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. (Default: 30s) |
| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. |
| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. |
| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions |
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie manually name for sticky sessions |
| `traefik.backend.loadbalancer.stickiness.secure=true` | Sets secure cookie option for sticky sessions. |
| `traefik.backend.loadbalancer.stickiness.httpOnly=true` | Sets http only cookie option for sticky sessions. |
| `traefik.backend.loadbalancer.stickiness.sameSite=none` | Sets same site cookie option for sticky sessions. (`none`, `lax`, `strict`) |
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). |
| `traefik.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. |
| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
| `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. |
| `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
| `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. |
| `traefik.frontend.auth.forward.authResponseHeaders=EXPR` | Sets the forward authentication authResponseHeaders in CSV format: `X-Auth-User,X-Auth-Header` |
| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. |
| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). |
| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. |
| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true` | If set to true invalid SSL certificates are accepted. |
| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. |
| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. |
| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. |
| `traefik.frontend.auth.removeHeader=true` | If set to true, removes the Authorization header. |
| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. |
| `traefik.frontend.passTLSClientCert.infos.issuer.commonName=true` | Add the issuer.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.country=true` | Add the issuer.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.domainComponent=true` | Add the issuer.domainComponent field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.locality=true` | Add the issuer.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.organization=true` | Add the issuer.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.province=true` | Add the issuer.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.serialNumber=true` | Add the issuer.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.notAfter=true` | Add the noAfter field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.notBefore=true` | Add the noBefore field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.sans=true` | Add the sans field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.commonName=true` | Add the subject.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.country=true` | Add the subject.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.domainComponent=true` | Add the subject.domainComponent field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.locality=true` | Add the subject.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.organization=true` | Add the subject.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.province=true` | Add the subject.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.serialNumber=true` | Add the subject.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.pem=true` | Pass the escaped pem in the `X-Forwarded-Ssl-Client-Cert` header. |
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
| `traefik.frontend.priority=10` | Overrides default frontend priority |
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) |
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. |
| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{discovery_name}.{domain}`. |
| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. |
### Custom Headers
| Label | Description |
|-------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
### Security Headers
| Label | Description |
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
| `traefik.frontend.headers.publicKey=VALUE` | Adds HPKP header. |
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
| `traefik.frontend.headers.SSLForceHost=true` | If `SSLForceHost` is `true` and `SSLHost` is set, requests will be forced to use `SSLHost` even the ones that are already using SSL. Default is false. |
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-Proto:https`).<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
### Applications with Multiple Ports (segment labels)
Segment labels are used to define routes to an application exposing multiple ports.
A segment is a group of labels that apply to a port exposed by an application.
You can define as many segments as ports exposed in an application.
Additionally, if a segment name matches a named port, that port will be used unless `portIndex`, `portName`, or `port` labels are specified for that segment.
Segment labels override the default behavior.
| Label | Description |
|------------------------------------------------------------------------------------|------------------------------------------------------------------------|
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
| `traefik.<segment_name>.portIndex=1` | Same as `traefik.portIndex` |
| `traefik.<segment_name>.portName=web` | Same as `traefik.portName` |
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
| `traefik.<segment_name>.frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` |
| `traefik.<segment_name>.frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` |
| `traefik.<segment_name>.frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` |
| `traefik.<segment_name>.frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` |
| `traefik.<segment_name>.frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` |
| `traefik.<segment_name>.frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` |
| `traefik.<segment_name>.frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` |
| `traefik.<segment_name>.frontend.auth.forward.authResponseHeaders=EXPR` | Same as `traefik.frontend.auth.forward.authResponseHeaders` |
| `traefik.<segment_name>.frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` |
| `traefik.<segment_name>.frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` |
| `traefik.<segment_name>.frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` |
| `traefik.<segment_name>.frontend.auth.forward.tls.insecureSkipVerify=true` | Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` |
| `traefik.<segment_name>.frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` |
| `traefik.<segment_name>.frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` |
| `traefik.<segment_name>.frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` |
| `traefik.<segment_name>.frontend.auth.removeHeader=true` | Same as `traefik.frontend.auth.removeHeader` |
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notAfter=true` | Same as `traefik.frontend.passTLSClientCert.infos.notAfter` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notBefore=true` | Same as `traefik.frontend.passTLSClientCert.infos.notBefore` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.sans=true` | Same as `traefik.frontend.passTLSClientCert.infos.sans` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.commonName=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.commonName` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.country=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.country` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.locality=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.locality` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.organization=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.organization`|
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.province=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.province` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.serialNumber`|
| `traefik.<segment_name>.frontend.passTLSClientCert.pem=true` | Same as `traefik.frontend.passTLSClientCert.infos.pem` |
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
#### Custom Headers
| Label | Description |
|----------------------------------------------------------------------|----------------------------------------------------------|
| `traefik.<segment_name>.frontend.headers.customRequestHeaders=EXPR ` | Same as `traefik.frontend.headers.customRequestHeaders` |
| `traefik.<segment_name>.frontend.headers.customResponseHeaders=EXPR` | Same as `traefik.frontend.headers.customResponseHeaders` |
#### Security Headers
| Label | Description |
|-------------------------------------------------------------------------|--------------------------------------------------------------|
| `traefik.<segment_name>.frontend.headers.allowedHosts=EXPR` | Same as `traefik.frontend.headers.allowedHosts` |
| `traefik.<segment_name>.frontend.headers.browserXSSFilter=true` | Same as `traefik.frontend.headers.browserXSSFilter` |
| `traefik.<segment_name>.frontend.headers.contentSecurityPolicy=VALUE` | Same as `traefik.frontend.headers.contentSecurityPolicy` |
| `traefik.<segment_name>.frontend.headers.contentTypeNosniff=true` | Same as `traefik.frontend.headers.contentTypeNosniff` |
| `traefik.<segment_name>.frontend.headers.customBrowserXSSValue=VALUE` | Same as `traefik.frontend.headers.customBrowserXSSValue` |
| `traefik.<segment_name>.frontend.headers.customFrameOptionsValue=VALUE` | Same as `traefik.frontend.headers.customFrameOptionsValue` |
| `traefik.<segment_name>.frontend.headers.forceSTSHeader=false` | Same as `traefik.frontend.headers.forceSTSHeader` |
| `traefik.<segment_name>.frontend.headers.frameDeny=false` | Same as `traefik.frontend.headers.frameDeny` |
| `traefik.<segment_name>.frontend.headers.hostsProxyHeaders=EXPR` | Same as `traefik.frontend.headers.hostsProxyHeaders` |
| `traefik.<segment_name>.frontend.headers.isDevelopment=false` | Same as `traefik.frontend.headers.isDevelopment` |
| `traefik.<segment_name>.frontend.headers.publicKey=VALUE` | Same as `traefik.frontend.headers.publicKey` |
| `traefik.<segment_name>.frontend.headers.referrerPolicy=VALUE` | Same as `traefik.frontend.headers.referrerPolicy` |
| `traefik.<segment_name>.frontend.headers.SSLRedirect=true` | Same as `traefik.frontend.headers.SSLRedirect` |
| `traefik.<segment_name>.frontend.headers.SSLTemporaryRedirect=true` | Same as `traefik.frontend.headers.SSLTemporaryRedirect` |
| `traefik.<segment_name>.frontend.headers.SSLHost=HOST` | Same as `traefik.frontend.headers.SSLHost` |
| `traefik.<segment_name>.frontend.headers.SSLForceHost=true` | Same as `traefik.frontend.headers.SSLForceHost` |
| `traefik.<segment_name>.frontend.headers.SSLProxyHeaders=EXPR` | Same as `traefik.frontend.headers.SSLProxyHeaders=EXPR` |
| `traefik.<segment_name>.frontend.headers.STSSeconds=315360000` | Same as `traefik.frontend.headers.STSSeconds=315360000` |
| `traefik.<segment_name>.frontend.headers.STSIncludeSubdomains=true` | Same as `traefik.frontend.headers.STSIncludeSubdomains=true` |
| `traefik.<segment_name>.frontend.headers.STSPreload=true` | Same as `traefik.frontend.headers.STSPreload=true` |

View File

@@ -0,0 +1,359 @@
# Rancher Provider
Traefik can be configured to use Rancher as a provider.
!!! important
This provider is specific to Rancher 1.x.
Rancher 2.x requires Kubernetes and does not have a metadata endpoint of its own for Traefik to query.
As such, Rancher 2.x users should utilize the [Kubernetes provider](./kubernetes.md) directly.
## Global Configuration
```toml
################################################################
# Rancher Provider
################################################################
# Enable Rancher Provider.
[rancher]
# Default base domain used for the frontend rules.
# Can be overridden by setting the "traefik.domain" label on an service.
#
# Required
#
domain = "rancher.localhost"
# Enable watch Rancher changes.
#
# Optional
# Default: true
#
watch = true
# Polling interval (in seconds).
#
# Optional
# Default: 15
#
refreshSeconds = 15
# Expose Rancher services by default in Traefik.
#
# Optional
# Default: true
#
exposedByDefault = false
# Filter services with unhealthy states and inactive states.
#
# Optional
# Default: false
#
enableServiceHealthFilter = true
# Override default configuration template.
# For advanced users :)
#
# Optional
#
# filename = "rancher.tmpl"
# Override template version
# For advanced users :)
#
# Optional
# - "1": previous template version (must be used only with older custom templates, see "filename")
# - "2": current template version (must be used to force template version when "filename" is used)
#
# templateVersion = 2
```
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
## Rancher Metadata Service
```toml
# Enable Rancher metadata service provider instead of the API
# provider.
#
# Optional
# Default: false
#
[rancher.metadata]
# Poll the Rancher metadata service for changes every `rancher.refreshSeconds`.
# NOTE: this is less accurate than the default long polling technique which
# will provide near instantaneous updates to Traefik
#
# Optional
# Default: false
#
intervalPoll = true
# Prefix used for accessing the Rancher metadata service.
#
# Optional
# Default: "/latest"
#
prefix = "/2016-07-29"
```
## Rancher API
```toml
# Enable Rancher API provider.
#
# Optional
# Default: true
#
[rancher.api]
# Endpoint to use when connecting to the Rancher API.
#
# Required
endpoint = "http://rancherserver.example.com/v1"
# AccessKey to use when connecting to the Rancher API.
#
# Required
accessKey = "XXXXXXXXXXXXXXXXXXXX"
# SecretKey to use when connecting to the Rancher API.
#
# Required
secretKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```
!!! note
If Traefik needs access to the Rancher API, you need to set the `endpoint`, `accesskey` and `secretkey` parameters.
To enable Traefik to fetch information about the Environment it's deployed in only, you need to create an `Environment API Key`.
This can be found within the API Key advanced options.
Add these labels to traefik docker deployment to autogenerated these values:
```
io.rancher.container.agent.role: environment
io.rancher.container.create_agent: true
```
## Labels: overriding default behavior
### On Containers
Labels can be used on task containers to override default behavior:
| Label | Description |
|-------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.domain` | Sets the default base domain for the frontend rules. |
| `traefik.enable=false` | Disables this container in Traefik. |
| `traefik.port=80` | Registers this port. Useful when the container exposes multiple ports. |
| `traefik.protocol=https` | Overrides the default `http` protocol. |
| `traefik.weight=10` | Assigns this weight to the container. |
| `traefik.backend=foo` | Overrides the service name by `foo` in the generated name of the backend. |
| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend |
| `traefik.backend.responseForwarding.flushInterval=10ms` | Defines the interval between two flushes when forwarding response from backend to client. |
| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. |
| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. |
| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. |
| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. |
| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions |
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions |
| `traefik.backend.loadbalancer.stickiness.secure=true` | Sets secure cookie option for sticky sessions. |
| `traefik.backend.loadbalancer.stickiness.httpOnly=true` | Sets http only cookie option for sticky sessions. |
| `traefik.backend.loadbalancer.stickiness.sameSite=none` | Sets same site cookie option for sticky sessions. (`none`, `lax`, `strict`) |
| `traefik.backend.loadbalancer.sticky=true` | Enables backend sticky sessions (DEPRECATED) |
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
| `traefik.frontend.auth.basic=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). |
| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
| `traefik.frontend.auth.basic.users=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` . |
| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets the basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
| `traefik.frontend.auth.digest.users=EXPR` | Sets the digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. |
| `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets the digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
| `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. |
| `traefik.frontend.auth.forward.authResponseHeaders=EXPR` | Sets the forward authentication authResponseHeaders in CSV format: `X-Auth-User,X-Auth-Header` |
| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. |
| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). |
| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. |
| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true` | If set to true invalid SSL certificates are accepted. |
| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. |
| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. |
| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. |
| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. |
| `traefik.frontend.passTLSClientCert.infos.issuer.commonName=true` | Add the issuer.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.country=true` | Add the issuer.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.domainComponent=true` | Add the issuer.domainComponent field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.locality=true` | Add the issuer.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.organization=true` | Add the issuer.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.province=true` | Add the issuer.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.issuer.serialNumber=true` | Add the issuer.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.notAfter=true` | Add the noAfter field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.notBefore=true` | Add the noBefore field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.sans=true` | Add the sans field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.commonName=true` | Add the subject.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.country=true` | Add the subject.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.domainComponent=true` | Add the subject.domainComponent field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.locality=true` | Add the subject.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.organization=true` | Add the subject.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.province=true` | Add the subject.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.infos.subject.serialNumber=true` | Add the subject.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
| `traefik.frontend.passTLSClientCert.pem=true` | Pass the escaped pem in the `X-Forwarded-Ssl-Client-Cert` header. |
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
| `traefik.frontend.priority=10` | Overrides default frontend priority |
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) |
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. |
| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{containerName}.{domain}` or `Host:{service}.{project_name}.{domain}` if you are using `docker-compose`. |
| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access.<br>If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. |
#### Custom Headers
| Label | Description |
|-------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
#### Security Headers
| Label | Description |
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |
| `traefik.frontend.headers.publicKey=VALUE` | Adds HPKP header. |
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
| `traefik.frontend.headers.SSLForceHost=true` | If `SSLForceHost` is `true` and `SSLHost` is set, requests will be forced to use `SSLHost` even the ones that are already using SSL. Default is false. |
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-Proto:https`).<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
### On containers with Multiple Ports (segment labels)
Segment labels are used to define routes to a container exposing multiple ports.
A segment is a group of labels that apply to a port exposed by a container.
You can define as many segments as ports exposed in a container.
Segment labels override the default behavior.
| Label | Description |
|----------------------------------------------------------------------------------------|----------------------------------------------------------------------------|
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
| `traefik.<segment_name>.frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` |
| `traefik.<segment_name>.frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` |
| `traefik.<segment_name>.frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` |
| `traefik.<segment_name>.frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` |
| `traefik.<segment_name>.frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` |
| `traefik.<segment_name>.frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` |
| `traefik.<segment_name>.frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` |
| `traefik.<segment_name>.frontend.auth.forward.authResponseHeaders=EXPR` | Same as `traefik.frontend.auth.forward.authResponseHeaders` |
| `traefik.<segment_name>.frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` |
| `traefik.<segment_name>.frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` |
| `traefik.<segment_name>.frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` |
| `traefik.<segment_name>.frontend.auth.forward.tls.insecureSkipVerify=true` | Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` |
| `traefik.<segment_name>.frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` |
| `traefik.<segment_name>.frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` |
| `traefik.<segment_name>.frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` |
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.commonName=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.commonName` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.country=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.country` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.domainComponent=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.domainComponent` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.locality=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.locality` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.organization=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.organization` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.province=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.province` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.issuer.serialNumber=true` | Same as `traefik.frontend.passTLSClientCert.infos.issuer.serialNumber` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notAfter=true` | Same as `traefik.frontend.passTLSClientCert.infos.notAfter` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notBefore=true` | Same as `traefik.frontend.passTLSClientCert.infos.notBefore` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.sans=true` | Same as `traefik.frontend.passTLSClientCert.infos.sans` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.commonName=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.commonName` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.country=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.country` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.domainComponent=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.domainComponent` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.locality=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.locality` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.organization=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.organization` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.province=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.province` |
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.serialNumber=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.serialNumber` |
| `traefik.<segment_name>.frontend.passTLSClientCert.pem=true` | Same as `traefik.frontend.passTLSClientCert.infos.pem` |
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
#### Custom Headers
| Label | Description |
|----------------------------------------------------------------------|------------------------------------------------------------|
| `traefik.<segment_name>.frontend.headers.customRequestHeaders=EXPR ` | overrides `traefik.frontend.headers.customRequestHeaders` |
| `traefik.<segment_name>.frontend.headers.customResponseHeaders=EXPR` | overrides `traefik.frontend.headers.customResponseHeaders` |
#### Security Headers
| Label | Description |
|-------------------------------------------------------------------------|--------------------------------------------------------------|
| `traefik.<segment_name>.frontend.headers.allowedHosts=EXPR` | overrides `traefik.frontend.headers.allowedHosts` |
| `traefik.<segment_name>.frontend.headers.browserXSSFilter=true` | overrides `traefik.frontend.headers.browserXSSFilter` |
| `traefik.<segment_name>.frontend.headers.contentSecurityPolicy=VALUE` | overrides `traefik.frontend.headers.contentSecurityPolicy` |
| `traefik.<segment_name>.frontend.headers.contentTypeNosniff=true` | overrides `traefik.frontend.headers.contentTypeNosniff` |
| `traefik.<segment_name>.frontend.headers.customBrowserXSSValue=VALUE` | overrides `traefik.frontend.headers.customBrowserXSSValue` |
| `traefik.<segment_name>.frontend.headers.customFrameOptionsValue=VALUE` | overrides `traefik.frontend.headers.customFrameOptionsValue` |
| `traefik.<segment_name>.frontend.headers.forceSTSHeader=false` | overrides `traefik.frontend.headers.forceSTSHeader` |
| `traefik.<segment_name>.frontend.headers.frameDeny=false` | overrides `traefik.frontend.headers.frameDeny` |
| `traefik.<segment_name>.frontend.headers.hostsProxyHeaders=EXPR` | overrides `traefik.frontend.headers.hostsProxyHeaders` |
| `traefik.<segment_name>.frontend.headers.isDevelopment=false` | overrides `traefik.frontend.headers.isDevelopment` |
| `traefik.<segment_name>.frontend.headers.publicKey=VALUE` | overrides `traefik.frontend.headers.publicKey` |
| `traefik.<segment_name>.frontend.headers.referrerPolicy=VALUE` | overrides `traefik.frontend.headers.referrerPolicy` |
| `traefik.<segment_name>.frontend.headers.SSLRedirect=true` | overrides `traefik.frontend.headers.SSLRedirect` |
| `traefik.<segment_name>.frontend.headers.SSLTemporaryRedirect=true` | overrides `traefik.frontend.headers.SSLTemporaryRedirect` |
| `traefik.<segment_name>.frontend.headers.SSLHost=HOST` | overrides `traefik.frontend.headers.SSLHost` |
| `traefik.<segment_name>.frontend.headers.SSLForceHost=true` | overrides `traefik.frontend.headers.SSLForceHost` |
| `traefik.<segment_name>.frontend.headers.SSLProxyHeaders=EXPR` | overrides `traefik.frontend.headers.SSLProxyHeaders` |
| `traefik.<segment_name>.frontend.headers.STSSeconds=315360000` | overrides `traefik.frontend.headers.STSSeconds` |
| `traefik.<segment_name>.frontend.headers.STSIncludeSubdomains=true` | overrides `traefik.frontend.headers.STSIncludeSubdomains` |
| `traefik.<segment_name>.frontend.headers.STSPreload=true` | overrides `traefik.frontend.headers.STSPreload` |

Some files were not shown because too many files have changed in this diff Show More