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

Compare commits

...

298 Commits

Author SHA1 Message Date
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
SALLEYRON Julien
395b1702de Extract providers configuration from server.go 2018-01-29 14:58: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
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
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
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
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
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
Ludovic Fernandez
72bc74001f Add Tracing entry in the documentation. 2018-01-17 13:14:03 +01:00
Fernandez Ludovic
89d90de7d8 Merge branch '1.5.0-rc5' into master 2018-01-15 17:27:37 +01:00
Ludovic Fernandez
2618aef008 Switch to golang/dep. 2018-01-11 17:46:04 +01:00
Michael
709d50836b Fix bad access log 2018-01-11 10:04: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
Ludovic Fernandez
870c0b5cf4 homogenization of templates: Marathon 2018-01-10 11:58:03 +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
gopenguin
8327dd0c0b Add support for fetching k8s Ingress TLS data from secrets 2018-01-08 00:36: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
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
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
Marco Jantke
cbd54470ba Improve logging output for failing healthchecks 2018-01-03 12:32:03 +01:00
Fernandez Ludovic
c84fb9895e Merge branch 'v1.5' into master 2018-01-02 15:21:17 +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
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
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
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
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
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
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
ferhat elmas
6890dc1844 Simplification 2017-12-06 10:52: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
Fernandez Ludovic
dc74f76a03 Merge branch 'v1.5' into master 2017-12-04 13:35:02 +01:00
Sami Jawhar
e042ef3f27 Bump Lego Version for GoDaddy DNS Provider 2017-11-30 10:44:03 +01:00
3198 changed files with 501505 additions and 490612 deletions

2
.gitattributes vendored Normal file
View File

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

View File

@@ -22,7 +22,7 @@ If you intend to ask a support question: DO NOT FILE AN ISSUE.
HOW TO WRITE A GOOD ISSUE? HOW TO WRITE A GOOD ISSUE?
- Respect the issue template as more as possible. - Respect the issue template as much as possible.
- If it's possible use the command `traefik bug`. See https://www.youtube.com/watch?v=Lyz62L8m93I. - If it's possible use the command `traefik bug`. See https://www.youtube.com/watch?v=Lyz62L8m93I.
- The title must be short and descriptive. - The title must be short and descriptive.
- Explain the conditions which led you to write this issue: the context. - Explain the conditions which led you to write this issue: the context.
@@ -62,7 +62,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) (paste your output here)

68
.github/ISSUE_TEMPLATE/bugs.md vendored Normal file
View File

@@ -0,0 +1,68 @@
<!--
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://traefik.herokuapp.com
-->
### Do you want to request a *feature* or report a *bug*?
Bug
### What did you do?
<!--
HOW TO WRITE A GOOD ISSUE?
- Respect the issue template as much 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?_)
<!--
For the Traefik Docker image:
docker run [IMAGE] version
ex: docker run 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)
```

32
.github/ISSUE_TEMPLATE/features.md vendored Normal file
View File

@@ -0,0 +1,32 @@
<!--
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://traefik.herokuapp.com
-->
### 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 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
-->

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.

42
.gometalinter.json Normal file
View File

@@ -0,0 +1,42 @@
{
"Vendor": true,
"Sort": [
"path",
"line",
"column",
"severity",
"linter"
],
"Test": true,
"Cyclo": 15,
"Enable": [
"gotypex",
"nakedret",
"vet",
"goimports",
"golint",
"ineffassign",
"gotype",
"misspell",
"structcheck",
"gosimple",
"unconvert",
"varcheck",
"errcheck",
"unused",
"deadcode",
"staticcheck"
],
"Disable": [
"gas",
"maligned",
"interfacer",
"goconst",
"gocyclo",
"vetshadow"
],
"Exclude": [
"autogen/.*"
],
"Deadline": "5m"
}

View File

@@ -10,7 +10,7 @@ else
export VERSION='' export VERSION=''
fi fi
export CODENAME=cancoillotte export CODENAME=tetedemoine
export N_MAKE_JOBS=2 export N_MAKE_JOBS=2

View File

@@ -11,7 +11,7 @@ env:
global: global:
- REPO: $TRAVIS_REPO_SLUG - REPO: $TRAVIS_REPO_SLUG
- VERSION: $TRAVIS_TAG - VERSION: $TRAVIS_TAG
- CODENAME: cancoillotte - CODENAME: tetedemoine
- N_MAKE_JOBS: 2 - N_MAKE_JOBS: 2
script: script:
@@ -24,14 +24,14 @@ before_deploy:
sudo -E apt-get -yq update; sudo -E apt-get -yq update;
sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install docker-ce=${DOCKER_VERSION}*; sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install docker-ce=${DOCKER_VERSION}*;
docker version; docker version;
make image;
if [ "$TRAVIS_TAG" ]; then if [ "$TRAVIS_TAG" ]; then
make -j${N_MAKE_JOBS} crossbinary-parallel; make -j${N_MAKE_JOBS} crossbinary-parallel;
make image-dirty;
tar cfz dist/traefik-${VERSION}.src.tar.gz --exclude-vcs --exclude dist .; tar cfz dist/traefik-${VERSION}.src.tar.gz --exclude-vcs --exclude dist .;
fi; fi;
curl -sI https://github.com/containous/structor/releases/latest | grep -Fi Location | tr -d '\r' | sed "s/tag/download/g" | awk -F " " '{ print $2 "/structor_linux-amd64"}' | wget --output-document=$GOPATH/bin/structor -i -; curl -sI https://github.com/containous/structor/releases/latest | grep -Fi Location | tr -d '\r' | sed "s/tag/download/g" | awk -F " " '{ print $2 "/structor_linux-amd64"}' | wget --output-document=$GOPATH/bin/structor -i -;
chmod +x $GOPATH/bin/structor; chmod +x $GOPATH/bin/structor;
structor -o containous -r traefik --dockerfile-url="https://raw.githubusercontent.com/containous/traefik/master/docs.Dockerfile" --menu.js-url="https://raw.githubusercontent.com/containous/structor/master/traefik-menu.js.gotmpl" --exp-branch=master --debug; structor -o containous -r traefik --dockerfile-url="https://raw.githubusercontent.com/containous/traefik/master/docs.Dockerfile" --menu.js-url="https://raw.githubusercontent.com/containous/structor/master/traefik-menu.js.gotmpl" --rqts-url="https://raw.githubusercontent.com/containous/structor/master/requirements-override.txt" --exp-branch=master --debug;
fi fi
deploy: deploy:
- provider: releases - provider: releases
@@ -54,7 +54,7 @@ deploy:
on: on:
repo: containous/traefik repo: containous/traefik
- provider: pages - provider: pages
edge: true edge: false
github_token: ${GITHUB_TOKEN} github_token: ${GITHUB_TOKEN}
local_dir: site local_dir: site
skip_cleanup: true skip_cleanup: true

View File

@@ -1,5 +1,209 @@
# Change Log # Change Log
## [v1.6.0-rc3](https://github.com/containous/traefik/tree/v1.6.0-rc3) (2018-03-28)
[All Commits](https://github.com/containous/traefik/compare/v1.6.0-rc2...v1.6.0-rc3)
**Bug fixes:**
- **[docker,rancher]** Frontend rule and segment labels. ([#3091](https://github.com/containous/traefik/pull/3091) by [ldez](https://github.com/ldez))
## [v1.6.0-rc2](https://github.com/containous/traefik/tree/v1.6.0-rc2) (2018-03-27)
[All Commits](https://github.com/containous/traefik/compare/v1.6.0-rc1...v1.6.0-rc2)
**Bug fixes:**
- **[acme]** Fix panic with wrong ACME configuration ([#3084](https://github.com/containous/traefik/pull/3084) by [nmengin](https://github.com/nmengin))
- **[acme]** Fix wildcard match to ACME domains in cluster mode ([#3080](https://github.com/containous/traefik/pull/3080) by [oldmantaiter](https://github.com/oldmantaiter))
**Documentation:**
- **[servicefabric]** Update SF white list documentation section. ([#3082](https://github.com/containous/traefik/pull/3082) by [ldez](https://github.com/ldez))
- Fix basic documentation ([#3086](https://github.com/containous/traefik/pull/3086) by [mmatur](https://github.com/mmatur))
## [v1.6.0-rc1](https://github.com/containous/traefik/tree/v1.6.0-rc1) (2018-03-26)
[All Commits](https://github.com/containous/traefik/compare/v1.5.0-rc1...v1.6.0-rc1)
**Enhancements:**
- **[acme]** Bump Lego Version for GoDaddy DNS Provider ([#2482](https://github.com/containous/traefik/pull/2482) by [sjawhar](https://github.com/sjawhar))
- **[acme]** Simplify storing renewed acme certificate ([#2614](https://github.com/containous/traefik/pull/2614) by [ferhatelmas](https://github.com/ferhatelmas))
- **[acme]** Delete TLS-SNI-01 challenge from ACME ([#2971](https://github.com/containous/traefik/pull/2971) by [nmengin](https://github.com/nmengin))
- **[acme]** ACME V2 Integration ([#3063](https://github.com/containous/traefik/pull/3063) by [nmengin](https://github.com/nmengin))
- **[acme]** Update Lego (Gandi API v5, cloudxns, ...) ([#2844](https://github.com/containous/traefik/pull/2844) by [ldez](https://github.com/ldez))
- **[acme]** Create ACME Provider ([#2889](https://github.com/containous/traefik/pull/2889) by [nmengin](https://github.com/nmengin))
- **[api,cluster]** Added cluster/leader endpoint ([#3009](https://github.com/containous/traefik/pull/3009) by [aantono](https://github.com/aantono))
- **[authentication]** Forward Authentication: add X-Forwarded-Uri ([#2398](https://github.com/containous/traefik/pull/2398) by [sebastianbauer](https://github.com/sebastianbauer))
- **[boltdb,consul,etcd,kv,zk]** homogenization of templates: KV ([#2661](https://github.com/containous/traefik/pull/2661) by [ldez](https://github.com/ldez))
- **[boltdb,consul,etcd,kv,zk]** Add all available configuration to KV Backend ([#2652](https://github.com/containous/traefik/pull/2652) by [ldez](https://github.com/ldez))
- **[boltdb,consul,etcd,kv,zk]** Homogenization of the providers (part 1): KV ([#2616](https://github.com/containous/traefik/pull/2616) by [ldez](https://github.com/ldez))
- **[consul,consulcatalog]** Homogenization of templates: Consul Catalog ([#2668](https://github.com/containous/traefik/pull/2668) by [ldez](https://github.com/ldez))
- **[consul,consulcatalog]** Split consul and consul catalog. ([#2655](https://github.com/containous/traefik/pull/2655) by [ldez](https://github.com/ldez))
- **[consulcatalog]** Add all available tags to Consul Catalog Backend ([#2646](https://github.com/containous/traefik/pull/2646) by [ldez](https://github.com/ldez))
- **[consulcatalog]** Check for endpoints while detecting Consul service changes ([#2882](https://github.com/containous/traefik/pull/2882) by [caseycs](https://github.com/caseycs))
- **[consulcatalog]** TLS Support for ConsulCatalog ([#2900](https://github.com/containous/traefik/pull/2900) by [mmatur](https://github.com/mmatur))
- **[docker,docker/swarm]** Fix support for macvlan driver in docker provider ([#2827](https://github.com/containous/traefik/pull/2827) by [mmatur](https://github.com/mmatur))
- **[docker,marathon,rancher]** Segments Labels: Rancher &amp; Marathon ([#3073](https://github.com/containous/traefik/pull/3073) by [ldez](https://github.com/ldez))
- **[docker]** Custom headers by service labels for docker backends ([#2514](https://github.com/containous/traefik/pull/2514) by [Tiscs](https://github.com/Tiscs))
- **[docker]** Homogenization of templates: Docker ([#2659](https://github.com/containous/traefik/pull/2659) by [ldez](https://github.com/ldez))
- **[docker]** Segment labels: Docker ([#3055](https://github.com/containous/traefik/pull/3055) by [ldez](https://github.com/ldez))
- **[docker]** Add all available labels to Docker Backend ([#2584](https://github.com/containous/traefik/pull/2584) by [ldez](https://github.com/ldez))
- **[dynamodb,ecs]** Upgrade AWS SKD to version v1.13.1 ([#2908](https://github.com/containous/traefik/pull/2908) by [mmatur](https://github.com/mmatur))
- **[ecs]** Add all available labels to ECS Backend ([#2605](https://github.com/containous/traefik/pull/2605) by [ldez](https://github.com/ldez))
- **[ecs]** Homogenization of templates: ECS ([#2663](https://github.com/containous/traefik/pull/2663) by [ldez](https://github.com/ldez))
- **[eureka]** Replace Delay by RefreshSecond in Eureka ([#2972](https://github.com/containous/traefik/pull/2972) by [ldez](https://github.com/ldez))
- **[eureka]** Homogenization of templates: Eureka ([#2846](https://github.com/containous/traefik/pull/2846) by [ldez](https://github.com/ldez))
- **[file]** Added support for templates to file provider ([#2991](https://github.com/containous/traefik/pull/2991) by [aantono](https://github.com/aantono))
- **[healthcheck]** Toggle /ping to artificially return unhealthy response on SIGTERM during requestAcceptGraceTimeout interval ([#3062](https://github.com/containous/traefik/pull/3062) by [ravilr](https://github.com/ravilr))
- **[healthcheck]** Improve logging output for failing healthchecks ([#2443](https://github.com/containous/traefik/pull/2443) by [marco-jantke](https://github.com/marco-jantke))
- **[k8s,tls]** Add support for fetching k8s Ingress TLS data from secrets ([#2439](https://github.com/containous/traefik/pull/2439) by [gopenguin](https://github.com/gopenguin))
- **[k8s]** Bump kubernetes/client-go ([#2848](https://github.com/containous/traefik/pull/2848) by [yue9944882](https://github.com/yue9944882))
- **[k8s]** Allow custom value for kubernetes.io/ingress.class annotation ([#2222](https://github.com/containous/traefik/pull/2222) by [yuvipanda](https://github.com/yuvipanda))
- **[k8s]** Add app-root annotation support for kubernetes ingress ([#2522](https://github.com/containous/traefik/pull/2522) by [yue9944882](https://github.com/yue9944882))
- **[k8s]** Builders in k8s tests ([#2513](https://github.com/containous/traefik/pull/2513) by [ldez](https://github.com/ldez))
- **[k8s]** Add all available annotations to k8s Backend ([#2612](https://github.com/containous/traefik/pull/2612) by [ldez](https://github.com/ldez))
- **[k8s]** Introduce k8s informer factory ([#2867](https://github.com/containous/traefik/pull/2867) by [yue9944882](https://github.com/yue9944882))
- **[logs,middleware]** Add access log filter for retry attempts ([#3042](https://github.com/containous/traefik/pull/3042) by [marco-jantke](https://github.com/marco-jantke))
- **[logs,middleware]** Ultimate Access log filter ([#2988](https://github.com/containous/traefik/pull/2988) by [mmatur](https://github.com/mmatur))
- **[logs,middleware]** Add username in accesslog ([#2111](https://github.com/containous/traefik/pull/2111) by [bastiaanb](https://github.com/bastiaanb))
- **[logs]** Allow overriding the log level in debug mode. ([#3050](https://github.com/containous/traefik/pull/3050) by [timoreimann](https://github.com/timoreimann))
- **[logs]** Display file log when test fails. ([#2801](https://github.com/containous/traefik/pull/2801) by [ldez](https://github.com/ldez))
- **[marathon]** Remove health check filter from Marathon tasks. ([#2817](https://github.com/containous/traefik/pull/2817) by [timoreimann](https://github.com/timoreimann))
- **[marathon]** Add all available labels to Marathon Backend ([#2602](https://github.com/containous/traefik/pull/2602) by [ldez](https://github.com/ldez))
- **[marathon]** homogenization of templates: Marathon ([#2665](https://github.com/containous/traefik/pull/2665) by [ldez](https://github.com/ldez))
- **[mesos]** Add all available labels to Mesos Backend ([#2687](https://github.com/containous/traefik/pull/2687) by [ldez](https://github.com/ldez))
- **[metrics]** Added entrypoint metrics to influxdb ([#2992](https://github.com/containous/traefik/pull/2992) by [adityacs](https://github.com/adityacs))
- **[metrics]** Extend metrics and rebuild prometheus exporting logic ([#2567](https://github.com/containous/traefik/pull/2567) by [marco-jantke](https://github.com/marco-jantke))
- **[metrics]** Added missing metrics to registry for DataDog and StatsD ([#2890](https://github.com/containous/traefik/pull/2890) by [aantono](https://github.com/aantono))
- **[metrics]** Remove unnecessary conversion ([#2850](https://github.com/containous/traefik/pull/2850) by [ferhatelmas](https://github.com/ferhatelmas))
- **[middleware,consul,consulcatalog,docker,ecs,k8s,marathon,mesos,rancher]** New option in secure middleware ([#2958](https://github.com/containous/traefik/pull/2958) by [mmatur](https://github.com/mmatur))
- **[middleware,consulcatalog,docker,ecs,k8s,kv,marathon,mesos,rancher]** Ability to use &#34;X-Forwarded-For&#34; as a source of IP for white list. ([#3070](https://github.com/containous/traefik/pull/3070) by [ldez](https://github.com/ldez))
- **[middleware,docker]** Use pointer of error pages ([#2607](https://github.com/containous/traefik/pull/2607) by [ldez](https://github.com/ldez))
- **[middleware,provider]** Redirection: permanent move option. ([#2774](https://github.com/containous/traefik/pull/2774) by [ldez](https://github.com/ldez))
- **[middleware]** Add new options to the CLI entrypoint definition. ([#2799](https://github.com/containous/traefik/pull/2799) by [ldez](https://github.com/ldez))
- **[middleware]** Change port of traefik for error pages integration test ([#2907](https://github.com/containous/traefik/pull/2907) by [mmatur](https://github.com/mmatur))
- **[middleware]** Request buffering middleware ([#2217](https://github.com/containous/traefik/pull/2217) by [harnash](https://github.com/harnash))
- **[middleware]** Remove unnecessary returns in tracing setup ([#2880](https://github.com/containous/traefik/pull/2880) by [ferhatelmas](https://github.com/ferhatelmas))
- **[provider]** Homogenization of the providers (part 1) ([#2518](https://github.com/containous/traefik/pull/2518) by [ldez](https://github.com/ldez))
- **[provider]** No error pages must return nil. ([#2610](https://github.com/containous/traefik/pull/2610) by [ldez](https://github.com/ldez))
- **[rancher]** Homogenization of templates: Rancher ([#2662](https://github.com/containous/traefik/pull/2662) by [ldez](https://github.com/ldez))
- **[rancher]** Add all available labels to Rancher Backend ([#2601](https://github.com/containous/traefik/pull/2601) by [ldez](https://github.com/ldez))
- **[rules]** Externalize Træfik rules in a dedicated package ([#2933](https://github.com/containous/traefik/pull/2933) by [nmengin](https://github.com/nmengin))
- **[servicefabric]** Update Service Fabric backend. ([#3064](https://github.com/containous/traefik/pull/3064) by [ldez](https://github.com/ldez))
- **[servicefabric]** Add white list for Service Fabric. ([#3079](https://github.com/containous/traefik/pull/3079) by [ldez](https://github.com/ldez))
- **[tls]** Use default entryPoints when certificates are added with no entryPoints. ([#2534](https://github.com/containous/traefik/pull/2534) by [nmengin](https://github.com/nmengin))
- **[tracing]** Handle zipkin collector creation ([#2860](https://github.com/containous/traefik/pull/2860) by [ferhatelmas](https://github.com/ferhatelmas))
- **[tracing]** Opentracing support ([#2587](https://github.com/containous/traefik/pull/2587) by [mmatur](https://github.com/mmatur))
- **[webui]** Add status code text to webui bar chart tooltip ([#2639](https://github.com/containous/traefik/pull/2639) by [wader](https://github.com/wader))
- Separate command from the main package ([#2951](https://github.com/containous/traefik/pull/2951) by [Juliens](https://github.com/Juliens))
- Use context in Server ([#3007](https://github.com/containous/traefik/pull/3007) by [Juliens](https://github.com/Juliens))
- Logger and Leaks ([#2847](https://github.com/containous/traefik/pull/2847) by [ldez](https://github.com/ldez))
**Bug fixes:**
- **[acme]** Check all the C/N and SANs of provided certificates before generating ACME certificates in ACME provider ([#2970](https://github.com/containous/traefik/pull/2970) by [nmengin](https://github.com/nmengin))
- **[docker,k8s,marathon]** Fix custom headers template ([#2622](https://github.com/containous/traefik/pull/2622) by [ldez](https://github.com/ldez))
- **[k8s]** Missing annotation prefix support. ([#2915](https://github.com/containous/traefik/pull/2915) by [ldez](https://github.com/ldez))
- **[k8s]** Remove hardcoded frontend prefix in Kubernetes template ([#2914](https://github.com/containous/traefik/pull/2914) by [psalaberria002](https://github.com/psalaberria002))
- **[logs,middleware]** Fix bad access log ([#2682](https://github.com/containous/traefik/pull/2682) by [mmatur](https://github.com/mmatur))
- **[middleware,tracing]** Fix &lt;nil&gt; tracer value in KV ([#2911](https://github.com/containous/traefik/pull/2911) by [mmatur](https://github.com/mmatur))
- **[middleware]** Use responseModifier to override secure headers ([#2946](https://github.com/containous/traefik/pull/2946) by [mmatur](https://github.com/mmatur))
- **[middleware]** Correct conditional setting of buffering retry expression. ([#2865](https://github.com/containous/traefik/pull/2865) by [ldez](https://github.com/ldez))
- **[middleware]** Fix high memory usage in retry middleware ([#2740](https://github.com/containous/traefik/pull/2740) by [marco-jantke](https://github.com/marco-jantke))
- **[provider]** Add some missing quotes in templates ([#2973](https://github.com/containous/traefik/pull/2973) by [ldez](https://github.com/ldez))
- **[tracing]** Fix missing configuration for jaeger reporter ([#2720](https://github.com/containous/traefik/pull/2720) by [mmatur](https://github.com/mmatur))
- **[tracing]** Tracing statusCodeTracker need to implement CloseNotify ([#2733](https://github.com/containous/traefik/pull/2733) by [mmatur](https://github.com/mmatur))
- **[tracing]** Fix integration tests in tracing ([#2759](https://github.com/containous/traefik/pull/2759) by [mmatur](https://github.com/mmatur))
- Remove unnecessary mutex usage in health checks ([#2726](https://github.com/containous/traefik/pull/2726) by [marco-jantke](https://github.com/marco-jantke))
**Documentation:**
- **[docker]** Add default values for some Docker labels ([#2604](https://github.com/containous/traefik/pull/2604) by [ldez](https://github.com/ldez))
- **[k8s]** Remove web provider in example ([#2807](https://github.com/containous/traefik/pull/2807) by [pigletfly](https://github.com/pigletfly))
- **[k8s]** Update traefik-ds.yaml with --api command line parameter ([#2803](https://github.com/containous/traefik/pull/2803) by [maniankara](https://github.com/maniankara))
- **[k8s]** Drop capabilities in Kubernetes DaemonSet example ([#3028](https://github.com/containous/traefik/pull/3028) by [nogoegst](https://github.com/nogoegst))
- **[k8s]** Docs: Fix typos in k8s user-guide ([#2898](https://github.com/containous/traefik/pull/2898) by [cez81](https://github.com/cez81))
- **[k8s]** Change boolean annotation values to string ([#2839](https://github.com/containous/traefik/pull/2839) by [hobti01](https://github.com/hobti01))
- **[provider]** Split security labels and custom labels documentation. ([#2872](https://github.com/containous/traefik/pull/2872) by [ldez](https://github.com/ldez))
- **[provider]** Remove non-supported label. ([#3065](https://github.com/containous/traefik/pull/3065) by [ldez](https://github.com/ldez))
- **[provider]** Remove obsolete paragraph about error pages. ([#2608](https://github.com/containous/traefik/pull/2608) by [ldez](https://github.com/ldez))
- **[servicefabric]** Add SF to supported backends in docs ([#3033](https://github.com/containous/traefik/pull/3033) by [lawrencegripper](https://github.com/lawrencegripper))
- Fix typo in doc for rate limit label ([#2790](https://github.com/containous/traefik/pull/2790) by [mmatur](https://github.com/mmatur))
- Add Tracing entry in the documentation. ([#2713](https://github.com/containous/traefik/pull/2713) by [ldez](https://github.com/ldez))
**Misc:**
- **[boltdb,consul,etcd,kv,zk]** Migrate from libkv to valkeyrie library ([#2743](https://github.com/containous/traefik/pull/2743) by [nmengin](https://github.com/nmengin))
- Merge v1.5.4 into master ([#3024](https://github.com/containous/traefik/pull/3024) by [ldez](https://github.com/ldez))
- Merge v1.5.3 into master ([#2943](https://github.com/containous/traefik/pull/2943) by [ldez](https://github.com/ldez))
- Merge v1.5.2 into master ([#2843](https://github.com/containous/traefik/pull/2843) by [ldez](https://github.com/ldez))
- Merge v1.5.1 into master ([#2781](https://github.com/containous/traefik/pull/2781) by [ldez](https://github.com/ldez))
- Merge v1.5.0-rc5 into master ([#2708](https://github.com/containous/traefik/pull/2708) by [ldez](https://github.com/ldez))
- Merge 1.5.0-rc3 into master ([#2600](https://github.com/containous/traefik/pull/2600) by [ldez](https://github.com/ldez))
- Drop unnecessary type conversions ([#2583](https://github.com/containous/traefik/pull/2583) by [ferhatelmas](https://github.com/ferhatelmas))
- Merge 1.5.0-rc2 into master ([#2536](https://github.com/containous/traefik/pull/2536) by [ldez](https://github.com/ldez))
- Code simplification ([#2516](https://github.com/containous/traefik/pull/2516) by [ferhatelmas](https://github.com/ferhatelmas))
## [v1.5.4](https://github.com/containous/traefik/tree/v1.5.4) (2018-03-15)
[All Commits](https://github.com/containous/traefik/compare/v1.5.3...v1.5.4)
**Bug fixes:**
- **[acme]** Fix panic when parsing resolv.conf ([#2955](https://github.com/containous/traefik/pull/2955) by [ldez](https://github.com/ldez))
- **[acme]** Don&#39;t failed traefik start if register and subscribe failed on acme ([#2977](https://github.com/containous/traefik/pull/2977) by [Juliens](https://github.com/Juliens))
- **[ecs]** Safe access to ECS API pointer values. ([#2983](https://github.com/containous/traefik/pull/2983) by [ldez](https://github.com/ldez))
- **[kv]** Add lower-case passHostHeader key support. ([#3015](https://github.com/containous/traefik/pull/3015) by [ldez](https://github.com/ldez))
- **[middleware]** Propagate insecure in white list. ([#2981](https://github.com/containous/traefik/pull/2981) by [ldez](https://github.com/ldez))
- **[rancher]** Fix Rancher Healthcheck when upgrading a service ([#2962](https://github.com/containous/traefik/pull/2962) by [jmirc](https://github.com/jmirc))
- **[websocket]** Capitalize Sec-WebSocket-Protocol Header ([#2975](https://github.com/containous/traefik/pull/2975) by [Juliens](https://github.com/Juliens))
- Use goroutine pool in throttleProvider ([#3013](https://github.com/containous/traefik/pull/3013) by [Juliens](https://github.com/Juliens))
- Handle quoted strings in UnmarshalJSON ([#3004](https://github.com/containous/traefik/pull/3004) by [Juliens](https://github.com/Juliens))
**Documentation:**
- **[acme]** Clarify some deprecations. ([#2959](https://github.com/containous/traefik/pull/2959) by [ldez](https://github.com/ldez))
- **[acme]** Second defaultEntryPoint should be https, not http. ([#2948](https://github.com/containous/traefik/pull/2948) by [GerbenWelter](https://github.com/GerbenWelter))
- **[api]** Enhance API, REST, ping documentation. ([#2950](https://github.com/containous/traefik/pull/2950) by [ldez](https://github.com/ldez))
- **[k8s]** Add TLS Docs ([#3012](https://github.com/containous/traefik/pull/3012) by [dtomcej](https://github.com/dtomcej))
- Enhance Traefik TOML sample. ([#2996](https://github.com/containous/traefik/pull/2996) by [ldez](https://github.com/ldez))
- Fix typo in docs ([#2990](https://github.com/containous/traefik/pull/2990) by [mo](https://github.com/mo))
- Clarify how setting a frontend priority works ([#2984](https://github.com/containous/traefik/pull/2984) by [jbdoumenjou](https://github.com/jbdoumenjou))
- Add [file] in syntax reference ([#3016](https://github.com/containous/traefik/pull/3016) by [ldez](https://github.com/ldez))
- Updated the test-it example according to the latest docker version ([#3000](https://github.com/containous/traefik/pull/3000) by [geraldcroes](https://github.com/geraldcroes))
## [v1.5.3](https://github.com/containous/traefik/tree/v1.5.3) (2018-02-27)
[All Commits](https://github.com/containous/traefik/compare/v1.5.2...v1.5.3)
**Bug fixes:**
- **[acme]** Check all the C/N and SANs of provided certificates before generating ACME certificates ([#2913](https://github.com/containous/traefik/pull/2913) by [nmengin](https://github.com/nmengin))
- **[docker/swarm]** Empty IP address when use endpoint mode dnsrr ([#2887](https://github.com/containous/traefik/pull/2887) by [mmatur](https://github.com/mmatur))
- **[middleware]** Infinite entry point redirection. ([#2929](https://github.com/containous/traefik/pull/2929) by [ldez](https://github.com/ldez))
- **[provider]** Isolate backend with same name on different provider ([#2862](https://github.com/containous/traefik/pull/2862) by [Juliens](https://github.com/Juliens))
- **[tls]** Starting Træfik even if TLS certificates are in error ([#2909](https://github.com/containous/traefik/pull/2909) by [nmengin](https://github.com/nmengin))
- **[tls]** Add DEBUG log when no provided certificate can check a domain ([#2938](https://github.com/containous/traefik/pull/2938) by [nmengin](https://github.com/nmengin))
- **[webui]** Smooth dashboard refresh. ([#2871](https://github.com/containous/traefik/pull/2871) by [ldez](https://github.com/ldez))
- Fix Duration JSON unmarshal ([#2935](https://github.com/containous/traefik/pull/2935) by [ldez](https://github.com/ldez))
- Default value for lifecycle ([#2934](https://github.com/containous/traefik/pull/2934) by [Juliens](https://github.com/Juliens))
- Check ping configuration. ([#2852](https://github.com/containous/traefik/pull/2852) by [ldez](https://github.com/ldez))
**Documentation:**
- **[docker]** it&#39;s -&gt; its ([#2901](https://github.com/containous/traefik/pull/2901) by [piec](https://github.com/piec))
- **[tls]** Fix doc cipher suites ([#2894](https://github.com/containous/traefik/pull/2894) by [emilevauge](https://github.com/emilevauge))
- Add a CLI help command for Docker. ([#2921](https://github.com/containous/traefik/pull/2921) by [ldez](https://github.com/ldez))
- Fix traffic pronounce dead link ([#2870](https://github.com/containous/traefik/pull/2870) by [emilevauge](https://github.com/emilevauge))
- Update documentation on onHostRule, ping examples, and web deprecation ([#2863](https://github.com/containous/traefik/pull/2863) by [Juliens](https://github.com/Juliens))
## [v1.5.2](https://github.com/containous/traefik/tree/v1.5.2) (2018-02-12)
[All Commits](https://github.com/containous/traefik/compare/v1.5.1...v1.5.2)
**Bug fixes:**
- **[acme,cluster,kv]** Compress ACME certificates in KV stores. ([#2814](https://github.com/containous/traefik/pull/2814) by [nmengin](https://github.com/nmengin))
- **[acme]** Traefik still start when Let&#39;s encrypt is down ([#2794](https://github.com/containous/traefik/pull/2794) by [Juliens](https://github.com/Juliens))
- **[docker]** Fix dnsrr endpoint mode excluded when not using swarm LB ([#2795](https://github.com/containous/traefik/pull/2795) by [mmatur](https://github.com/mmatur))
- **[eureka]** Continue refresh the configuration after a failure. ([#2838](https://github.com/containous/traefik/pull/2838) by [ldez](https://github.com/ldez))
- **[logs]** Reduce oxy round trip logs to debug. ([#2821](https://github.com/containous/traefik/pull/2821) by [timoreimann](https://github.com/timoreimann))
- **[websocket]** Fix goroutine leaks in websocket ([#2825](https://github.com/containous/traefik/pull/2825) by [Juliens](https://github.com/Juliens))
- Hide the pflag error when displaying help. ([#2800](https://github.com/containous/traefik/pull/2800) by [ldez](https://github.com/ldez))
**Documentation:**
- **[docker]** Explain how to write entrypoints definition in a compose file ([#2834](https://github.com/containous/traefik/pull/2834) by [mmatur](https://github.com/mmatur))
- **[docker]** Fix typo ([#2813](https://github.com/containous/traefik/pull/2813) by [uschtwill](https://github.com/uschtwill))
- **[k8s]** typo in &#34;i&#34;ngress annotations. ([#2780](https://github.com/containous/traefik/pull/2780) by [RRAlex](https://github.com/RRAlex))
- Clarify how setting a frontend priority works ([#2818](https://github.com/containous/traefik/pull/2818) by [sirlatrom](https://github.com/sirlatrom))
- Fixed typo. ([#2811](https://github.com/containous/traefik/pull/2811) by [sonus21](https://github.com/sonus21))
- Docs: regex+replacement hints for URL rewriting ([#2802](https://github.com/containous/traefik/pull/2802) by [djeeg](https://github.com/djeeg))
- Add documentation about entry points definition with CLI. ([#2798](https://github.com/containous/traefik/pull/2798) by [ldez](https://github.com/ldez))
## [v1.5.1](https://github.com/containous/traefik/tree/v1.5.1) (2018-01-29) ## [v1.5.1](https://github.com/containous/traefik/tree/v1.5.1) (2018-01-29)
[All Commits](https://github.com/containous/traefik/compare/v1.5.0...v1.5.1) [All Commits](https://github.com/containous/traefik/compare/v1.5.0...v1.5.1)

View File

@@ -13,7 +13,7 @@ You need to run the `binary` target. This will create binaries for Linux platfor
$ make binary $ make binary
docker build -t "traefik-dev:no-more-godep-ever" -f build.Dockerfile . docker build -t "traefik-dev:no-more-godep-ever" -f build.Dockerfile .
Sending build context to Docker daemon 295.3 MB Sending build context to Docker daemon 295.3 MB
Step 0 : FROM golang:1.9-alpine Step 0 : FROM golang:1.10-alpine
---> 8c6473912976 ---> 8c6473912976
Step 1 : RUN go get github.com/golang/dep/cmd/dep Step 1 : RUN go get github.com/golang/dep/cmd/dep
[...] [...]
@@ -64,7 +64,7 @@ Once your environment is set up and the Træfik repository cloned you can build
cd ~/go/src/github.com/containous/traefik cd ~/go/src/github.com/containous/traefik
# Get go-bindata. Please note, the ellipses are required # Get go-bindata. Please note, the ellipses are required
go get github.com/jteeuwen/go-bindata/... go get github.com/containous/go-bindata/...
# Start build # Start build
@@ -87,9 +87,11 @@ If you happen to update the provider templates (in `/templates`), you need to ru
[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) [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) >= O.4.1.
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). 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 prune-dep` run should be triggered to trim down the size of the vendor folder. 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. The final result must be committed into VCS.
Here's a full example using dep to add a new dependency: Here's a full example using dep to add a new dependency:

682
Gopkg.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -19,8 +19,6 @@
# name = "github.com/x/y" # name = "github.com/x/y"
# version = "2.4.0" # version = "2.4.0"
ignored = ["github.com/sirupsen/logrus"]
[[constraint]] [[constraint]]
branch = "master" branch = "master"
name = "github.com/ArthurHlt/go-eureka-client" name = "github.com/ArthurHlt/go-eureka-client"
@@ -48,7 +46,7 @@ ignored = ["github.com/sirupsen/logrus"]
[[constraint]] [[constraint]]
name = "github.com/aws/aws-sdk-go" name = "github.com/aws/aws-sdk-go"
version = "1.6.18" version = "1.13.1"
[[constraint]] [[constraint]]
branch = "master" branch = "master"
@@ -64,11 +62,11 @@ ignored = ["github.com/sirupsen/logrus"]
[[constraint]] [[constraint]]
name = "github.com/containous/staert" name = "github.com/containous/staert"
version = "2.0.0" version = "3.1.0"
[[constraint]] [[constraint]]
name = "github.com/containous/traefik-extra-service-fabric" name = "github.com/containous/traefik-extra-service-fabric"
version = "1.0.5" version = "1.1.1"
[[constraint]] [[constraint]]
name = "github.com/coreos/go-systemd" name = "github.com/coreos/go-systemd"
@@ -79,10 +77,6 @@ ignored = ["github.com/sirupsen/logrus"]
name = "github.com/docker/leadership" name = "github.com/docker/leadership"
source = "github.com/containous/leadership" source = "github.com/containous/leadership"
[[constraint]]
name = "github.com/docker/libkv"
source = "github.com/abronan/libkv"
[[constraint]] [[constraint]]
name = "github.com/eapache/channels" name = "github.com/eapache/channels"
version = "1.1.0" version = "1.1.0"
@@ -92,6 +86,12 @@ ignored = ["github.com/sirupsen/logrus"]
name = "github.com/elazarl/go-bindata-assetfs" name = "github.com/elazarl/go-bindata-assetfs"
[[constraint]] [[constraint]]
branch = "fork-containous"
name = "github.com/go-check/check"
source = "github.com/containous/check"
[[override]]
branch = "fork-containous"
name = "github.com/go-check/check" name = "github.com/go-check/check"
source = "github.com/containous/check" source = "github.com/containous/check"
@@ -99,6 +99,14 @@ ignored = ["github.com/sirupsen/logrus"]
name = "github.com/go-kit/kit" name = "github.com/go-kit/kit"
version = "0.3.0" version = "0.3.0"
[[constraint]]
branch = "master"
name = "github.com/gorilla/websocket"
[[constraint]]
name = "github.com/hashicorp/consul"
version = "1.0.6"
[[constraint]] [[constraint]]
name = "github.com/influxdata/influxdb" name = "github.com/influxdata/influxdb"
version = "1.3.7" version = "1.3.7"
@@ -108,8 +116,8 @@ ignored = ["github.com/sirupsen/logrus"]
name = "github.com/jjcollinge/servicefabric" name = "github.com/jjcollinge/servicefabric"
[[constraint]] [[constraint]]
name = "github.com/mattn/go-shellwords" branch = "master"
version = "1.0.3" name = "github.com/abronan/valkeyrie"
[[constraint]] [[constraint]]
name = "github.com/mesosphere/mesos-dns" name = "github.com/mesosphere/mesos-dns"
@@ -128,8 +136,13 @@ ignored = ["github.com/sirupsen/logrus"]
name = "github.com/mitchellh/mapstructure" name = "github.com/mitchellh/mapstructure"
[[constraint]] [[constraint]]
branch = "master" name = "github.com/opentracing/opentracing-go"
version = "1.0.2"
[[constraint]]
branch = "containous-fork"
name = "github.com/rancher/go-rancher-metadata" name = "github.com/rancher/go-rancher-metadata"
source = "github.com/containous/go-rancher-metadata"
[[constraint]] [[constraint]]
branch = "master" branch = "master"
@@ -143,18 +156,33 @@ ignored = ["github.com/sirupsen/logrus"]
branch = "master" branch = "master"
name = "github.com/stvp/go-udp-testing" name = "github.com/stvp/go-udp-testing"
[[constraint]]
name = "github.com/stretchr/testify"
version = "1.2.1"
[[constraint]]
name = "github.com/uber/jaeger-client-go"
version = "2.9.0"
[[constraint]]
name = "github.com/uber/jaeger-lib"
version = "1.1.0"
[[constraint]]
branch = "v1"
name = "github.com/unrolled/secure"
[[constraint]] [[constraint]]
name = "github.com/vdemeester/shakers" name = "github.com/vdemeester/shakers"
version = "0.1.0" version = "0.1.0"
[[constraint]] [[constraint]]
branch = "containous-fork" branch = "master"
name = "github.com/vulcand/oxy" name = "github.com/vulcand/oxy"
source = "https://github.com/containous/oxy.git"
[[constraint]] [[constraint]]
branch = "acmev2"
name = "github.com/xenolf/lego" name = "github.com/xenolf/lego"
version = "0.4.1"
[[constraint]] [[constraint]]
name = "google.golang.org/grpc" name = "google.golang.org/grpc"
@@ -162,31 +190,68 @@ ignored = ["github.com/sirupsen/logrus"]
[[constraint]] [[constraint]]
name = "gopkg.in/fsnotify.v1" name = "gopkg.in/fsnotify.v1"
source = "github.com/fsnotify/fsnotify"
version = "1.4.2" version = "1.4.2"
[[constraint]] [[constraint]]
name = "k8s.io/client-go" name = "k8s.io/client-go"
version = "2.0.0" version = "6.0.0"
[[constraint]]
name = "k8s.io/api"
version = "kubernetes-1.9.0"
[[constraint]]
name = "k8s.io/apimachinery"
version = "kubernetes-1.9.0"
[[constraint]]
branch = "master"
name = "github.com/libkermit/docker"
[[constraint]]
branch = "master"
name = "github.com/libkermit/docker-check"
[[constraint]]
branch = "master"
name = "github.com/libkermit/compose"
[[constraint]]
name = "github.com/docker/docker"
revision = "7848b8beb9d38a98a78b75f78e05f8d2255f9dfe"
[[override]]
name = "github.com/docker/docker"
revision = "7848b8beb9d38a98a78b75f78e05f8d2255f9dfe"
[[override]]
name = "github.com/docker/cli"
revision = "6b63d7b96a41055baddc3fa71f381c7f60bd5d8e"
[[override]]
name = "github.com/docker/distribution"
revision = "edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c"
[[override]]
branch = "master"
name = "github.com/docker/libcompose"
[[override]] [[override]]
name = "github.com/Nvveen/Gotty" name = "github.com/Nvveen/Gotty"
revision = "6018b68f96b839edfbe3fb48668853f5dbad88a3" revision = "a8b993ba6abdb0e0c12b0125c603323a71c7790c"
source = "github.com/ijc25/Gotty" source = "github.com/ijc25/Gotty"
[[override]] [[override]]
name = "github.com/gorilla/websocket" # ALWAYS keep this override
revision = "a69d9f6de432e2c6b296a947d8a5ee88f68522cf"
[[override]]
# always keep this override
name = "github.com/mailgun/timetools" name = "github.com/mailgun/timetools"
revision = "7e6055773c5137efbeb3bd2410d705fe10ab6bfd" revision = "7e6055773c5137efbeb3bd2410d705fe10ab6bfd"
[[override]] [[override]]
name = "github.com/vulcand/predicate" branch = "master"
revision = "19b9dde14240d94c804ae5736ad0e1de10bf8fe6" name = "github.com/miekg/dns"
[[override]] [prune]
# remove override on master non-go = true
name = "github.com/coreos/bbolt" go-tests = true
revision = "32c383e75ce054674c53b5a07e55de85332aee14" unused-packages = true

View File

@@ -73,7 +73,7 @@ test-integration: build ## run the integration tests
$(DOCKER_RUN_TRAEFIK) ./script/make.sh generate binary test-integration $(DOCKER_RUN_TRAEFIK) ./script/make.sh generate binary test-integration
TEST_HOST=1 ./script/make.sh test-integration TEST_HOST=1 ./script/make.sh test-integration
validate: build ## validate gofmt, golint and go vet validate: build ## validate code, vendor and autogen
$(DOCKER_RUN_TRAEFIK) ./script/make.sh validate-gofmt validate-govet validate-golint validate-misspell validate-vendor validate-autogen $(DOCKER_RUN_TRAEFIK) ./script/make.sh validate-gofmt validate-govet validate-golint validate-misspell validate-vendor validate-autogen
build: dist build: dist
@@ -127,7 +127,11 @@ fmt:
pull-images: pull-images:
grep --no-filename -E '^\s+image:' ./integration/resources/compose/*.yml | awk '{print $$2}' | sort | uniq | xargs -P 6 -n 1 docker pull grep --no-filename -E '^\s+image:' ./integration/resources/compose/*.yml | awk '{print $$2}' | sort | uniq | xargs -P 6 -n 1 docker pull
prune-dep: dep-ensure:
dep ensure -v
./script/prune-dep.sh
dep-prune:
./script/prune-dep.sh ./script/prune-dep.sh
help: ## this help help: ## this help

215
README.md
View File

@@ -12,8 +12,9 @@
[![Twitter](https://img.shields.io/twitter/follow/traefikproxy.svg?style=social)](https://twitter.com/intent/follow?screen_name=traefikproxy) [![Twitter](https://img.shields.io/twitter/follow/traefikproxy.svg?style=social)](https://twitter.com/intent/follow?screen_name=traefikproxy)
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. Træfik is a modern HTTP reverse proxy and load balancer that makes deploying microservices easy.
It supports several backends ([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 a lot more) to manage its configuration automatically and dynamically. Træfik 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.
Telling Træfik where your orchestrator is could be the _only_ configuration step you need to do.
--- ---
@@ -36,60 +37,101 @@ It supports several backends ([Docker](https://www.docker.com/), [Swarm mode](ht
## Overview ## 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. 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).
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: 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 Traditional reverse-proxies require that you configure _each_ route that will connect paths and subdomains to _each_ microservice.
- path `domain.com/web` will point the microservice `web` in your private network 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.
- domain `backoffice.domain.com` will point the microservices `backoffice` in your private network, load-balancing between your multiple instances
Microservices are often deployed in dynamic environments where services are added, removed, killed, upgraded or scaled many times a day. **This is when Træfik can help you!**
Traditional reverse-proxies are not natively dynamic. You can't change their configuration and hot-reload easily. Træfik 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 Træfik and let it do the work for you!**
_(But if you'd rather configure some of your routes manually, Træfik supports that too!)_
![Architecture](docs/img/architecture.png) ![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 ## Features
- [It's fast](https://docs.traefik.io/benchmarks) - Continuously updates its configuration (No restarts!)
- No dependency hell, single binary made with go - Supports multiple load balancing algorithms
- [Tiny](https://microbadger.com/images/traefik) [official](https://hub.docker.com/r/_/traefik/) docker image - Provides HTTPS to your microservices by leveraging [Let's Encrypt](https://letsencrypt.org) (wildcard certificates support)
- Rest API
- Hot-reloading of configuration. No need to restart the process
- Circuit breakers, retry - Circuit breakers, retry
- Round Robin, rebalancer load-balancers
- Metrics (Rest, Prometheus, Datadog, Statsd, InfluxDB)
- Clean AngularJS Web UI
- Websocket, HTTP/2, GRPC ready
- Access Logs (JSON, CLF)
- [Let's Encrypt](https://letsencrypt.org) support (Automatic HTTPS with renewal)
- [Proxy Protocol](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) support
- High Availability with cluster mode (beta) - 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](https://docs.traefik.io/benchmarks) ... which is nice
- 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://www.docker.com/) / [Swarm mode](https://docs.docker.com/engine/swarm/) ## Supported Backends
- [Kubernetes](https://kubernetes.io)
- [Mesos](https://github.com/apache/mesos) / [Marathon](https://mesosphere.github.io/marathon/) - [Docker](docs/configuration/backends/docker/) / [Swarm mode](docs/configuration/backends/docker/#docker-swarm-mode)
- [Rancher](https://rancher.com) (API, Metadata) - [Kubernetes](docs/configuration/backends/kubernetes/)
- [Consul](https://www.consul.io/) / [Etcd](https://coreos.com/etcd/) / [Zookeeper](https://zookeeper.apache.org) / [BoltDB](https://github.com/boltdb/bolt) - [Mesos](docs/configuration/backends/mesos/) / [Marathon](docs/configuration/backends/marathon/)
- [Eureka](https://github.com/Netflix/eureka) - [Rancher](docs/configuration/backends/rancher/) (API, Metadata)
- [Amazon ECS](https://aws.amazon.com/ecs) - [Service Fabric](docs/configuration/backends/servicefabric/)
- [Amazon DynamoDB](https://aws.amazon.com/dynamodb) - [Consul Catalog](docs/configuration/backends/consulcatalog/)
- File - [Consul](docs/configuration/backends/consul/) / [Etcd](docs/configuration/backends/etcd/) / [Zookeeper](docs/configuration/backends/zookeeper/) / [BoltDB](docs/configuration/backends/boltdb/)
- Rest API - [Eureka](docs/configuration/backends/eureka/)
- [Amazon ECS](docs/configuration/backends/ecs/)
- [Amazon DynamoDB](docs/configuration/backends/dynamodb/)
- [File](docs/configuration/backends/file/)
- [Rest](docs/configuration/backends/rest/)
## Quickstart ## 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. 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. To get your hands on Træfik, you can use the [5-Minute Quickstart](http://docs.traefik.io/#the-trfik-quickstart-using-docker) in our documentation (you will need Docker).
Alternatively, if you don't want to install anything on your computer, you can try Træfik 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.
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.
![Web UI Providers](docs/img/web.frontend.png)
![Web UI Health](docs/img/traefik-health.png)
## Documentation
You can find the complete documentation at [https://docs.traefik.io](https://docs.traefik.io).
A collection of contributions around Træfik can be found at [https://awesome.traefik.io](https://awesome.traefik.io).
## Support
To get community support, you can:
- join the Træfik community Slack channel: [![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)
- use [Stack Overflow](https://stackoverflow.com/questions/tagged/traefik) (using the `traefik` tag)
If you need commercial support, please contact [Containo.us](https://containo.us) by mail: <mailto:support@containo.us>.
## Download
- Grab the latest binary from the [releases](https://github.com/containous/traefik/releases) page and run it with the [sample configuration file](https://raw.githubusercontent.com/containous/traefik/master/traefik.sample.toml):
```shell
./traefik --configFile=traefik.toml
```
- Or use the official tiny Docker image and run it with the [sample configuration file](https://raw.githubusercontent.com/containous/traefik/master/traefik.sample.toml):
```shell
docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/etc/traefik/traefik.toml traefik
```
- Or get the sources:
```shell
git clone https://github.com/containous/traefik
```
## Introductory Videos
Here is a talk given by [Emile Vauge](https://github.com/emilevauge) at [GopherCon 2017](https://gophercon.com/). Here is a talk given by [Emile Vauge](https://github.com/emilevauge) at [GopherCon 2017](https://gophercon.com/).
You will learn Træfik basics in less than 10 minutes. You will learn Træfik basics in less than 10 minutes.
@@ -101,81 +143,26 @@ You will learn fundamental Træfik features and see some demos with Kubernetes.
[![Traefik ContainerCamp UK](https://img.youtube.com/vi/aFtpIShV60I/0.jpg)](https://www.youtube.com/watch?v=aFtpIShV60I) [![Traefik ContainerCamp UK](https://img.youtube.com/vi/aFtpIShV60I/0.jpg)](https://www.youtube.com/watch?v=aFtpIShV60I)
## Web UI
You can access the simple HTML frontend of Træfik.
![Web UI Providers](docs/img/web.frontend.png)
![Web UI Health](docs/img/traefik-health.png)
## Test it
- 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):
```shell
./traefik --configFile=traefik.toml
```
- Use the tiny Docker image and just run it with the [sample configuration file](https://raw.githubusercontent.com/containous/traefik/master/traefik.sample.toml):
```shell
docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/etc/traefik/traefik.toml traefik
```
- From sources:
```shell
git clone https://github.com/containous/traefik
```
## Documentation
You can find the complete documentation at [https://docs.traefik.io](https://docs.traefik.io).
A collection of contributions around Træfik can be found at [https://awesome.traefik.io](https://awesome.traefik.io).
## Support
To get basic support, you can:
- join the Træfik community Slack channel: [![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)
- use [Stack Overflow](https://stackoverflow.com/questions/tagged/traefik) (using the `traefik` tag)
If you prefer commercial support, please contact [containo.us](https://containo.us) by mail: <mailto:support@containo.us>.
## Release cycle
- Release: We try to release a new version every 2 months
- i.e.: 1.3.0, 1.4.0, 1.5.0
- Release candidate: we do RC (1.**x**.0-rc**y**) before the final release (1.**x**.0)
- i.e.: 1.1.0-rc1 -> 1.1.0-rc2 -> 1.1.0-rc3 -> 1.1.0-rc4 -> 1.1.0
- Bug-fixes: For each version we release bug fixes
- i.e.: 1.1.1, 1.1.2, 1.1.3
- those versions contain only bug-fixes
- no additional features are delivered in those versions
- Each version is supported until the next one is released
- i.e.: 1.1.x will be supported until 1.2.0 is out
- We use [Semantic Versioning](http://semver.org/)
## Contributing
Please refer to [contributing documentation](CONTRIBUTING.md).
### 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.
## Maintainers ## Maintainers
[Information about process and maintainers](MAINTAINER.md) [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/)
## Plumbing ## Plumbing
@@ -184,11 +171,11 @@ By participating in this project you agree to abide by its terms.
- [Negroni](https://github.com/urfave/negroni): web middlewares made simple - [Negroni](https://github.com/urfave/negroni): web middlewares made simple
- [Lego](https://github.com/xenolf/lego): the best [Let's Encrypt](https://letsencrypt.org) library in go - [Lego](https://github.com/xenolf/lego): the best [Let's Encrypt](https://letsencrypt.org) library in go
## Credits ## Credits
Kudos to [Peka](http://peka.byethost11.com/photoblog/) for his awesome work on the logo ![logo](docs/img/traefik.icon.png). 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). Traefik's logo was inspired by the gopher stickers made by Takuya Ueda (https://twitter.com/tenntenn).
The original Go gopher was designed by Renee French (http://reneefrench.blogspot.com/). The original Go gopher was designed by Renee French (http://reneefrench.blogspot.com/).

View File

@@ -14,7 +14,8 @@ import (
"time" "time"
"github.com/containous/traefik/log" "github.com/containous/traefik/log"
"github.com/xenolf/lego/acme" "github.com/containous/traefik/types"
acme "github.com/xenolf/lego/acmev2"
) )
// Account is used to store lets encrypt registration info // Account is used to store lets encrypt registration info
@@ -34,7 +35,7 @@ type ChallengeCert struct {
certificate *tls.Certificate certificate *tls.Certificate
} }
// Init inits account struct // Init account struct
func (a *Account) Init() error { func (a *Account) Init() error {
err := a.DomainsCertificate.Init() err := a.DomainsCertificate.Init()
if err != nil { if err != nil {
@@ -49,6 +50,7 @@ func (a *Account) Init() error {
} }
cert.certificate = &certificate cert.certificate = &certificate
} }
if cert.certificate.Leaf == nil { if cert.certificate.Leaf == nil {
leaf, err := x509.ParseCertificate(cert.certificate.Certificate[0]) leaf, err := x509.ParseCertificate(cert.certificate.Certificate[0])
if err != nil { if err != nil {
@@ -61,14 +63,19 @@ func (a *Account) Init() error {
} }
// NewAccount creates an account // NewAccount creates an account
func NewAccount(email string) (*Account, error) { func NewAccount(email string, certs []*DomainsCertificate) (*Account, error) {
// Create a user. New accounts need an email and private key to start // Create a user. New accounts need an email and private key to start
privateKey, err := rsa.GenerateKey(rand.Reader, 4096) privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil { if err != nil {
return nil, err return nil, err
} }
domainsCerts := DomainsCertificates{Certs: []*DomainsCertificate{}}
domainsCerts.Init() domainsCerts := DomainsCertificates{Certs: certs}
err = domainsCerts.Init()
if err != nil {
return nil, err
}
return &Account{ return &Account{
Email: email, Email: email,
PrivateKey: x509.MarshalPKCS1PrivateKey(privateKey), PrivateKey: x509.MarshalPKCS1PrivateKey(privateKey),
@@ -91,6 +98,7 @@ func (a *Account) GetPrivateKey() crypto.PrivateKey {
if privateKey, err := x509.ParsePKCS1PrivateKey(a.PrivateKey); err == nil { if privateKey, err := x509.ParsePKCS1PrivateKey(a.PrivateKey); err == nil {
return privateKey return privateKey
} }
log.Errorf("Cannot unmarshall private key %+v", a.PrivateKey) log.Errorf("Cannot unmarshall private key %+v", a.PrivateKey)
return nil return nil
} }
@@ -122,9 +130,11 @@ func (dc *DomainsCertificates) Less(i, j int) bool {
if reflect.DeepEqual(dc.Certs[i].Domains, dc.Certs[j].Domains) { 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) 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 { 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 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 return dc.Certs[i].Domains.Main < dc.Certs[j].Domains.Main
} }
@@ -142,29 +152,34 @@ func (dc *DomainsCertificates) removeDuplicates() {
} }
} }
// Init inits DomainsCertificates // Init DomainsCertificates
func (dc *DomainsCertificates) Init() error { func (dc *DomainsCertificates) Init() error {
dc.lock.Lock() dc.lock.Lock()
defer dc.lock.Unlock() defer dc.lock.Unlock()
for _, domainsCertificate := range dc.Certs { for _, domainsCertificate := range dc.Certs {
tlsCert, err := tls.X509KeyPair(domainsCertificate.Certificate.Certificate, domainsCertificate.Certificate.PrivateKey) tlsCert, err := tls.X509KeyPair(domainsCertificate.Certificate.Certificate, domainsCertificate.Certificate.PrivateKey)
if err != nil { if err != nil {
return err return err
} }
domainsCertificate.tlsCert = &tlsCert domainsCertificate.tlsCert = &tlsCert
if domainsCertificate.tlsCert.Leaf == nil { if domainsCertificate.tlsCert.Leaf == nil {
leaf, err := x509.ParseCertificate(domainsCertificate.tlsCert.Certificate[0]) leaf, err := x509.ParseCertificate(domainsCertificate.tlsCert.Certificate[0])
if err != nil { if err != nil {
return err return err
} }
domainsCertificate.tlsCert.Leaf = leaf domainsCertificate.tlsCert.Leaf = leaf
} }
} }
dc.removeDuplicates() dc.removeDuplicates()
return nil return nil
} }
func (dc *DomainsCertificates) renewCertificates(acmeCert *Certificate, domain Domain) error { func (dc *DomainsCertificates) renewCertificates(acmeCert *Certificate, domain types.Domain) error {
dc.lock.Lock() dc.lock.Lock()
defer dc.lock.Unlock() defer dc.lock.Unlock()
@@ -174,15 +189,17 @@ func (dc *DomainsCertificates) renewCertificates(acmeCert *Certificate, domain D
if err != nil { if err != nil {
return err return err
} }
domainsCertificate.Certificate = acmeCert domainsCertificate.Certificate = acmeCert
domainsCertificate.tlsCert = &tlsCert domainsCertificate.tlsCert = &tlsCert
return nil return nil
} }
} }
return fmt.Errorf("certificate to renew not found for domain %s", 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() dc.lock.Lock()
defer dc.lock.Unlock() defer dc.lock.Unlock()
@@ -190,6 +207,7 @@ func (dc *DomainsCertificates) addCertificateForDomains(acmeCert *Certificate, d
if err != nil { if err != nil {
return nil, err return nil, err
} }
cert := DomainsCertificate{Domains: domain, Certificate: acmeCert, tlsCert: &tlsCert} cert := DomainsCertificate{Domains: domain, Certificate: acmeCert, tlsCert: &tlsCert}
dc.Certs = append(dc.Certs, &cert) dc.Certs = append(dc.Certs, &cert)
return &cert, nil return &cert, nil
@@ -198,11 +216,12 @@ func (dc *DomainsCertificates) addCertificateForDomains(acmeCert *Certificate, d
func (dc *DomainsCertificates) getCertificateForDomain(domainToFind string) (*DomainsCertificate, bool) { func (dc *DomainsCertificates) getCertificateForDomain(domainToFind string) (*DomainsCertificate, bool) {
dc.lock.RLock() dc.lock.RLock()
defer dc.lock.RUnlock() defer dc.lock.RUnlock()
for _, domainsCertificate := range dc.Certs { for _, domainsCertificate := range dc.Certs {
domains := []string{} for _, domain := range domainsCertificate.Domains.ToStrArray() {
domains = append(domains, domainsCertificate.Domains.Main) if strings.HasPrefix(domain, "*.") && types.MatchDomain(domainToFind, domain) {
domains = append(domains, domainsCertificate.Domains.SANs...) return domainsCertificate, true
for _, domain := range domains { }
if domain == domainToFind { if domain == domainToFind {
return domainsCertificate, true return domainsCertificate, true
} }
@@ -211,9 +230,10 @@ func (dc *DomainsCertificates) getCertificateForDomain(domainToFind string) (*Do
return nil, false return nil, false
} }
func (dc *DomainsCertificates) exists(domainToFind Domain) (*DomainsCertificate, bool) { func (dc *DomainsCertificates) exists(domainToFind types.Domain) (*DomainsCertificate, bool) {
dc.lock.RLock() dc.lock.RLock()
defer dc.lock.RUnlock() defer dc.lock.RUnlock()
for _, domainsCertificate := range dc.Certs { for _, domainsCertificate := range dc.Certs {
if reflect.DeepEqual(domainToFind, domainsCertificate.Domains) { if reflect.DeepEqual(domainToFind, domainsCertificate.Domains) {
return domainsCertificate, true return domainsCertificate, true
@@ -222,9 +242,29 @@ func (dc *DomainsCertificates) exists(domainToFind Domain) (*DomainsCertificate,
return nil, false 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 // DomainsCertificate contains a certificate for multiple domains
type DomainsCertificate struct { type DomainsCertificate struct {
Domains Domain Domains types.Domain
Certificate *Certificate Certificate *Certificate
tlsCert *tls.Certificate tlsCert *tls.Certificate
} }
@@ -236,8 +276,9 @@ func (dc *DomainsCertificate) needRenew() bool {
// If there's an error, we assume the cert is broken, and needs update // If there's an error, we assume the cert is broken, and needs update
return true return true
} }
// <= 30 days left, renew certificate // <= 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 return true
} }
} }

View File

@@ -10,7 +10,7 @@ import (
"net" "net"
"net/http" "net/http"
"os" "os"
"regexp" "reflect"
"strings" "strings"
"time" "time"
@@ -21,12 +21,12 @@ import (
"github.com/containous/staert" "github.com/containous/staert"
"github.com/containous/traefik/cluster" "github.com/containous/traefik/cluster"
"github.com/containous/traefik/log" "github.com/containous/traefik/log"
acmeprovider "github.com/containous/traefik/provider/acme"
"github.com/containous/traefik/safe" "github.com/containous/traefik/safe"
traefikTls "github.com/containous/traefik/tls"
"github.com/containous/traefik/tls/generate" "github.com/containous/traefik/tls/generate"
"github.com/containous/traefik/types" "github.com/containous/traefik/types"
"github.com/eapache/channels" "github.com/eapache/channels"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acmev2"
"github.com/xenolf/lego/providers/dns" "github.com/xenolf/lego/providers/dns"
) )
@@ -36,24 +36,24 @@ var (
) )
// ACME allows to connect to lets encrypt and retrieve certs // ACME allows to connect to lets encrypt and retrieve certs
// Deprecated Please use provider/acme/Provider
type ACME struct { type ACME struct {
Email string `description:"Email address used for registration"` Email string `description:"Email address used for registration"`
Domains []Domain `description:"SANs (alternative domains) to each main domain using format: --acme.domains='main.com,san1.com,san2.com' --acme.domains='main.net,san1.net,san2.net'"` Domains []types.Domain `description:"SANs (alternative domains) to each main domain using format: --acme.domains='main.com,san1.com,san2.com' --acme.domains='main.net,san1.net,san2.net'"`
Storage string `description:"File or key used for certificates storage."` Storage string `description:"File or key used for certificates storage."`
StorageFile string // deprecated StorageFile string // deprecated
OnDemand bool `description:"Enable on demand certificate generation. This will request a certificate from Let's Encrypt during the first TLS handshake for a hostname that does not yet have a certificate."` //deprecated OnDemand bool `description:"Enable on demand certificate generation. This will request a certificate from Let's Encrypt during the first TLS handshake for a hostname that does not yet have a certificate."` //deprecated
OnHostRule bool `description:"Enable certificate generation on frontends Host rules."` OnHostRule bool `description:"Enable certificate generation on frontends Host rules."`
CAServer string `description:"CA server to use."` CAServer string `description:"CA server to use."`
EntryPoint string `description:"Entrypoint to proxy acme challenge to."` EntryPoint string `description:"Entrypoint to proxy acme challenge to."`
DNSChallenge *DNSChallenge `description:"Activate DNS-01 Challenge"` DNSChallenge *acmeprovider.DNSChallenge `description:"Activate DNS-01 Challenge"`
HTTPChallenge *HTTPChallenge `description:"Activate HTTP-01 Challenge"` HTTPChallenge *acmeprovider.HTTPChallenge `description:"Activate HTTP-01 Challenge"`
DNSProvider string `description:"Use a DNS-01 acme challenge rather than TLS-SNI-01 challenge."` // deprecated DNSProvider string `description:"Activate DNS-01 Challenge (Deprecated)"` // deprecated
DelayDontCheckDNS flaeg.Duration `description:"Assume DNS propagates after a delay in seconds rather than finding and querying nameservers."` // deprecated DelayDontCheckDNS flaeg.Duration `description:"Assume DNS propagates after a delay in seconds rather than finding and querying nameservers."` // deprecated
ACMELogging bool `description:"Enable debug logging of ACME actions."` ACMELogging bool `description:"Enable debug logging of ACME actions."`
client *acme.Client client *acme.Client
defaultCertificate *tls.Certificate defaultCertificate *tls.Certificate
store cluster.Store store cluster.Store
challengeTLSProvider *challengeTLSProvider
challengeHTTPProvider *challengeHTTPProvider challengeHTTPProvider *challengeHTTPProvider
checkOnDemandDomain func(domain string) bool checkOnDemandDomain func(domain string) bool
jobs *channels.InfiniteChannel jobs *channels.InfiniteChannel
@@ -61,58 +61,6 @@ type ACME struct {
dynamicCerts *safe.Safe dynamicCerts *safe.Safe
} }
// DNSChallenge contains DNS challenge Configuration
type DNSChallenge struct {
Provider string `description:"Use a DNS-01 based challenge provider rather than HTTPS."`
DelayBeforeCheck flaeg.Duration `description:"Assume DNS propagates after a delay in seconds rather than finding and querying nameservers."`
}
// HTTPChallenge contains HTTP challenge Configuration
type HTTPChallenge struct {
EntryPoint string `description:"HTTP challenge EntryPoint"`
}
//Domains parse []Domain
type Domains []Domain
//Set []Domain
func (ds *Domains) Set(str string) error {
fargs := func(c rune) bool {
return c == ',' || c == ';'
}
// get function
slice := strings.FieldsFunc(str, fargs)
if len(slice) < 1 {
return fmt.Errorf("Parse error ACME.Domain. Imposible to parse %s", str)
}
d := Domain{
Main: slice[0],
SANs: []string{},
}
if len(slice) > 1 {
d.SANs = slice[1:]
}
*ds = append(*ds, d)
return nil
}
//Get []Domain
func (ds *Domains) Get() interface{} { return []Domain(*ds) }
//String returns []Domain in string
func (ds *Domains) String() string { return fmt.Sprintf("%+v", *ds) }
//SetValue sets []Domain into the parser
func (ds *Domains) SetValue(val interface{}) {
*ds = Domains(val.([]Domain))
}
// Domain holds a domain name with SANs
type Domain struct {
Main string
SANs []string
}
func (a *ACME) init() error { func (a *ACME) init() error {
// FIXME temporary fix, waiting for https://github.com/xenolf/lego/pull/478 // FIXME temporary fix, waiting for https://github.com/xenolf/lego/pull/478
acme.HTTPClient = http.Client{ acme.HTTPClient = http.Client{
@@ -211,7 +159,6 @@ func (a *ACME) CreateClusterConfig(leadership *cluster.Leadership, tlsConfig *tl
} }
a.store = datastore a.store = datastore
a.challengeTLSProvider = &challengeTLSProvider{store: a.store}
ticker := time.NewTicker(24 * time.Hour) ticker := time.NewTicker(24 * time.Hour)
leadership.Pool.AddGoCtx(func(ctx context.Context) { leadership.Pool.AddGoCtx(func(ctx context.Context) {
@@ -237,20 +184,30 @@ func (a *ACME) leadershipListener(elected bool) error {
if err != nil { if err != nil {
return err return err
} }
transaction, object, err := a.store.Begin() transaction, object, err := a.store.Begin()
if err != nil { if err != nil {
return err return err
} }
account := object.(*Account) account := object.(*Account)
account.Init() account.Init()
var needRegister bool var needRegister bool
if account == nil || len(account.Email) == 0 { if account == nil || len(account.Email) == 0 {
account, err = NewAccount(a.Email) domainsCerts := DomainsCertificates{Certs: []*DomainsCertificate{}}
if account != nil {
domainsCerts = account.DomainsCertificate
}
account, err = NewAccount(a.Email, domainsCerts.Certs)
if err != nil { if err != nil {
return err return err
} }
needRegister = true needRegister = true
} }
a.client, err = a.buildACMEClient(account) a.client, err = a.buildACMEClient(account)
if err != nil { if err != nil {
return err return err
@@ -258,29 +215,15 @@ func (a *ACME) leadershipListener(elected bool) error {
if needRegister { if needRegister {
// New users will need to register; be sure to save it // New users will need to register; be sure to save it
log.Debug("Register...") log.Debug("Register...")
reg, err := a.client.Register()
reg, err := a.client.Register(true)
if err != nil { if err != nil {
return err return err
} }
account.Registration = reg account.Registration = reg
} }
// The client has a URL to the current Let's Encrypt Subscriber
// Agreement. The user will need to agree to it.
log.Debug("AgreeToTOS...")
err = a.client.AgreeToTOS()
if err != nil {
log.Debug(err)
// Let's Encrypt Subscriber Agreement renew ?
reg, err := a.client.QueryRegistration()
if err != nil {
return err
}
account.Registration = reg
err = a.client.AgreeToTOS()
if err != nil {
log.Errorf("Error sending ACME agreement to TOS: %+v: %s", account, err.Error())
}
}
err = transaction.Commit(account) err = transaction.Commit(account)
if err != nil { if err != nil {
return err return err
@@ -293,110 +236,14 @@ func (a *ACME) leadershipListener(elected bool) error {
return nil return nil
} }
// CreateLocalConfig creates a tls.config using local ACME configuration
func (a *ACME) CreateLocalConfig(tlsConfig *tls.Config, certs *safe.Safe, checkOnDemandDomain func(domain string) bool) error {
err := a.init()
if err != nil {
return err
}
if len(a.Storage) == 0 {
return errors.New("Empty Store, please provide a filename for certs storage")
}
a.checkOnDemandDomain = checkOnDemandDomain
a.dynamicCerts = certs
tlsConfig.Certificates = append(tlsConfig.Certificates, *a.defaultCertificate)
tlsConfig.GetCertificate = a.getCertificate
a.TLSConfig = tlsConfig
localStore := NewLocalStore(a.Storage)
a.store = localStore
a.challengeTLSProvider = &challengeTLSProvider{store: a.store}
var needRegister bool
var account *Account
if fileInfo, fileErr := os.Stat(a.Storage); fileErr == nil && fileInfo.Size() != 0 {
log.Info("Loading ACME Account...")
// load account
object, err := localStore.Load()
if err != nil {
return err
}
account = object.(*Account)
} else {
log.Info("Generating ACME Account...")
account, err = NewAccount(a.Email)
if err != nil {
return err
}
needRegister = true
}
a.client, err = a.buildACMEClient(account)
if err != nil {
return err
}
if needRegister {
// New users will need to register; be sure to save it
log.Info("Register...")
reg, err := a.client.Register()
if err != nil {
return err
}
account.Registration = reg
}
// The client has a URL to the current Let's Encrypt Subscriber
// Agreement. The user will need to agree to it.
log.Debug("AgreeToTOS...")
err = a.client.AgreeToTOS()
if err != nil {
// Let's Encrypt Subscriber Agreement renew ?
reg, err := a.client.QueryRegistration()
if err != nil {
return err
}
account.Registration = reg
err = a.client.AgreeToTOS()
if err != nil {
log.Errorf("Error sending ACME agreement to TOS: %+v: %s", account, err.Error())
}
}
// save account
transaction, _, err := a.store.Begin()
if err != nil {
return err
}
err = transaction.Commit(account)
if err != nil {
return err
}
a.retrieveCertificates()
a.renewCertificates()
a.runJobs()
ticker := time.NewTicker(24 * time.Hour)
safe.Go(func() {
for range ticker.C {
a.renewCertificates()
}
})
return nil
}
func (a *ACME) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) { func (a *ACME) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
domain := types.CanonicalDomain(clientHello.ServerName) domain := types.CanonicalDomain(clientHello.ServerName)
account := a.store.Get().(*Account) account := a.store.Get().(*Account)
if providedCertificate := a.getProvidedCertificate([]string{domain}); providedCertificate != nil { if providedCertificate := a.getProvidedCertificate(domain); providedCertificate != nil {
return providedCertificate, nil return providedCertificate, nil
} }
if challengeCert, ok := a.challengeTLSProvider.getCertificate(domain); ok {
log.Debugf("ACME got challenge %s", domain)
return challengeCert, nil
}
if domainCert, ok := account.DomainsCertificate.getCertificateForDomain(domain); ok { if domainCert, ok := account.DomainsCertificate.getCertificateForDomain(domain); ok {
log.Debugf("ACME got domain cert %s", domain) log.Debugf("ACME got domain cert %s", domain)
return domainCert.tlsCert, nil return domainCert.tlsCert, nil
@@ -414,36 +261,50 @@ func (a *ACME) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificat
func (a *ACME) retrieveCertificates() { func (a *ACME) retrieveCertificates() {
a.jobs.In() <- func() { a.jobs.In() <- func() {
log.Info("Retrieving ACME certificates...") log.Info("Retrieving ACME certificates...")
for _, domain := range a.Domains {
a.deleteUnnecessaryDomains()
for i := 0; i < len(a.Domains); i++ {
domain := a.Domains[i]
// check if cert isn't already loaded // check if cert isn't already loaded
account := a.store.Get().(*Account) account := a.store.Get().(*Account)
if _, exists := account.DomainsCertificate.exists(domain); !exists { if _, exists := account.DomainsCertificate.exists(domain); !exists {
domains := []string{} var domains []string
domains = append(domains, domain.Main) domains = append(domains, domain.Main)
domains = append(domains, domain.SANs...) domains = append(domains, domain.SANs...)
domains, err := a.getValidDomains(domains, true)
if err != nil {
log.Errorf("Error validating ACME certificate for domain %q: %s", domains, err)
continue
}
certificateResource, err := a.getDomainsCertificates(domains) certificateResource, err := a.getDomainsCertificates(domains)
if err != nil { if err != nil {
log.Errorf("Error getting ACME certificate for domain %s: %s", domains, err.Error()) log.Errorf("Error getting ACME certificate for domain %q: %s", domains, err)
continue continue
} }
transaction, object, err := a.store.Begin() transaction, object, err := a.store.Begin()
if err != nil { if err != nil {
log.Errorf("Error creating ACME store transaction from domain %s: %s", domain, err.Error()) log.Errorf("Error creating ACME store transaction from domain %q: %s", domain, err)
continue continue
} }
account = object.(*Account) account = object.(*Account)
_, err = account.DomainsCertificate.addCertificateForDomains(certificateResource, domain) _, err = account.DomainsCertificate.addCertificateForDomains(certificateResource, domain)
if err != nil { if err != nil {
log.Errorf("Error adding ACME certificate for domain %s: %s", domains, err.Error()) log.Errorf("Error adding ACME certificate for domain %q: %s", domains, err)
continue continue
} }
if err = transaction.Commit(account); err != nil { if err = transaction.Commit(account); err != nil {
log.Errorf("Error Saving ACME account %+v: %s", account, err.Error()) log.Errorf("Error Saving ACME account %+v: %s", account, err)
continue continue
} }
} }
} }
log.Info("Retrieved ACME certificates") log.Info("Retrieved ACME certificates")
} }
} }
@@ -461,7 +322,7 @@ func (a *ACME) renewCertificates() {
continue continue
} }
operation := func() error { operation := func() error {
return a.storeRenewedCertificate(account, certificateResource, renewedACMECert) return a.storeRenewedCertificate(certificateResource, renewedACMECert)
} }
notify := func(err error, time time.Duration) { notify := func(err error, time time.Duration) {
log.Warnf("Renewed certificate storage error: %v, retrying in %s", err, time) log.Warnf("Renewed certificate storage error: %v, retrying in %s", err, time)
@@ -499,14 +360,14 @@ func (a *ACME) renewACMECertificate(certificateResource *DomainsCertificate) (*C
}, nil }, nil
} }
func (a *ACME) storeRenewedCertificate(account *Account, certificateResource *DomainsCertificate, renewedACMECert *Certificate) error { func (a *ACME) storeRenewedCertificate(certificateResource *DomainsCertificate, renewedACMECert *Certificate) error {
transaction, object, err := a.store.Begin() transaction, object, err := a.store.Begin()
if err != nil { if err != nil {
return fmt.Errorf("error during transaction initialization for renewing certificate: %v", err) return fmt.Errorf("error during transaction initialization for renewing certificate: %v", err)
} }
log.Infof("Renewing certificate in data store : %+v ", certificateResource.Domains) log.Infof("Renewing certificate in data store : %+v ", certificateResource.Domains)
account = object.(*Account) account := object.(*Account)
err = account.DomainsCertificate.renewCertificates(renewedACMECert, certificateResource.Domains) err = account.DomainsCertificate.renewCertificates(renewedACMECert, certificateResource.Domains)
if err != nil { if err != nil {
return fmt.Errorf("error renewing certificate in datastore: %v ", err) return fmt.Errorf("error renewing certificate in datastore: %v ", err)
@@ -544,7 +405,7 @@ func dnsOverrideDelay(delay flaeg.Duration) error {
func (a *ACME) buildACMEClient(account *Account) (*acme.Client, error) { func (a *ACME) buildACMEClient(account *Account) (*acme.Client, error) {
log.Debug("Building ACME client...") log.Debug("Building ACME client...")
caServer := "https://acme-v01.api.letsencrypt.org/directory" caServer := "https://acme-v02.api.letsencrypt.org/directory"
if len(a.CAServer) > 0 { if len(a.CAServer) > 0 {
caServer = a.CAServer caServer = a.CAServer
} }
@@ -567,15 +428,15 @@ func (a *ACME) buildACMEClient(account *Account) (*acme.Client, error) {
return nil, err return nil, err
} }
client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.TLSSNI01}) client.ExcludeChallenges([]acme.Challenge{acme.HTTP01})
err = client.SetChallengeProvider(acme.DNS01, provider) err = client.SetChallengeProvider(acme.DNS01, provider)
} else if a.HTTPChallenge != nil && len(a.HTTPChallenge.EntryPoint) > 0 { } else if a.HTTPChallenge != nil && len(a.HTTPChallenge.EntryPoint) > 0 {
client.ExcludeChallenges([]acme.Challenge{acme.DNS01, acme.TLSSNI01}) log.Debug("Using HTTP Challenge provider.")
client.ExcludeChallenges([]acme.Challenge{acme.DNS01})
a.challengeHTTPProvider = &challengeHTTPProvider{store: a.store} a.challengeHTTPProvider = &challengeHTTPProvider{store: a.store}
err = client.SetChallengeProvider(acme.HTTP01, a.challengeHTTPProvider) err = client.SetChallengeProvider(acme.HTTP01, a.challengeHTTPProvider)
} else { } else {
client.ExcludeChallenges([]acme.Challenge{acme.HTTP01, acme.DNS01}) return nil, errors.New("ACME challenge not specified, please select HTTP or DNS Challenge")
err = client.SetChallengeProvider(acme.TLSSNI01, a.challengeTLSProvider)
} }
if err != nil { if err != nil {
@@ -601,7 +462,7 @@ func (a *ACME) loadCertificateOnDemand(clientHello *tls.ClientHelloInfo) (*tls.C
return nil, err return nil, err
} }
account = object.(*Account) account = object.(*Account)
cert, err := account.DomainsCertificate.addCertificateForDomains(certificate, Domain{Main: domain}) cert, err := account.DomainsCertificate.addCertificateForDomains(certificate, types.Domain{Main: domain})
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -616,15 +477,9 @@ func (a *ACME) LoadCertificateForDomains(domains []string) {
a.jobs.In() <- func() { a.jobs.In() <- func() {
log.Debugf("LoadCertificateForDomains %v...", domains) log.Debugf("LoadCertificateForDomains %v...", domains)
if len(domains) == 0 { domains, err := a.getValidDomains(domains, false)
// no domain if err != nil {
return log.Errorf("Error getting valid domain: %v", err)
}
domains = fun.Map(types.CanonicalDomain, domains).([]string)
// Check provided certificates
if a.getProvidedCertificate(domains) != nil {
return return
} }
@@ -639,38 +494,40 @@ func (a *ACME) LoadCertificateForDomains(domains []string) {
} }
ebo := backoff.NewExponentialBackOff() ebo := backoff.NewExponentialBackOff()
ebo.MaxElapsedTime = 30 * time.Second ebo.MaxElapsedTime = 30 * time.Second
err := backoff.RetryNotify(safe.OperationWithRecover(operation), ebo, notify) err = backoff.RetryNotify(safe.OperationWithRecover(operation), ebo, notify)
if err != nil { if err != nil {
log.Errorf("Error getting ACME client: %v", err) log.Errorf("Error getting ACME client: %v", err)
return return
} }
account := a.store.Get().(*Account) account := a.store.Get().(*Account)
var domain Domain
if len(domains) > 1 { // Check provided certificates
domain = Domain{Main: domains[0], SANs: domains[1:]} uncheckedDomains := a.getUncheckedDomains(domains, account)
} else { if len(uncheckedDomains) == 0 {
domain = Domain{Main: domains[0]}
}
if _, exists := account.DomainsCertificate.exists(domain); exists {
// domain already exists
return return
} }
certificate, err := a.getDomainsCertificates(domains) certificate, err := a.getDomainsCertificates(uncheckedDomains)
if err != nil { if err != nil {
log.Errorf("Error getting ACME certificates %+v : %v", domains, err) log.Errorf("Error getting ACME certificates %+v : %v", uncheckedDomains, err)
return return
} }
log.Debugf("Got certificate for domains %+v", domains) log.Debugf("Got certificate for domains %+v", uncheckedDomains)
transaction, object, err := a.store.Begin() transaction, object, err := a.store.Begin()
if err != nil { if err != nil {
log.Errorf("Error creating transaction %+v : %v", domains, err) log.Errorf("Error creating transaction %+v : %v", uncheckedDomains, err)
return return
} }
var domain types.Domain
if len(uncheckedDomains) > 1 {
domain = types.Domain{Main: uncheckedDomains[0], SANs: uncheckedDomains[1:]}
} else {
domain = types.Domain{Main: uncheckedDomains[0]}
}
account = object.(*Account) account = object.(*Account)
_, err = account.DomainsCertificate.addCertificateForDomains(certificate, domain) _, err = account.DomainsCertificate.addCertificateForDomains(certificate, domain)
if err != nil { if err != nil {
log.Errorf("Error adding ACME certificates %+v : %v", domains, err) log.Errorf("Error adding ACME certificates %+v : %v", uncheckedDomains, err)
return return
} }
if err = transaction.Commit(account); err != nil { if err = transaction.Commit(account); err != nil {
@@ -682,36 +539,88 @@ func (a *ACME) LoadCertificateForDomains(domains []string) {
// Get provided certificate which check a domains list (Main and SANs) // Get provided certificate which check a domains list (Main and SANs)
// from static and dynamic provided certificates // from static and dynamic provided certificates
func (a *ACME) getProvidedCertificate(domains []string) *tls.Certificate { func (a *ACME) getProvidedCertificate(domains string) *tls.Certificate {
log.Debugf("Looking for provided certificate to validate %s...", domains) log.Debugf("Looking for provided certificate to validate %s...", domains)
cert := searchProvidedCertificateForDomains(domains, a.TLSConfig.NameToCertificate) cert := searchProvidedCertificateForDomains(domains, a.TLSConfig.NameToCertificate)
if cert == nil && a.dynamicCerts != nil && a.dynamicCerts.Get() != nil { if cert == nil && a.dynamicCerts != nil && a.dynamicCerts.Get() != nil {
cert = searchProvidedCertificateForDomains(domains, a.dynamicCerts.Get().(*traefikTls.DomainsCertificates).Get().(map[string]*tls.Certificate)) cert = searchProvidedCertificateForDomains(domains, a.dynamicCerts.Get().(map[string]*tls.Certificate))
}
if cert == nil {
log.Debugf("No provided certificate found for domains %s, get ACME certificate.", domains)
} }
log.Debugf("No provided certificate found for domains %s, get ACME certificate.", domains)
return cert return cert
} }
func searchProvidedCertificateForDomains(domains []string, certs map[string]*tls.Certificate) *tls.Certificate { func searchProvidedCertificateForDomains(domain string, certs map[string]*tls.Certificate) *tls.Certificate {
// Use regex to test for provided certs that might have been added into TLSConfig // Use regex to test for provided certs that might have been added into TLSConfig
providedCertMatch := false for certDomains := range certs {
for k := range certs { domainChecked := false
selector := "^" + strings.Replace(k, "*.", "[^\\.]*\\.?", -1) + "$" for _, certDomain := range strings.Split(certDomains, ",") {
for _, domainToCheck := range domains { domainChecked = types.MatchDomain(domain, certDomain)
providedCertMatch, _ = regexp.MatchString(selector, domainToCheck) if domainChecked {
if !providedCertMatch {
break break
} }
} }
if providedCertMatch { if domainChecked {
log.Debugf("Got provided certificate for domains %s", domains) log.Debugf("Domain %q checked by provided certificate %q", domain, certDomains)
return certs[k] return certs[certDomains]
} }
} }
return nil return nil
} }
// Get provided certificate which check a domains list (Main and SANs)
// from static and dynamic provided certificates
func (a *ACME) getUncheckedDomains(domains []string, account *Account) []string {
log.Debugf("Looking for provided certificate to validate %s...", domains)
allCerts := make(map[string]*tls.Certificate)
// Get static certificates
for domains, certificate := range a.TLSConfig.NameToCertificate {
allCerts[domains] = certificate
}
// Get dynamic certificates
if a.dynamicCerts != nil && a.dynamicCerts.Get() != nil {
for domains, certificate := range a.dynamicCerts.Get().(map[string]*tls.Certificate) {
allCerts[domains] = certificate
}
}
// Get ACME certificates
if account != nil {
for domains, certificate := range account.DomainsCertificate.toDomainsMap() {
allCerts[domains] = certificate
}
}
// Get Configuration Domains
for i := 0; i < len(a.Domains); i++ {
allCerts[a.Domains[i].Main] = &tls.Certificate{}
for _, san := range a.Domains[i].SANs {
allCerts[san] = &tls.Certificate{}
}
}
return searchUncheckedDomains(domains, allCerts)
}
func searchUncheckedDomains(domains []string, certs map[string]*tls.Certificate) []string {
var uncheckedDomains []string
for _, domainToCheck := range domains {
if !isDomainAlreadyChecked(domainToCheck, certs) {
uncheckedDomains = append(uncheckedDomains, domainToCheck)
}
}
if len(uncheckedDomains) == 0 {
log.Debugf("No ACME certificate to generate for domains %q.", domains)
} else {
log.Debugf("Domains %q need ACME certificates generation for domains %q.", domains, strings.Join(uncheckedDomains, ","))
}
return uncheckedDomains
}
func (a *ACME) getDomainsCertificates(domains []string) (*Certificate, error) { func (a *ACME) getDomainsCertificates(domains []string) (*Certificate, error) {
domains = fun.Map(types.CanonicalDomain, domains).([]string) domains = fun.Map(types.CanonicalDomain, domains).([]string)
log.Debugf("Loading ACME certificates %s...", domains) log.Debugf("Loading ACME certificates %s...", domains)
@@ -739,3 +648,99 @@ func (a *ACME) runJobs() {
} }
}) })
} }
// getValidDomains checks if given domain is allowed to generate a ACME certificate and return it
func (a *ACME) getValidDomains(domains []string, wildcardAllowed bool) ([]string, error) {
if len(domains) == 0 || (len(domains) == 1 && len(domains[0]) == 0) {
return nil, errors.New("unable to generate a certificate when no domain is given")
}
if strings.HasPrefix(domains[0], "*") {
if !wildcardAllowed {
return nil, fmt.Errorf("unable to generate a wildcard certificate for domain %q from a 'Host' rule", strings.Join(domains, ","))
}
if a.DNSChallenge == nil && len(a.DNSProvider) == 0 {
return nil, fmt.Errorf("unable to generate a wildcard certificate for domain %q : ACME needs a DNSChallenge", strings.Join(domains, ","))
}
if len(domains) > 1 {
return nil, fmt.Errorf("unable to generate a wildcard certificate for domain %q : SANs are not allowed", strings.Join(domains, ","))
}
} else {
for _, san := range domains[1:] {
if strings.HasPrefix(san, "*") {
return nil, fmt.Errorf("unable to generate a certificate in ACME provider for domains %q: SANs can not be a wildcard domain", strings.Join(domains, ","))
}
}
}
domains = fun.Map(types.CanonicalDomain, domains).([]string)
return domains, nil
}
func isDomainAlreadyChecked(domainToCheck string, existentDomains map[string]*tls.Certificate) bool {
for certDomains := range existentDomains {
for _, certDomain := range strings.Split(certDomains, ",") {
if types.MatchDomain(domainToCheck, certDomain) {
return true
}
}
}
return false
}
// deleteUnnecessaryDomains deletes from the configuration :
// - Duplicated domains
// - Domains which are checked by wildcard domain
func (a *ACME) deleteUnnecessaryDomains() {
var newDomains []types.Domain
for idxDomainToCheck, domainToCheck := range a.Domains {
keepDomain := true
for idxDomain, domain := range a.Domains {
if idxDomainToCheck == idxDomain {
continue
}
if reflect.DeepEqual(domain, domainToCheck) {
if idxDomainToCheck > idxDomain {
log.Warnf("The domain %v is duplicated in the configuration but will be process by ACME only once.", domainToCheck)
keepDomain = false
}
break
} else if strings.HasPrefix(domain.Main, "*") && domain.SANs == nil {
// Check if domains can be validated by the wildcard domain
var newDomainsToCheck []string
// Check if domains can be validated by the wildcard domain
domainsMap := make(map[string]*tls.Certificate)
domainsMap[domain.Main] = &tls.Certificate{}
for _, domainProcessed := range domainToCheck.ToStrArray() {
if isDomainAlreadyChecked(domainProcessed, domainsMap) {
log.Warnf("Domain %q will not be processed by ACME because it is validated by the wildcard %q", domainProcessed, domain.Main)
continue
}
newDomainsToCheck = append(newDomainsToCheck, domainProcessed)
}
// Delete the domain if both Main and SANs can be validated by the wildcard domain
// otherwise keep the unchecked values
if newDomainsToCheck == nil {
keepDomain = false
break
}
domainToCheck.Set(newDomainsToCheck)
}
}
if keepDomain {
newDomains = append(newDomains, domainToCheck)
}
}
a.Domains = newDomains
}

View File

@@ -10,76 +10,122 @@ import (
"testing" "testing"
"time" "time"
acmeprovider "github.com/containous/traefik/provider/acme"
"github.com/containous/traefik/tls/generate" "github.com/containous/traefik/tls/generate"
"github.com/containous/traefik/types"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/xenolf/lego/acme" "github.com/xenolf/lego/acmev2"
) )
func TestDomainsSet(t *testing.T) { func TestDomainsSet(t *testing.T) {
checkMap := map[string]Domains{ testCases := []struct {
"": {}, input string
"foo.com": {Domain{Main: "foo.com", SANs: []string{}}}, expected types.Domains
"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"}}}, {
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{} for _, test := range testCases {
ds.Set(in) test := test
if !reflect.DeepEqual(check, ds) { t.Run(test.input, func(t *testing.T) {
t.Errorf("Expected %+v\nGot %+v", check, ds) t.Parallel()
}
domains := types.Domains{}
domains.Set(test.input)
assert.Exactly(t, test.expected, domains)
})
} }
} }
func TestDomainsSetAppend(t *testing.T) { func TestDomainsSetAppend(t *testing.T) {
inSlice := []string{ testCases := []struct {
"", input string
"foo1.com", expected types.Domains
"foo2.com,bar.net", }{
"foo3.com,bar1.net,bar2.net,bar3.net", {
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{
{}, // append to
{ domains := types.Domains{}
Domain{ for _, test := range testCases {
Main: "foo1.com", t.Run(test.input, func(t *testing.T) {
SANs: []string{}}},
{ domains.Set(test.input)
Domain{ assert.Exactly(t, test.expected, domains)
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)
}
} }
} }
func TestCertificatesRenew(t *testing.T) { func TestCertificatesRenew(t *testing.T) {
foo1Cert, foo1Key, _ := generate.KeyPair("foo1.com", time.Now()) foo1Cert, foo1Key, _ := generate.KeyPair("foo1.com", time.Now())
foo2Cert, foo2Key, _ := generate.KeyPair("foo2.com", time.Now()) foo2Cert, foo2Key, _ := generate.KeyPair("foo2.com", time.Now())
domainsCertificates := DomainsCertificates{ domainsCertificates := DomainsCertificates{
lock: sync.RWMutex{}, lock: sync.RWMutex{},
Certs: []*DomainsCertificate{ Certs: []*DomainsCertificate{
{ {
Domains: Domain{ Domains: types.Domain{
Main: "foo1.com", Main: "foo1.com"},
SANs: []string{}},
Certificate: &Certificate{ Certificate: &Certificate{
Domain: "foo1.com", Domain: "foo1.com",
CertURL: "url", CertURL: "url",
@@ -89,9 +135,8 @@ func TestCertificatesRenew(t *testing.T) {
}, },
}, },
{ {
Domains: Domain{ Domains: types.Domain{
Main: "foo2.com", Main: "foo2.com"},
SANs: []string{}},
Certificate: &Certificate{ Certificate: &Certificate{
Domain: "foo2.com", Domain: "foo2.com",
CertURL: "url", CertURL: "url",
@@ -102,6 +147,7 @@ func TestCertificatesRenew(t *testing.T) {
}, },
}, },
} }
foo1Cert, foo1Key, _ = generate.KeyPair("foo1.com", time.Now()) foo1Cert, foo1Key, _ = generate.KeyPair("foo1.com", time.Now())
newCertificate := &Certificate{ newCertificate := &Certificate{
Domain: "foo1.com", Domain: "foo1.com",
@@ -111,17 +157,15 @@ func TestCertificatesRenew(t *testing.T) {
Certificate: foo1Cert, Certificate: foo1Cert,
} }
err := domainsCertificates.renewCertificates( err := domainsCertificates.renewCertificates(newCertificate, types.Domain{Main: "foo1.com"})
newCertificate,
Domain{
Main: "foo1.com",
SANs: []string{}})
if err != nil { if err != nil {
t.Errorf("Error in renewCertificates :%v", err) t.Errorf("Error in renewCertificates :%v", err)
} }
if len(domainsCertificates.Certs) != 2 { if len(domainsCertificates.Certs) != 2 {
t.Errorf("Expected domainsCertificates length %d %+v\nGot %+v", 2, domainsCertificates.Certs, len(domainsCertificates.Certs)) t.Errorf("Expected domainsCertificates length %d %+v\nGot %+v", 2, domainsCertificates.Certs, len(domainsCertificates.Certs))
} }
if !reflect.DeepEqual(domainsCertificates.Certs[0].Certificate, newCertificate) { if !reflect.DeepEqual(domainsCertificates.Certs[0].Certificate, newCertificate) {
t.Errorf("Expected new certificate %+v \nGot %+v", newCertificate, domainsCertificates.Certs[0].Certificate) t.Errorf("Expected new certificate %+v \nGot %+v", newCertificate, domainsCertificates.Certs[0].Certificate)
} }
@@ -137,9 +181,8 @@ func TestRemoveDuplicates(t *testing.T) {
lock: sync.RWMutex{}, lock: sync.RWMutex{},
Certs: []*DomainsCertificate{ Certs: []*DomainsCertificate{
{ {
Domains: Domain{ Domains: types.Domain{
Main: "foo.com", Main: "foo.com"},
SANs: []string{}},
Certificate: &Certificate{ Certificate: &Certificate{
Domain: "foo.com", Domain: "foo.com",
CertURL: "url", CertURL: "url",
@@ -149,9 +192,8 @@ func TestRemoveDuplicates(t *testing.T) {
}, },
}, },
{ {
Domains: Domain{ Domains: types.Domain{
Main: "foo.com", Main: "foo.com"},
SANs: []string{}},
Certificate: &Certificate{ Certificate: &Certificate{
Domain: "foo.com", Domain: "foo.com",
CertURL: "url", CertURL: "url",
@@ -161,9 +203,8 @@ func TestRemoveDuplicates(t *testing.T) {
}, },
}, },
{ {
Domains: Domain{ Domains: types.Domain{
Main: "foo.com", Main: "foo.com"},
SANs: []string{}},
Certificate: &Certificate{ Certificate: &Certificate{
Domain: "foo.com", Domain: "foo.com",
CertURL: "url", CertURL: "url",
@@ -173,9 +214,8 @@ func TestRemoveDuplicates(t *testing.T) {
}, },
}, },
{ {
Domains: Domain{ Domains: types.Domain{
Main: "bar.com", Main: "bar.com"},
SANs: []string{}},
Certificate: &Certificate{ Certificate: &Certificate{
Domain: "bar.com", Domain: "bar.com",
CertURL: "url", CertURL: "url",
@@ -185,9 +225,8 @@ func TestRemoveDuplicates(t *testing.T) {
}, },
}, },
{ {
Domains: Domain{ Domains: types.Domain{
Main: "foo.com", Main: "foo.com"},
SANs: []string{}},
Certificate: &Certificate{ Certificate: &Certificate{
Domain: "foo.com", Domain: "foo.com",
CertURL: "url", CertURL: "url",
@@ -260,14 +299,19 @@ llJh9MC0svjevGtNlxJoE3lmEQIhAKXy1wfZ32/XtcrnENPvi6lzxI0T94X7s5pP3aCoPPoJAiEAl
cijFkALeQp/qyeXdFld2v9gUN3eCgljgcl0QweRoIc=---`) cijFkALeQp/qyeXdFld2v9gUN3eCgljgcl0QweRoIc=---`)
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{ w.Write([]byte(`{
"new-authz": "https://foo/acme/new-authz", "GPHhmRVEDas": "https://community.letsencrypt.org/t/adding-random-entries-to-the-directory/33417",
"new-cert": "https://foo/acme/new-cert", "keyChange": "https://foo/acme/key-change",
"new-reg": "https://foo/acme/new-reg", "meta": {
"revoke-cert": "https://foo/acme/revoke-cert" "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"
}`)) }`))
})) }))
defer ts.Close() defer ts.Close()
a := ACME{DNSChallenge: &DNSChallenge{Provider: "manual", DelayBeforeCheck: 10}, CAServer: ts.URL} a := ACME{DNSChallenge: &acmeprovider.DNSChallenge{Provider: "manual", DelayBeforeCheck: 10}, CAServer: ts.URL}
client, err := a.buildACMEClient(account) client, err := a.buildACMEClient(account)
if err != nil { if err != nil {
@@ -281,7 +325,7 @@ cijFkALeQp/qyeXdFld2v9gUN3eCgljgcl0QweRoIc=---`)
} }
} }
func TestAcme_getProvidedCertificate(t *testing.T) { func TestAcme_getUncheckedCertificates(t *testing.T) {
mm := make(map[string]*tls.Certificate) mm := make(map[string]*tls.Certificate)
mm["*.containo.us"] = &tls.Certificate{} mm["*.containo.us"] = &tls.Certificate{}
mm["traefik.acme.io"] = &tls.Certificate{} mm["traefik.acme.io"] = &tls.Certificate{}
@@ -289,9 +333,204 @@ func TestAcme_getProvidedCertificate(t *testing.T) {
a := ACME{TLSConfig: &tls.Config{NameToCertificate: mm}} a := ACME{TLSConfig: &tls.Config{NameToCertificate: mm}}
domains := []string{"traefik.containo.us", "trae.containo.us"} domains := []string{"traefik.containo.us", "trae.containo.us"}
certificate := a.getProvidedCertificate(domains) uncheckedDomains := a.getUncheckedDomains(domains, nil)
assert.NotNil(t, certificate) assert.Empty(t, uncheckedDomains)
domains = []string{"traefik.acme.io", "trae.acme.io"} domains = []string{"traefik.acme.io", "trae.acme.io"}
certificate = a.getProvidedCertificate(domains) 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)
}
func TestAcme_getProvidedCertificate(t *testing.T) {
mm := make(map[string]*tls.Certificate)
mm["*.containo.us"] = &tls.Certificate{}
mm["traefik.acme.io"] = &tls.Certificate{}
a := ACME{TLSConfig: &tls.Config{NameToCertificate: mm}}
domain := "traefik.containo.us"
certificate := a.getProvidedCertificate(domain)
assert.NotNil(t, certificate)
domain = "trae.acme.io"
certificate = a.getProvidedCertificate(domain)
assert.Nil(t, certificate) 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: "unexpected SANs",
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\" : SANs are not allowed",
expectedDomains: nil,
},
}
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)
})
}
}

View File

@@ -9,7 +9,7 @@ import (
"github.com/containous/traefik/cluster" "github.com/containous/traefik/cluster"
"github.com/containous/traefik/log" "github.com/containous/traefik/log"
"github.com/containous/traefik/safe" "github.com/containous/traefik/safe"
"github.com/xenolf/lego/acme" acme "github.com/xenolf/lego/acmev2"
) )
var _ acme.ChallengeProviderTimeout = (*challengeHTTPProvider)(nil) var _ acme.ChallengeProviderTimeout = (*challengeHTTPProvider)(nil)

View File

@@ -1,150 +0,0 @@
package acme
import (
"crypto"
"crypto/ecdsa"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"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/containous/traefik/tls/generate"
"github.com/xenolf/lego/acme"
)
var _ acme.ChallengeProviderTimeout = (*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 := 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 *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
}
// 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 := generate.PemCert(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)}
case *x509.CertificateRequest:
pemBlock = &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: key.Raw}
case []byte:
pemBlock = &pem.Block{Type: "CERTIFICATE", Bytes: []byte(data.([]byte))}
}
return pem.EncodeToMemory(pemBlock)
}

View File

@@ -2,22 +2,17 @@ package acme
import ( import (
"encoding/json" "encoding/json"
"fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"sync" "regexp"
"github.com/containous/traefik/cluster"
"github.com/containous/traefik/log" "github.com/containous/traefik/log"
"github.com/containous/traefik/provider/acme"
) )
var _ cluster.Store = (*LocalStore)(nil)
// LocalStore is a store using a file as storage // LocalStore is a store using a file as storage
type LocalStore struct { type LocalStore struct {
file string file string
storageLock sync.RWMutex
account *Account
} }
// NewLocalStore create a LocalStore // NewLocalStore create a LocalStore
@@ -27,71 +22,148 @@ func NewLocalStore(file string) *LocalStore {
} }
} }
// Get atomically a struct from the file storage // Get loads file into store and returns the Account
func (s *LocalStore) Get() cluster.Object { func (s *LocalStore) Get() (*Account, error) {
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()
account := &Account{} account := &Account{}
err := checkPermissions(s.file) hasData, err := checkFile(s.file)
if err != nil { if err != nil {
return nil, err return nil, err
} }
f, err := os.Open(s.file)
if err != nil { if hasData {
return nil, err 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
}
// Check if ACME Account is in ACME V1 format
if account != nil && account.Registration != nil {
isOldRegistration, err := regexp.MatchString(acme.RegistrationURLPathV1Regexp, account.Registration.URI)
if err != nil {
return nil, err
}
if isOldRegistration {
account.Email = ""
account.Registration = nil
account.PrivateKey = nil
}
}
} }
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 return account, nil
} }
// Begin creates a transaction with the KV store. // ConvertToNewFormat converts old acme.json format to the new one and store the result into the file (used for the backward compatibility)
func (s *LocalStore) Begin() (cluster.Transaction, cluster.Object, error) { func ConvertToNewFormat(fileName string) {
s.storageLock.Lock() localStore := acme.NewLocalStore(fileName)
return &localTransaction{LocalStore: s}, s.account, nil
}
var _ cluster.Transaction = (*localTransaction)(nil) storeAccount, err := localStore.GetAccount()
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")
}
// write account to file
data, err := json.MarshalIndent(object, "", " ")
if err != nil { if err != nil {
return err log.Warnf("Failed to read new account, ACME data conversion is not available : %v", err)
return
} }
err = ioutil.WriteFile(t.file, data, 0600)
storeCertificates, err := localStore.GetCertificates()
if err != nil { if err != nil {
return err log.Warnf("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.Warnf("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 {
newAccount = &acme.Account{
PrivateKey: account.PrivateKey,
Registration: account.Registration,
Email: account.Email,
}
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}
} }
t.dirty = true }
return nil
// FromNewToOldFormat converts new acme.json format 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{},
}
}
// 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
} }

View File

@@ -5,37 +5,27 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"testing" "testing"
"github.com/stretchr/testify/assert"
) )
func TestLoad(t *testing.T) { func TestGet(t *testing.T) {
acmeFile := "./acme_example.json" acmeFile := "./acme_example.json"
folder, prefix := filepath.Split(acmeFile) folder, prefix := filepath.Split(acmeFile)
tmpFile, err := ioutil.TempFile(folder, prefix) tmpFile, err := ioutil.TempFile(folder, prefix)
defer os.Remove(tmpFile.Name()) defer os.Remove(tmpFile.Name())
if err != nil { assert.NoError(t, err)
t.Error(err)
}
fileContent, err := ioutil.ReadFile(acmeFile) fileContent, err := ioutil.ReadFile(acmeFile)
if err != nil { assert.NoError(t, err)
t.Error(err)
}
tmpFile.Write(fileContent) tmpFile.Write(fileContent)
localStore := NewLocalStore(tmpFile.Name()) localStore := NewLocalStore(tmpFile.Name())
obj, err := localStore.Load() account, err := localStore.Get()
if err != nil { assert.NoError(t, err)
t.Error(err)
}
account, ok := obj.(*Account)
if !ok {
t.Error("Object is not an ACME Account")
}
if len(account.DomainsCertificate.Certs) != 1 { assert.Len(t, account.DomainsCertificate.Certs, 1)
t.Errorf("Must found %d and found %d certificates in Account", 3, len(account.DomainsCertificate.Certs))
}
} }

View File

@@ -7,19 +7,22 @@ import (
"os" "os"
) )
// Check file permissions // Check file permissions and content size
func checkPermissions(name string) error { func checkFile(name string) (bool, error) {
f, err := os.Open(name) f, err := os.Open(name)
if err != nil { if err != nil {
return err return false, err
} }
defer f.Close() defer f.Close()
fi, err := f.Stat() fi, err := f.Stat()
if err != nil { if err != nil {
return err return false, err
} }
if fi.Mode().Perm()&0077 != 0 { if fi.Mode().Perm()&0077 != 0 {
return fmt.Errorf("permissions %o for %s are too open, please use 600", fi.Mode().Perm(), name) return false, fmt.Errorf("permissions %o for %s are too open, please use 600", fi.Mode().Perm(), name)
} }
return nil
return fi.Size() > 0, nil
} }

View File

@@ -1,6 +1,20 @@
package acme package acme
import "os"
// Check file content size
// Do not check file permissions on Windows right now // Do not check file permissions on Windows right now
func checkPermissions(name string) error { func checkFile(name string) (bool, error) {
return nil f, err := os.Open(name)
if err != nil {
return false, err
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
return false, err
}
return fi.Size() > 0, nil
} }

View File

@@ -9,8 +9,10 @@ import (
"github.com/containous/traefik/acme" "github.com/containous/traefik/acme"
"github.com/containous/traefik/configuration" "github.com/containous/traefik/configuration"
"github.com/containous/traefik/provider" "github.com/containous/traefik/provider"
acmeprovider "github.com/containous/traefik/provider/acme"
"github.com/containous/traefik/provider/boltdb" "github.com/containous/traefik/provider/boltdb"
"github.com/containous/traefik/provider/consul" "github.com/containous/traefik/provider/consul"
"github.com/containous/traefik/provider/consulcatalog"
"github.com/containous/traefik/provider/docker" "github.com/containous/traefik/provider/docker"
"github.com/containous/traefik/provider/dynamodb" "github.com/containous/traefik/provider/dynamodb"
"github.com/containous/traefik/provider/ecs" "github.com/containous/traefik/provider/ecs"
@@ -23,7 +25,7 @@ import (
"github.com/containous/traefik/provider/mesos" "github.com/containous/traefik/provider/mesos"
"github.com/containous/traefik/provider/rancher" "github.com/containous/traefik/provider/rancher"
"github.com/containous/traefik/provider/zk" "github.com/containous/traefik/provider/zk"
traefikTls "github.com/containous/traefik/tls" traefiktls "github.com/containous/traefik/tls"
"github.com/containous/traefik/types" "github.com/containous/traefik/types"
) )
@@ -43,16 +45,15 @@ func TestDo_globalConfiguration(t *testing.T) {
config.LogLevel = "LogLevel" config.LogLevel = "LogLevel"
config.EntryPoints = configuration.EntryPoints{ config.EntryPoints = configuration.EntryPoints{
"foo": { "foo": {
Network: "foo Network",
Address: "foo Address", Address: "foo Address",
TLS: &traefikTls.TLS{ TLS: &traefiktls.TLS{
MinVersion: "foo MinVersion", MinVersion: "foo MinVersion",
CipherSuites: []string{"foo CipherSuites 1", "foo CipherSuites 2", "foo CipherSuites 3"}, CipherSuites: []string{"foo CipherSuites 1", "foo CipherSuites 2", "foo CipherSuites 3"},
Certificates: traefikTls.Certificates{ Certificates: traefiktls.Certificates{
{CertFile: "CertFile 1", KeyFile: "KeyFile 1"}, {CertFile: "CertFile 1", KeyFile: "KeyFile 1"},
{CertFile: "CertFile 2", KeyFile: "KeyFile 2"}, {CertFile: "CertFile 2", KeyFile: "KeyFile 2"},
}, },
ClientCA: traefikTls.ClientCA{ ClientCA: traefiktls.ClientCA{
Files: []string{"foo ClientCAFiles 1", "foo ClientCAFiles 2", "foo ClientCAFiles 3"}, Files: []string{"foo ClientCAFiles 1", "foo ClientCAFiles 2", "foo ClientCAFiles 3"},
Optional: false, Optional: false,
}, },
@@ -89,16 +90,15 @@ func TestDo_globalConfiguration(t *testing.T) {
}, },
}, },
"fii": { "fii": {
Network: "fii Network",
Address: "fii Address", Address: "fii Address",
TLS: &traefikTls.TLS{ TLS: &traefiktls.TLS{
MinVersion: "fii MinVersion", MinVersion: "fii MinVersion",
CipherSuites: []string{"fii CipherSuites 1", "fii CipherSuites 2", "fii CipherSuites 3"}, CipherSuites: []string{"fii CipherSuites 1", "fii CipherSuites 2", "fii CipherSuites 3"},
Certificates: traefikTls.Certificates{ Certificates: traefiktls.Certificates{
{CertFile: "CertFile 1", KeyFile: "KeyFile 1"}, {CertFile: "CertFile 1", KeyFile: "KeyFile 1"},
{CertFile: "CertFile 2", KeyFile: "KeyFile 2"}, {CertFile: "CertFile 2", KeyFile: "KeyFile 2"},
}, },
ClientCA: traefikTls.ClientCA{ ClientCA: traefiktls.ClientCA{
Files: []string{"fii ClientCAFiles 1", "fii ClientCAFiles 2", "fii ClientCAFiles 3"}, Files: []string{"fii ClientCAFiles 1", "fii ClientCAFiles 2", "fii ClientCAFiles 3"},
Optional: false, Optional: false,
}, },
@@ -156,7 +156,7 @@ func TestDo_globalConfiguration(t *testing.T) {
} }
config.ACME = &acme.ACME{ config.ACME = &acme.ACME{
Email: "acme Email", Email: "acme Email",
Domains: []acme.Domain{ Domains: []types.Domain{
{ {
Main: "Domains Main", Main: "Domains Main",
SANs: []string{"Domains acme SANs 1", "Domains acme SANs 2", "Domains acme SANs 3"}, SANs: []string{"Domains acme SANs 1", "Domains acme SANs 2", "Domains acme SANs 3"},
@@ -168,7 +168,7 @@ func TestDo_globalConfiguration(t *testing.T) {
OnHostRule: true, OnHostRule: true,
CAServer: "CAServer", CAServer: "CAServer",
EntryPoint: "EntryPoint", EntryPoint: "EntryPoint",
DNSChallenge: &acme.DNSChallenge{Provider: "DNSProvider"}, DNSChallenge: &acmeprovider.DNSChallenge{Provider: "DNSProvider"},
DelayDontCheckDNS: 666, DelayDontCheckDNS: 666,
ACMELogging: true, ACMELogging: true,
TLSConfig: &tls.Config{ TLSConfig: &tls.Config{
@@ -181,7 +181,7 @@ func TestDo_globalConfiguration(t *testing.T) {
config.MaxIdleConnsPerHost = 666 config.MaxIdleConnsPerHost = 666
config.IdleTimeout = flaeg.Duration(666 * time.Second) config.IdleTimeout = flaeg.Duration(666 * time.Second)
config.InsecureSkipVerify = true config.InsecureSkipVerify = true
config.RootCAs = traefikTls.RootCAs{"RootCAs 1", "RootCAs 2", "RootCAs 3"} config.RootCAs = traefiktls.RootCAs{"RootCAs 1", "RootCAs 2", "RootCAs 3"}
config.Retry = &configuration.Retry{ config.Retry = &configuration.Retry{
Attempts: 666, Attempts: 666,
} }
@@ -333,7 +333,7 @@ func TestDo_globalConfiguration(t *testing.T) {
}, },
RespectReadinessChecks: true, RespectReadinessChecks: true,
} }
config.ConsulCatalog = &consul.CatalogProvider{ config.ConsulCatalog = &consulcatalog.Provider{
BaseProvider: provider.BaseProvider{ BaseProvider: provider.BaseProvider{
Watch: true, Watch: true,
Filename: "ConsulCatalog Filename", Filename: "ConsulCatalog Filename",
@@ -432,8 +432,9 @@ func TestDo_globalConfiguration(t *testing.T) {
Trace: true, Trace: true,
DebugLogGeneratedTemplate: true, DebugLogGeneratedTemplate: true,
}, },
Endpoint: "eureka Endpoint", Endpoint: "eureka Endpoint",
Delay: "eureka Delay", Delay: flaeg.Duration(30 * time.Second),
RefreshSeconds: flaeg.Duration(30 * time.Second),
} }
config.ECS = &ecs.Provider{ config.ECS = &ecs.Provider{
BaseProvider: provider.BaseProvider{ BaseProvider: provider.BaseProvider{

View File

@@ -29,7 +29,6 @@ func Test_doOnJSON(t *testing.T) {
"Compress": false "Compress": false
}, },
"https": { "https": {
"Network": "",
"Address": ":443", "Address": ":443",
"TLS": { "TLS": {
"MinVersion": "", "MinVersion": "",
@@ -119,7 +118,6 @@ func Test_doOnJSON(t *testing.T) {
"Compress": false "Compress": false
}, },
"https": { "https": {
"Network": "",
"Address": ":443", "Address": ":443",
"TLS": { "TLS": {
"MinVersion": "", "MinVersion": "",

File diff suppressed because it is too large Load Diff

View File

@@ -1,25 +1,23 @@
FROM golang:1.9-alpine FROM golang:1.10-alpine
RUN apk --update upgrade \ RUN apk --update upgrade \
&& apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar \ && apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar \
&& rm -rf /var/cache/apk/* && rm -rf /var/cache/apk/*
RUN go get github.com/jteeuwen/go-bindata/... \ RUN go get github.com/containous/go-bindata/... \
&& go get github.com/golang/lint/golint \ && go get github.com/golang/lint/golint \
&& go get github.com/kisielk/errcheck \ && go get github.com/kisielk/errcheck \
&& go get github.com/client9/misspell/cmd/misspell && go get github.com/client9/misspell/cmd/misspell
# Which docker version to test on # Which docker version to test on
ARG DOCKER_VERSION=17.03.2 ARG DOCKER_VERSION=17.03.2
ARG DEP_VERSION=0.3.2 ARG DEP_VERSION=0.4.1
# Download dep binary to bin folder in $GOPATH # Download dep binary to bin folder in $GOPATH
RUN mkdir -p /usr/local/bin \ RUN mkdir -p /usr/local/bin \
&& curl -fsSL -o /usr/local/bin/dep https://github.com/golang/dep/releases/download/v${DEP_VERSION}/dep-linux-amd64 \ && curl -fsSL -o /usr/local/bin/dep https://github.com/golang/dep/releases/download/v${DEP_VERSION}/dep-linux-amd64 \
&& chmod +x /usr/local/bin/dep && chmod +x /usr/local/bin/dep
# Download docker # Download docker
RUN mkdir -p /usr/local/bin \ RUN mkdir -p /usr/local/bin \
&& curl -fL https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}-ce.tgz \ && curl -fL https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}-ce.tgz \

View File

@@ -7,12 +7,12 @@ import (
"sync" "sync"
"time" "time"
"github.com/abronan/valkeyrie/store"
"github.com/cenk/backoff" "github.com/cenk/backoff"
"github.com/containous/staert" "github.com/containous/staert"
"github.com/containous/traefik/job" "github.com/containous/traefik/job"
"github.com/containous/traefik/log" "github.com/containous/traefik/log"
"github.com/containous/traefik/safe" "github.com/containous/traefik/safe"
"github.com/docker/libkv/store"
"github.com/satori/go.uuid" "github.com/satori/go.uuid"
) )

View File

@@ -2,15 +2,22 @@ package cluster
import ( import (
"context" "context"
"net/http"
"time" "time"
"github.com/cenk/backoff" "github.com/cenk/backoff"
"github.com/containous/mux"
"github.com/containous/traefik/log" "github.com/containous/traefik/log"
"github.com/containous/traefik/safe" "github.com/containous/traefik/safe"
"github.com/containous/traefik/types" "github.com/containous/traefik/types"
"github.com/docker/leadership" "github.com/docker/leadership"
"github.com/unrolled/render"
) )
var templatesRenderer = render.New(render.Options{
Directory: "nowhere",
})
// Leadership allows leadership election using a KV store // Leadership allows leadership election using a KV store
type Leadership struct { type Leadership struct {
*safe.Pool *safe.Pool
@@ -98,7 +105,32 @@ func (l *Leadership) onElection(elected bool) {
} }
} }
type leaderResponse struct {
Leader bool `json:"leader"`
}
func (l *Leadership) getLeaderHandler(response http.ResponseWriter, request *http.Request) {
leader := &leaderResponse{Leader: l.IsLeader()}
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 // IsLeader returns true if current node is leader
func (l *Leadership) IsLeader() bool { func (l *Leadership) IsLeader() bool {
return l.leader.Get().(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)
}

View File

@@ -1,4 +1,4 @@
package main package bug
import ( import (
"bytes" "bytes"
@@ -9,7 +9,9 @@ import (
"text/template" "text/template"
"github.com/containous/flaeg" "github.com/containous/flaeg"
"github.com/containous/traefik/cmd/traefik/anonymize" "github.com/containous/traefik/anonymize"
"github.com/containous/traefik/cmd"
"github.com/containous/traefik/cmd/version"
) )
const ( const (
@@ -74,7 +76,7 @@ HOW TO WRITE A GOOD ISSUE?
Add more configuration information here. 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) (paste your output here)
@@ -83,8 +85,8 @@ Add more configuration information here.
` `
) )
// newBugCmd builds a new Bug command // NewCmd builds a new Bug command
func newBugCmd(traefikConfiguration *TraefikConfiguration, traefikPointersConfiguration *TraefikConfiguration) *flaeg.Command { func NewCmd(traefikConfiguration *cmd.TraefikConfiguration, traefikPointersConfiguration *cmd.TraefikConfiguration) *flaeg.Command {
//version Command init //version Command init
return &flaeg.Command{ return &flaeg.Command{
@@ -92,30 +94,30 @@ func newBugCmd(traefikConfiguration *TraefikConfiguration, traefikPointersConfig
Description: `Report an issue on Traefik bugtracker`, Description: `Report an issue on Traefik bugtracker`,
Config: traefikConfiguration, Config: traefikConfiguration,
DefaultPointersConfig: traefikPointersConfiguration, DefaultPointersConfig: traefikPointersConfiguration,
Run: runBugCmd(traefikConfiguration), Run: runCmd(traefikConfiguration),
Metadata: map[string]string{ Metadata: map[string]string{
"parseAllSources": "true", "parseAllSources": "true",
}, },
} }
} }
func runBugCmd(traefikConfiguration *TraefikConfiguration) func() error { func runCmd(traefikConfiguration *cmd.TraefikConfiguration) func() error {
return func() error { return func() error {
body, err := createBugReport(traefikConfiguration) body, err := createReport(traefikConfiguration)
if err != nil { if err != nil {
return err return err
} }
sendBugReport(body) sendReport(body)
return nil return nil
} }
} }
func createBugReport(traefikConfiguration *TraefikConfiguration) (string, error) { func createReport(traefikConfiguration *cmd.TraefikConfiguration) (string, error) {
var version bytes.Buffer var versionPrint bytes.Buffer
if err := getVersionPrint(&version); err != nil { if err := version.GetPrint(&versionPrint); err != nil {
return "", err return "", err
} }
@@ -133,7 +135,7 @@ func createBugReport(traefikConfiguration *TraefikConfiguration) (string, error)
Version string Version string
Configuration string Configuration string
}{ }{
Version: version.String(), Version: versionPrint.String(),
Configuration: config, Configuration: config,
} }
@@ -145,7 +147,7 @@ func createBugReport(traefikConfiguration *TraefikConfiguration) (string, error)
return bug.String(), nil return bug.String(), nil
} }
func sendBugReport(body string) { func sendReport(body string) {
URL := bugTracker + "?body=" + url.QueryEscape(body) URL := bugTracker + "?body=" + url.QueryEscape(body)
if err := openBrowser(URL); err != nil { if err := openBrowser(URL); err != nil {
fmt.Printf("Please file a new issue at %s using this template:\n\n", bugTracker) fmt.Printf("Please file a new issue at %s using this template:\n\n", bugTracker)

View File

@@ -1,9 +1,10 @@
package main package bug
import ( import (
"testing" "testing"
"github.com/containous/traefik/cmd/traefik/anonymize" "github.com/containous/traefik/anonymize"
"github.com/containous/traefik/cmd"
"github.com/containous/traefik/configuration" "github.com/containous/traefik/configuration"
"github.com/containous/traefik/provider/file" "github.com/containous/traefik/provider/file"
"github.com/containous/traefik/tls" "github.com/containous/traefik/tls"
@@ -11,8 +12,8 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func Test_createBugReport(t *testing.T) { func Test_createReport(t *testing.T) {
traefikConfiguration := &TraefikConfiguration{ traefikConfiguration := &cmd.TraefikConfiguration{
ConfigFile: "FOO", ConfigFile: "FOO",
GlobalConfiguration: configuration.GlobalConfiguration{ GlobalConfiguration: configuration.GlobalConfiguration{
EntryPoints: configuration.EntryPoints{ EntryPoints: configuration.EntryPoints{
@@ -37,7 +38,7 @@ func Test_createBugReport(t *testing.T) {
}, },
} }
report, err := createBugReport(traefikConfiguration) report, err := createReport(traefikConfiguration)
assert.NoError(t, err, report) assert.NoError(t, err, report)
// exported anonymous configuration // exported anonymous configuration
@@ -47,7 +48,7 @@ func Test_createBugReport(t *testing.T) {
} }
func Test_anonymize_traefikConfiguration(t *testing.T) { func Test_anonymize_traefikConfiguration(t *testing.T) {
traefikConfiguration := &TraefikConfiguration{ traefikConfiguration := &cmd.TraefikConfiguration{
ConfigFile: "FOO", ConfigFile: "FOO",
GlobalConfiguration: configuration.GlobalConfiguration{ GlobalConfiguration: configuration.GlobalConfiguration{
EntryPoints: configuration.EntryPoints{ EntryPoints: configuration.EntryPoints{

324
cmd/configuration.go Normal file
View File

@@ -0,0 +1,324 @@
package cmd
import (
"time"
"github.com/containous/flaeg"
"github.com/containous/traefik-extra-service-fabric"
"github.com/containous/traefik/api"
"github.com/containous/traefik/configuration"
"github.com/containous/traefik/middlewares/accesslog"
"github.com/containous/traefik/middlewares/tracing"
"github.com/containous/traefik/middlewares/tracing/jaeger"
"github.com/containous/traefik/middlewares/tracing/zipkin"
"github.com/containous/traefik/ping"
"github.com/containous/traefik/provider/boltdb"
"github.com/containous/traefik/provider/consul"
"github.com/containous/traefik/provider/consulcatalog"
"github.com/containous/traefik/provider/docker"
"github.com/containous/traefik/provider/dynamodb"
"github.com/containous/traefik/provider/ecs"
"github.com/containous/traefik/provider/etcd"
"github.com/containous/traefik/provider/eureka"
"github.com/containous/traefik/provider/file"
"github.com/containous/traefik/provider/kubernetes"
"github.com/containous/traefik/provider/marathon"
"github.com/containous/traefik/provider/mesos"
"github.com/containous/traefik/provider/rancher"
"github.com/containous/traefik/provider/rest"
"github.com/containous/traefik/provider/zk"
"github.com/containous/traefik/types"
sf "github.com/jjcollinge/servicefabric"
)
// 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
// 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",
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(60 * 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}}"
// 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",
Jaeger: &jaeger.Config{
SamplingServerURL: "http://localhost:5778/sampling",
SamplingType: "const",
SamplingParam: 1.0,
LocalAgentHostPort: "127.0.0.1:6832",
},
Zipkin: &zipkin.Config{
HTTPEndpoint: "http://localhost:9411/api/v1/spans",
SameSpan: false,
ID128Bit: true,
Debug: 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",
PushInterval: "10s",
},
}
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,
}
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/containous/traefik/cmd"
"github.com/containous/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,186 @@
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/containous/traefik/acme"
"github.com/containous/traefik/cluster"
"github.com/containous/traefik/cmd"
"github.com/containous/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
}
}
// Store the ACME Account into the KV Store
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
}
}
// 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
}
} 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,294 +0,0 @@
package main
import (
"time"
"github.com/containous/flaeg"
"github.com/containous/traefik-extra-service-fabric"
"github.com/containous/traefik/api"
"github.com/containous/traefik/configuration"
"github.com/containous/traefik/middlewares/accesslog"
"github.com/containous/traefik/ping"
"github.com/containous/traefik/provider/boltdb"
"github.com/containous/traefik/provider/consul"
"github.com/containous/traefik/provider/docker"
"github.com/containous/traefik/provider/dynamodb"
"github.com/containous/traefik/provider/ecs"
"github.com/containous/traefik/provider/etcd"
"github.com/containous/traefik/provider/eureka"
"github.com/containous/traefik/provider/file"
"github.com/containous/traefik/provider/kubernetes"
"github.com/containous/traefik/provider/marathon"
"github.com/containous/traefik/provider/mesos"
"github.com/containous/traefik/provider/rancher"
"github.com/containous/traefik/provider/rest"
"github.com/containous/traefik/provider/zk"
"github.com/containous/traefik/types"
sf "github.com/jjcollinge/servicefabric"
)
// 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
// 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",
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(60 * 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 consul.CatalogProvider
defaultConsulCatalog.Endpoint = "127.0.0.1:8500"
defaultConsulCatalog.ExposedByDefault = true
defaultConsulCatalog.Constraints = types.Constraints{}
defaultConsulCatalog.Prefix = "traefik"
defaultConsulCatalog.FrontEndRule = "Host:{{.ServiceName}}.{{.Domain}}"
// 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.Endpoint = ""
defaultKubernetes.LabelSelector = ""
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.Delay = "30s"
// 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: "",
}
// 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 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",
PushInterval: "10s",
},
}
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,
}
return &TraefikConfiguration{
GlobalConfiguration: defaultConfiguration,
}
}
// NewTraefikConfiguration creates a TraefikConfiguration with default values
func NewTraefikConfiguration() *TraefikConfiguration {
return &TraefikConfiguration{
GlobalConfiguration: configuration.GlobalConfiguration{
AccessLogsFile: "",
TraefikLogsFile: "",
LogLevel: "ERROR",
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),
},
CheckNewVersion: true,
},
ConfigFile: "",
}
}

View File

@@ -1,72 +0,0 @@
package main
import (
"crypto/tls"
"errors"
"fmt"
"net/http"
"os"
"time"
"github.com/containous/flaeg"
"github.com/containous/traefik/configuration"
)
func newHealthCheckCmd(traefikConfiguration *TraefikConfiguration, traefikPointersConfiguration *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: runHealthCheck(traefikConfiguration),
Metadata: map[string]string{
"parseAllSources": "true",
},
}
}
func runHealthCheck(traefikConfiguration *TraefikConfiguration) func() error {
return func() error {
traefikConfiguration.GlobalConfiguration.SetEffectiveConfiguration(traefikConfiguration.ConfigFile)
if traefikConfiguration.Ping == nil {
fmt.Println("Please enable `ping` to use healtcheck.")
os.Exit(1)
}
resp, errPing := healthCheck(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
}
}
func healthCheck(globalConfiguration configuration.GlobalConfiguration) (*http.Response, error) {
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

@@ -1,145 +0,0 @@
package main
import (
"encoding/json"
"fmt"
stdlog "log"
"github.com/containous/flaeg"
"github.com/containous/staert"
"github.com/containous/traefik/acme"
"github.com/containous/traefik/cluster"
"github.com/docker/libkv/store"
)
func newStoreConfigCmd(traefikConfiguration *TraefikConfiguration, traefikPointersConfiguration *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",
},
}
}
func runStoreConfig(kv *staert.KvSource, traefikConfiguration *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.LoadConfig()
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 {
var object cluster.Object
if len(traefikConfiguration.GlobalConfiguration.ACME.StorageFile) > 0 {
// convert ACME json file to KV store
localStore := acme.NewLocalStore(traefikConfiguration.GlobalConfiguration.ACME.StorageFile)
object, err = localStore.Load()
if err != nil {
return err
}
} else {
// Create an empty account to create all the keys into the KV store
account := &acme.Account{}
account.Init()
object = account
}
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
}
// Force to delete storagefile
err = kv.Delete(kv.Prefix + "/acme/storagefile")
if err != nil {
return err
}
}
return nil
}
}
// createKvSource creates KvSource
// TLS support is enable for Consul and Etcd backends
func createKvSource(traefikConfiguration *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,6 +1,7 @@
package main package main
import ( import (
"context"
"encoding/json" "encoding/json"
fmtlog "log" fmtlog "log"
"net/http" "net/http"
@@ -10,31 +11,38 @@ import (
"strings" "strings"
"time" "time"
"github.com/Sirupsen/logrus"
"github.com/cenk/backoff" "github.com/cenk/backoff"
"github.com/containous/flaeg" "github.com/containous/flaeg"
"github.com/containous/staert" "github.com/containous/staert"
"github.com/containous/traefik/acme" "github.com/containous/traefik/cmd"
"github.com/containous/traefik/cmd/bug"
"github.com/containous/traefik/cmd/healthcheck"
"github.com/containous/traefik/cmd/storeconfig"
cmdVersion "github.com/containous/traefik/cmd/version"
"github.com/containous/traefik/collector" "github.com/containous/traefik/collector"
"github.com/containous/traefik/configuration" "github.com/containous/traefik/configuration"
"github.com/containous/traefik/job" "github.com/containous/traefik/job"
"github.com/containous/traefik/log" "github.com/containous/traefik/log"
"github.com/containous/traefik/provider/acme"
"github.com/containous/traefik/provider/ecs" "github.com/containous/traefik/provider/ecs"
"github.com/containous/traefik/provider/kubernetes" "github.com/containous/traefik/provider/kubernetes"
"github.com/containous/traefik/safe" "github.com/containous/traefik/safe"
"github.com/containous/traefik/server" "github.com/containous/traefik/server"
"github.com/containous/traefik/server/uuid" "github.com/containous/traefik/server/uuid"
traefikTls "github.com/containous/traefik/tls" traefiktls "github.com/containous/traefik/tls"
"github.com/containous/traefik/types" "github.com/containous/traefik/types"
"github.com/containous/traefik/version" "github.com/containous/traefik/version"
"github.com/coreos/go-systemd/daemon" "github.com/coreos/go-systemd/daemon"
"github.com/ogier/pflag"
"github.com/sirupsen/logrus"
) )
func main() { func main() {
//traefik config inits // traefik config inits
traefikConfiguration := NewTraefikConfiguration() traefikConfiguration := cmd.NewTraefikConfiguration()
traefikPointersConfiguration := NewTraefikDefaultPointersConfiguration() traefikPointersConfiguration := cmd.NewTraefikDefaultPointersConfiguration()
//traefik Command init
// traefik Command init
traefikCmd := &flaeg.Command{ traefikCmd := &flaeg.Command{
Name: "traefik", Name: "traefik",
Description: `traefik is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease. Description: `traefik is a modern HTTP reverse proxy and load balancer made to deploy microservices with ease.
@@ -42,66 +50,72 @@ Complete documentation is available at https://traefik.io`,
Config: traefikConfiguration, Config: traefikConfiguration,
DefaultPointersConfig: traefikPointersConfiguration, DefaultPointersConfig: traefikPointersConfiguration,
Run: func() error { Run: func() error {
run(&traefikConfiguration.GlobalConfiguration, traefikConfiguration.ConfigFile) runCmd(&traefikConfiguration.GlobalConfiguration, traefikConfiguration.ConfigFile)
return nil return nil
}, },
} }
//storeconfig Command init // storeconfig Command init
storeConfigCmd := newStoreConfigCmd(traefikConfiguration, traefikPointersConfiguration) storeConfigCmd := storeconfig.NewCmd(traefikConfiguration, traefikPointersConfiguration)
//init flaeg source // init flaeg source
f := flaeg.New(traefikCmd, os.Args[1:]) f := flaeg.New(traefikCmd, os.Args[1:])
//add custom parsers // add custom parsers
f.AddParser(reflect.TypeOf(configuration.EntryPoints{}), &configuration.EntryPoints{}) f.AddParser(reflect.TypeOf(configuration.EntryPoints{}), &configuration.EntryPoints{})
f.AddParser(reflect.TypeOf(configuration.DefaultEntryPoints{}), &configuration.DefaultEntryPoints{}) f.AddParser(reflect.TypeOf(configuration.DefaultEntryPoints{}), &configuration.DefaultEntryPoints{})
f.AddParser(reflect.TypeOf(traefikTls.RootCAs{}), &traefikTls.RootCAs{}) f.AddParser(reflect.TypeOf(traefiktls.RootCAs{}), &traefiktls.RootCAs{})
f.AddParser(reflect.TypeOf(types.Constraints{}), &types.Constraints{}) f.AddParser(reflect.TypeOf(types.Constraints{}), &types.Constraints{})
f.AddParser(reflect.TypeOf(kubernetes.Namespaces{}), &kubernetes.Namespaces{}) f.AddParser(reflect.TypeOf(kubernetes.Namespaces{}), &kubernetes.Namespaces{})
f.AddParser(reflect.TypeOf(ecs.Clusters{}), &ecs.Clusters{}) f.AddParser(reflect.TypeOf(ecs.Clusters{}), &ecs.Clusters{})
f.AddParser(reflect.TypeOf([]acme.Domain{}), &acme.Domains{}) f.AddParser(reflect.TypeOf([]types.Domain{}), &types.Domains{})
f.AddParser(reflect.TypeOf(types.Buckets{}), &types.Buckets{}) 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 // add commands
f.AddCommand(newVersionCmd()) f.AddCommand(cmdVersion.NewCmd())
f.AddCommand(newBugCmd(traefikConfiguration, traefikPointersConfiguration)) f.AddCommand(bug.NewCmd(traefikConfiguration, traefikPointersConfiguration))
f.AddCommand(storeConfigCmd) f.AddCommand(storeConfigCmd)
f.AddCommand(newHealthCheckCmd(traefikConfiguration, traefikPointersConfiguration)) f.AddCommand(healthcheck.NewCmd(traefikConfiguration, traefikPointersConfiguration))
usedCmd, err := f.GetCommand() usedCmd, err := f.GetCommand()
if err != nil { if err != nil {
fmtlog.Println(err) fmtlog.Println(err)
os.Exit(-1) os.Exit(1)
} }
if _, err := f.Parse(usedCmd); err != nil { if _, err := f.Parse(usedCmd); err != nil {
if err == pflag.ErrHelp {
os.Exit(0)
}
fmtlog.Printf("Error parsing command: %s\n", err) fmtlog.Printf("Error parsing command: %s\n", err)
os.Exit(-1) os.Exit(1)
} }
//staert init // staert init
s := staert.NewStaert(traefikCmd) s := staert.NewStaert(traefikCmd)
//init toml source // init TOML source
toml := staert.NewTomlSource("traefik", []string{traefikConfiguration.ConfigFile, "/etc/traefik/", "$HOME/.traefik/", "."}) toml := staert.NewTomlSource("traefik", []string{traefikConfiguration.ConfigFile, "/etc/traefik/", "$HOME/.traefik/", "."})
//add sources to staert // add sources to staert
s.AddSource(toml) s.AddSource(toml)
s.AddSource(f) s.AddSource(f)
if _, err := s.LoadConfig(); err != nil { if _, err := s.LoadConfig(); err != nil {
fmtlog.Printf("Error reading TOML config file %s : %s\n", toml.ConfigFileUsed(), err) fmtlog.Printf("Error reading TOML config file %s : %s\n", toml.ConfigFileUsed(), err)
os.Exit(-1) os.Exit(1)
} }
traefikConfiguration.ConfigFile = toml.ConfigFileUsed() traefikConfiguration.ConfigFile = toml.ConfigFileUsed()
kv, err := createKvSource(traefikConfiguration) kv, err := storeconfig.CreateKvSource(traefikConfiguration)
if err != nil { if err != nil {
fmtlog.Printf("Error creating kv store: %s\n", err) fmtlog.Printf("Error creating kv store: %s\n", err)
os.Exit(-1) os.Exit(1)
} }
storeConfigCmd.Run = runStoreConfig(kv, traefikConfiguration) 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 kv != nil && usedCmd == traefikCmd {
if traefikConfiguration.Cluster == nil { if traefikConfiguration.Cluster == nil {
traefikConfiguration.Cluster = &types.Cluster{Node: uuid.Get()} traefikConfiguration.Cluster = &types.Cluster{Node: uuid.Get()}
@@ -120,19 +134,19 @@ Complete documentation is available at https://traefik.io`,
err := backoff.RetryNotify(safe.OperationWithRecover(operation), job.NewBackOff(backoff.NewExponentialBackOff()), notify) err := backoff.RetryNotify(safe.OperationWithRecover(operation), job.NewBackOff(backoff.NewExponentialBackOff()), notify)
if err != nil { if err != nil {
fmtlog.Printf("Error loading configuration: %s\n", err) fmtlog.Printf("Error loading configuration: %s\n", err)
os.Exit(-1) os.Exit(1)
} }
} }
if err := s.Run(); err != nil { if err := s.Run(); err != nil {
fmtlog.Printf("Error running traefik: %s\n", err) fmtlog.Printf("Error running traefik: %s\n", err)
os.Exit(-1) os.Exit(1)
} }
os.Exit(0) os.Exit(0)
} }
func run(globalConfiguration *configuration.GlobalConfiguration, configFile string) { func runCmd(globalConfiguration *configuration.GlobalConfiguration, configFile string) {
configureLogging(globalConfiguration) configureLogging(globalConfiguration)
if len(configFile) > 0 { if len(configFile) > 0 {
@@ -154,8 +168,17 @@ func run(globalConfiguration *configuration.GlobalConfiguration, configFile stri
stats(globalConfiguration) stats(globalConfiguration)
log.Debugf("Global configuration loaded %s", string(jsonConf)) log.Debugf("Global configuration loaded %s", string(jsonConf))
svr := server.NewServer(*globalConfiguration) if acme.IsEnabled() {
svr.Start() store := acme.NewLocalStore(acme.Get().Storage)
acme.Get().Store = &store
}
svr := server.NewServer(*globalConfiguration, configuration.NewProviderAggregator(globalConfiguration))
if acme.IsEnabled() && acme.Get().OnHostRule {
acme.Get().SetConfigListenerChan(make(chan types.Configuration))
svr.AddListener(acme.Get().ListenConfiguration)
}
ctx := cmd.ContextWithSignal(context.Background())
svr.StartWithContext(ctx)
defer svr.Close() defer svr.Close()
sent, err := daemon.SdNotify(false, "READY=1") sent, err := daemon.SdNotify(false, "READY=1")
@@ -173,7 +196,7 @@ func run(globalConfiguration *configuration.GlobalConfiguration, configFile stri
safe.Go(func() { safe.Go(func() {
tick := time.Tick(t) tick := time.Tick(t)
for range tick { for range tick {
_, errHealthCheck := healthCheck(*globalConfiguration) _, errHealthCheck := healthcheck.Do(*globalConfiguration)
if globalConfiguration.Ping == nil || errHealthCheck == nil { if globalConfiguration.Ping == nil || errHealthCheck == nil {
if ok, _ := daemon.SdNotify(false, "WATCHDOG=1"); !ok { if ok, _ := daemon.SdNotify(false, "WATCHDOG=1"); !ok {
log.Error("Fail to tick watchdog") log.Error("Fail to tick watchdog")
@@ -194,12 +217,18 @@ func configureLogging(globalConfiguration *configuration.GlobalConfiguration) {
// configure default log flags // configure default log flags
fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags) fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags)
if globalConfiguration.Debug {
globalConfiguration.LogLevel = "DEBUG"
}
// configure log level // configure log level
level, err := logrus.ParseLevel(strings.ToLower(globalConfiguration.LogLevel)) // 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 { if err != nil {
log.Error("Error getting level", err) log.Error("Error getting level", err)
} }
@@ -219,10 +248,7 @@ func configureLogging(globalConfiguration *configuration.GlobalConfiguration) {
if globalConfiguration.TraefikLog != nil && globalConfiguration.TraefikLog.Format == "json" { if globalConfiguration.TraefikLog != nil && globalConfiguration.TraefikLog.Format == "json" {
formatter = &logrus.JSONFormatter{} formatter = &logrus.JSONFormatter{}
} else { } else {
disableColors := false disableColors := len(logFile) > 0
if len(logFile) > 0 {
disableColors = true
}
formatter = &logrus.TextFormatter{DisableColors: disableColors, FullTimestamp: true, DisableSorting: true} formatter = &logrus.TextFormatter{DisableColors: disableColors, FullTimestamp: true, DisableSorting: true}
} }
log.SetFormatter(formatter) log.SetFormatter(formatter)
@@ -230,8 +256,7 @@ func configureLogging(globalConfiguration *configuration.GlobalConfiguration) {
if len(logFile) > 0 { if len(logFile) > 0 {
dir := filepath.Dir(logFile) dir := filepath.Dir(logFile)
err := os.MkdirAll(dir, 0755) if err := os.MkdirAll(dir, 0755); err != nil {
if err != nil {
log.Errorf("Failed to create log path %s: %s", dir, err) log.Errorf("Failed to create log path %s: %s", dir, 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.Print("\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/containous/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)
}

View File

@@ -9,7 +9,7 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/containous/traefik/cmd/traefik/anonymize" "github.com/containous/traefik/anonymize"
"github.com/containous/traefik/configuration" "github.com/containous/traefik/configuration"
"github.com/containous/traefik/log" "github.com/containous/traefik/log"
"github.com/containous/traefik/version" "github.com/containous/traefik/version"

View File

@@ -10,9 +10,12 @@ import (
"github.com/containous/traefik/acme" "github.com/containous/traefik/acme"
"github.com/containous/traefik/api" "github.com/containous/traefik/api"
"github.com/containous/traefik/log" "github.com/containous/traefik/log"
"github.com/containous/traefik/middlewares/tracing"
"github.com/containous/traefik/ping" "github.com/containous/traefik/ping"
acmeprovider "github.com/containous/traefik/provider/acme"
"github.com/containous/traefik/provider/boltdb" "github.com/containous/traefik/provider/boltdb"
"github.com/containous/traefik/provider/consul" "github.com/containous/traefik/provider/consul"
"github.com/containous/traefik/provider/consulcatalog"
"github.com/containous/traefik/provider/docker" "github.com/containous/traefik/provider/docker"
"github.com/containous/traefik/provider/dynamodb" "github.com/containous/traefik/provider/dynamodb"
"github.com/containous/traefik/provider/ecs" "github.com/containous/traefik/provider/ecs"
@@ -59,6 +62,7 @@ type GlobalConfiguration struct {
AccessLog *types.AccessLog `description:"Access log settings" export:"true"` 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 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"` 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"` 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"` 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 `description:"Enable clustering" export:"true"` Cluster *types.Cluster `description:"Enable clustering" export:"true"`
@@ -79,7 +83,7 @@ type GlobalConfiguration struct {
File *file.Provider `description:"Enable File 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"` Marathon *marathon.Provider `description:"Enable Marathon backend with default settings" export:"true"`
Consul *consul.Provider `description:"Enable Consul backend with default settings" export:"true"` Consul *consul.Provider `description:"Enable Consul backend with default settings" export:"true"`
ConsulCatalog *consul.CatalogProvider `description:"Enable Consul catalog 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"` Etcd *etcd.Provider `description:"Enable Etcd backend with default settings" export:"true"`
Zookeeper *zk.Provider `description:"Enable Zookeeper 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"` Boltdb *boltdb.Provider `description:"Enable Boltdb backend with default settings" export:"true"`
@@ -178,12 +182,23 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
} }
} }
// ForwardedHeaders must be remove in the next breaking version
for entryPointName := range gc.EntryPoints { for entryPointName := range gc.EntryPoints {
entryPoint := gc.EntryPoints[entryPointName] entryPoint := gc.EntryPoints[entryPointName]
// ForwardedHeaders must be remove in the next breaking version
if entryPoint.ForwardedHeaders == nil { if entryPoint.ForwardedHeaders == nil {
entryPoint.ForwardedHeaders = &ForwardedHeaders{Insecure: true} 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
}
}
} }
// Make sure LifeCycle isn't nil to spare nil checks elsewhere. // Make sure LifeCycle isn't nil to spare nil checks elsewhere.
@@ -197,7 +212,39 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
gc.LifeCycle.GraceTimeOut = gc.GraceTimeOut 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.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.Eureka != nil {
if gc.Eureka.Delay != 0 {
log.Warn("Delay has been deprecated -- please use RefreshSeconds")
gc.Eureka.RefreshSeconds = gc.Eureka.Delay
}
}
if gc.Rancher != nil { 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 // Ensure backwards compatibility for now
if len(gc.Rancher.AccessKey) > 0 || if len(gc.Rancher.AccessKey) > 0 ||
len(gc.Rancher.Endpoint) > 0 || len(gc.Rancher.Endpoint) > 0 ||
@@ -223,17 +270,13 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
gc.API.Debug = gc.Debug gc.API.Debug = gc.Debug
} }
if gc.Debug {
gc.LogLevel = "DEBUG"
}
if gc.Web != nil && (gc.Web.Path == "" || !strings.HasSuffix(gc.Web.Path, "/")) { if gc.Web != nil && (gc.Web.Path == "" || !strings.HasSuffix(gc.Web.Path, "/")) {
gc.Web.Path += "/" gc.Web.Path += "/"
} }
// Try to fallback to traefik config file in case the file provider is enabled // Try to fallback to traefik config file in case the file provider is enabled
// but has no file name configured. // but has no file name configured and is not in a directory mode.
if gc.File != nil && len(gc.File.Filename) == 0 { if gc.File != nil && len(gc.File.Filename) == 0 && len(gc.File.Directory) == 0 {
if len(configFile) > 0 { if len(configFile) > 0 {
gc.File.Filename = configFile gc.File.Filename = configFile
} else { } else {
@@ -241,6 +284,10 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
} }
} }
gc.initACMEProvider()
}
func (gc *GlobalConfiguration) initACMEProvider() {
if gc.ACME != nil { if gc.ACME != nil {
// TODO: to remove in the futurs // TODO: to remove in the futurs
if len(gc.ACME.StorageFile) > 0 && len(gc.ACME.Storage) == 0 { if len(gc.ACME.StorageFile) > 0 && len(gc.ACME.Storage) == 0 {
@@ -250,12 +297,30 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
if len(gc.ACME.DNSProvider) > 0 { if len(gc.ACME.DNSProvider) > 0 {
log.Warn("ACME.DNSProvider is deprecated, use ACME.DNSChallenge instead") log.Warn("ACME.DNSProvider is deprecated, use ACME.DNSChallenge instead")
gc.ACME.DNSChallenge = &acme.DNSChallenge{Provider: gc.ACME.DNSProvider, DelayBeforeCheck: gc.ACME.DelayDontCheckDNS} gc.ACME.DNSChallenge = &acmeprovider.DNSChallenge{Provider: gc.ACME.DNSProvider, DelayBeforeCheck: gc.ACME.DelayDontCheckDNS}
} }
if gc.ACME.OnDemand { if gc.ACME.OnDemand {
log.Warn("ACME.OnDemand is deprecated") log.Warn("ACME.OnDemand is deprecated")
} }
// TODO: Remove when Provider ACME will replace totally ACME
// If provider file, use Provider ACME instead of ACME
if gc.Cluster == nil {
acmeprovider.Get().Configuration = &acmeprovider.Configuration{
OnHostRule: gc.ACME.OnHostRule,
OnDemand: gc.ACME.OnDemand,
Email: gc.ACME.Email,
Storage: gc.ACME.Storage,
HTTPChallenge: gc.ACME.HTTPChallenge,
DNSChallenge: gc.ACME.DNSChallenge,
Domains: gc.ACME.Domains,
ACMELogging: gc.ACME.ACMELogging,
CAServer: gc.ACME.CAServer,
EntryPoint: gc.ACME.EntryPoint,
}
gc.ACME = nil
}
} }
} }
@@ -266,7 +331,15 @@ func (gc *GlobalConfiguration) ValidateConfiguration() {
log.Fatalf("Unknown entrypoint %q for ACME configuration", gc.ACME.EntryPoint) log.Fatalf("Unknown entrypoint %q for ACME configuration", gc.ACME.EntryPoint)
} else { } else {
if gc.EntryPoints[gc.ACME.EntryPoint].TLS == nil { if gc.EntryPoints[gc.ACME.EntryPoint].TLS == nil {
log.Fatalf("Entrypoint without TLS %q for ACME configuration", gc.ACME.EntryPoint) log.Fatalf("Entrypoint %q has no TLS configuration for ACME configuration", gc.ACME.EntryPoint)
}
}
} else if acmeprovider.IsEnabled() {
if _, ok := gc.EntryPoints[acmeprovider.Get().EntryPoint]; !ok {
log.Fatalf("Unknown entrypoint %q for provider ACME configuration", acmeprovider.Get().EntryPoint)
} else {
if gc.EntryPoints[acmeprovider.Get().EntryPoint].TLS == nil {
log.Fatalf("Entrypoint %q has no TLS configuration for provider ACME configuration", acmeprovider.Get().EntryPoint)
} }
} }
} }
@@ -297,12 +370,12 @@ func (dep *DefaultEntryPoints) Set(value string) error {
// Get return the EntryPoints map // Get return the EntryPoints map
func (dep *DefaultEntryPoints) Get() interface{} { func (dep *DefaultEntryPoints) Get() interface{} {
return DefaultEntryPoints(*dep) return *dep
} }
// SetValue sets the EntryPoints map with val // SetValue sets the EntryPoints map with val
func (dep *DefaultEntryPoints) SetValue(val interface{}) { func (dep *DefaultEntryPoints) SetValue(val interface{}) {
*dep = DefaultEntryPoints(val.(DefaultEntryPoints)) *dep = val.(DefaultEntryPoints)
} }
// Type is type of the struct // Type is type of the struct
@@ -310,157 +383,6 @@ func (dep *DefaultEntryPoints) Type() string {
return "defaultentrypoints" return "defaultentrypoints"
} }
// 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", *ep)
}
// 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 configTLS *tls.TLS
if len(result["tls"]) > 0 {
certs := tls.Certificates{}
if err := certs.Set(result["tls"]); err != nil {
return err
}
configTLS = &tls.TLS{
Certificates: certs,
}
} else if len(result["tls_acme"]) > 0 {
configTLS = &tls.TLS{
Certificates: tls.Certificates{},
}
}
if len(result["ca"]) > 0 {
files := strings.Split(result["ca"], ",")
optional := toBool(result, "ca_optional")
configTLS.ClientCA = tls.ClientCA{
Files: files,
Optional: optional,
}
}
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"],
}
}
whiteListSourceRange := []string{}
if len(result["whitelistsourcerange"]) > 0 {
whiteListSourceRange = strings.Split(result["whitelistsourcerange"], ",")
}
compress := toBool(result, "compress")
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, ",")
}
}
// 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, ",")
}
if proxyProtocol != nil && proxyProtocol.Insecure {
log.Warn("ProxyProtocol.Insecure:true is dangerous. Please use 'ProxyProtocol.TrustedIPs:IPs' and remove 'ProxyProtocol.Insecure:true'")
}
(*ep)[result["name"]] = &EntryPoint{
Address: result["address"],
TLS: configTLS,
Redirect: redirect,
Compress: compress,
WhitelistSourceRange: whiteListSourceRange,
ProxyProtocol: proxyProtocol,
ForwardedHeaders: forwardedHeaders,
}
return 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
}
// Get return the EntryPoints map
func (ep *EntryPoints) Get() interface{} {
return EntryPoints(*ep)
}
// SetValue sets the EntryPoints map with val
func (ep *EntryPoints) SetValue(val interface{}) {
*ep = EntryPoints(val.(EntryPoints))
}
// Type is type of the struct
func (ep *EntryPoints) Type() string {
return "entrypoints"
}
// EntryPoint holds an entry point configuration of the reverse proxy (ip, port, TLS...)
type EntryPoint struct {
Network string
Address string
TLS *tls.TLS `export:"true"`
Redirect *types.Redirect `export:"true"`
Auth *types.Auth `export:"true"`
WhitelistSourceRange []string
Compress bool `export:"true"`
ProxyProtocol *ProxyProtocol `export:"true"`
ForwardedHeaders *ForwardedHeaders `export:"true"`
}
// Retry contains request retry config // Retry contains request retry config
type Retry struct { type Retry struct {
Attempts int `description:"Number of attempts" export:"true"` Attempts int `description:"Number of attempts" export:"true"`
@@ -484,18 +406,6 @@ type ForwardingTimeouts struct {
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"` 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"`
} }
// ProxyProtocol contains Proxy-Protocol configuration
type ProxyProtocol struct {
Insecure bool
TrustedIPs []string
}
// ForwardedHeaders Trust client forwarding headers
type ForwardedHeaders struct {
Insecure bool
TrustedIPs []string
}
// LifeCycle contains configurations relevant to the lifecycle (such as the // LifeCycle contains configurations relevant to the lifecycle (such as the
// shutdown phase) of Traefik. // shutdown phase) of Traefik.
type LifeCycle struct { type LifeCycle struct {

View File

@@ -7,305 +7,10 @@ import (
"github.com/containous/flaeg" "github.com/containous/flaeg"
"github.com/containous/traefik/provider" "github.com/containous/traefik/provider"
"github.com/containous/traefik/provider/file" "github.com/containous/traefik/provider/file"
"github.com/containous/traefik/tls"
"github.com/containous/traefik/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
const defaultConfigFile = "traefik.toml" const defaultConfigFile = "traefik.toml"
func Test_parseEntryPointsConfiguration(t *testing.T) {
testCases := []struct {
name string
value string
expectedResult map[string]string
}{
{
name: "all parameters",
value: "Name:foo TLS:goo TLS CA:car Redirect.EntryPoint:RedirectEntryPoint Redirect.Regex:RedirectRegex Redirect.Replacement:RedirectReplacement Compress:true WhiteListSourceRange:WhiteListSourceRange ProxyProtocol.TrustedIPs:192.168.0.1 ProxyProtocol.Insecure:false Address::8000",
expectedResult: map[string]string{
"name": "foo",
"address": ":8000",
"ca": "car",
"tls": "goo",
"tls_acme": "TLS",
"redirect_entrypoint": "RedirectEntryPoint",
"redirect_regex": "RedirectRegex",
"redirect_replacement": "RedirectReplacement",
"whitelistsourcerange": "WhiteListSourceRange",
"proxyprotocol_trustedips": "192.168.0.1",
"proxyprotocol_insecure": "false",
"compress": "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 TLS CA:car CA.Optional:false Redirect.EntryPoint:RedirectEntryPoint Redirect.Regex:RedirectRegex Redirect.Replacement:RedirectReplacement Compress:true WhiteListSourceRange:Range ProxyProtocol.TrustedIPs:192.168.0.1 ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
Address: ":8000",
Redirect: &types.Redirect{
EntryPoint: "RedirectEntryPoint",
Regex: "RedirectRegex",
Replacement: "RedirectReplacement",
},
Compress: true,
ProxyProtocol: &ProxyProtocol{
TrustedIPs: []string{"192.168.0.1"},
},
ForwardedHeaders: &ForwardedHeaders{
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
},
WhitelistSourceRange: []string{"Range"},
TLS: &tls.TLS{
ClientCA: tls.ClientCA{
Files: []string{"car"},
Optional: false,
},
Certificates: tls.Certificates{
{
CertFile: tls.FileOrContent("goo"),
KeyFile: tls.FileOrContent("gii"),
},
},
},
},
},
{
name: "all parameters lowercase",
expression: "name:foo address::8000 tls:goo,gii tls ca:car ca.optional:true redirect.entryPoint:RedirectEntryPoint redirect.regex:RedirectRegex redirect.replacement:RedirectReplacement compress:true whiteListSourceRange:Range proxyProtocol.trustedIPs:192.168.0.1 forwardedHeaders.trustedIPs:10.0.0.3/24,20.0.0.3/24",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
Address: ":8000",
Redirect: &types.Redirect{
EntryPoint: "RedirectEntryPoint",
Regex: "RedirectRegex",
Replacement: "RedirectReplacement",
},
Compress: true,
ProxyProtocol: &ProxyProtocol{
TrustedIPs: []string{"192.168.0.1"},
},
ForwardedHeaders: &ForwardedHeaders{
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
},
WhitelistSourceRange: []string{"Range"},
TLS: &tls.TLS{
ClientCA: tls.ClientCA{
Files: []string{"car"},
Optional: true,
},
Certificates: tls.Certificates{
{
CertFile: tls.FileOrContent("goo"),
KeyFile: tls.FileOrContent("gii"),
},
},
},
},
},
{
name: "default",
expression: "Name:foo",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
WhitelistSourceRange: []string{},
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
},
},
{
name: "ForwardedHeaders insecure true",
expression: "Name:foo ForwardedHeaders.Insecure:true",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
WhitelistSourceRange: []string{},
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
},
},
{
name: "ForwardedHeaders insecure false",
expression: "Name:foo ForwardedHeaders.Insecure:false",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
WhitelistSourceRange: []string{},
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{
WhitelistSourceRange: []string{},
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{
WhitelistSourceRange: []string{},
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
ProxyProtocol: &ProxyProtocol{Insecure: true},
},
},
{
name: "ProxyProtocol insecure false",
expression: "Name:foo ProxyProtocol.Insecure:false",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
WhitelistSourceRange: []string{},
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{
WhitelistSourceRange: []string{},
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,
WhitelistSourceRange: []string{},
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
},
},
{
name: "compress true",
expression: "Name:foo Compress:true",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
Compress: true,
WhitelistSourceRange: []string{},
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)
})
}
}
func TestSetEffectiveConfigurationGraceTimeout(t *testing.T) { func TestSetEffectiveConfigurationGraceTimeout(t *testing.T) {
tests := []struct { tests := []struct {
desc string desc string
@@ -372,6 +77,11 @@ func TestSetEffectiveConfigurationFileProviderFilename(t *testing.T) {
fileProvider: &file.Provider{BaseProvider: provider.BaseProvider{Filename: "other.toml"}}, fileProvider: &file.Provider{BaseProvider: provider.BaseProvider{Filename: "other.toml"}},
wantFileProviderFilename: "other.toml", wantFileProviderFilename: "other.toml",
}, },
{
desc: "directory for file provider given",
fileProvider: &file.Provider{Directory: "/"},
wantFileProviderFilename: "",
},
} }
for _, test := range tests { for _, test := range tests {

View File

@@ -0,0 +1,266 @@
package configuration
import (
"fmt"
"strings"
"github.com/containous/traefik/log"
"github.com/containous/traefik/tls"
"github.com/containous/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, ","),
}
}
var digest *types.Digest
if v, ok := result["auth_digest_users"]; ok {
digest = &types.Digest{
Users: strings.Split(v, ","),
}
}
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,
}
}
forward = &types.Forward{
Address: address,
TLS: clientTLS,
TrustForwardHeader: toBool(result, "auth_forward_trustforwardheader"),
}
}
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 len(result["ca"]) > 0 {
files := strings.Split(result["ca"], ",")
optional := toBool(result, "ca_optional")
configTLS.ClientCA = tls.ClientCA{
Files: files,
Optional: optional,
}
}
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,459 @@
package configuration
import (
"testing"
"github.com/containous/traefik/tls"
"github.com/containous/traefik/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
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 " +
"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.Digest.Users:test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e " +
"Auth.HeaderField:X-WebAuth-User " +
"Auth.Forward.Address:https://authserver.com/auth " +
"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_digest_users": "test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e",
"auth_forward_address": "https://authserver.com/auth",
"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",
"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 " +
"TLS " +
"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.Digest.Users:test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e " +
"Auth.HeaderField:X-WebAuth-User " +
"Auth.Forward.Address:https://authserver.com/auth " +
"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{
Certificates: tls.Certificates{
{
CertFile: tls.FileOrContent("goo"),
KeyFile: tls.FileOrContent("gii"),
},
},
ClientCA: tls.ClientCA{
Files: []string{"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",
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 " +
"tls " +
"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.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{
Certificates: tls.Certificates{
{
CertFile: tls.FileOrContent("goo"),
KeyFile: tls.FileOrContent("gii"),
},
},
ClientCA: tls.ClientCA{
Files: []string{"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",
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,97 @@
package configuration
import (
"encoding/json"
"reflect"
"github.com/containous/traefik/acme"
"github.com/containous/traefik/log"
"github.com/containous/traefik/provider"
acmeprovider "github.com/containous/traefik/provider/acme"
"github.com/containous/traefik/safe"
"github.com/containous/traefik/types"
)
type providerAggregator struct {
providers []provider.Provider
}
// NewProviderAggregator return an aggregate of all the providers configured in GlobalConfiguration
func NewProviderAggregator(gc *GlobalConfiguration) provider.Provider {
provider := providerAggregator{}
if gc.Docker != nil {
provider.providers = append(provider.providers, gc.Docker)
}
if gc.Marathon != nil {
provider.providers = append(provider.providers, gc.Marathon)
}
if gc.File != nil {
provider.providers = append(provider.providers, gc.File)
}
if gc.Rest != nil {
provider.providers = append(provider.providers, gc.Rest)
}
if gc.Consul != nil {
provider.providers = append(provider.providers, gc.Consul)
}
if gc.ConsulCatalog != nil {
provider.providers = append(provider.providers, gc.ConsulCatalog)
}
if gc.Etcd != nil {
provider.providers = append(provider.providers, gc.Etcd)
}
if gc.Zookeeper != nil {
provider.providers = append(provider.providers, gc.Zookeeper)
}
if gc.Boltdb != nil {
provider.providers = append(provider.providers, gc.Boltdb)
}
if gc.Kubernetes != nil {
provider.providers = append(provider.providers, gc.Kubernetes)
}
if gc.Mesos != nil {
provider.providers = append(provider.providers, gc.Mesos)
}
if gc.Eureka != nil {
provider.providers = append(provider.providers, gc.Eureka)
}
if gc.ECS != nil {
provider.providers = append(provider.providers, gc.ECS)
}
if gc.Rancher != nil {
provider.providers = append(provider.providers, gc.Rancher)
}
if gc.DynamoDB != nil {
provider.providers = append(provider.providers, gc.DynamoDB)
}
if gc.ServiceFabric != nil {
provider.providers = append(provider.providers, gc.ServiceFabric)
}
if acmeprovider.IsEnabled() {
provider.providers = append(provider.providers, acmeprovider.Get())
acme.ConvertToNewFormat(acmeprovider.Get().Storage)
}
if len(provider.providers) == 1 {
return provider.providers[0]
}
return provider
}
func (p providerAggregator) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
for _, p := range p.providers {
providerType := reflect.TypeOf(p)
jsonConf, err := json.Marshal(p)
if err != nil {
log.Debugf("Unable to marshal provider conf %v with error: %v", providerType, err)
}
log.Infof("Starting provider %v %s", providerType, jsonConf)
currentProvider := p
safe.Go(func() {
err := currentProvider.Provide(configurationChan, pool, constraints)
if err != nil {
log.Errorf("Error starting provider %v: %s", providerType, err)
}
})
}
return nil
}

View File

@@ -66,7 +66,7 @@ ${USAGE}" >&2
bad_acme() { bad_acme() {
echo " echo "
There was a problem parsing your acme.json file. There was a problem parsing your acme.json file. $1
${USAGE}" >&2 ${USAGE}" >&2
exit 2 exit 2
@@ -104,7 +104,7 @@ fi
jq=$(command -v jq) || exit_jq jq=$(command -v jq) || exit_jq
priv=$(${jq} -e -r '.PrivateKey' "${acmefile}") || bad_acme priv=$(${jq} -e -r '.Account.PrivateKey' "${acmefile}") || bad_acme
if [ ! -n "${priv}" ]; then if [ ! -n "${priv}" ]; then
echo " echo "
@@ -155,16 +155,16 @@ echo -e "-----BEGIN RSA PRIVATE KEY-----\n${priv}\n-----END RSA PRIVATE KEY-----
| openssl rsa -inform pem -out "${pdir}/letsencrypt.key" | openssl rsa -inform pem -out "${pdir}/letsencrypt.key"
# Process the certificates for each of the domains in acme.json # Process the certificates for each of the domains in acme.json
for domain in $(jq -r '.DomainsCertificate.Certs[].Certificate.Domain' ${acmefile}); do for domain in $(jq -r '.Certificates[].Domain.Main' ${acmefile}); do
# Traefik stores a cert bundle for each domain. Within this cert # Traefik stores a cert bundle for each domain. Within this cert
# bundle there is both proper the certificate and the Let's Encrypt CA # bundle there is both proper the certificate and the Let's Encrypt CA
echo "Extracting cert bundle for ${domain}" echo "Extracting cert bundle for ${domain}"
cert=$(jq -e -r --arg domain "$domain" '.DomainsCertificate.Certs[].Certificate | cert=$(jq -e -r --arg domain "$domain" '.Certificates[] |
select (.Domain == $domain )| .Certificate' ${acmefile}) || bad_acme select (.Domain.Main == $domain )| .Certificate' ${acmefile}) || bad_acme
echo "${cert}" | ${CMD_DECODE_BASE64} > "${cdir}/${domain}.crt" echo "${cert}" | ${CMD_DECODE_BASE64} > "${cdir}/${domain}.crt"
echo "Extracting private key for ${domain}" echo "Extracting private key for ${domain}"
key=$(jq -e -r --arg domain "$domain" '.DomainsCertificate.Certs[].Certificate | key=$(jq -e -r --arg domain "$domain" '.Certificates[] |
select (.Domain == $domain )| .PrivateKey' ${acmefile}) || bad_acme select (.Domain.Main == $domain )| .Key' ${acmefile}) || bad_acme
echo "${key}" | ${CMD_DECODE_BASE64} > "${pdir}/${domain}.key" echo "${key}" | ${CMD_DECODE_BASE64} > "${pdir}/${domain}.key"
done done

View File

@@ -62,13 +62,12 @@ And here is another example with client certificate authentication:
[entryPoints.https] [entryPoints.https]
address = ":443" address = ":443"
[entryPoints.https.tls] [entryPoints.https.tls]
[entryPoints.https.tls] [entryPoints.https.tls.ClientCA]
[entryPoints.https.tls.ClientCA] files = ["tests/clientca1.crt", "tests/clientca2.crt"]
files = ["tests/clientca1.crt", "tests/clientca2.crt"] optional = false
optional = false [[entryPoints.https.tls.certificates]]
[[entryPoints.https.tls.certificates]] certFile = "tests/traefik.crt"
certFile = "tests/traefik.crt" keyFile = "tests/traefik.key"
keyFile = "tests/traefik.key"
``` ```
- We enable SSL on `https` by giving a certificate and a key. - We enable SSL on `https` by giving a certificate and a key.
@@ -234,27 +233,26 @@ The following rules are both `Matchers` and `Modifiers`, so the `Matcher` portio
#### Priorities #### Priorities
By default, routes will be sorted (in descending order) using rules length (to avoid path overlap): 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).
You can customize priority by frontend: You can customize priority by frontend. The priority value override the rule length during sorting:
```toml ```toml
[frontends] [frontends]
[frontends.frontend1] [frontends.frontend1]
backend = "backend1" backend = "backend1"
priority = 10 priority = 20
passHostHeader = true passHostHeader = true
[frontends.frontend1.routes.test_1] [frontends.frontend1.routes.test_1]
rule = "PathPrefix:/to" rule = "PathPrefix:/to"
[frontends.frontend2] [frontends.frontend2]
priority = 5
backend = "backend2" backend = "backend2"
passHostHeader = true passHostHeader = true
[frontends.frontend2.routes.test_1] [frontends.frontend2.routes.test_1]
rule = "PathPrefix:/toto" rule = "PathPrefix:/toto"
``` ```
Here, `frontend1` will be matched before `frontend2` (`10 > 5`). Here, `frontend1` will be matched before `frontend2` (`20 > 16`).
#### Custom headers #### Custom headers
@@ -484,7 +482,7 @@ Each item takes precedence over the item below it:
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.
!!! note !!! note
the provider-enabling argument parameters (e.g., `--docker`) set all default values for the specific provider. 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. It must not be used if a configuration source with less precedence wants to set a non-default provider value.
@@ -569,6 +567,11 @@ Each command is described at the beginning of the help section:
```bash ```bash
traefik --help traefik --help
# or
docker run traefik[:version] --help
# ex: docker run traefik:1.5 --help
``` ```
### Command: bug ### Command: bug

View File

@@ -38,23 +38,20 @@ storage = "acme.json"
# or `storage = "traefik/acme/account"` if using KV store. # or `storage = "traefik/acme/account"` if using KV store.
# Entrypoint to proxy acme apply certificates to. # Entrypoint to proxy acme apply certificates to.
# WARNING, if the TLS-SNI-01 challenge is used, it must point to an entrypoint on port 443
# #
# Required # Required
# #
entryPoint = "https" entryPoint = "https"
# Use a DNS-01 acme challenge rather than TLS-SNI-01 challenge # Deprecated, replaced by [acme.dnsChallenge].
# #
# Optional (Deprecated, replaced by [acme.dnsChallenge]) # Optional.
# #
# dnsProvider = "digitalocean" # dnsProvider = "digitalocean"
# By default, the dnsProvider will verify the TXT DNS challenge record before letting ACME verify. # Deprecated, replaced by [acme.dnsChallenge.delayBeforeCheck].
# If delayDontCheckDNS is greater than zero, avoid this & instead just wait so many seconds.
# Useful if internal networks block external DNS queries.
# #
# Optional (Deprecated, replaced by [acme.dnsChallenge]) # Optional
# Default: 0 # Default: 0
# #
# delayDontCheckDNS = 0 # delayDontCheckDNS = 0
@@ -85,11 +82,12 @@ entryPoint = "https"
# - Leave comment to go to prod. # - Leave comment to go to prod.
# #
# Optional # Optional
# Default: "https://acme-v01.api.letsencrypt.org/directory" # Default: "https://acme-v02.api.letsencrypt.org/directory"
# #
# caServer = "https://acme-staging.api.letsencrypt.org/directory" # caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
# Domains list. # Domains list.
# Only domains defined here can generate wildcard certificates.
# #
# [[acme.domains]] # [[acme.domains]]
# main = "local1.com" # main = "local1.com"
@@ -102,19 +100,20 @@ entryPoint = "https"
# [[acme.domains]] # [[acme.domains]]
# main = "local4.com" # main = "local4.com"
# Use a HTTP-01 acme challenge rather than TLS-SNI-01 challenge # Use a HTTP-01 acme challenge.
# #
# Optional but recommend # Optional but recommend
# #
[acme.httpChallenge] [acme.httpChallenge]
# EntryPoint to use for the challenges. # EntryPoint to use for the HTTP-01 challenges.
# #
# Required # Required
# #
entryPoint = "http" entryPoint = "http"
# Use a DNS-01 acme challenge rather than TLS-SNI-01 challenge # Use a DNS-01/DNS-02 acme challenge rather than HTTP-01 challenge.
# Note : Mandatory for wildcard certificates generation.
# #
# Optional # Optional
# #
@@ -135,15 +134,28 @@ entryPoint = "https"
# #
# delayBeforeCheck = 0 # delayBeforeCheck = 0
``` ```
!!! note
Even if `TLS-SNI-01` challenge is [disabled](https://community.letsencrypt.org/t/2018-01-11-update-regarding-acme-tls-sni-and-shared-hosting-infrastructure/50188) for the moment, it stays the _by default_ ACME Challenge in Træfik.
If `TLS-SNI-01` challenge is not re-enabled in the future, it we will be removed from Træfik.
!!! note !!! note
If `TLS-SNI-01` challenge is used, `acme.entryPoint` has to be reachable by Let's Encrypt through the port 443.
If `HTTP-01` challenge is used, `acme.httpChallenge.entryPoint` has to be defined and reachable by Let's Encrypt through the port 80. If `HTTP-01` challenge is used, `acme.httpChallenge.entryPoint` has to be defined and reachable by Let's Encrypt through the port 80.
These are Let's Encrypt limitations as described on the [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72). These are Let's Encrypt limitations as described on the [community forum](https://community.letsencrypt.org/t/support-for-ports-other-than-80-and-443/3419/72).
!!! note
Wildcard certificates can be generated only if `acme.dnsChallenge`
option is enable.
### Let's Encrypt downtime
Let's Encrypt functionality will be limited until Træfik is restarted.
If Let's Encrypt is not reachable, these certificates will be used :
- ACME certificates already generated before downtime
- Expired ACME certificates
- Provided certificates
!!! note
Default Træfik certificate will be used instead of ACME certificates for new (sub)domains (which need Let's Encrypt challenge).
### `storage` ### `storage`
```toml ```toml
@@ -153,27 +165,14 @@ storage = "acme.json"
# ... # ...
``` ```
File or key used for certificates storage. The `storage` option sets where are stored your ACME certificates.
**WARNING:** If you use Træfik in Docker, you have 2 options: There are two kind of `storage` :
- create a file on your host and mount it as a volume: - a JSON file,
```toml - a KV store entry.
storage = "acme.json"
```
```bash
docker run -v "/my/host/acme.json:acme.json" traefik
```
- mount the folder containing the file as a volume !!! danger "DEPRECATED"
```toml
storage = "/etc/traefik/acme/acme.json"
```
```bash
docker run -v "/my/host/acme:/etc/traefik/acme" traefik
```
!!! note
`storage` replaces `storageFile` which is deprecated. `storage` replaces `storageFile` which is deprecated.
!!! note !!! note
@@ -182,7 +181,47 @@ docker run -v "/my/host/acme:/etc/traefik/acme" traefik
- `storageFile` will contain the path to the `acme.json` file to migrate. - `storageFile` will contain the path to the `acme.json` file to migrate.
- `storage` will contain the key where the certificates will be stored. - `storage` will contain the key where the certificates will be stored.
### `acme.httpChallenge` #### Store data in a file
ACME certificates can be stored in a JSON file which with the `600` right mode.
There are two ways to store ACME certificates in a file from Docker:
- create a file on your host and mount it as a volume:
```toml
storage = "acme.json"
```
```bash
docker run -v "/my/host/acme.json:acme.json" traefik
```
- mount the folder containing the file as a volume
```toml
storage = "/etc/traefik/acme/acme.json"
```
```bash
docker run -v "/my/host/acme:/etc/traefik/acme" traefik
```
!!! warning
This file cannot be shared per many instances of Træfik at the same time.
If you have to use Træfik cluster mode, please use [a KV Store entry](/configuration/acme/#storage-kv-entry).
#### Store data in a KV store entry
ACME certificates can be stored in a KV Store entry.
```toml
storage = "traefik/acme/account"
```
**This kind of storage is mandatory in cluster mode.**
Because KV stores (like Consul) have limited entries size, the certificates list is compressed before to be set in a KV store entry.
!!! note
It's possible to store up to approximately 100 ACME certificates in Consul.
### `httpChallenge`
Use `HTTP-01` challenge to generate/renew ACME certificates. Use `HTTP-01` challenge to generate/renew ACME certificates.
@@ -202,6 +241,8 @@ entryPoint = "https"
Specify the entryPoint to use during the challenges. Specify the entryPoint to use during the challenges.
```toml ```toml
defaultEntryPoints = ["http", "https"]
[entryPoints] [entryPoints]
[entryPoints.http] [entryPoints.http]
address = ":80" address = ":80"
@@ -221,9 +262,9 @@ Specify the entryPoint to use during the challenges.
`acme.httpChallenge.entryPoint` has to be reachable by Let's Encrypt through the port 80. `acme.httpChallenge.entryPoint` has to be reachable by Let's Encrypt through the 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). 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).
### `acme.dnsChallenge` ### `dnsChallenge`
Use `DNS-01` challenge to generate/renew ACME certificates. Use `DNS-01/DNS-02` challenge to generate/renew ACME certificates.
```toml ```toml
[acme] [acme]
@@ -234,7 +275,10 @@ Use `DNS-01` challenge to generate/renew ACME certificates.
# ... # ...
``` ```
#### `provider` !!! note
ACME wildcard certificates can only be generated thanks to a `DNS-02` challenge.
#### `provider`
Select the provider that matches the DNS domain that will host the challenge TXT record, and provide environment variables to enable setting it: Select the provider that matches the DNS domain that will host the challenge TXT record, and provide environment variables to enable setting it:
@@ -243,6 +287,7 @@ Select the provider that matches the DNS domain that will host the challenge TXT
| [Auroradns](https://www.pcextreme.com/aurora/dns) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` | | [Auroradns](https://www.pcextreme.com/aurora/dns) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` |
| [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP` | | [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP` |
| [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CLOUDFLARE_EMAIL`, `CLOUDFLARE_API_KEY` - The Cloudflare `Global API Key` needs to be used and not the `Origin CA Key` | | [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CLOUDFLARE_EMAIL`, `CLOUDFLARE_API_KEY` - The Cloudflare `Global API Key` needs to be used and not the `Origin CA Key` |
| [CloudXNS](https://www.cloudxns.net) | `cloudxns` | `CLOUDXNS_API_KEY`, `CLOUDXNS_SECRET_KEY` |
| [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` | | [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` |
| [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` | | [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` |
| [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` | | [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` |
@@ -250,6 +295,7 @@ Select the provider that matches the DNS domain that will host the challenge TXT
| [Dyn](https://dyn.com) | `dyn` | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` | | [Dyn](https://dyn.com) | `dyn` | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` |
| [Exoscale](https://www.exoscale.ch) | `exoscale` | `EXOSCALE_API_KEY`, `EXOSCALE_API_SECRET`, `EXOSCALE_ENDPOINT` | | [Exoscale](https://www.exoscale.ch) | `exoscale` | `EXOSCALE_API_KEY`, `EXOSCALE_API_SECRET`, `EXOSCALE_ENDPOINT` |
| [Gandi](https://www.gandi.net) | `gandi` | `GANDI_API_KEY` | | [Gandi](https://www.gandi.net) | `gandi` | `GANDI_API_KEY` |
| [Gandi V5](http://doc.livedns.gandi.net) | `gandiv5` | `GANDIV5_API_KEY` |
| [GoDaddy](https://godaddy.com/domains) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` | | [GoDaddy](https://godaddy.com/domains) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` |
| [Google Cloud DNS](https://cloud.google.com/dns/docs/) | `gcloud` | `GCE_PROJECT`, `GCE_SERVICE_ACCOUNT_FILE` | | [Google Cloud DNS](https://cloud.google.com/dns/docs/) | `gcloud` | `GCE_PROJECT`, `GCE_SERVICE_ACCOUNT_FILE` |
| [Linode](https://www.linode.com) | `linode` | `LINODE_API_KEY` | | [Linode](https://www.linode.com) | `linode` | `LINODE_API_KEY` |
@@ -276,7 +322,7 @@ Useful if internal networks block external DNS queries.
### `onDemand` (Deprecated) ### `onDemand` (Deprecated)
!!! warning !!! danger "DEPRECATED"
This option is deprecated. This option is deprecated.
```toml ```toml
@@ -288,11 +334,11 @@ onDemand = true
Enable on demand certificate. Enable on demand certificate.
This will request a certificate from Let's Encrypt during the first TLS handshake for a hostname that does not yet have a certificate. This will request a certificate from Let's Encrypt during the first TLS handshake for a host name that does not yet have a certificate.
!!! warning !!! warning
TLS handshakes will be slow when requesting a hostname certificate for the first time, this can lead to DoS attacks. TLS handshakes will be slow when requesting a host name certificate for the first time, this can lead to DoS attacks.
!!! warning !!! warning
Take note that Let's Encrypt have [rate limiting](https://letsencrypt.org/docs/rate-limits). Take note that Let's Encrypt have [rate limiting](https://letsencrypt.org/docs/rate-limits).
@@ -305,18 +351,22 @@ onHostRule = true
# ... # ...
``` ```
Enable certificate generation on frontends Host rules. Enable certificate generation on frontends `Host` rules (for frontends wired on the `acme.entryPoint`).
This will request a certificate from Let's Encrypt for each frontend with a Host rule. This will request a certificate from Let's Encrypt for each frontend with a Host rule.
For example, a rule `Host:test1.traefik.io,test2.traefik.io` will request a certificate with main domain `test1.traefik.io` and SAN `test2.traefik.io`. For example, a 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 [the wildcard generation section](/configuration/acme/#wildcard-domain) for more information.
### `caServer` ### `caServer`
```toml ```toml
[acme] [acme]
# ... # ...
caServer = "https://acme-staging.api.letsencrypt.org/directory" caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
# ... # ...
``` ```
@@ -325,7 +375,7 @@ CA server to use.
- Uncomment the line to run on the staging Let's Encrypt server. - Uncomment the line to run on the staging Let's Encrypt server.
- Leave comment to go to prod. - Leave comment to go to prod.
### `acme.domains` ### `domains`
```toml ```toml
[acme] [acme]
@@ -339,10 +389,22 @@ CA server to use.
[[acme.domains]] [[acme.domains]]
main = "local3.com" main = "local3.com"
[[acme.domains]] [[acme.domains]]
main = "local4.com" main = "*.local4.com"
# ... # ...
``` ```
#### Wildcard domains
Wildcard domain has to be defined as a main domain **with no SANs** (alternative domains).
All domains must have A/AAAA records pointing to Træfik.
!!! warning
Note that Let's Encrypt has [rate limiting](https://letsencrypt.org/docs/rate-limits).
Each domain & SANs will lead to a certificate request.
#### Others domains
You can provide SANs (alternative domains) to each main domain. You can provide SANs (alternative domains) to each main domain.
All domains must have A/AAAA records pointing to Træfik. All domains must have A/AAAA records pointing to Træfik.
@@ -353,12 +415,48 @@ Each domain & SANs will lead to a certificate request.
### `dnsProvider` (Deprecated) ### `dnsProvider` (Deprecated)
!!! warning !!! danger "DEPRECATED"
This option is deprecated. This option is deprecated, use [dnsChallenge.provider](/configuration/acme/#dnschallenge) instead.
Please refer to [DNS challenge provider section](/configuration/acme/#provider)
### `delayDontCheckDNS` (Deprecated) ### `delayDontCheckDNS` (Deprecated)
!!! warning !!! danger "DEPRECATED"
This option is deprecated. This option is deprecated, use [dnsChallenge.delayBeforeCheck](/configuration/acme/#dnschallenge) instead.
Please refer to [DNS challenge delayBeforeCheck section](/configuration/acme/#delaybeforecheck)
## Wildcard certificates
[ACME V2](https://community.letsencrypt.org/t/acme-v2-and-wildcard-certificate-support-is-live/55579) allows wildcard certificate support.
However, this feature needs a specific configuration.
### DNS-02 Challenge
As described in [Let's Encrypt post](https://community.letsencrypt.org/t/staging-endpoint-for-acme-v2/49605), wildcard certificates can only be generated through a `DNS-02`Challenge.
This challenge is linked to the Træfik option `acme.dnsChallenge`.
```toml
[acme]
# ...
[acme.dnsChallenge]
provider = "digitalocean"
delayBeforeCheck = 0
# ...
```
For more information about this option, please refer to the [dnsChallenge section](/configuration/acme/#dnschallenge).
### Wildcard domain
Wildcard domains can currently be provided only by to the `acme.domains` option.
Theses domains can not have SANs.
```toml
[acme]
# ...
[[acme.domains]]
main = "*local1.com"
[[acme.domains]]
main = "*.local2.com"
# ...
```
For more information about this option, please refer to the [domains section](/configuration/acme/#domains).

View File

@@ -1,5 +1,7 @@
# API Definition # API Definition
## Configuration
```toml ```toml
# API definition # API definition
[api] [api]
@@ -9,14 +11,14 @@
# Default: "traefik" # Default: "traefik"
# #
entryPoint = "traefik" entryPoint = "traefik"
# Enabled Dashboard # Enabled Dashboard
# #
# Optional # Optional
# Default: true # Default: true
# #
dashboard = true dashboard = true
# Enable debug mode. # Enable debug mode.
# This will install HTTP handlers to expose Go expvars under /debug/vars and # This will install HTTP handlers to expose Go expvars under /debug/vars and
# pprof profiling data under /debug/pprof. # pprof profiling data under /debug/pprof.
@@ -28,6 +30,8 @@
debug = true debug = true
``` ```
For more customization, see [entry points](/configuration/entrypoints/) documentation and [examples](/user-guide/examples/#ping-health-check).
## Web UI ## Web UI
![Web UI Providers](/img/web.frontend.png) ![Web UI Providers](/img/web.frontend.png)
@@ -39,10 +43,11 @@
| Path | Method | Description | | Path | Method | Description |
|-----------------------------------------------------------------|------------------|-------------------------------------------| |-----------------------------------------------------------------|------------------|-------------------------------------------|
| `/` | `GET` | Provides a simple HTML frontend of Træfik | | `/` | `GET` | Provides a simple HTML frontend of Træfik |
| `/health` | `GET` | json health metrics | | `/cluster/leader` | `GET` | JSON leader true/false response |
| `/health` | `GET` | JSON health metrics |
| `/api` | `GET` | Configuration for all providers | | `/api` | `GET` | Configuration for all providers |
| `/api/providers` | `GET` | Providers | | `/api/providers` | `GET` | Providers |
| `/api/providers/{provider}` | `GET`, `PUT` | Get or update provider | | `/api/providers/{provider}` | `GET`, `PUT` | Get or update provider (1) |
| `/api/providers/{provider}/backends` | `GET` | List backends | | `/api/providers/{provider}/backends` | `GET` | List backends |
| `/api/providers/{provider}/backends/{backend}` | `GET` | Get backend | | `/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` | `GET` | List servers in backend |
@@ -52,11 +57,108 @@
| `/api/providers/{provider}/frontends/{frontend}/routes` | `GET` | List routes in 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 | | `/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 !!! warning
For compatibility reason, when you activate the rest provider, you can use `web` or `rest` as `provider` value. 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`. But be careful, in the configuration for all providers the key is still `web`.
### Provider configurations ### 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, administration panel, and health-check as follows:
* Regular path: `http://hostname:80/path`
* Admin Panel: `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 ```shell
curl -s "http://localhost:8080/api" | jq . curl -s "http://localhost:8080/api" | jq .
@@ -121,6 +223,25 @@ curl -s "http://localhost:8080/api" | jq .
} }
``` ```
### 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 ### Health
```shell ```shell
@@ -185,6 +306,7 @@ curl -s "http://localhost:8080/health" | jq .
## Metrics ## Metrics
You can enable Traefik to export internal metrics to different monitoring systems. You can enable Traefik to export internal metrics to different monitoring systems.
```toml ```toml
[api] [api]
# ... # ...

View File

@@ -1,4 +1,4 @@
# Consul Key-Value backend # Consul Key-Value Backend
Træfik can be configured to use Consul as a backend configuration. Træfik can be configured to use Consul as a backend configuration.

View File

@@ -48,46 +48,121 @@ prefix = "traefik"
# Default: "Host:{{.ServiceName}}.{{.Domain}}" # Default: "Host:{{.ServiceName}}.{{.Domain}}"
# #
#frontEndRule = "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
``` ```
This backend will create routes matching on hostname based on the service name used in Consul. This backend will create routes matching on hostname based on the service name used in Consul.
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific). To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
### Tags ## Tags
Additional settings can be defined using Consul Catalog tags. Additional settings can be defined using Consul Catalog tags.
| Tag | Description | !!! note
|-----------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| The default prefix is `traefik`.
| `traefik.enable=false` | Disable this container in Træfik |
| `traefik.protocol=https` | Override the default `http` protocol | | Label | Description |
| `traefik.backend.weight=10` | Assign this weight to the container | |-------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.backend.circuitbreaker=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend, ex: `NetworkErrorRatio() > 0.` | | `<prefix>.enable=false` | Disable this container in Træfik. |
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend. Must be used in conjunction with the below label to take effect. | | `<prefix>.port=80` | Register this port. Useful when the container exposes multiples ports. |
| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by. Must be used in conjunction with the above label to take effect. | | `<prefix>.protocol=https` | Override the default `http` protocol. |
| `traefik.frontend.rule=Host:test.traefik.io` | Override the default frontend rule (Default: `Host:{{.ServiceName}}.{{.Domain}}`). | | `<prefix>.weight=10` | Assign this weight to the container. |
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. | | `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.frontend.priority=10` | Override default frontend priority | | `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints`. | | `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` | | `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.loadbalancer=drr` | override the default `wrr` load balancer algorithm | | `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.loadbalancer.stickiness=true` | enable backend sticky sessions | | `<prefix>.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend. ex: `NetworkErrorRatio() > 0.` |
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions | | `<prefix>.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. |
| `traefik.backend.loadbalancer.sticky=true` | enable backend sticky sessions (DEPRECATED) | | `<prefix>.backend.healthcheck.port=8080` | Allow to use a different port for the health check. |
| `<prefix>.backend.healthcheck.interval=1s` | Define the health check interval. |
| `<prefix>.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm. |
| `<prefix>.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions. |
| `<prefix>.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions. |
| `<prefix>.backend.loadbalancer.sticky=true` | Enable backend sticky sessions. (DEPRECATED) |
| `<prefix>.backend.maxconn.amount=10` | Set 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` | 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. |
| `<prefix>.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
| `<prefix>.frontend.entryPoints=http,https` | Assign 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` | Forward client `Host` header to the backend. |
| `<prefix>.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. |
| `<prefix>.frontend.priority=10` | Override 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 for that frontend (e.g. HTTPS). |
| `<prefix>.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
| `<prefix>.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
| `<prefix>.frontend.redirect.permanent=true` | Return 301 instead of 302. |
| `<prefix>.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{{.ServiceName}}.{{.Domain}}`. |
| `<prefix>.frontend.whiteList.sourceRange=RANGE` | 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` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
### 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.hostsProxyHeaders=EXPR` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
| `<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.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> |
| `<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. |
| `<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.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `<prefix>.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `<prefix>.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `<prefix>.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `<prefix>.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `<prefix>.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
| `<prefix>.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
| `<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. |
### Examples ### Examples
If you want that Træfik uses Consul tags correctly you need to defined them like that: If you want that Træfik uses Consul tags correctly you need to defined them like that:
```json
```js
traefik.enable=true traefik.enable=true
traefik.tags=api traefik.tags=api
traefik.tags=external traefik.tags=external
``` ```
If the prefix defined in Træfik configuration is `bla`, tags need to be defined like that: If the prefix defined in Træfik configuration is `bla`, tags need to be defined like that:
```json
```js
bla.enable=true bla.enable=true
bla.tags=api bla.tags=api
bla.tags=external bla.tags=external
``` ```

View File

@@ -39,6 +39,15 @@ watch = true
# #
# filename = "docker.tmpl" # 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. # Expose containers by default in Traefik.
# If set to false, containers that don't have `traefik.enable=true` will be ignored. # If set to false, containers that don't have `traefik.enable=true` will be ignored.
# #
@@ -123,6 +132,15 @@ swarmmode = true
# #
# filename = "docker.tmpl" # 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. # Expose services by default in Traefik.
# #
# Optional # Optional
@@ -143,12 +161,12 @@ exposedbydefault = false
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific). To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
## Labels: overriding default behaviour ## Labels: overriding default behavior
!!! note ### Using Docker with Swarm Mode
If you use a compose file, 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/#labels-1)). 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/#labels-1)).
```yaml ```yaml
version: "3" version: "3"
@@ -159,50 +177,88 @@ services:
traefik.docker.network: traefik traefik.docker.network: traefik
``` ```
### 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 ### On Containers
Labels can be used on containers to override default behaviour. Labels can be used on containers to override default behavior.
| Label | Description | | Label | Description |
|------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.backend=foo` | Give the name `foo` to the generated backend for this container. | | `traefik.docker.network` | Set the docker network to use for connections to this container. [1] |
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend. Must be used in conjunction with the below label to take effect. | | `traefik.enable=false` | Disable this container in Træfik |
| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by. Must be used in conjunction with the above label to take effect. | | `traefik.port=80` | Register this port. Useful when the container exposes multiples ports. |
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm | | `traefik.protocol=https` | Override the default `http` protocol |
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions | | `traefik.weight=10` | Assign this weight to the container |
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions | | `traefik.backend=foo` | Give the name `foo` to the generated backend for this container. |
| `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) | | `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.loadbalancer.swarm=true` | Use Swarm's inbuilt load balancer (only relevant under Swarm Mode). | | `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend | | `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.port=80` | Register this port. Useful when the container exposes multiples ports. | | `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.protocol=https` | Override the default `http` protocol | | `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.weight=10` | Assign this weight to the container | | `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
| `traefik.enable=false` | Disable this container in Træfik | | `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. |
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{containerName}.{domain}` or `Host:{service}.{project_name}.{domain}` if you are using `docker-compose`. | | `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. |
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. | | `traefik.backend.healthcheck.interval=1s` | Define the health check interval. |
| `traefik.frontend.priority=10` | Override default frontend priority | | `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints` | | `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` | | `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
| `traefik.frontend.whitelistSourceRange:RANGE` | List of IP-Ranges which are allowed to access. 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.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) |
| `traefik.docker.network` | Set the docker network to use for connections to this container. 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. | | `traefik.backend.loadbalancer.swarm=true` | Use Swarm's inbuilt load balancer (only relevant under Swarm Mode). |
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) | | `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend. Must be set with `traefik.frontend.redirect.replacement`. | | `traefik.backend.maxconn.extractorfunc=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.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend. Must be set with `traefik.frontend.redirect.regex`. | | `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
| `traefik.frontend.entryPoints=http,https` | Assign 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` | Forward client `Host` header to the backend. |
| `traefik.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. |
| `traefik.frontend.priority=10` | Override 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 for that frontend (e.g. HTTPS) |
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. |
| `traefik.frontend.rule=EXPR` | Override 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` | 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` | Use `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.
#### 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 #### Security Headers
| Label | Description | | Label | Description |
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed. Format: `Host1,Host2` | | `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
| `traefik.frontend.headers.customRequestHeaders=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> | | `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
| `traefik.frontend.headers.customResponseHeaders=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> |
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored. Format: `HEADER1,HEADER2` |
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. | | `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.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.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.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`). Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> | | `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.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.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. | | `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
@@ -211,36 +267,81 @@ Labels can be used on containers to override default behaviour.
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. | | `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. | | `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. | | `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. | | `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. | | `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy 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.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. |
### On Service ### On containers with Multiple Ports (segment labels)
Services labels can be used for overriding default behaviour 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 | | Label | Description |
|---------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------| |---------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|
| `traefik.<service-name>.port=PORT` | Overrides `traefik.port`. If several ports need to be exposed, the service labels could be used. | | `traefik.<segment_name>.port=PORT` | Overrides `traefik.port`. If several ports need to be exposed, the segment labels could be used. |
| `traefik.<service-name>.protocol` | Overrides `traefik.protocol`. | | `traefik.<segment_name>.protocol` | Overrides `traefik.protocol`. |
| `traefik.<service-name>.weight` | Assign this service weight. Overrides `traefik.weight`. | | `traefik.<segment_name>.weight` | Assign this segment weight. Overrides `traefik.weight`. |
| `traefik.<service-name>.frontend.backend=BACKEND` | Assign this service frontend to `BACKEND`. Default is to assign to the service backend. | | `traefik.<segment_name>.frontend.auth.basic` | Sets a Basic Auth for that frontend |
| `traefik.<service-name>.frontend.entryPoints` | Overrides `traefik.frontend.entrypoints` | | `traefik.<segment_name>.frontend.backend=BACKEND` | Assign this segment frontend to `BACKEND`. Default is to assign to the segment backend. |
| `traefik.<service-name>.frontend.auth.basic` | Sets a Basic Auth for that frontend | | `traefik.<segment_name>.frontend.entryPoints` | Overrides `traefik.frontend.entrypoints` |
| `traefik.<service-name>.frontend.passHostHeader` | Overrides `traefik.frontend.passHostHeader`. | | `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.<service-name>.frontend.priority` | Overrides `traefik.frontend.priority`. | | `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.<service-name>.frontend.rule` | Overrides `traefik.frontend.rule`. | | `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.<service-name>.frontend.redirect` | Overrides `traefik.frontend.redirect`. | | `traefik.<segment_name>.frontend.passHostHeader` | Overrides `traefik.frontend.passHostHeader`. |
| `traefik.<service-name>.frontend.redirect.entryPoint=https` | Overrides `traefik.frontend.redirect.entryPoint`. | | `traefik.<segment_name>.frontend.passTLSCert` | Overrides `traefik.frontend.passTLSCert`. |
| `traefik.<service-name>.frontend.redirect.regex=^http://localhost/(.*)` | Overrides `traefik.frontend.redirect.regex`. | | `traefik.<segment_name>.frontend.priority` | Overrides `traefik.frontend.priority`. |
| `traefik.<service-name>.frontend.redirect.replacement=http://mydomain/$1` | Overrides `traefik.frontend.redirect.replacement`. | | `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Overrides `traefik.frontend.redirect.entryPoint`. |
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Overrides `traefik.frontend.redirect.regex`. |
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Overrides `traefik.frontend.redirect.replacement`. |
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Return 301 instead of 302. |
| `traefik.<segment_name>.frontend.rule` | Overrides `traefik.frontend.rule`. |
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Overrides `traefik.frontend.whiteList.sourceRange`. |
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Overrides `traefik.frontend.whiteList.useXForwardedFor`. |
#### Custom Headers
| Label | Description |
|----------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.<segment_name>.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.<segment_name>.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.<segment_name>.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
| `traefik.<segment_name>.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
| `traefik.<segment_name>.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
| `traefik.<segment_name>.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.<segment_name>.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.<segment_name>.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.<segment_name>.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
| `traefik.<segment_name>.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
| `traefik.<segment_name>.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
| `traefik.<segment_name>.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
| `traefik.<segment_name>.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `traefik.<segment_name>.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.<segment_name>.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.<segment_name>.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.<segment_name>.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.<segment_name>.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.<segment_name>.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
| `traefik.<segment_name>.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
| `traefik.<segment_name>.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. |
!!! note !!! note
If a label is defined both as a `container label` and a `service label` (for example `traefik.<service-name>.port=PORT` and `traefik.port=PORT` ), the `service label` is used to defined the `<service-name>` property (`port` in the example). 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 `service labels`, in this case `container labels` are used as default value for missing `service labels` but no frontends are going to be created with the `container labels`. 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). More details in this [example](/user-guide/docker-and-lets-encrypt/#labels).

View File

@@ -124,20 +124,75 @@ Træfik needs the following policy to read ECS information:
Labels can be used on task containers to override default behaviour: Labels can be used on task containers to override default behaviour:
| Label | Description | | Label | Description |
|-----------------------------------------------------------|------------------------------------------------------------------------------------------| |------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.protocol=https` | override the default `http` protocol | | `traefik.enable=false` | Disable this container in Træfik |
| `traefik.weight=10` | assign this weight to the container | | `traefik.port=80` | Override the default `port` value. Overrides `NetworkBindings` from Docker Container |
| `traefik.enable=false` | disable this container in Træfik | | `traefik.protocol=https` | Override the default `http` protocol |
| `traefik.port=80` | override the default `port` value. Overrides `NetworkBindings` from Docker Container | | `traefik.weight=10` | Assign this weight to the container |
| `traefik.backend.loadbalancer.method=drr` | override the default `wrr` load balancer algorithm | | `traefik.backend=foo` | Give the name `foo` to the generated backend for this container. |
| `traefik.backend.loadbalancer.stickiness=true` | enable backend sticky sessions | | `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions | | `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.loadbalancer.sticky=true` | enable backend sticky sessions (DEPRECATED) | | `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.healthcheck.path=/health` | enable health checks for the backend, hitting the container at `path` | | `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.healthcheck.interval=1s` | configure the health check interval | | `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.frontend.rule=Host:test.traefik.io` | override the default frontend rule (Default: `Host:{containerName}.{domain}`). | | `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
| `traefik.frontend.passHostHeader=true` | forward client `Host` header to the backend. | | `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. |
| `traefik.frontend.priority=10` | override default frontend priority | | `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. |
| `traefik.frontend.entryPoints=http,https` | assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints`. | | `traefik.backend.healthcheck.interval=1s` | Define the health check interval. (Default: 30s) |
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` | | `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
| `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) |
| `traefik.backend.maxconn.amount=10` | Set 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` | 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.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
| `traefik.frontend.entryPoints=http,https` | Assign 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` | Forward client `Host` header to the backend. |
| `traefik.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. |
| `traefik.frontend.priority=10` | Override 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 for that frontend (e.g. HTTPS) |
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. |
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{instance_name}.{domain}`. |
| `traefik.frontend.whiteList.sourceRange=RANGE` | 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` | Use `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.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
| `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.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. |
| `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.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key 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. |

View File

@@ -21,7 +21,7 @@ endpoint = "http://my.eureka.server/eureka"
# Optional # Optional
# Default: 30s # Default: 30s
# #
delay = "1m" refreshSeconds = "1m"
# Override default configuration template. # Override default configuration template.
# For advanced users :) # For advanced users :)

View File

@@ -5,6 +5,8 @@ Træfik can be configured with a file.
## Reference ## Reference
```toml ```toml
[file]
# Backends # Backends
[backends] [backends]
@@ -52,7 +54,10 @@ Træfik can be configured with a file.
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
] ]
whitelistSourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
[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]
[frontends.frontend1.routes.route0] [frontends.frontend1.routes.route0]
@@ -119,6 +124,7 @@ Træfik can be configured with a file.
entryPoint = "https" entryPoint = "https"
regex = "^http://localhost/(.*)" regex = "^http://localhost/(.*)"
replacement = "http://mydomain/$1" replacement = "http://mydomain/$1"
permanent = true
[frontends.frontend2] [frontends.frontend2]
# ... # ...
@@ -185,7 +191,10 @@ defaultEntryPoints = ["http", "https"]
``` ```
!!! note !!! note
adding certificates directly to the entrypoint is still maintained but certificates declared in this way cannot be managed dynamically. 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. It's recommended to use the file provider to declare certificates.
### Rules in a Separate File ### Rules in a Separate File

View File

@@ -50,6 +50,17 @@ See also [Kubernetes user guide](/user-guide/kubernetes).
# #
# labelselector = "A and not B" # 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.
#
# Note : `ingressClass` option must begin with the "traefik" prefix.
#
# Optional
# Default: empty
#
# ingressClass = "traefik-internal"
# Disable PassHost Headers. # Disable PassHost Headers.
# #
# Optional # Optional
@@ -94,83 +105,147 @@ 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. See [label-selectors](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors) for details.
### 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.
If the service port defined in the ingress spec is 443, 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 ## Annotations
### General annotations ### General annotations
The following general annotations are applicable on the Ingress object: The following general annotations are applicable on the Ingress object:
- `traefik.frontend.rule.type: PathPrefixStrip` | Annotation | Description |
Override the default frontend rule type. Default: `PathPrefix`. |---------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|
- `traefik.frontend.priority: "3"` | `traefik.ingress.kubernetes.io/buffering: <YML>` | (3) See [buffering](/configuration/commons/#buffering) section. |
Override the default frontend rule priority. | `traefik.ingress.kubernetes.io/error-pages: <YML>` | (1) See [custom error pages](/configuration/commons/#custom-error-pages) section. |
- `traefik.frontend.redirect.entryPoint: https`: | `traefik.ingress.kubernetes.io/frontend-entry-points: http,https` | Override the default frontend endpoints. |
Enables Redirect to another entryPoint for that frontend (e.g. HTTPS). | `traefik.ingress.kubernetes.io/pass-tls-cert: "true"` | Override the default frontend PassTLSCert value. Default: `false`. |
- `traefik.frontend.redirect.regex: ^http://localhost/(.*)`: | `traefik.ingress.kubernetes.io/preserve-host: "true"` | Forward client `Host` header to the backend. |
Redirect to another URL for that frontend. Must be set with `traefik.frontend.redirect.replacement`. | `traefik.ingress.kubernetes.io/priority: "3"` | Override the default frontend rule priority. |
- `traefik.frontend.redirect.replacement: http://mydomain/$1`: | `traefik.ingress.kubernetes.io/rate-limit: <YML>` | (2) See [rate limiting](/configuration/commons/#rate-limiting) section. |
Redirect to another URL for that frontend. Must be set with `traefik.frontend.redirect.regex`. | `traefik.ingress.kubernetes.io/redirect-entry-point: https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS). |
- `traefik.frontend.entryPoints: http,https` | `traefik.ingress.kubernetes.io/redirect-permanent: "true"` | Return 301 instead of 302. |
Override the default frontend endpoints. | `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.frontend.passTLSCert: true` | `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`. |
Override the default frontend PassTLSCert value. Default: `false`. | `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. |
- `ingress.kubernetes.io/rewrite-target: /users` | `traefik.ingress.kubernetes.io/rule-type: PathPrefixStrip` | Override the default frontend rule type. Default: `PathPrefix`. |
Replaces each matched Ingress path with the specified one, and adds the old path to the `X-Replaced-Path` header. | `traefik.ingress.kubernetes.io/whitelist-source-range: "1.2.3.0/24, fe80::/16"` | A comma-separated list of IP ranges permitted for access. all source IPs are permitted if the list is empty or a single range is ill-formatted. |
- `ingress.kubernetes.io/whitelist-source-range: "1.2.3.0/24, fe80::/16"` | `traefik.ingress.kubernetes.io/app-root: "/index.html"` | Redirects all requests for `/` to the defined path. (4) |
A comma-separated list of IP ranges permitted for access. all source IPs are permitted if the list is empty or a single range is ill-formatted.
<1> `traefik.ingress.kubernetes.io/error-pages` example:
```yaml
foo:
status:
- "404"
backend: bar
query: /bar
fii:
status:
- "503"
- "500"
backend: bar
query: /bir
```
<2> `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
```
<3> `traefik.ingress.kubernetes.io/buffering` example:
```yaml
maxrequestbodybytes: 10485760
memrequestbodybytes: 2097153
maxresponsebodybytes: 10485761
memresponsebodybytes: 2097152
retryexpression: IsNetworkError() && Attempts() <= 2
```
<4> `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 the `ReplacePath` rule type or any other annotation leveraging that rule type.
Trying to do so leads to an error and the corresponding Ingress object being ignored.
!!! note !!! note
Please note that `traefik.frontend.redirect.regex` and `traefik.frontend.redirect.replacement` do not have to be set if `traefik.frontend.redirect.entryPoint` is defined for the redirection (they will not be used in this case). 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: The following annotations are applicable on the Service object associated with a particular Ingress object:
- `traefik.backend.loadbalancer.method=drr` | Annotation | Description |
Override the default `wrr` load balancer algorithm. |--------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
- `traefik.backend.loadbalancer.stickiness=true` | `traefik.backend.loadbalancer.sticky: "true"` | Enable backend sticky sessions (DEPRECATED). |
Enable backend sticky sessions. | `traefik.ingress.kubernetes.io/affinity: "true"` | Enable backend sticky sessions. |
- `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | `traefik.ingress.kubernetes.io/circuit-breaker-expression: <expression>` | Set the circuit breaker expression for the backend. |
Manually set the cookie name for sticky sessions. | `traefik.ingress.kubernetes.io/load-balancer-method: drr` | Override the default `wrr` load balancer algorithm. |
- `traefik.backend.loadbalancer.sticky=true` | `traefik.ingress.kubernetes.io/max-conn-amount: 10` | Set a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
Enable backend sticky sessions (DEPRECATED). | `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.backend.circuitbreaker: <expression>` | `traefik.ingress.kubernetes.io/session-cookie-name: <NAME>` | Manually set the cookie name for sticky sessions. |
Set the circuit breaker expression for the backend.
### Security annotations !!! 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: The following security annotations are applicable on the Ingress object:
| Annotation | Description | | 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/allowed-hosts: EXPR` | Provides a list of allowed hosts that requests will be processed. Format: `Host1,Host2` |
| `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/proxy-headers: EXPR` | Provides a list of headers that the proxied hostname may be stored. Format: `HEADER1,HEADER2` |
| `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> | | `ingress.kubernetes.io/ssl-redirect: "true"` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
| `ingress.kubernetes.io/proxy-headers:EXPR` | Provides a list of headers that the proxied hostname may be stored. Format: `HEADER1,HEADER2` | | `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-redirect:true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. | | `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-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-proxy-headers: EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`). Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `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/hsts-max-age: "315360000"` | Sets the max-age of the HSTS header. |
| `ingress.kubernetes.io/ssl-proxy-headers:EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`). Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> | | `ingress.kubernetes.io/hsts-include-subdomains: "true"` | Adds the IncludeSubdomains section of the STS header. |
| `ingress.kubernetes.io/hsts-max-age:315360000` | Sets the max-age of the HSTS header. | | `ingress.kubernetes.io/hsts-preload: "true"` | Adds the preload flag to the HSTS header. |
| `ngress.kubernetes.io/hsts-include-subdomains:true` | Adds the IncludeSubdomains section of the STS header. | | `ingress.kubernetes.io/force-hsts: "false"` | Adds the STS header to non-SSL requests. |
| `ingress.kubernetes.io/hsts-preload:true` | Adds the preload flag to the HSTS header. | | `ingress.kubernetes.io/frame-deny: "false"` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `ingress.kubernetes.io/force-hsts:false` | Adds the STS header to non-SSL requests. | | `ingress.kubernetes.io/custom-frame-options-value: VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `ingress.kubernetes.io/frame-deny:false` | Adds the `X-Frame-Options` header with the value of `DENY`. | | `ingress.kubernetes.io/content-type-nosniff: "true"` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `ingress.kubernetes.io/custom-frame-options-value:VALUE` | Overrides the `X-Frame-Options` header with the custom value. | | `ingress.kubernetes.io/browser-xss-filter: "true"` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `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/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-security-policy:VALUE` | Adds CSP Header with the custom value. | | `ingress.kubernetes.io/public-key: VALUE` | Adds pinned HTST public key header. |
| `ingress.kubernetes.io/public-key:VALUE` | Adds pinned HTST public key header. | | `ingress.kubernetes.io/referrer-policy: VALUE` | Adds referrer policy header. |
| `ingress.kubernetes.io/referrer-policy:VALUE` | Adds referrer policy 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/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. |
### Authentication ### Authentication
Is possible to add additional authentication annotations to the Ingress object. Is possible to add additional authentication annotations to the Ingress object.
The source of the authentication is a Secret object that contains the credentials. The source of the authentication is a Secret object that contains the credentials.
- `ingress.kubernetes.io/auth-type`: `basic` | Annotation | Description |
Contains the authentication type. The only permitted type is `basic`. |-----------------------------------------------|-------------------------------------------------------------------------------------------------------------|
- `ingress.kubernetes.io/auth-secret`: `mysecret` | `ingress.kubernetes.io/auth-type: basic` | Contains the authentication type. The only permitted type is `basic`. |
Contains the username and password with access to the paths defined in the Ingress object. | `ingress.kubernetes.io/auth-secret: mysecret` | Name of Secret containing the username and password with access to the paths defined in the Ingress object. |
The secret must be created in the same namespace as the Ingress object. The secret must be created in the same namespace as the Ingress object.

View File

@@ -3,7 +3,7 @@
Træfik can be configured to use Marathon as a backend configuration. Træfik can be configured to use Marathon as a backend configuration.
See also [Marathon user guide](/user-guide/marathon). See also [Marathon user guide](/user-guide/marathon).
## Configuration ## Configuration
@@ -45,6 +45,15 @@ domain = "marathon.localhost"
# #
# filename = "marathon.tmpl" # 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. # Expose Marathon apps by default in Traefik.
# #
# Optional # Optional
@@ -70,7 +79,7 @@ domain = "marathon.localhost"
# Enable filtering using Marathon constraints.. # Enable filtering using Marathon constraints..
# If enabled, Traefik will read Marathon constraints, as defined in https://mesosphere.github.io/marathon/docs/constraints.html # 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. # 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 ":" # i.e. "rack_id:CLUSTER:rack-1", with all constraint groups concatenated together using ":"
# #
# Optional # Optional
@@ -150,52 +159,154 @@ domain = "marathon.localhost"
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific). To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
## Labels: overriding default behaviour ## Labels: overriding default behavior
Marathon labels may be used to dynamically change the routing and forwarding behaviour. 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. They may be specified on one of two levels: Application or service.
### Application Level ### Application Level
The following labels can be defined on Marathon applications. They adjust the behaviour for the entire application. The following labels can be defined on Marathon applications. They adjust the behavior for the entire application.
| Label | Description | | Label | Description |
|-----------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.backend=foo` | assign the application to `foo` backend | | `traefik.enable=false` | Disable this container in Træfik |
| `traefik.backend.maxconn.amount=10` | set a maximum number of connections to the backend. Must be used in conjunction with the below label to take effect. | | `traefik.port=80` | Register this port. Useful when the container exposes multiples ports. |
| `traefik.backend.maxconn.extractorfunc=client.ip` | set the function to be used against the request to determine what to limit maximum connections to the backend by. Must be used in conjunction with the above label to take effect. | | `traefik.portIndex=1` | Register port by index in the application's ports array. Useful when the application exposes multiple ports. |
| `traefik.backend.loadbalancer.method=drr` | override the default `wrr` load balancer algorithm | | `traefik.protocol=https` | Override the default `http` protocol |
| `traefik.backend.loadbalancer.sticky=true` | enable backend sticky sessions (DEPRECATED) | | `traefik.weight=10` | Assign this weight to the container |
| `traefik.backend.loadbalancer.stickiness=true` | enable backend sticky sessions | | `traefik.backend=foo` | Give the name `foo` to the generated backend for this container. |
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions | | `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.circuitbreaker.expression=NetworkErrorRatio() > 0.5` | create a [circuit breaker](/basics/#backends) to be used against the backend | | `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.healthcheck.path=/health` | set the Traefik health check path [default: no health checks] | | `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.healthcheck.interval=5s` | sets a custom health check interval in Go-parseable (`time.ParseDuration`) format [default: 30s] | | `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.portIndex=1` | register port by index in the application's ports array. Useful when the application exposes multiple ports. | | `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.port=80` | register the explicit application port value. Cannot be used alongside `traefik.portIndex`. | | `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
| `traefik.protocol=https` | override the default `http` protocol | | `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. |
| `traefik.weight=10` | assign this weight to the application | | `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. |
| `traefik.enable=false` | disable this application in Træfik | | `traefik.backend.healthcheck.interval=1s` | Define the health check interval. (Default: 30s) |
| `traefik.frontend.rule=Host:test.traefik.io` | override the default frontend rule (Default: `Host:{containerName}.{domain}`). | | `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
| `traefik.frontend.passHostHeader=true` | forward client `Host` header to the backend. | | `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
| `traefik.frontend.priority=10` | override default frontend priority | | `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
| `traefik.frontend.entryPoints=http,https` | assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints`. | | `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) |
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash`. | | `traefik.backend.maxconn.amount=10` | Set 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` | 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.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
| `traefik.frontend.entryPoints=http,https` | Assign 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` | Forward client `Host` header to the backend. |
| `traefik.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. |
| `traefik.frontend.priority=10` | Override 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 for that frontend (e.g. HTTPS) |
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. |
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{sub_domain}.{domain}`. |
| `traefik.frontend.whiteList.sourceRange=RANGE` | 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` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
### Service Level #### Custom Headers
For applications that expose multiple ports, specific labels can be used to extract one frontend/backend configuration pair per port. Each such pair is called a _service_. The (freely choosable) name of the service is an integral part of the service label name. | 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> |
|
| Label | Description | #### Security Headers
|--------------------------------------------------------|------------------------------------------------------------------------------------------------------|
| `traefik.<service-name>.port=443` | create a service binding with frontend/backend using this port. Overrides `traefik.port`. | | Label | Description |
| `traefik.<service-name>.portIndex=1` | create a service binding with frontend/backend using this port index. Overrides `traefik.portIndex`. | |----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.<service-name>.protocol=https` | assign `https` protocol. Overrides `traefik.protocol`. | | `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
| `traefik.<service-name>.weight=10` | assign this service weight. Overrides `traefik.weight`. | | `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
| `traefik.<service-name>.frontend.backend=fooBackend` | assign this service frontend to `foobackend`. Default is to assign to the service backend. | | `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
| `traefik.<service-name>.frontend.entryPoints=http` | assign this service entrypoints. Overrides `traefik.frontend.entrypoints`. | | `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.<service-name>.frontend.auth.basic=test:EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` | | `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.<service-name>.frontend.passHostHeader=true` | Forward client `Host` header to the backend. Overrides `traefik.frontend.passHostHeader`. | | `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.<service-name>.frontend.priority=10` | assign the service frontend priority. Overrides `traefik.frontend.priority`. | | `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
| `traefik.<service-name>.frontend.rule=Path:/foo` | assign the service frontend rule. Overrides `traefik.frontend.rule`. | | `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. |
| `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.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key 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. |
### 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>.portIndex=1` | Create a service binding with frontend/backend using this port index. Overrides `traefik.portIndex`. |
| `traefik.<segment_name>.port=PORT` | Overrides `traefik.port`. If several ports need to be exposed, the service labels could be used. |
| `traefik.<segment_name>.protocol=http` | Overrides `traefik.protocol`. |
| `traefik.<segment_name>.weight=10` | Assign this service weight. Overrides `traefik.weight`. |
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Sets a Basic Auth for that frontend |
| `traefik.<segment_name>.frontend.backend=BACKEND` | Assign this service frontend to `BACKEND`. Default is to assign to the service backend. |
| `traefik.<segment_name>.frontend.entryPoints=https` | Overrides `traefik.frontend.entrypoints` |
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.<segment_name>.frontend.passHostHeader=true` | Overrides `traefik.frontend.passHostHeader`. |
| `traefik.<segment_name>.frontend.passTLSCert=true` | Overrides `traefik.frontend.passTLSCert`. |
| `traefik.<segment_name>.frontend.priority=10` | Overrides `traefik.frontend.priority`. |
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Overrides `traefik.frontend.redirect.entryPoint`. |
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Overrides `traefik.frontend.redirect.regex`. |
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Overrides `traefik.frontend.redirect.replacement`. |
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Return 301 instead of 302. |
| `traefik.<segment_name>.frontend.rule=EXP` | Overrides `traefik.frontend.rule`. Default: `{service_name}.{sub_domain}.{domain}` |
| `traefik.<segment_name>.frontend.whitelistSourceRange=RANGE` | Overrides `traefik.frontend.whitelistSourceRange`. |
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Overrides `traefik.frontend.whiteList.sourceRange`. |
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
#### Custom Headers
| Label | Description |
|----------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.<segment_name>.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.<segment_name>.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.<segment_name>.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
| `traefik.<segment_name>.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
| `traefik.<segment_name>.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
| `traefik.<segment_name>.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.<segment_name>.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.<segment_name>.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.<segment_name>.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
| `traefik.<segment_name>.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
| `traefik.<segment_name>.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
| `traefik.<segment_name>.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
| `traefik.<segment_name>.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `traefik.<segment_name>.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.<segment_name>.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.<segment_name>.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.<segment_name>.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.<segment_name>.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.<segment_name>.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key header. |
| `traefik.<segment_name>.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
| `traefik.<segment_name>.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. |

View File

@@ -69,7 +69,7 @@ domain = "mesos.localhost"
# #
# RefreshSeconds = 30 # RefreshSeconds = 30
# IP sources (e.g. host, docker, mesos, rkt). # IP sources (e.g. host, docker, mesos, netinfo).
# #
# Optional # Optional
# #
@@ -91,3 +91,80 @@ domain = "mesos.localhost"
# #
# groupsAsSubDomains = true # groupsAsSubDomains = true
``` ```
## Labels: overriding default behaviour
The following labels can be defined on Mesos tasks. They adjust the behaviour for the entire application.
| Label | Description |
|------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.enable=false` | Disable this container in Træfik |
| `traefik.port=80` | Register this port. Useful when the container exposes multiples ports. |
| `traefik.portIndex=1` | Register port by index in the application's ports array. Useful when the application exposes multiple ports. |
| `traefik.protocol=https` | Override the default `http` protocol |
| `traefik.weight=10` | Assign this weight to the container |
| `traefik.backend=foo` | Give the name `foo` to the generated backend for this 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. |
| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
| `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. |
| `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. |
| `traefik.backend.healthcheck.interval=1s` | Define the health check interval. (Default: 30s) |
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
| `traefik.backend.maxconn.amount=10` | Set 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` | 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.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
| `traefik.frontend.entryPoints=http,https` | Assign 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` | Forward client `Host` header to the backend. |
| `traefik.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. |
| `traefik.frontend.priority=10` | Override 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 for that frontend (e.g. HTTPS) |
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. |
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{discovery_name}.{domain}`. |
| `traefik.frontend.whiteList.sourceRange=RANGE` | 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` | Use `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.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
| `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.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. |
| `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.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key 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. |

View File

@@ -46,6 +46,22 @@ exposedByDefault = false
# Default: false # Default: false
# #
enableServiceHealthFilter = true 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 [backend-specific constraints section](/configuration/commons/#backend-specific). To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
@@ -116,25 +132,146 @@ secretKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
io.rancher.container.create_agent: true io.rancher.container.create_agent: true
``` ```
## Labels: overriding default behaviour ## Labels: overriding default behavior
Labels can be used on task containers to override default behaviour: ### On Containers
| Label | Description | Labels can be used on task containers to override default behavior:
|-----------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|
| `traefik.protocol=https` | Override the default `http` protocol | | Label | Description |
| `traefik.weight=10` | Assign this weight to the container | |------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.enable=false` | Disable this container in Træfik | | `traefik.enable=false` | Disable this container in Træfik |
| `traefik.frontend.rule=Host:test.traefik.io` | Override the default frontend rule (Default: `Host:{containerName}.{domain}`). | | `traefik.port=80` | Register this port. Useful when the container exposes multiples ports. |
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. | | `traefik.protocol=https` | Override the default `http` protocol |
| `traefik.frontend.priority=10` | Override default frontend priority | | `traefik.weight=10` | Assign this weight to the container |
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints`. | | `traefik.backend=foo` | Give the name `foo` to the generated backend for this container. |
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash`. | | `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) | | `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.frontend.redirect.regex: ^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. | | `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.frontend.redirect.replacement: http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. | | `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.circuitbreaker.expression=NetworkErrorRatio() > 0.5` | Create a [circuit breaker](/basics/#backends) to be used against the backend | | `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm | | `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions | | `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. |
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions | | `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. |
| `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) | | `traefik.backend.healthcheck.interval=1s` | Define the health check interval. |
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
| `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) |
| `traefik.backend.maxconn.amount=10` | Set 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` | 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.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
| `traefik.frontend.entryPoints=http,https` | Assign 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` | Forward client `Host` header to the backend. |
| `traefik.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. |
| `traefik.frontend.priority=10` | Override 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 for that frontend (e.g. HTTPS) |
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. |
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Default: `Host:{service_name}.{stack_name}.{domain}`. |
| `traefik.frontend.whiteList.sourceRange=RANGE` | 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` | Use `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.hostsProxyHeaders=EXPR` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
| `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.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. |
| `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.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key 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. |
### 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>.port=PORT` | Overrides `traefik.port`. If several ports need to be exposed, the segment labels could be used. |
| `traefik.<segment_name>.protocol` | Overrides `traefik.protocol`. |
| `traefik.<segment_name>.weight` | Assign this segment weight. Overrides `traefik.weight`. |
| `traefik.<segment_name>.frontend.auth.basic` | Sets a Basic Auth for that frontend |
| `traefik.<segment_name>.frontend.backend=BACKEND` | Assign this segment frontend to `BACKEND`. Default is to assign to the segment backend. |
| `traefik.<segment_name>.frontend.entryPoints` | Overrides `traefik.frontend.entrypoints` |
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
| `traefik.<segment_name>.frontend.passHostHeader` | Overrides `traefik.frontend.passHostHeader`. |
| `traefik.<segment_name>.frontend.passTLSCert` | Overrides `traefik.frontend.passTLSCert`. |
| `traefik.<segment_name>.frontend.priority` | Overrides `traefik.frontend.priority`. |
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Overrides `traefik.frontend.redirect.entryPoint`. |
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Overrides `traefik.frontend.redirect.regex`. |
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Overrides `traefik.frontend.redirect.replacement`. |
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Return 301 instead of 302. |
| `traefik.<segment_name>.frontend.rule` | Overrides `traefik.frontend.rule`. |
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Overrides `traefik.frontend.whiteList.sourceRange`. |
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Overrides `traefik.frontend.whiteList.useXForwardedFor`. |
#### Custom Headers
| Label | Description |
|----------------------------------------------------------------------|-----------------------------------------------------------------|
| `traefik.<segment_name>.frontend.headers.customRequestHeaders=EXPR ` | overrides `traefik.frontend.headers.customRequestHeaders=EXPR ` |
| `traefik.<segment_name>.frontend.headers.customResponseHeaders=EXPR` | overrides `traefik.frontend.headers.customResponseHeaders=EXPR` |
#### Security Headers
| Label | Description |
|-------------------------------------------------------------------------|--------------------------------------------------------------------|
| `traefik.<segment_name>.frontend.headers.allowedHosts=EXPR` | overrides `traefik.frontend.headers.allowedHosts=EXPR` |
| `traefik.<segment_name>.frontend.headers.hostsProxyHeaders=EXPR` | overrides `traefik.frontend.headers.hostsProxyHeaders=EXPR` |
| `traefik.<segment_name>.frontend.headers.SSLRedirect=true` | overrides `traefik.frontend.headers.SSLRedirect=true` |
| `traefik.<segment_name>.frontend.headers.SSLTemporaryRedirect=true` | overrides `traefik.frontend.headers.SSLTemporaryRedirect=true` |
| `traefik.<segment_name>.frontend.headers.SSLHost=HOST` | overrides `traefik.frontend.headers.SSLHost=HOST` |
| `traefik.<segment_name>.frontend.headers.SSLProxyHeaders=EXPR` | overrides `traefik.frontend.headers.SSLProxyHeaders=EXPR` |
| `traefik.<segment_name>.frontend.headers.STSSeconds=315360000` | overrides `traefik.frontend.headers.STSSeconds=315360000` |
| `traefik.<segment_name>.frontend.headers.STSIncludeSubdomains=true` | overrides `traefik.frontend.headers.STSIncludeSubdomains=true` |
| `traefik.<segment_name>.frontend.headers.STSPreload=true` | overrides `traefik.frontend.headers.STSPreload=true` |
| `traefik.<segment_name>.frontend.headers.forceSTSHeader=false` | overrides `traefik.frontend.headers.forceSTSHeader=false` |
| `traefik.<segment_name>.frontend.headers.frameDeny=false` | overrides `traefik.frontend.headers.frameDeny=false` |
| `traefik.<segment_name>.frontend.headers.customFrameOptionsValue=VALUE` | overrides `traefik.frontend.headers.customFrameOptionsValue=VALUE` |
| `traefik.<segment_name>.frontend.headers.contentTypeNosniff=true` | overrides `traefik.frontend.headers.contentTypeNosniff=true` |
| `traefik.<segment_name>.frontend.headers.browserXSSFilter=true` | overrides `traefik.frontend.headers.browserXSSFilter=true` |
| `traefik.<segment_name>.frontend.headers.customBrowserXSSValue=VALUE` | overrides `traefik.frontend.headers.customBrowserXSSValue=VALUE` |
| `traefik.<segment_name>.frontend.headers.contentSecurityPolicy=VALUE` | overrides `traefik.frontend.headers.contentSecurityPolicy=VALUE` |
| `traefik.<segment_name>.frontend.headers.publicKey=VALUE` | overrides `traefik.frontend.headers.publicKey=VALUE` |
| `traefik.<segment_name>.frontend.headers.referrerPolicy=VALUE` | overrides `traefik.frontend.headers.referrerPolicy=VALUE` |
| `traefik.<segment_name>.frontend.headers.isDevelopment=false` | overrides `traefik.frontend.headers.isDevelopment=false` |

View File

@@ -29,9 +29,10 @@ Træfik can be configured:
```shell ```shell
curl -XPUT @file "http://localhost:8080/api" curl -XPUT @file "http://localhost:8080/api/providers/rest"
``` ```
with `@file`
with `@file`:
```json ```json
{ {
"frontends": { "frontends": {
@@ -88,4 +89,4 @@ with `@file`
} }
} }
} }
``` ```

View File

@@ -69,10 +69,10 @@ Here is an example of an extension setting Træfik labels:
</StatelessServiceType> </StatelessServiceType>
``` ```
#### Property Manager #### Property Manager
Set Labels with the property manager API to overwrite and add labels, while your service is running. Set Labels with the property manager API to overwrite and add labels, while your service is running.
Here is an example of adding a frontend rule using the property manager API. Here is an example of adding a frontend rule using the property manager API.
```shell ```shell
curl -X PUT \ curl -X PUT \
@@ -92,23 +92,64 @@ curl -X PUT \
## Available Labels ## Available Labels
Labels, set through extensions or the property manager, can be used on services to override default behaviour. Labels, set through extensions or the property manager, can be used on services to override default behavior.
| Label | Description | | Label | Description |
|-----------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. | | `traefik.enable=false` | Disable this container in Træfik |
| `traefik.backend.maxconn.extractorfunc=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.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm | | `traefik.backend.group.name` | Group all services with the same name into a single backend in Træfik |
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions | | `traefik.backend.group.weight` | Set the weighting of the current services nodes in the backend group |
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions | | `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. |
| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend | | `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. |
| `traefik.backend.weight=10` | Assign this weight to the container | | `traefik.backend.healthcheck.interval=1s` | Define the health check interval. |
| `traefik.expose=true` | Expose this service using træfik | | `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Defaults to SF address. | | `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. | | `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
| `traefik.frontend.priority=10` | Override default frontend priority | | `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) |
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints` | | `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
| `traefik.frontend.auth.basic=EXPR` | Set basic authentication for that frontend in CSV format: `User:Hash,User:Hash` | | `traefik.backend.maxconn.extractorfunc=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.frontend.whitelistSourceRange:RANGE` | List of IP-Ranges which are allowed to access. 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.backend.weight=10` | Assign this weight to the container |
| `traefik.backend.group.name` | Group all services with the same name into a single backend in Træfik | | `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
| `traefik.backend.group.weight` | Set the weighting of the current services nodes in the backend group | | `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
| `traefik.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. |
| `traefik.frontend.priority=10` | Override default frontend priority |
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) |
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. |
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Defaults to SF address. |
| `traefik.frontend.whiteList.sourceRange=RANGE` | 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` | Use `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.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
| `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.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. |
| `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.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.frontend.headers.publicKey=VALUE` | Adds pinned HTST public key 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. |

View File

@@ -36,7 +36,6 @@ address = ":8080"
# #
readOnly = true readOnly = true
# Set the root path for webui and API # Set the root path for webui and API
# #
# Deprecated # Deprecated
@@ -55,13 +54,13 @@ readOnly = true
### Authentication ### Authentication
!!! note !!! note
The `/ping` path of the api is excluded from authentication (since 1.4). The `/ping` path of the API is excluded from authentication (since 1.4).
#### Basic Authentication #### Basic Authentication
Passwords can be encoded in MD5, SHA1 and BCrypt: you can use `htpasswd` to generate those ones. Passwords can be encoded in MD5, SHA1 and BCrypt: you can use `htpasswd` to generate those ones.
Users can be specified directly in the toml file, or indirectly by referencing an external file; Users can be specified directly in the TOML file, or indirectly by referencing an external file;
if both are provided, the two are merged, with external file contents having precedence. if both are provided, the two are merged, with external file contents having precedence.
```toml ```toml
@@ -80,7 +79,7 @@ usersFile = "/path/to/.htpasswd"
You can use `htdigest` to generate those ones. You can use `htdigest` to generate those ones.
Users can be specified directly in the toml file, or indirectly by referencing an external file; Users can be specified directly in the TOML file, or indirectly by referencing an external file;
if both are provided, the two are merged, with external file contents having precedence if both are provided, the two are merged, with external file contents having precedence
```toml ```toml
@@ -98,7 +97,7 @@ usersFile = "/path/to/.htdigest"
## Metrics ## Metrics
You can enable Traefik to export internal metrics to different monitoring systems. You can enable Træfik to export internal metrics to different monitoring systems.
### Prometheus ### Prometheus
@@ -114,7 +113,7 @@ You can enable Traefik to export internal metrics to different monitoring system
# Optional # Optional
# Default: [0.1, 0.3, 1.2, 5] # Default: [0.1, 0.3, 1.2, 5]
buckets=[0.1,0.3,1.2,5.0] buckets=[0.1,0.3,1.2,5.0]
# ... # ...
``` ```
@@ -221,7 +220,7 @@ recentErrors = 10
|-----------------------------------------------------------------|:-------------:|----------------------------------------------------------------------------------------------------| |-----------------------------------------------------------------|:-------------:|----------------------------------------------------------------------------------------------------|
| `/` | `GET` | Provides a simple HTML frontend of Træfik | | `/` | `GET` | Provides a simple HTML frontend of Træfik |
| `/ping` | `GET`, `HEAD` | A simple endpoint to check for Træfik process liveness. Return a code `200` with the content: `OK` | | `/ping` | `GET`, `HEAD` | A simple endpoint to check for Træfik process liveness. Return a code `200` with the content: `OK` |
| `/health` | `GET` | json health metrics | | `/health` | `GET` | JSON health metrics |
| `/api` | `GET` | Configuration for all providers | | `/api` | `GET` | Configuration for all providers |
| `/api/providers` | `GET` | Providers | | `/api/providers` | `GET` | Providers |
| `/api/providers/{provider}` | `GET`, `PUT` | Get or update provider | | `/api/providers/{provider}` | `GET`, `PUT` | Get or update provider |
@@ -244,7 +243,7 @@ curl -sv "http://localhost:8080/ping"
``` ```
```shell ```shell
* Trying ::1... * Trying ::1...
* Connected to localhost (::1) port 8080 (#0) * Connected to localhost (::1) port 8080 (\#0)
> GET /ping HTTP/1.1 > GET /ping HTTP/1.1
> Host: localhost:8080 > Host: localhost:8080
> User-Agent: curl/7.43.0 > User-Agent: curl/7.43.0
@@ -255,7 +254,7 @@ curl -sv "http://localhost:8080/ping"
< Content-Length: 2 < Content-Length: 2
< Content-Type: text/plain; charset=utf-8 < Content-Type: text/plain; charset=utf-8
< <
* Connection #0 to host localhost left intact * Connection \#0 to host localhost left intact
OK OK
``` ```
@@ -309,7 +308,7 @@ curl -s "http://localhost:8080/health" | jq .
"status": "Internal Server Error", "status": "Internal Server Error",
// request HTTP method // request HTTP method
"method": "GET", "method": "GET",
// request hostname // request host name
"host": "localhost", "host": "localhost",
// request path // request path
"path": "/path", "path": "/path",
@@ -385,23 +384,62 @@ curl -s "http://localhost:8080/api" | jq .
} }
``` ```
## Path ### Deprecation compatibility
As web is deprecated, you can handle the `Path` option like this #### Address
As the web provider is deprecated, you can handle the `Address` option like this:
```toml ```toml
[entrypoints.http] defaultEntryPoints = ["http"]
address=":80"
[entrypoints.dashboard] [entryPoints]
address=":8080" [entryPoints.http]
address = ":80"
[entrypoints.api] [entryPoints.foo]
address=":8081" address = ":8082"
[entryPoints.bar]
address = ":8083"
[ping]
entryPoint = "foo"
#Activate API and Dashboard
[api] [api]
entrypoint="api" entryPoint = "bar"
```
In the above example, you would access a regular path, administration panel, and health-check as follows:
* Regular path: `http://hostname:80/path`
* Admin Panel: `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.
#### Path
As the web provider is deprecated, you can handle the `Path` option 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] [file]
[backends] [backends]
@@ -411,8 +449,34 @@ entrypoint="api"
[frontends] [frontends]
[frontends.frontend1] [frontends.frontend1]
entrypoints=["dashboard"] entryPoints = ["foo"]
backend = "backend1" backend = "backend1"
[frontends.frontend1.routes.test_1] [frontends.frontend1.routes.test_1]
rule = "PathPrefixStrip:/yourprefix;PathPrefix:/yourprefix" rule = "PathPrefixStrip:/yourprefix;PathPrefix:/yourprefix"
``` ```
#### Authentication
As the web provider is deprecated, you can handle the `auth` option 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/) .

View File

@@ -19,7 +19,7 @@
# Enable debug mode. # Enable debug mode.
# This will install HTTP handlers to expose Go expvars under /debug/vars and # This will install HTTP handlers to expose Go expvars under /debug/vars and
# pprof profiling data under /debug/pprof. # pprof profiling data under /debug/pprof.
# Additionally, the log level will be set to DEBUG. # The log level will be set to DEBUG unless `logLevel` is specified.
# #
# Optional # Optional
# Default: false # Default: false
@@ -154,89 +154,6 @@ constraints = ["tag==api", "tag!=v*-beta"]
``` ```
## Logs Definition
### Traefik logs
```toml
# Traefik logs file
# If not defined, logs to stdout
#
# DEPRECATED - see [traefikLog] lower down
# In case both traefikLogsFile and traefikLog.filePath are specified, the latter will take precedence.
# Optional
#
traefikLogsFile = "log/traefik.log"
# Log level
#
# Optional
# Default: "ERROR"
#
# Accepted values, in order of severity: "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "PANIC"
# Messages at and above the selected level will be logged.
#
logLevel = "ERROR"
```
## Traefik Logs
By default the Traefik log is written to stdout in text format.
To write the logs into a logfile specify the `filePath`.
```toml
[traefikLog]
filePath = "/path/to/traefik.log"
```
To write JSON format logs, specify `json` as the format:
```toml
[traefikLog]
filePath = "/path/to/traefik.log"
format = "json"
```
### Access Logs
Access logs are written when `[accessLog]` is defined.
By default it will write to stdout and produce logs in the textual Common Log Format (CLF), extended with additional fields.
To enable access logs using the default settings just add the `[accessLog]` entry.
```toml
[accessLog]
```
To write the logs into a logfile specify the `filePath`.
```toml
[accessLog]
filePath = "/path/to/access.log"
```
To write JSON format logs, specify `json` as the format:
```toml
[accessLog]
filePath = "/path/to/access.log"
format = "json"
```
Deprecated way (before 1.4):
```toml
# Access logs file
#
# DEPRECATED - see [accessLog] lower down
#
accessLogsFile = "log/access.log"
```
### Log Rotation
Traefik will close and reopen its log files, assuming they're configured, on receipt of a USR1 signal.
This allows the logs to be rotated and processed by an external program, such as `logrotate`.
!!! note
This does not work on Windows due to the lack of USR signals.
## Custom Error pages ## Custom Error pages
Custom error pages can be returned, in lieu of the default, according to frontend-configured ranges of HTTP Status codes. Custom error pages can be returned, in lieu of the default, according to frontend-configured ranges of HTTP Status codes.
@@ -273,9 +190,6 @@ Instead, the query parameter can also be set to some generic error page like so:
Now the `500s.html` error page is returned for the configured code range. Now the `500s.html` error page is returned for the configured code range.
The configured status code ranges are inclusive; that is, in the above example, the `500s.html` page will be returned for status codes `500` through, and including, `599`. The configured status code ranges are inclusive; that is, in the above example, the `500s.html` page will be returned for status codes `500` through, and including, `599`.
Custom error pages are easiest to implement using the file provider.
For dynamic providers, the corresponding template file needs to be customized accordingly and referenced in the Traefik configuration.
## Rate limiting ## Rate limiting
@@ -302,6 +216,26 @@ In the above example, frontend1 is configured to limit requests by the client's
An average of 5 requests every 3 seconds is allowed and an average of 100 requests every 10 seconds. An average of 5 requests every 3 seconds is allowed and an average of 100 requests every 10 seconds.
These can "burst" up to 10 and 200 in each period respectively. These can "burst" up to 10 and 200 in each period respectively.
## Buffering
In some cases request/buffering can be enabled for a specific backend.
By enabling this, Træfik will read the entire request into memory (possibly buffering large requests into disk) and will reject requests that are over a specified limit.
This may help services deal with large data (multipart/form-data for example) more efficiently and should minimise time spent when sending data to a backend server.
For more information please check [oxy/buffer](http://godoc.org/github.com/vulcand/oxy/buffer) documentation.
Example configuration:
```toml
[backends]
[backends.backend1]
[backends.backend1.buffering]
maxRequestBodyBytes = 10485760
memRequestBodyBytes = 2097152
maxResponseBodyBytes = 10485760
memResponseBodyBytes = 2097152
retryExpression = "IsNetworkError() && Attempts() <= 2"
```
## Retry Configuration ## Retry Configuration

View File

@@ -2,16 +2,24 @@
## Reference ## Reference
### TOML
```toml ```toml
[entryPoints] [entryPoints]
[entryPoints.http] [entryPoints.http]
address = ":80" address = ":80"
whitelistSourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
compress = true compress = true
[entryPoints.http.whitelist]
sourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
useXForwardedFor = true
[entryPoints.http.tls] [entryPoints.http.tls]
minVersion = "VersionTLS12" minVersion = "VersionTLS12"
cipherSuites = ["TLS_RSA_WITH_AES_256_GCM_SHA384"] cipherSuites = [
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_RSA_WITH_AES_256_GCM_SHA384"
]
[[entryPoints.http.tls.certificates]] [[entryPoints.http.tls.certificates]]
certFile = "path/to/my.cert" certFile = "path/to/my.cert"
keyFile = "path/to/my.key" keyFile = "path/to/my.key"
@@ -64,6 +72,65 @@
# ... # ...
``` ```
### CLI
For more information about the CLI, see the documentation about [Traefik command](/basics/#traefik).
```shell
--entryPoints='Name:http Address::80'
--entryPoints='Name:https Address::443 TLS'
```
!!! note
Whitespace is used as option separator and `,` is used as value separator for the list.
The names of the options are case-insensitive.
In compose file the entrypoint syntax is different:
```yaml
traefik:
image: traefik
command:
- --defaultentrypoints=powpow
- "--entryPoints=Name:powpow Address::42 Compress:true"
```
or
```yaml
traefik:
image: traefik
command: --defaultentrypoints=powpow --entryPoints='Name:powpow Address::42 Compress:true'
```
#### All available options:
```ini
Name:foo
Address::80
TLS:goo,gii
TLS
CA:car
CA.Optional:true
Redirect.EntryPoint:https
Redirect.Regex:http://localhost/(.*)
Redirect.Replacement:http://mydomain/$1
Redirect.Permanent:true
Compress:true
WhiteList.SourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16
WhiteList.UseXForwardedFor:true
ProxyProtocol.TrustedIPs:192.168.0.1
ProxyProtocol.Insecure:tue
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.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
```
## Basic ## Basic
@@ -118,7 +185,11 @@ To redirect an entrypoint rewriting the URL.
``` ```
!!! note !!! note
Please note that `regex` and `replacement` do not have to be set in the `redirect` structure if an entrypoint is defined for the redirection (they will not be used in this case). Please note that `regex` and `replacement` do not have to be set in the `redirect` structure if an `entrypoint` is defined for the redirection (they will not be used in this case).
Care should be taken when defining replacement expand variables: `$1x` is equivalent to `${1x}`, not `${1}x` (see [Regexp.Expand](https://golang.org/pkg/regexp/#Regexp.Expand)), so use `${1}` syntax.
Regular expressions and replacements can be tested using online tools such as [Go Playground](https://play.golang.org/p/mWU9p-wk2ru) or the [Regex101](https://regex101.com/r/58sIgx/2).
## TLS ## TLS
@@ -175,17 +246,16 @@ In the example below both `snitest.com` and `snitest.org` will require client ce
``` ```
!!! note !!! note
The deprecated argument `ClientCAFiles` allows adding Client CA files which are mandatory.
The deprecated argument `ClientCAFiles` allows adding Client CA files which are mandatory. If this parameter exists, the new ones are not checked.
If this parameter exists, the new ones are not checked.
## Authentication ## Authentication
### Basic Authentication ### Basic Authentication
Passwords can be encoded in MD5, SHA1 and BCrypt: you can use `htpasswd` to generate those ones. Passwords can be encoded in MD5, SHA1 and BCrypt: you can use `htpasswd` to generate them.
Users can be specified directly in the toml file, or indirectly by referencing an external file; Users can be specified directly in the TOML file, or indirectly by referencing an external file;
if both are provided, the two are merged, with external file contents having precedence. if both are provided, the two are merged, with external file contents having precedence.
```toml ```toml
@@ -200,9 +270,9 @@ Users can be specified directly in the toml file, or indirectly by referencing a
### Digest Authentication ### Digest Authentication
You can use `htdigest` to generate those ones. You can use `htdigest` to generate them.
Users can be specified directly in the toml file, or indirectly by referencing an external file; Users can be specified directly in the TOML file, or indirectly by referencing an external file;
if both are provided, the two are merged, with external file contents having precedence if both are provided, the two are merged, with external file contents having precedence
```toml ```toml
@@ -220,7 +290,7 @@ Users can be specified directly in the toml file, or indirectly by referencing a
This configuration will first forward the request to `http://authserver.com/auth`. This configuration will first forward the request to `http://authserver.com/auth`.
If the response code is 2XX, access is granted and the original request is performed. If the response code is 2XX, access is granted and the original request is performed.
Otherwise, the response from the auth server is returned. Otherwise, the response from the authentication server is returned.
```toml ```toml
[entryPoints] [entryPoints]
@@ -257,7 +327,10 @@ To specify an https entry point with a minimum TLS version, and specifying an ar
address = ":443" address = ":443"
[entryPoints.https.tls] [entryPoints.https.tls]
minVersion = "VersionTLS12" minVersion = "VersionTLS12"
cipherSuites = ["TLS_RSA_WITH_AES_256_GCM_SHA384"] cipherSuites = [
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_RSA_WITH_AES_256_GCM_SHA384"
]
[[entryPoints.https.tls.certificates]] [[entryPoints.https.tls.certificates]]
certFile = "integration/fixtures/https/snitest.com.cert" certFile = "integration/fixtures/https/snitest.com.cert"
keyFile = "integration/fixtures/https/snitest.com.key" keyFile = "integration/fixtures/https/snitest.com.key"
@@ -283,15 +356,18 @@ Responses are compressed when:
* And the `Accept-Encoding` request header contains `gzip` * And the `Accept-Encoding` request header contains `gzip`
* And the response is not already compressed, i.e. the `Content-Encoding` response header is not already set. * And the response is not already compressed, i.e. the `Content-Encoding` response header is not already set.
## Whitelisting ## White Listing
To enable IP whitelisting at the entrypoint level. To enable IP white listing at the entry point level.
```toml ```toml
[entryPoints] [entryPoints]
[entryPoints.http] [entryPoints.http]
address = ":80" address = ":80"
whiteListSourceRange = ["127.0.0.1/32", "192.168.1.7"]
[entryPoints.http.whiteList]
sourceRange = ["127.0.0.1/32", "192.168.1.7"]
# useXForwardedFor = true
``` ```
## ProxyProtocol ## ProxyProtocol

252
docs/configuration/logs.md Normal file
View File

@@ -0,0 +1,252 @@
# Logs Definition
## Reference
### TOML
```toml
logLevel = "INFO"
[traefikLog]
filePath = "/path/to/traefik.log"
format = "json"
[accessLog]
filePath = "/path/to/access.log"
format = "json"
[accessLog.filters]
statusCodes = ["200", "300-302"]
retryAttempts = true
[accessLog.fields]
defaultMode = "keep"
[accessLog.fields.names]
"ClientUsername" = "drop"
# ...
[accessLog.fields.headers]
defaultMode = "keep"
[accessLog.fields.headers.names]
"User-Agent" = "redact"
"Authorization" = "drop"
"Content-Type" = "keep"
# ...
```
### CLI
For more information about the CLI, see the documentation about [Traefik command](/basics/#traefik).
```shell
--logLevel="DEBUG"
--traefikLog.filePath="/path/to/traefik.log"
--traefikLog.format="json"
--accessLog.filePath="/path/to/access.log"
--accessLog.format="json"
--accessLog.filters.statusCodes="200,300-302"
--accessLog.filters.retryAttempts="true"
--accessLog.fields.defaultMode="keep"
--accessLog.fields.names="Username=drop Hostname=drop"
--accessLog.fields.headers.defaultMode="keep"
--accessLog.fields.headers.names="User-Agent=redact Authorization=drop Content-Type=keep"
```
## Traefik Logs
By default the Traefik log is written to stdout in text format.
To write the logs into a log file specify the `filePath`:
```toml
[traefikLog]
filePath = "/path/to/traefik.log"
```
To write JSON format logs, specify `json` as the format:
```toml
[traefikLog]
filePath = "/path/to/traefik.log"
format = "json"
```
Deprecated way (before 1.4):
!!! danger "DEPRECATED"
`traefikLogsFile` is deprecated, use [traefikLog](/configuration/logs/#traefik-logs) instead.
```toml
# Traefik logs file
# If not defined, logs to stdout
#
# DEPRECATED - see [traefikLog] lower down
# In case both traefikLogsFile and traefikLog.filePath are specified, the latter will take precedence.
# Optional
#
traefikLogsFile = "log/traefik.log"
```
To customize the log level:
```toml
# Log level
#
# Optional
# Default: "ERROR"
#
# Accepted values, in order of severity: "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "PANIC"
# Messages at and above the selected level will be logged.
#
logLevel = "ERROR"
```
## Access Logs
Access logs are written when `[accessLog]` is defined.
By default it will write to stdout and produce logs in the textual Common Log Format (CLF), extended with additional fields.
To enable access logs using the default settings just add the `[accessLog]` entry:
```toml
[accessLog]
```
To write the logs into a log file specify the `filePath`:
```toml
[accessLog]
filePath = "/path/to/access.log"
```
To write JSON format logs, specify `json` as the format:
```toml
[accessLog]
filePath = "/path/to/access.log"
format = "json"
```
To filter logs you can specify a set of filters which are logically "OR-connected". Thus, specifying multiple filters will keep more access logs than specifying only one:
```toml
[accessLog]
filePath = "/path/to/access.log"
format = "json"
[accessLog.filters]
# statusCodes keep access logs with status codes in the specified range
#
# Optional
# Default: []
#
statusCodes = ["200", "300-302"]
# retryAttempts keep access logs when at least one retry happened
#
# Optional
# Default: false
#
retryAttempts = true
```
To customize logs format:
```toml
[accessLog]
filePath = "/path/to/access.log"
format = "json"
[accessLog.filters]
# statusCodes keep only access logs with status codes in the specified range
#
# Optional
# Default: []
#
statusCodes = ["200", "300-302"]
[accessLog.fields]
# defaultMode
#
# Optional
# Default: "keep"
#
# Accepted values "keep", "drop"
#
defaultMode = "keep"
# Fields map which is used to override fields defaultMode
[accessLog.fields.names]
"ClientUsername" = "drop"
# ...
[accessLog.fields.headers]
# defaultMode
#
# Optional
# Default: "keep"
#
# Accepted values "keep", "drop", "redact"
#
defaultMode = "keep"
# Fields map which is used to override headers defaultMode
[accessLog.fields.headers.names]
"User-Agent" = "redact"
"Authorization" = "drop"
"Content-Type" = "keep"
# ...
```
#### List of all available fields
```ini
StartUTC
StartLocal
Duration
FrontendName
BackendName
BackendURL
BackendAddr
ClientAddr
ClientHost
ClientPort
ClientUsername
RequestAddr
RequestHost
RequestPort
RequestMethod
RequestPath
RequestProtocol
RequestLine
RequestContentSize
OriginDuration
OriginContentSize
OriginStatus
OriginStatusLine
DownstreamStatus
DownstreamStatusLine
DownstreamContentSize
RequestCount
GzipRatio
Overhead
RetryAttempts
```
Deprecated way (before 1.4):
!!! danger "DEPRECATED"
`accessLogsFile` is deprecated, use [accessLog](/configuration/logs/#access-logs) instead.
```toml
# Access logs file
#
# DEPRECATED - see [accessLog]
#
accessLogsFile = "log/access.log"
```
## Log Rotation
Traefik will close and reopen its log files, assuming they're configured, on receipt of a USR1 signal.
This allows the logs to be rotated and processed by an external program, such as `logrotate`.
!!! note
This does not work on Windows due to the lack of USR signals.

View File

@@ -1,5 +1,7 @@
# Ping Definition # Ping Definition
## Configuration
```toml ```toml
# Ping definition # Ping definition
[ping] [ping]
@@ -19,24 +21,71 @@
!!! warning !!! warning
Even if you have authentication configured on entry point, the `/ping` path of the api is excluded from authentication. Even if you have authentication configured on entry point, the `/ping` path of the api is excluded from authentication.
### Example ## Examples
The `/ping` health-check URL is enabled with the command-line `--ping` or config file option `[ping]`.
Thus, if you have a regular path for `/foo` and an entrypoint on `:80`, you would access them as follows:
* Regular path: `http://hostname:80/foo`
* Admin panel: `http://hostname:8080/`
* Ping URL: `http://hostname:8080/ping`
However, for security reasons, you may want to be able to expose the `/ping` health-check URL to outside health-checkers, e.g. an Internet service or cloud load-balancer, _without_ exposing your administration panel's port.
In many environments, the security staff may not _allow_ you to expose it.
You have two options:
* Enable `/ping` on a regular entry point
* Enable `/ping` on a dedicated port
### Ping health check on a regular entry point
To proxy `/ping` from a regular entry point to the administration one without exposing the panel, do the following:
```toml
defaultEntryPoints = ["http"]
[entryPoints]
[entryPoints.http]
address = ":80"
[ping]
entryPoint = "http"
```shell
curl -sv "http://localhost:8080/ping"
``` ```
```shell
* Trying ::1... The above link `ping` on the `http` entry point and then expose it on port `80`
* Connected to localhost (::1) port 8080 (#0)
> GET /ping HTTP/1.1 ### Enable ping health check on dedicated port
> Host: localhost:8080
> User-Agent: curl/7.43.0 If you do not want to or cannot expose the health-check on a regular entry point - e.g. your security rules do not allow it, or you have a conflicting path - then you can enable health-check on its own entry point.
> Accept: */* Use the following configuration:
>
< HTTP/1.1 200 OK ```toml
< Date: Thu, 25 Aug 2016 01:35:36 GMT defaultEntryPoints = ["http"]
< Content-Length: 2
< Content-Type: text/plain; charset=utf-8 [entryPoints]
< [entryPoints.http]
* Connection #0 to host localhost left intact address = ":80"
OK [entryPoints.ping]
``` address = ":8082"
[ping]
entryPoint = "ping"
```
The above is similar to the previous example, but instead of enabling `/ping` on the _default_ entry point, we enable it on a _dedicated_ entry point.
In the above example, you would access a regular path and health-check as follows:
* Regular path: `http://hostname:80/foo`
* Ping URL: `http://hostname:8082/ping`
Note the dedicated port `:8082` for `/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 this entry point.
### Using ping for external Load-balancer rotation health check
If you are running traefik behind a external Load-balancer, and want to configure rotation health check on the Load-balancer to take a traefik instance out of rotation gracefully, you can configure [lifecycle.requestAcceptGraceTimeout](/configuration/commons.md#life-cycle) and the ping endpoint will return `503` response on traefik server termination, so that the Load-balancer can take the terminating traefik instance out of rotation, before it stops responding.

View File

@@ -0,0 +1,97 @@
# Tracing
Tracing system allows developers to visualize call flows in there infrastructures.
We use [OpenTracing](http://opentracing.io). It is an open standard designed for distributed tracing.
Træfik supports two backends: Jaeger and Zipkin.
## Jaeger
```toml
# Tracing definition
[tracing]
# Backend name used to send tracing data
#
# Default: "jaeger"
#
Backend = "jaeger"
# Service name used in Jaeger backend
#
# Default: "traefik"
#
ServiceName = "traefik"
[tracing.jaeger]
# SamplingServerURL is the address of jaeger-agent's HTTP sampling server
#
# Default: "http://localhost:5778/sampling"
#
SamplingServerURL = "http://localhost:5778/sampling"
# Sampling Type specifies the type of the sampler: const, probabilistic, rateLimiting
#
# Default: "const"
#
SamplingType = "const"
# SamplingParam Param is a value passed to the sampler.
# Valid values for Param field are:
# - for "const" sampler, 0 or 1 for always false/true respectively
# - for "probabilistic" sampler, a probability between 0 and 1
# - for "rateLimiting" sampler, the number of spans per second
#
# Default: 1.0
#
SamplingParam = 1.0
# LocalAgentHostPort instructs reporter to send spans to jaeger-agent at this address
#
# Default: "127.0.0.1:6832"
#
LocalAgentHostPort = "127.0.0.1:6832"
```
## Zipkin
```toml
# Tracing definition
[tracing]
# Backend name used to send tracing data
#
# Default: "jaeger"
#
Backend = "zipkin"
# Service name used in Zipkin backend
#
# Default: "traefik"
#
ServiceName = "traefik"
[tracing.zipkin]
# Zipking HTTP endpoint used to send data
#
# Default: "http://localhost:9411/api/v1/spans"
#
HTTPEndpoint = "http://localhost:9411/api/v1/spans"
# Enable Zipkin debug
#
# Default: false
#
Debug = false
# Use ZipKin SameSpan RPC style traces
#
# Default: false
#
SameSpan = false
# Use ZipKin 128 bit root span IDs
#
# Default: true
#
ID128Bit = true
```

View File

@@ -10,65 +10,165 @@
[![Twitter](https://img.shields.io/twitter/follow/traefikproxy.svg?style=social)](https://twitter.com/intent/follow?screen_name=traefikproxy) [![Twitter](https://img.shields.io/twitter/follow/traefikproxy.svg?style=social)](https://twitter.com/intent/follow?screen_name=traefikproxy)
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. Træfik is a modern HTTP reverse proxy and load balancer that makes deploying microservices easy.
It supports several backends ([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 a lot more) to manage its configuration automatically and dynamically. Træfik 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.
Telling Træfik where your orchestrator is could be the _only_ configuration step you need to do.
## Overview ## 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. 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).
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: 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 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.
- 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
Microservices are often deployed in dynamic environments where services are added, removed, killed, upgraded or scaled many times a day. **This is when Træfik can help you!**
Traditional reverse-proxies are not natively dynamic. You can't change their configuration and hot-reload easily. Træfik 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 Træfik and let it do the work for you!**
_(But if you'd rather configure some of your routes manually, Træfik supports that too!)_
![Architecture](img/architecture.png) ![Architecture](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 ## Features
- [It's fast](/benchmarks) - Continuously updates its configuration (No restarts!)
- No dependency hell, single binary made with go - Supports multiple load balancing algorithms
- [Tiny](https://microbadger.com/images/traefik) [official](https://hub.docker.com/r/_/traefik/) docker image - Provides HTTPS to your microservices by leveraging [Let's Encrypt](https://letsencrypt.org) (wildcard certificates support)
- Rest API
- Hot-reloading of configuration. No need to restart the process
- Circuit breakers, retry - Circuit breakers, retry
- Round Robin, rebalancer load-balancers - High Availability with cluster mode (beta)
- Metrics (Rest, Prometheus, Datadog, Statsd, InfluxDB) - See the magic through its clean web UI
- Clean AngularJS Web UI
- Websocket, HTTP/2, GRPC ready - Websocket, HTTP/2, GRPC ready
- Access Logs (JSON, CLF) - Provides metrics (Rest, Prometheus, Datadog, Statsd, InfluxDB)
- [Let's Encrypt](https://letsencrypt.org) support (Automatic HTTPS with renewal) - Keeps access logs (JSON, CLF)
- High Availability with cluster mode - [Fast](/benchmarks) ... which is nice
- 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 ## Supported backends
- [Docker](https://www.docker.com/) / [Swarm mode](https://docs.docker.com/engine/swarm/) - [Docker](/configuration/backends/docker/) / [Swarm mode](/configuration/backends/docker/#docker-swarm-mode)
- [Kubernetes](https://kubernetes.io) - [Kubernetes](/configuration/backends/kubernetes/)
- [Mesos](https://github.com/apache/mesos) / [Marathon](https://mesosphere.github.io/marathon/) - [Mesos](/configuration/backends/mesos/) / [Marathon](/configuration/backends/marathon/)
- [Rancher](https://rancher.com) (API, Metadata) - [Rancher](/configuration/backends/rancher/) (API, Metadata)
- [Consul](https://www.consul.io/) / [Etcd](https://coreos.com/etcd/) / [Zookeeper](https://zookeeper.apache.org) / [BoltDB](https://github.com/boltdb/bolt) - [Service Fabric](/configuration/backends/servicefabric/)
- [Eureka](https://github.com/Netflix/eureka) - [Consul Catalog](/configuration/backends/consulcatalog/)
- [Amazon ECS](https://aws.amazon.com/ecs) - [Consul](/configuration/backends/consul/) / [Etcd](/configuration/backends/etcd/) / [Zookeeper](/configuration/backends/zookeeper/) / [BoltDB](/configuration/backends/boltdb/)
- [Amazon DynamoDB](https://aws.amazon.com/dynamodb) - [Eureka](/configuration/backends/eureka/)
- File - [Amazon ECS](/configuration/backends/ecs/)
- Rest API - [Amazon DynamoDB](/configuration/backends/dynamodb/)
- [File](/configuration/backends/file/)
- [Rest](/configuration/backends/rest/)
## The Træfik Quickstart (Using Docker)
## Quickstart In this quickstart, we'll use [Docker compose](https://docs.docker.com/compose) to create our demo infrastructure.
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 save some time, you can clone [Træfik's repository](https://github.com/containous/traefik) and use the quickstart files located in the [examples/quickstart](https://github.com/containous/traefik/tree/master/examples/quickstart/) directory.
### 1 — Launch Træfik — Tell It to Listen to Docker
Create a `docker-compose.yml` file where you will define a `reverse-proxy` service that uses the official Træfik image:
```yaml
version: '3'
services:
reverse-proxy:
image: traefik #The official Traefik docker image
command: --api --docker #Enables the web UI and tells Træfik to listen to docker
ports:
- "80:80" #The HTTP port
- "8080:8080" #The Web UI (enabled by --api)
volumes:
- /var/run/docker.sock:/var/run/docker.sock #So that Traefik can listen to the Docker events
```
**That's it. Now you can launch Træfik!**
Start your `reverse-proxy` with the following command:
```shell
docker-compose up -d reverse-proxy
```
You can open a browser and go to [http://localhost:8080](http://localhost:8080) to see Træfik's dashboard (we'll go back there once we have launched a service in step 2).
### 2 — Launch a Service — Træfik Detects It and Creates a Route for You
Now that we have a Træfik instance up and running, we will deploy new services.
Edit your `docker-compose.yml` file and add the following at the end of your file.
```yaml
# ...
whoami:
image: emilevauge/whoami #A container that exposes an API to show it's IP address
labels:
- "traefik.frontend.rule=Host:whoami.docker.localhost"
```
The above defines `whoami`: a simple web service that outputs information about the machine it is deployed on (its IP address, host, and so on).
Start the `whoami` service with the following command:
```shell
docker-compose up -d whoami
```
Go back to your browser ([http://localhost:8080](http://localhost:8080)) and see that Træfik has automatically detected the new container and updated its own configuration.
When Traefik detects new services, it creates the corresponding routes so you can call them ... _let's see!_ (Here, we're using curl)
```shell
curl -H Host:whoami.docker.localhost http://127.0.0.1
```
_Shows the following output:_
```yaml
Hostname: 8656c8ddca6c
IP: 172.27.0.3
#...
```
### 3 — Launch More Instances — Traefik Load Balances Them
Run more instances of your `whoami` service with the following command:
```shell
docker-compose up -d --scale whoami=2
```
Go back to your browser ([http://localhost:8080](http://localhost:8080)) and see that Træfik has automatically detected the new instance of the container.
Finally, see that Træfik load-balances between the two instances of your services by running twice the following command:
```shell
curl -H Host:whoami.docker.localhost http://127.0.0.1
```
The output will show alternatively one of the followings:
```yaml
Hostname: 8656c8ddca6c
IP: 172.27.0.3
#...
```
```yaml
Hostname: 8458f154e1f1
IP: 172.27.0.4
# ...
```
### 4 — Enjoy Træfik's Magic
Now that you have a basic understanding of how Træfik can automatically create the routes to your services and load balance them, it might be time to dive into [the documentation](https://docs.traefik.io/) and let Træfik work for you! Whatever your infrastructure is, there is probably [an available Træfik backend](https://docs.traefik.io/configuration/backends/available) that will do the job.
Our recommendation would be to see for yourself how simple it is to enable HTTPS with [Træfik's let's encrypt integration](https://docs.traefik.io/user-guide/examples/#lets-encrypt-support) using the dedicated [user guide](https://docs.traefik.io/user-guide/docker-and-lets-encrypt/).
## Resources
Here is a talk given by [Emile Vauge](https://github.com/emilevauge) at [GopherCon 2017](https://gophercon.com). Here is a talk given by [Emile Vauge](https://github.com/emilevauge) at [GopherCon 2017](https://gophercon.com).
You will learn Træfik basics in less than 10 minutes. You will learn Træfik basics in less than 10 minutes.
@@ -80,9 +180,9 @@ You will learn fundamental Træfik features and see some demos with Kubernetes.
[![Traefik ContainerCamp UK](https://img.youtube.com/vi/aFtpIShV60I/0.jpg)](https://www.youtube.com/watch?v=aFtpIShV60I) [![Traefik ContainerCamp UK](https://img.youtube.com/vi/aFtpIShV60I/0.jpg)](https://www.youtube.com/watch?v=aFtpIShV60I)
## Get it ## Downloads
### Binary ### The Official Binary File
You can 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): You can 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):
@@ -90,114 +190,10 @@ You can grab the latest binary from the [releases](https://github.com/containous
./traefik -c traefik.toml ./traefik -c traefik.toml
``` ```
### Docker ### The Official Docker Image
Using the tiny Docker image: Using the tiny Docker image:
```shell ```shell
docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/etc/traefik/traefik.toml traefik docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/etc/traefik/traefik.toml traefik
``` ```
## Test it
You can test Træfik easily using [Docker compose](https://docs.docker.com/compose), with this `docker-compose.yml` file in a folder named `traefik`:
```yaml
version: '2'
services:
proxy:
image: traefik
command: --api --docker --docker.domain=docker.localhost --logLevel=DEBUG
networks:
- webgateway
ports:
- "80:80"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /dev/null:/traefik.toml
networks:
webgateway:
driver: bridge
```
Start it from within the `traefik` folder:
```shell
docker-compose up -d
```
In a browser, you may open [http://localhost:8080](http://localhost:8080) to access Træfik's dashboard and observe the following magic.
Now, create a folder named `test` and create a `docker-compose.yml` in it with this content:
```yaml
version: '2'
services:
whoami:
image: emilevauge/whoami
networks:
- web
labels:
- "traefik.backend=whoami"
- "traefik.frontend.rule=Host:whoami.docker.localhost"
networks:
web:
external:
name: traefik_webgateway
```
Then, start and scale it in the `test` folder:
```shell
docker-compose up -d
docker-compose scale whoami=2
```
Finally, test load-balancing between the two services `test_whoami_1` and `test_whoami_2`:
```shell
curl -H Host:whoami.docker.localhost http://127.0.0.1
```
```yaml
Hostname: ef194d07634a
IP: 127.0.0.1
IP: ::1
IP: 172.17.0.4
IP: fe80::42:acff:fe11:4
GET / HTTP/1.1
Host: 172.17.0.4:80
User-Agent: curl/7.35.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 172.17.0.1
X-Forwarded-Host: 172.17.0.4:80
X-Forwarded-Proto: http
X-Forwarded-Server: dbb60406010d
```
```shell
curl -H Host:whoami.docker.localhost http://127.0.0.1
```
```yaml
Hostname: 6c3c5df0c79a
IP: 127.0.0.1
IP: ::1
IP: 172.17.0.3
IP: fe80::42:acff:fe11:3
GET / HTTP/1.1
Host: 172.17.0.3:80
User-Agent: curl/7.35.0
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 172.17.0.1
X-Forwarded-Host: 172.17.0.3:80
X-Forwarded-Proto: http
X-Forwarded-Server: dbb60406010d
```

View File

@@ -11,7 +11,7 @@ When you use Let's Encrypt, you need to store certificates, but not only.
When Træfik generates a new certificate, it configures a challenge and once Let's Encrypt will verify the ownership of the domain, it will ping back the challenge. When Træfik generates a new certificate, it configures a challenge and once Let's Encrypt will verify the ownership of the domain, it will ping back the challenge.
If the challenge is not knowing by other Træfik instances, the validation will fail. If the challenge is not knowing by other Træfik instances, the validation will fail.
For more information about challenge: [Automatic Certificate Management Environment (ACME)](https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#tls-with-server-name-indication-tls-sni) For more information about challenge: [Automatic Certificate Management Environment (ACME)](https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md#http-challenge)
## Prerequisites ## Prerequisites
@@ -35,14 +35,14 @@ TL;DR:
```shell ```shell
$ traefik \ $ traefik \
--entrypoints=Name:http Address::80 Redirect.EntryPoint:https \ --entrypoints='Name:http Address::80 Redirect.EntryPoint:https' \
--entrypoints=Name:https Address::443 TLS \ --entrypoints='Name:https Address::443 TLS' \
--defaultentrypoints=http,https --defaultentrypoints=http,https
``` ```
To listen to different ports, we need to create an entry point for each. To listen to different ports, we need to create an entry point for each.
The CLI syntax is `--entrypoints=Name:a_name Address:an_ip_or_empty:a_port options`. The CLI syntax is `--entrypoints='Name:a_name Address:an_ip_or_empty:a_port options'`.
If you want to redirect traffic from one entry point to another, it's the option `Redirect.EntryPoint:entrypoint_name`. If you want to redirect traffic from one entry point to another, it's the option `Redirect.EntryPoint:entrypoint_name`.
By default, we don't want to configure all our services to listen on http and https, we add a default entry point configuration: `--defaultentrypoints=http,https`. By default, we don't want to configure all our services to listen on http and https, we add a default entry point configuration: `--defaultentrypoints=http,https`.

View File

@@ -23,3 +23,11 @@ A Træfik cluster is based on a manager/worker model.
When starting, Træfik will elect a manager. When starting, Træfik will elect a manager.
If this instance fails, another manager will be automatically elected. If this instance fails, another manager will be automatically elected.
## Træfik cluster and Let's Encrypt
**In cluster mode, ACME certificates have to be stored in [a KV Store entry](/configuration/acme/#storage-kv-entry).**
Thanks to the Træfik cluster mode algorithm (based on [the Raft Consensus Algorithm](https://raft.github.io/)), only one instance will contact Let's encrypt to solve the challenges.
The others instances will get ACME certificate from the KV Store entry.

View File

@@ -69,7 +69,7 @@ networks:
``` ```
As you can see, we're mounting the `traefik.toml` file as well as the (empty) `acme.json` file in the container. As you can see, we're mounting the `traefik.toml` file as well as the (empty) `acme.json` file in the container.
Also, we're mounting the `/var/run/docker.sock` Docker socket in the container as well, so Træfik can listen to Docker events and reconfigure it's own internal configuration when containers are created (or shut down). Also, we're mounting the `/var/run/docker.sock` Docker socket in the container as well, so Træfik can listen to Docker events and reconfigure its own internal configuration when containers are created (or shut down).
Also, we're making sure the container is automatically restarted by the Docker engine in case of problems (or: if the server is rebooted). Also, we're making sure the container is automatically restarted by the Docker engine in case of problems (or: if the server is rebooted).
We're publishing the default HTTP ports `80` and `443` on the host, and making sure the container is placed within the `web` network we've created earlier on. We're publishing the default HTTP ports `80` and `443` on the host, and making sure the container is placed within the `web` network we've created earlier on.
Finally, we're giving this container a static name called `traefik`. Finally, we're giving this container a static name called `traefik`.
@@ -110,7 +110,7 @@ entryPoint = "http"
This is the minimum configuration required to do the following: This is the minimum configuration required to do the following:
- Log `ERROR`-level messages (or more severe) to the console, but silence `DEBUG`-level messagse - Log `ERROR`-level messages (or more severe) to the console, but silence `DEBUG`-level messages
- Check for new versions of Træfik periodically - Check for new versions of Træfik periodically
- Create two entry points, namely an `HTTP` endpoint on port `80`, and an `HTTPS` endpoint on port `443` where all incoming traffic on port `80` will immediately get redirected to `HTTPS`. - Create two entry points, namely an `HTTP` endpoint on port `80`, and an `HTTPS` endpoint on port `443` where all incoming traffic on port `80` will immediately get redirected to `HTTPS`.
- Enable the Docker configuration backend and listen for container events on the Docker unix socket we've mounted earlier. However, **new containers will not be exposed by Træfik by default, we'll get into this in a bit!** - Enable the Docker configuration backend and listen for container events on the Docker unix socket we've mounted earlier. However, **new containers will not be exposed by Træfik by default, we'll get into this in a bit!**
@@ -199,7 +199,7 @@ Since the `traefik` container we've created and started earlier is also attached
As mentioned earlier, we don't want containers exposed automatically by Træfik. As mentioned earlier, we don't want containers exposed automatically by Træfik.
The reason behind this is simple: we want to have control over this process ourselves. The reason behind this is simple: we want to have control over this process ourselves.
Thanks to Docker labels, we can tell Træfik how to create it's internal routing configuration. Thanks to Docker labels, we can tell Træfik how to create its internal routing configuration.
Let's take a look at the labels themselves for the `app` service, which is a HTTP webservice listing on port 9000: Let's take a look at the labels themselves for the `app` service, which is a HTTP webservice listing on port 9000:
@@ -222,7 +222,7 @@ We use both `container labels` and `service labels`.
First, we specify the `backend` name which corresponds to the actual service we're routing **to**. First, we specify the `backend` name which corresponds to the actual service we're routing **to**.
We also tell Træfik to use the `web` network to route HTTP traffic to this container. We also tell Træfik to use the `web` network to route HTTP traffic to this container.
With the `traefik.enable` label, we tell Træfik to include this container in it's internal configuration. With the `traefik.enable` label, we tell Træfik to include this container in its internal configuration.
With the `frontend.rule` label, we tell Træfik that we want to route to this container if the incoming HTTP request contains the `Host` `app.my-awesome-app.org`. With the `frontend.rule` label, we tell Træfik that we want to route to this container if the incoming HTTP request contains the `Host` `app.my-awesome-app.org`.
Essentially, this is the actual rule used for Layer-7 load balancing. Essentially, this is the actual rule used for Layer-7 load balancing.

View File

@@ -55,10 +55,6 @@ defaultEntryPoints = ["http", "https"]
## Let's Encrypt support ## Let's Encrypt support
!!! note
Even if `TLS-SNI-01` challenge is [disabled](https://community.letsencrypt.org/t/2018-01-11-update-regarding-acme-tls-sni-and-shared-hosting-infrastructure/50188), for the moment, it stays the _by default_ ACME Challenge in Træfik but all the examples use the `HTTP-01` challenge (except DNS challenge examples).
If `TLS-SNI-01` challenge is not re-enabled in the future, it we will be removed from Træfik.
### Basic example with HTTP challenge ### Basic example with HTTP challenge
```toml ```toml
@@ -91,7 +87,7 @@ entryPoint = "https"
This configuration allows generating Let's Encrypt certificates (thanks to `HTTP-01` challenge) for the four domains `local[1-4].com` with described SANs. This configuration allows generating Let's Encrypt certificates (thanks to `HTTP-01` challenge) for the four domains `local[1-4].com` with described SANs.
Traefik generates these certificates when it starts and it needs to be restart if new domains are added. Træfik generates these certificates when it starts and it needs to be restart if new domains are added.
### OnHostRule option (with HTTP challenge) ### OnHostRule option (with HTTP challenge)
@@ -126,9 +122,9 @@ entryPoint = "https"
This configuration allows generating Let's Encrypt certificates (thanks to `HTTP-01` challenge) for the four domains `local[1-4].com`. This configuration allows generating Let's Encrypt certificates (thanks to `HTTP-01` challenge) for the four domains `local[1-4].com`.
Traefik generates these certificates when it starts. Træfik generates these certificates when it starts.
If a backend is added with a `onHost` rule, Traefik will automatically generate the Let's Encrypt certificate for the new domain. If a backend is added with a `onHost` rule, Træfik will automatically generate the Let's Encrypt certificate for the new domain (for frontends wired on the `acme.entryPoint`).
### OnDemand option (with HTTP challenge) ### OnDemand option (with HTTP challenge)
@@ -152,11 +148,10 @@ entryPoint = "https"
This configuration allows generating a Let's Encrypt certificate (thanks to `HTTP-01` challenge) during the first HTTPS request on a new domain. This configuration allows generating a Let's Encrypt certificate (thanks to `HTTP-01` challenge) during the first HTTPS request on a new domain.
!!! note !!! note
This option simplifies the configuration but : This option simplifies the configuration but :
* TLS handshakes will be slow when requesting a hostname certificate for the first time, this can leads to DDoS attacks. * TLS handshakes will be slow when requesting a hostname certificate for the first time, which can lead to DDoS attacks.
* Let's Encrypt have rate limiting: https://letsencrypt.org/docs/rate-limits * Let's Encrypt have rate limiting: https://letsencrypt.org/docs/rate-limits
That's why, it's better to use the `onHostRule` option if possible. That's why, it's better to use the `onHostRule` option if possible.
@@ -191,10 +186,45 @@ entryPoint = "https"
``` ```
DNS challenge needs environment variables to be executed. DNS challenge needs environment variables to be executed.
This variables have to be set on the machine/container which host Traefik. These variables have to be set on the machine/container that host Træfik.
These variables are described [in this section](/configuration/acme/#provider). These variables are described [in this section](/configuration/acme/#provider).
### DNS challenge with wildcard domains
```toml
[entryPoints]
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]
[acme]
email = "test@traefik.io"
storage = "acme.json"
caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
entryPoint = "https"
[acme.dnsChallenge]
provider = "digitalocean" # DNS Provider name (cloudflare, OVH, gandi...)
delayBeforeCheck = 0
[[acme.domains]]
main = "*.local1.com"
[[acme.domains]]
main = "local2.com"
sans = ["test1.local2.com", "test2x.local2.com"]
[[acme.domains]]
main = "*.local3.com"
[[acme.domains]]
main = "*.local4.com"
```
DNS challenge needs environment variables to be executed.
These variables have to be set on the machine/container that host Træfik.
These variables are described [in this section](/configuration/acme/#provider).
More information about wildcard certificates are available [in this section](/configuration/acme/#wildcard-domain).
### OnHostRule option and provided certificates (with HTTP challenge) ### OnHostRule option and provided certificates (with HTTP challenge)
```toml ```toml
@@ -218,7 +248,7 @@ entryPoint = "https"
entryPoint = "http" entryPoint = "http"
``` ```
Traefik will only try to generate a Let's encrypt certificate (thanks to `HTTP-01` challenge) if the domain cannot be checked by the provided certificates. Træfik will only try to generate a Let's encrypt certificate (thanks to `HTTP-01` challenge) if the domain cannot be checked by the provided certificates.
### Cluster mode ### Cluster mode
@@ -292,14 +322,14 @@ The `consul` provider contains the configuration.
rule = "Path:/test" rule = "Path:/test"
``` ```
## Enable Basic authentication in an entrypoint ## Enable Basic authentication in an entry point
With two user/pass: With two user/pass:
- `test`:`test` - `test`:`test`
- `test2`:`test2` - `test2`:`test2`
Passwords are encoded in MD5: you can use htpasswd to generate those ones. Passwords are encoded in MD5: you can use `htpasswd` to generate them.
```toml ```toml
defaultEntryPoints = ["http"] defaultEntryPoints = ["http"]
@@ -336,86 +366,3 @@ providersThrottleDuration = "5s"
[respondingTimeouts] [respondingTimeouts]
idleTimeout = "360s" idleTimeout = "360s"
``` ```
## Securing Ping Health Check
The `/ping` health-check URL is enabled with the command-line `--ping` or config file option `[ping]`.
Thus, if you have a regular path for `/foo` and an entrypoint on `:80`, you would access them as follows:
* Regular path: `http://hostname:80/foo`
* Admin panel: `http://hostname:8080/`
* Ping URL: `http://hostname:8080/ping`
However, for security reasons, you may want to be able to expose the `/ping` health-check URL to outside health-checkers, e.g. an Internet service or cloud load-balancer, _without_ exposing your admin panel's port.
In many environments, the security staff may not _allow_ you to expose it.
You have two options:
* Enable `/ping` on a regular entrypoint
* Enable `/ping` on a dedicated port
### Enable ping health check on a regular entrypoint
To proxy `/ping` from a regular entrypoint to the admin one without exposing the panel, do the following:
```toml
[backends]
[backends.traefik]
[backends.traefik.servers.server1]
url = "http://localhost:8080"
weight = 10
[frontends]
[frontends.traefikadmin]
backend = "traefik"
[frontends.traefikadmin.routes.ping]
rule = "Path:/ping"
```
The above creates a new backend called `traefik`, listening on `http://localhost:8080`, i.e. the local admin port.
We only expose the admin panel via the `frontend` named `traefikadmin`, and only expose the `/ping` Path.
Be careful with the `traefikadmin` frontend. If you do _not_ specify a `Path:` rule, you would expose the entire dashboard.
### Enable ping health check on dedicated port
If you do not want to or cannot expose the health-check on a regular entrypoint - e.g. your security rules do not allow it, or you have a conflicting path - then you can enable health-check on its own entrypoint.
Use the following config:
```toml
defaultEntryPoints = ["http"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.ping]
address = ":8082"
[backends]
[backends.traefik]
[backends.traefik.servers.server1]
url = "http://localhost:8080"
weight = 10
[frontends]
[frontends.traefikadmin]
backend = "traefik"
entrypoints = ["ping"]
[frontends.traefikadmin.routes.ping]
rule = "Path:/ping"
```
The above is similar to the previous example, but instead of enabling `/ping` on the _default_ entrypoint, we enable it on a _dedicated_ entrypoint.
In the above example, you would access a regular path, admin panel and health-check as follows:
* Regular path: `http://hostname:80/foo`
* Admin panel: `http://hostname:8080/`
* Ping URL: `http://hostname:8082/ping`
Note the dedicated port `:8082` for `/ping`.
In the above example, it is _very_ important to create a named dedicated entrypoint, and do **not** include it in `defaultEntryPoints`.
Otherwise, you are likely to expose _all_ services via that entrypoint.
In the above example, we have two entrypoints, `http` and `ping`, but we only included `http` in `defaultEntryPoints`, while explicitly tying `frontend.traefikadmin` to the `ping` entrypoint.
This ensures that all the "normal" frontends will be exposed via entrypoint `http` and _not_ via entrypoint `ping`.

View File

@@ -81,7 +81,7 @@ For namespaced restrictions, one RoleBinding is required per watched namespace a
It is possible to use Træfik with a [Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) or a [DaemonSet](https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/) object, It is possible to use Træfik with a [Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) or a [DaemonSet](https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/) object,
whereas both options have their own pros and cons: whereas both options have their own pros and cons:
- The scalability is much better when using a Deployment, because you will have a Single-Pod-per-Node model when using the DeaemonSet. - The scalability is much better when using a Deployment, because you will have a Single-Pod-per-Node model when using the DaemonSet.
- It is possible to exclusively run a Service on a dedicated set of machines using taints and tolerations with a DaemonSet. - It is possible to exclusively run a Service on a dedicated set of machines using taints and tolerations with a DaemonSet.
- On the other hand the DaemonSet allows you to access any Node directly on Port 80 and 443, where you have to setup a [Service](https://kubernetes.io/docs/concepts/services-networking/service/) object with a Deployment. - On the other hand the DaemonSet allows you to access any Node directly on Port 80 and 443, where you have to setup a [Service](https://kubernetes.io/docs/concepts/services-networking/service/) object with a Deployment.
@@ -121,6 +121,7 @@ spec:
args: args:
- --api - --api
- --kubernetes - --kubernetes
- --logLevel=INFO
--- ---
kind: Service kind: Service
apiVersion: v1 apiVersion: v1
@@ -182,11 +183,15 @@ spec:
- name: admin - name: admin
containerPort: 8080 containerPort: 8080
securityContext: securityContext:
privileged: true capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
args: args:
- -d
- --api - --api
- --kubernetes - --kubernetes
- --logLevel=INFO
--- ---
kind: Service kind: Service
apiVersion: v1 apiVersion: v1
@@ -244,7 +249,7 @@ traefik-ingress-controller-678226159-eqseo 1/1 Running 0 7m
``` ```
You should see that after submitting the Deployment or DaemonSet to Kubernetes it has launched a Pod, and it is now running. You should see that after submitting the Deployment or DaemonSet to Kubernetes it has launched a Pod, and it is now running.
_It might take a few moments for kubernetes to pull the Træfik image and start the container._ _It might take a few moments for Kubernetes to pull the Træfik image and start the container._
!!! note !!! note
You could also check the deployment with the Kubernetes dashboard, run You could also check the deployment with the Kubernetes dashboard, run
@@ -279,7 +284,7 @@ All further examples below assume a DaemonSet installation. Deployment users wil
## Deploy Træfik using Helm Chart ## Deploy Træfik using Helm Chart
!!! note !!! note
The Helm Chart is maintained by the community, not the Traefik project maintainers. The Helm Chart is maintained by the community, not the Træfik project maintainers.
Instead of installing Træfik via Kubernetes object directly, you can also use the Træfik Helm chart. Instead of installing Træfik via Kubernetes object directly, you can also use the Træfik Helm chart.
@@ -342,9 +347,54 @@ echo "$(minikube ip) traefik-ui.minikube" | sudo tee -a /etc/hosts
We should now be able to visit [traefik-ui.minikube](http://traefik-ui.minikube) in the browser and view the Træfik web UI. We should now be able to visit [traefik-ui.minikube](http://traefik-ui.minikube) in the browser and view the Træfik web UI.
### Add a TLS Certificate to the Ingress
!!! note
For this example to work you need a TLS entrypoint. You don't have to provide a TLS certificate at this point. For more details see [here](/configuration/entrypoints/).
To setup an HTTPS-protected ingress, you can leverage the TLS feature of the ingress resource.
```yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: traefik-web-ui
namespace: kube-system
annotations:
kubernetes.io/ingress.class: traefik
spec:
rules:
- host: traefik-ui.minikube
http:
paths:
- backend:
serviceName: traefik-web-ui
servicePort: 80
tls:
secretName: traefik-ui-tls-cert
```
In addition to the modified ingress you need to provide the TLS certificate via a Kubernetes secret in the same namespace as the ingress. The following two commands will generate a new certificate and create a secret containing the key and cert files.
```shell
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=traefik-ui.minikube"
kubectl -n kube-system create secret tls traefik-ui-tls-cert --key=tls.key --cert=tls.crt
```
If there are any errors while loading the TLS section of an ingress, the whole ingress will be skipped.
!!! note
The secret must have two entries named `tls.key`and `tls.crt`. See the [Kubernetes documentation](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls) for more details.
!!! note
The TLS certificates will be added to all entrypoints defined by the ingress annotation `traefik.frontend.entryPoints`. If no such annotation is provided, the TLS certificates will be added to all TLS-enabled `defaultEntryPoints`.
!!! note
The field `hosts` in the TLS configuration is ignored. Instead, the domains provided by the certificate are used for this purpose. It is recommended to not use wildcard certificates as they will match globally.
## Basic Authentication ## Basic Authentication
It's possible to protect access to Traefik through basic authentication. (See the [Kubernetes Ingress](/configuration/backends/kubernetes) configuration page for syntactical details and restrictions.) It's possible to protect access to Træfik through basic authentication. (See the [Kubernetes Ingress](/configuration/backends/kubernetes) configuration page for syntactical details and restrictions.)
### Creating the Secret ### Creating the Secret
@@ -797,7 +847,7 @@ The examples shown deliberately do not specify any [resource limitations](https:
In a production environment, however, it is important to set proper bounds, especially with regards to CPU: In a production environment, however, it is important to set proper bounds, especially with regards to CPU:
- too strict and Traefik will be throttled while serving requests (as Kubernetes imposes hard quotas) - too strict and Træfik will be throttled while serving requests (as Kubernetes imposes hard quotas)
- too loose and Traefik may waste resources not available for other containers - too loose and Træfik may waste resources not available for other containers
When in doubt, you should measure your resource needs, and adjust requests and limits accordingly. When in doubt, you should measure your resource needs, and adjust requests and limits accordingly.

View File

@@ -1,6 +1,6 @@
# Key-value store configuration # Key-value store configuration
Both [static global configuration](/user-guide/kv-config/#static-configuration-in-key-value-store) and [dynamic](/user-guide/kv-config/#dynamic-configuration-in-key-value-store) configuration can be sorted in a Key-value store. Both [static global configuration](/user-guide/kv-config/#static-configuration-in-key-value-store) and [dynamic](/user-guide/kv-config/#dynamic-configuration-in-key-value-store) configuration can be stored in a Key-value store.
This section explains how to launch Træfik using a configuration loaded from a Key-value store. This section explains how to launch Træfik using a configuration loaded from a Key-value store.
@@ -275,12 +275,12 @@ Here is the toml configuration we would like to store in the store :
rule = "Path:/test" rule = "Path:/test"
[[tls]] [[tls]]
entryPoints = ["https"]
[tls.certificate] [tls.certificate]
certFile = "path/to/your.cert" certFile = "path/to/your.cert"
keyFile = "path/to/your.key" keyFile = "path/to/your.key"
[[tls]] [[tls]]
entryPoints = ["https","other-https"] entryPoints = ["https","other-https"]
[tls.certificate] [tls.certificate]
certFile = """-----BEGIN CERTIFICATE----- certFile = """-----BEGIN CERTIFICATE-----
<cert file content> <cert file content>
@@ -328,7 +328,7 @@ And there, the same dynamic configuration in a KV Store (using `prefix = "traefi
| Key | Value | | Key | Value |
|----------------------------------------------------|--------------------| |----------------------------------------------------|--------------------|
| `/traefik/frontends/frontend2/backend` | `backend1` | | `/traefik/frontends/frontend2/backend` | `backend1` |
| `/traefik/frontends/frontend2/passHostHeader` | `true` | | `/traefik/frontends/frontend2/passhostheader` | `true` |
| `/traefik/frontends/frontend2/priority` | `10` | | `/traefik/frontends/frontend2/priority` | `10` |
| `/traefik/frontends/frontend2/entrypoints` | `http,https` | | `/traefik/frontends/frontend2/entrypoints` | `http,https` |
| `/traefik/frontends/frontend2/routes/test_2/rule` | `PathPrefix:/test` | | `/traefik/frontends/frontend2/routes/test_2/rule` | `PathPrefix:/test` |
@@ -337,10 +337,12 @@ And there, the same dynamic configuration in a KV Store (using `prefix = "traefi
| Key | Value | | Key | Value |
|---------------------------------------|--------------------| |---------------------------------------|--------------------|
| `/traefik/tls/1/entrypoints` | `https` |
| `/traefik/tls/1/certificate/certfile` | `path/to/your.cert`| | `/traefik/tls/1/certificate/certfile` | `path/to/your.cert`|
| `/traefik/tls/1/certificate/keyfile` | `path/to/your.key` | | `/traefik/tls/1/certificate/keyfile` | `path/to/your.key` |
!!! note
As `/traefik/tls/1/entrypoints` is not defined, the certificate will be attached to all `defaulEntryPoints` with a TLS configuration (in the example, the entryPoint `https`)
- certificate 2 - certificate 2
| Key | Value | | Key | Value |

View File

@@ -130,7 +130,6 @@ As such, there is no way to handle this situation deterministically.
Finally, Marathon health checks are not mandatory (the default is to use the task state as reported by Mesos), so requiring them for Traefik would raise the entry barrier for Marathon users. Finally, Marathon health checks are not mandatory (the default is to use the task state as reported by Mesos), so requiring them for Traefik would raise the entry barrier for Marathon users.
Traefik used to use the health check results as a strict requirement but moved away from it as [users reported the dramatic consequences](https://github.com/containous/traefik/issues/653). Traefik used to use the health check results as a strict requirement but moved away from it as [users reported the dramatic consequences](https://github.com/containous/traefik/issues/653).
If health check results are known to exist, however, they will be used to signal task availability.
#### Draining #### Draining

View File

@@ -5,11 +5,15 @@ traefikLogsFile = "log/traefik.log"
accessLogsFile = "log/access.log" accessLogsFile = "log/access.log"
logLevel = "DEBUG" logLevel = "DEBUG"
[entryPoints]
[entryPoints.api]
address = ":7888"
################################################################ ################################################################
# Web configuration backend # API configuration
################################################################ ################################################################
[web] [api]
address = ":7888" entryPoint = "api"
################################################################ ################################################################
# File configuration backend # File configuration backend

View File

@@ -5,11 +5,15 @@ traefikLogsFile = "log/traefik.log"
accessLogsFile = "log/access.log" accessLogsFile = "log/access.log"
logLevel = "DEBUG" logLevel = "DEBUG"
[entryPoints]
[entryPoints.api]
address = ":7888"
################################################################ ################################################################
# Web configuration backend # API configuration
################################################################ ################################################################
[web] [api]
address = ":7888" entryPoint = "api"
################################################################ ################################################################
# File configuration backend # File configuration backend

View File

@@ -11,7 +11,7 @@ The provided Boulder stack is based on the environment used during integration t
## Directory content ## Directory content
* **compose-acme.yml** : Docker-Compose file which contains the description of Traefik and all the boulder stack containers to get, * **docker-compose.yml** : Docker-Compose file which contains the description of Traefik and all the boulder stack containers to get,
* **acme.toml** : Traefik configuration file used by the Traefik container described above, * **acme.toml** : Traefik configuration file used by the Traefik container described above,
* **manage_acme_docker_environment.sh** Shell script which does all needed checks and manages the docker-compose environment. * **manage_acme_docker_environment.sh** Shell script which does all needed checks and manages the docker-compose environment.
@@ -25,6 +25,7 @@ To work fine, boulder needs a domain name, with a related IP and storage file. T
The script **manage_acme_docker_environment.sh** requires one argument. This argument can have 3 values : The script **manage_acme_docker_environment.sh** requires one argument. This argument can have 3 values :
* **--start** : Check environment and launch a new Docker environment. * **--start** : Launch a new Docker environment Boulder + Traefik.
* **--stop** : Stop and delete the current Docker environment. * **--stop** : Stop and delete the current Docker environment.
* **--restart--** : Concatenate **--stop** and **--start** actions. * **--restart--** : Concatenate **--stop** and **--start** actions.
* **--dev** : Launch a new Boulder Docker environment.

View File

@@ -11,20 +11,17 @@ defaultEntryPoints = ["http", "https"]
address = ":443" address = ":443"
[entryPoints.https.tls] [entryPoints.https.tls]
[acme] [acme]
email = "test@traefik.io" email = "test@traefik.io"
storage = "/etc/traefik/conf/acme.json" storage = "/etc/traefik/conf/acme.json"
entryPoint = "https" entryPoint = "https"
onDemand = false onDemand = false
OnHostRule = true OnHostRule = true
caServer = "http://traefik.localhost.com:4000/directory" caServer = "http://traefik.boulder.com:4001/directory"
[acme.httpChallenge] [acme.httpChallenge]
entryPoint="http" entryPoint="http"
[api]
[web]
address = ":8080"
[docker] [docker]
endpoint = "unix:///var/run/docker.sock" endpoint = "unix:///var/run/docker.sock"

View File

@@ -1,90 +0,0 @@
version: "2"
# IP_HOST : Docker host IP (not 127.0.0.1)
services :
boulder:
image: containous/boulder:release
environment:
FAKE_DNS: $IP_HOST
PKCS11_PROXY_SOCKET: tcp://boulder-hsm:5657
extra_hosts:
- le.wtf:127.0.0.1
- boulder:127.0.0.1
ports:
- 4000:4000 # ACME
- 4002:4002 # OCSP
- 4003:4003 # OCSP
- 4500:4500 # ct-test-srv
- 8000:8000 # debug ports
- 8001:8001
- 8002:8002
- 8003:8003
- 8004:8004
- 8055:8055 # dns-test-srv updates
- 9380:9380 # mail-test-srv
- 9381:9381 # mail-test-srv
restart: unless-stopped
depends_on:
- bhsm
- bmysql
- brabbitmq
bhsm:
image: letsencrypt/boulder-tools:2016-11-02
hostname: boulder-hsm
networks:
default:
aliases:
- boulder-hsm
environment:
PKCS11_DAEMON_SOCKET: tcp://0.0.0.0:5657
command: /usr/local/bin/pkcs11-daemon /usr/lib/softhsm/libsofthsm.so
expose:
- 5657
bmysql:
image: mariadb:10.1
hostname: boulder-mysql
networks:
default:
aliases:
- boulder-mysql
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
brabbitmq:
image: rabbitmq:3-alpine
hostname: boulder-rabbitmq
networks:
default:
aliases:
- boulder-rabbitmq
environment:
RABBITMQ_NODE_IP_ADDRESS: "0.0.0.0"
traefik:
build:
context: ../..
image: containous/traefik:latest
command: --configFile=/etc/traefik/conf/acme.toml
restart: unless-stopped
extra_hosts:
- traefik.localhost.com:$IP_HOST
volumes:
- "./acme.toml:/etc/traefik/conf/acme.toml:ro"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./acme.json:/etc/traefik/conf/acme.json:rw"
ports:
- "80:80"
- "443:443"
- "5001:443" # Needed for SNI challenge
- "5002:80" # Needed for HTTP challenge
expose:
- "8080"
labels:
- "traefik.port=8080"
- "traefik.backend=traefikception"
- "traefik.frontend.rule=Host:traefik.localhost.com"
- "traefik.enable=true"
depends_on:
- boulder

View File

@@ -0,0 +1,97 @@
version: "2"
services :
boulder:
# To minimize fetching this should be the same version used below
image: containous/boulder:containous-acmev2
environment:
FAKE_DNS: 172.17.0.1
PKCS11_PROXY_SOCKET: tcp://boulder-hsm:5657
restart: unless-stopped
extra_hosts:
- le.wtf:127.0.0.1
- boulder:127.0.0.1
ports:
- 4000:4000 # ACME
- 4001:4001 # ACMEv2
- 4002:4002 # OCSP
- 4003:4003 # OCSP
- 4430:4430 # ACME via HTTPS
- 4431:4431 # ACMEv2 via HTTPS
- 4500:4500 # ct-test-srv
- 6000:6000 # gsb-test-srv
- 8000:8000 # debug ports
- 8001:8001
- 8002:8002
- 8003:8003
- 8004:8004
- 8005:8005
- 8006:8006
- 8008:8008
- 8009:8009
- 8010:8010
- 8055:8055 # dns-test-srv updates
- 9380:9380 # mail-test-srv
- 9381:9381 # mail-test-srv
depends_on:
- bhsm
- bmysql
networks:
- default
bhsm:
# To minimize fetching this should be the same version used above
image: letsencrypt/boulder-tools:2018-03-07
hostname: boulder-hsm
environment:
PKCS11_DAEMON_SOCKET: tcp://0.0.0.0:5657
command: /usr/local/bin/pkcs11-daemon /usr/lib/softhsm/libsofthsm2.so
expose:
- 5657
networks:
default:
aliases:
- boulder-hsm
bmysql:
image: mariadb:10.1
hostname: boulder-mysql
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
command: mysqld --bind-address=0.0.0.0
logging:
driver: none
networks:
default:
aliases:
- boulder-mysql
## TRAEFIK part ##
traefik:
build:
context: ../..
image: containous/traefik:latest
command: --configFile=/etc/traefik/conf/acme.toml
restart: unless-stopped
extra_hosts:
- traefik.boulder.com:172.17.0.1
volumes:
- "./acme.toml:/etc/traefik/conf/acme.toml:ro"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./acme.json:/etc/traefik/conf/acme.json:rw"
ports:
- "80:80"
- "443:443"
- "5001:443" # Needed for SNI challenge
- "5002:80" # Needed for HTTP challenge
expose:
- "8080"
labels:
- "traefik.port=8080"
- "traefik.backend=traefikception"
- "traefik.frontend.rule=Host:traefik.localhost.com"
- "traefik.enable=true"
depends_on:
- boulder

View File

@@ -3,7 +3,7 @@
# Initialize variables # Initialize variables
readonly traefik_url="traefik.localhost.com" readonly traefik_url="traefik.localhost.com"
readonly basedir=$(dirname $0) readonly basedir=$(dirname $0)
readonly doc_file=$basedir"/compose-acme.yml" readonly doc_file=$basedir"/docker-compose.yml"
# Stop and remove Docker environment # Stop and remove Docker environment
down_environment() { down_environment() {
@@ -22,21 +22,6 @@ up_environment() {
# Init the environment : get IP address and create needed files # Init the environment : get IP address and create needed files
init_environment() { init_environment() {
for netw in $(ip addr show | grep -v "LOOPBACK" | grep -v docker | grep -oE "^[0-9]{1}: .*:" | cut -d ':' -f2); do
ip_addr=$(ip addr show $netw | grep -E "inet " | grep -Eo "[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*" | head -n 1)
[[ ! -z $ip_addr ]] && break
done
[[ -z $ip_addr ]] && \
echo "[ERROR] Impossible to find an IP address for the Docker host" && exit 31
# The $traefik_url entry must exist into /etc/hosts file
# It has to refer to the $ip_addr IP address
[[ $(cat /etc/hosts | grep $traefik_url | grep -vE "^#" | grep -oE "([0-9]+(\.)?){4}") != $ip_addr ]] && \
echo "[ERROR] Domain ${traefik_url} has to refer to ${ip_addr} into /etc/hosts file." && exit 32
# Export IP_HOST to use it in the DOcker COmpose file
export IP_HOST=$ip_addr
echo "CREATE empty acme.json file" echo "CREATE empty acme.json file"
rm -f $basedir/acme.json && \ rm -f $basedir/acme.json && \
touch $basedir/acme.json && \ touch $basedir/acme.json && \
@@ -44,14 +29,14 @@ init_environment() {
} }
# Start all the environement # Start all the environement
start() { start_boulder() {
init_environment init_environment
echo "Start boulder environment" echo "Start boulder environment"
up_environment bmysql brabbitmq bhsm boulder up_environment bmysql bhsm boulder
waiting_counter=12 waiting_counter=12
# Not start Traefik if boulder is not started # Not start Traefik if boulder is not started
echo "WAIT for boulder..." echo "WAIT for boulder..."
while [[ -z $(curl -s http://$traefik_url:4000/directory) ]]; do while [[ -z $(curl -s http://127.0.0.1:4000/directory) ]]; do
sleep 5 sleep 5
let waiting_counter-=1 let waiting_counter-=1
if [[ $waiting_counter -eq 0 ]]; then if [[ $waiting_counter -eq 0 ]]; then
@@ -60,8 +45,6 @@ start() {
exit 41 exit 41
fi fi
done done
echo "START Traefik container"
up_environment traefik
} }
# Script usage # Script usage
@@ -78,9 +61,14 @@ main() {
[[ $# -ne 1 ]] && show_usage && exit 1 [[ $# -ne 1 ]] && show_usage && exit 1
case $1 in case $1 in
"--dev")
start_boulder
;;
"--start") "--start")
# Start boulder environment # Start boulder environment
start start_boulder
echo "START Traefik container"
up_environment traefik
echo "ENVIRONMENT SUCCESSFULLY STARTED" echo "ENVIRONMENT SUCCESSFULLY STARTED"
;; ;;
"--stop") "--stop")
@@ -89,8 +77,10 @@ main() {
;; ;;
"--restart") "--restart")
down_environment down_environment
start start_boulder
echo "ENVIRONMENT SUCCESSFULLY STARTED" echo "START Traefik container"
up_environment traefik
echo "ENVIRONMENT SUCCESSFULLY RESTARTED"
;; ;;
*) *)
show_usage && exit 2 show_usage && exit 2

View File

@@ -7,193 +7,195 @@ services:
# CONSUL # CONSUL
consul: consul:
image: progrium/consul image: progrium/consul
command: -server -bootstrap -log-level debug -ui-dir /ui command: -server -bootstrap -log-level debug -ui-dir /ui
ports: ports:
- "8400:8400" - "8400:8400"
- "8500:8500" - "8500:8500"
- "8600:53/udp" - "8600:53/udp"
expose: expose:
- "8300" - "8300"
- "8301" - "8301"
- "8301/udp" - "8301/udp"
- "8302" - "8302"
- "8302/udp" - "8302/udp"
networks: networks:
net: net:
ipv4_address: 10.0.1.2 ipv4_address: 10.0.1.2
# ETCD V3 # ETCD V3
etcd3: etcd3:
image: quay.io/coreos/etcd:v3.2.9 image: quay.io/coreos/etcd:v3.2.9
command: /usr/local/bin/etcd --data-dir=/etcd-data --name node1 --initial-advertise-peer-urls http://10.0.1.12:2380 --listen-peer-urls http://10.0.1.12:2380 --advertise-client-urls http://10.0.1.12:2379,http://10.0.1.12:4001 --listen-client-urls http://10.0.1.12:2379,http://10.0.1.12:4001 --initial-cluster node1=http://10.0.1.12:2380 --debug command: /usr/local/bin/etcd --data-dir=/etcd-data --name node1 --initial-advertise-peer-urls http://10.0.1.12:2380 --listen-peer-urls http://10.0.1.12:2380 --advertise-client-urls http://10.0.1.12:2379,http://10.0.1.12:4001 --listen-client-urls http://10.0.1.12:2379,http://10.0.1.12:4001 --initial-cluster node1=http://10.0.1.12:2380 --debug
ports: ports:
- "4001:4001" - "4001:4001"
- "2380:2380" - "2380:2380"
- "2379:2379" - "2379:2379"
networks: networks:
net: net:
ipv4_address: 10.0.1.12 ipv4_address: 10.0.1.12
etcdctl-ping: etcdctl-ping:
image: tenstartups/etcdctl image: tenstartups/etcdctl
command: --endpoints=[10.0.1.12:2379] get "traefik/acme/storage" command: --endpoints=[10.0.1.12:2379] get "traefik/acme/storage"
environment: environment:
ETCDCTL_DIAL_: "TIMEOUT 10s" ETCDCTL_DIAL_: "TIMEOUT 10s"
ETCDCTL_API : "3" ETCDCTL_API : "3"
networks: networks:
- net - net
## BOULDER part ## ## BOULDER part ##
boulder: boulder:
image: containous/boulder:release # To minimize fetching this should be the same version used below
environment: image: containous/boulder:containous-acmev2
FAKE_DNS: 172.17.0.1 environment:
PKCS11_PROXY_SOCKET: tcp://boulder-hsm:5657 FAKE_DNS: 172.17.0.1
extra_hosts: PKCS11_PROXY_SOCKET: tcp://boulder-hsm:5657
- le.wtf:127.0.0.1 restart: unless-stopped
- boulder:127.0.0.1 extra_hosts:
ports: - le.wtf:127.0.0.1
- 4000:4000 # ACME - boulder:127.0.0.1
- 4002:4002 # OCSP ports:
- 4003:4003 # OCSP - 4000:4000 # ACME
- 4500:4500 # ct-test-srv - 4001:4001 # ACMEv2
- 8000:8000 # debug ports - 4002:4002 # OCSP
- 8001:8001 - 4003:4003 # OCSP
- 8002:8002 - 4430:4430 # ACME via HTTPS
- 8003:8003 - 4431:4431 # ACMEv2 via HTTPS
- 8004:8004 - 4500:4500 # ct-test-srv
- 8055:8055 # dns-test-srv updates - 6000:6000 # gsb-test-srv
- 9380:9380 # mail-test-srv - 8000:8000 # debug ports
- 9381:9381 # mail-test-srv - 8001:8001
restart: unless-stopped - 8002:8002
depends_on: - 8003:8003
- bhsm - 8004:8004
- bmysql - 8005:8005
- brabbitmq - 8006:8006
networks: - 8008:8008
net: - 8009:8009
ipv4_address: 10.0.1.3 - 8010:8010
- 8055:8055 # dns-test-srv updates
- 9380:9380 # mail-test-srv
- 9381:9381 # mail-test-srv
depends_on:
- bhsm
- bmysql
networks:
net:
ipv4_address: 10.0.1.3
bhsm: bhsm:
image: letsencrypt/boulder-tools:2016-11-02 # To minimize fetching this should be the same version used above
hostname: boulder-hsm image: letsencrypt/boulder-tools:2018-03-07
environment: hostname: boulder-hsm
PKCS11_DAEMON_SOCKET: tcp://0.0.0.0:5657 environment:
command: /usr/local/bin/pkcs11-daemon /usr/lib/softhsm/libsofthsm.so PKCS11_DAEMON_SOCKET: tcp://0.0.0.0:5657
expose: command: /usr/local/bin/pkcs11-daemon /usr/lib/softhsm/libsofthsm2.so
- 5657 expose:
networks: - 5657
net: networks:
ipv4_address: 10.0.1.4 net:
aliases: ipv4_address: 10.0.1.4
- boulder-hsm aliases:
- boulder-hsm
bmysql: bmysql:
image: mariadb:10.1 image: mariadb:10.1
hostname: boulder-mysql hostname: boulder-mysql
environment: environment:
MYSQL_ALLOW_EMPTY_PASSWORD: "yes" MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
networks: command: mysqld --bind-address=0.0.0.0
net: logging:
ipv4_address: 10.0.1.5 driver: none
aliases: networks:
- boulder-mysql net:
ipv4_address: 10.0.1.5
brabbitmq: aliases:
image: rabbitmq:3-alpine - boulder-mysql
hostname: boulder-rabbitmq
environment:
RABBITMQ_NODE_IP_ADDRESS: "0.0.0.0"
networks:
net:
ipv4_address: 10.0.1.6
aliases:
- boulder-rabbitmq
## TRAEFIK part ## ## TRAEFIK part ##
traefik-storeconfig: storeconfig:
build: build:
context: ../.. context: ../..
image: containous/traefik image: containous/traefik
volumes: volumes:
- "./traefik.toml:/traefik.toml:ro" - "./traefik.toml:/traefik.toml:ro"
command: storeconfig --debug command: storeconfig --debug
networks: networks:
- net - net
traefik01: traefik01:
build: build:
context: ../.. context: ../..
image: containous/traefik image: containous/traefik
command: ${TRAEFIK_CMD} command: ${TRAEFIK_CMD}
extra_hosts: extra_hosts:
- traefik.boulder.com:172.17.0.1 - traefik.boulder.com:172.17.0.1
volumes: volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro" - "/var/run/docker.sock:/var/run/docker.sock:ro"
expose: expose:
- "443" - "443"
- "5001" - "5001"
- "5002" - "5002"
ports: ports:
- "80:80" - "80:80"
- "8080:8080" - "8080:8080"
- "443:443" - "443:443"
- "5001:443" # Needed for SNI challenge - "5001:443" # Needed for SNI challenge
- "5002:80" # Needed for HTTP challenge - "5002:80" # Needed for HTTP challenge
networks: networks:
net: net:
ipv4_address: 10.0.1.8 ipv4_address: 10.0.1.8
traefik02: traefik02:
build: build:
context: ../.. context: ../..
image: containous/traefik image: containous/traefik
command: ${TRAEFIK_CMD} command: ${TRAEFIK_CMD}
extra_hosts: extra_hosts:
- traefik.boulder.com:172.17.0.1 - traefik.boulder.com:172.17.0.1
volumes: volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro" - "/var/run/docker.sock:/var/run/docker.sock:ro"
expose: expose:
- "443" - "443"
- "5001" - "5001"
- "5002" - "5002"
ports: ports:
- "88:80" - "88:80"
- "8888:8080" - "8888:8080"
- "8443:443" - "8443:443"
depends_on: depends_on:
- traefik01 - traefik01
networks: networks:
net: net:
ipv4_address: 10.0.1.9 ipv4_address: 10.0.1.9
whoami01: whoami01:
image: emilevauge/whoami image: emilevauge/whoami
expose: expose:
- "80" - "80"
labels: labels:
- "traefik.port=80" - "traefik.port=80"
- "traefik.backend=wam01" - "traefik.backend=wam01"
- "traefik.frontend.rule=Host:who01.localhost.com" - "traefik.frontend.rule=Host:who01.localhost.com"
- "traefik.enable=true" - "traefik.enable=true"
networks: networks:
net: net:
ipv4_address: 10.0.1.10 ipv4_address: 10.0.1.10
whoami02: whoami02:
image: emilevauge/whoami image: emilevauge/whoami
expose: expose:
- "80" - "80"
labels: labels:
- "traefik.port=80" - "traefik.port=80"
- "traefik.backend=wam02" - "traefik.backend=wam02"
- "traefik.frontend.rule=Host:who02.localhost.com" - "traefik.frontend.rule=Host:who02.localhost.com"
- "traefik.enable=true" - "traefik.enable=true"
networks: networks:
- net - net
networks: networks:
net: net:

View File

@@ -74,10 +74,10 @@ start_storeconfig_consul() {
endpoint = "10.0.1.2:8500" endpoint = "10.0.1.2:8500"
watch = true watch = true
prefix = "traefik"' >> $basedir/traefik.toml prefix = "traefik"' >> $basedir/traefik.toml
up_environment traefik-storeconfig up_environment storeconfig
rm -f $basedir/traefik.toml rm -f $basedir/traefik.toml
waiting_counter=5 waiting_counter=5
delete_services traefik-storeconfig delete_services storeconfig
} }
@@ -90,7 +90,7 @@ start_storeconfig_etcd3() {
watch = true watch = true
prefix = "/traefik" prefix = "/traefik"
useAPIV3 = true' >> $basedir/traefik.toml useAPIV3 = true' >> $basedir/traefik.toml
up_environment traefik-storeconfig up_environment storeconfig
rm -f $basedir/traefik.toml rm -f $basedir/traefik.toml
waiting_counter=5 waiting_counter=5
# Don't start Traefik store config if ETCD3 is not started # Don't start Traefik store config if ETCD3 is not started
@@ -99,7 +99,7 @@ start_storeconfig_etcd3() {
sleep 5 sleep 5
let waiting_counter-=1 let waiting_counter-=1
done done
delete_services traefik-storeconfig etcdctl-ping delete_services storeconfig etcdctl-ping
} }
start_traefik() { start_traefik() {
@@ -136,11 +136,11 @@ start_traefik() {
# Start boulder services # Start boulder services
start_boulder() { start_boulder() {
echo "Start boulder environment" echo "Start boulder environment"
up_environment bmysql brabbitmq bhsm boulder up_environment bmysql bhsm boulder
waiting_counter=12 waiting_counter=12
# Not start Traefik if boulder is not started # Not start Traefik if boulder is not started
echo "WAIT for boulder..." echo "WAIT for boulder..."
while [[ -z $(curl -s http://10.0.1.3:4000/directory) ]]; do while [[ -z $(curl -s http://10.0.1.3:4001/directory) ]]; do
sleep 5 sleep 5
let waiting_counter-=1 let waiting_counter-=1
if [[ $waiting_counter -eq 0 ]]; then if [[ $waiting_counter -eq 0 ]]; then

View File

@@ -14,13 +14,12 @@ email = "test@traefik.io"
storage = "traefik/acme/account" storage = "traefik/acme/account"
entryPoint = "https" entryPoint = "https"
OnHostRule = true OnHostRule = true
caServer = "http://traefik.boulder.com:4000/directory" caServer = "http://traefik.boulder.com:4001/directory"
[acme.httpChallenge] [acme.httpChallenge]
entryPoint="http" entryPoint="http"
[web] [api]
address = ":8080"
[docker] [docker]
endpoint = "unix:///var/run/docker.sock" endpoint = "unix:///var/run/docker.sock"

View File

@@ -1,6 +1,6 @@
traefik: traefik:
image: traefik image: traefik
command: --web --rancher --rancher.domain=rancher.localhost --rancher.endpoint=http://example.com --rancher.accesskey=XXXXXXX --rancher.secretkey=YYYYYY --logLevel=DEBUG command: --api --rancher --rancher.domain=rancher.localhost --rancher.endpoint=http://example.com --rancher.accesskey=XXXXXXX --rancher.secretkey=YYYYYY --logLevel=DEBUG
ports: ports:
- "80:80" - "80:80"
- "443:443" - "443:443"

View File

@@ -1,6 +1,6 @@
traefik: traefik:
image: traefik image: traefik
command: -c /dev/null --web --docker --docker.domain=docker.localhost --logLevel=DEBUG command: -c /dev/null --api --docker --docker.domain=docker.localhost --logLevel=DEBUG
ports: ports:
- "80:80" - "80:80"
- "8080:8080" - "8080:8080"

View File

@@ -29,8 +29,9 @@ spec:
- image: traefik - image: traefik
name: traefik-ingress-lb name: traefik-ingress-lb
args: args:
- --web - --api
- --kubernetes - --kubernetes
- --logLevel=INFO
--- ---
kind: Service kind: Service
apiVersion: v1 apiVersion: v1

View File

@@ -32,11 +32,15 @@ spec:
- name: admin - name: admin
containerPort: 8080 containerPort: 8080
securityContext: securityContext:
privileged: true capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
args: args:
- -d - --api
- --web
- --kubernetes - --kubernetes
- --logLevel=INFO
--- ---
kind: Service kind: Service
apiVersion: v1 apiVersion: v1

View File

@@ -0,0 +1,106 @@
## The Træfik Quickstart (Using Docker)
In this quickstart, we'll use [Docker compose](https://docs.docker.com/compose) to create our demo infrastructure.
To save some time, you can clone [Træfik's repository](https://github.com/containous/traefik) and use the quickstart files located in the [examples/quickstart](https://github.com/containous/traefik/tree/master/examples/quickstart/) directory.
### 1 — Launch Træfik — Tell It to Listen to Docker
Create a `docker-compose.yml` file where you will define a `reverse-proxy` service that uses the official Træfik image:
```yaml
version: '3'
services:
reverse-proxy:
image: traefik #The official Traefik docker image
command: --api --docker #Enables the web UI and tells Træfik to listen to docker
ports:
- "80:80" #The HTTP port
- "8080:8080" #The Web UI (enabled by --api)
volumes:
- /var/run/docker.sock:/var/run/docker.sock #So that Traefik can listen to the Docker events
```
**That's it. Now you can launch Træfik!**
Start your `reverse-proxy` with the following command:
```shell
docker-compose up -d reverse-proxy
```
You can open a browser and go to [http://localhost:8080](http://localhost:8080) to see Træfik's dashboard (we'll go back there once we have launched a service in step 2).
### 2 — Launch a Service — Træfik Detects It and Creates a Route for You
Now that we have a Træfik instance up and running, we will deploy new services.
Edit your `docker-compose.yml` file and add the following at the end of your file.
```yaml
# ...
whoami:
image: emilevauge/whoami #A container that exposes an API to show it's IP address
labels:
- "traefik.frontend.rule=Host:whoami.docker.localhost"
```
The above defines `whoami`: a simple web service that outputs information about the machine it is deployed on (its IP address, host, and so on).
Start the `whoami` service with the following command:
```shell
docker-compose up -d whoami
```
Go back to your browser ([http://localhost:8080](http://localhost:8080)) and see that Træfik has automatically detected the new container and updated its own configuration.
When Traefik detects new services, it creates the corresponding routes so you can call them ... _let's see!_ (Here, we're using curl)
```shell
curl -H Host:whoami.docker.localhost http://127.0.0.1
```
_Shows the following output:_
```yaml
Hostname: 8656c8ddca6c
IP: 172.27.0.3
#...
```
### 3 — Launch More Instances — Traefik Load Balances Them
Run more instances of your `whoami` service with the following command:
```shell
docker-compose up -d --scale whoami=2
```
Go back to your browser ([http://localhost:8080](http://localhost:8080)) and see that Træfik has automatically detected the new instance of the container.
Finally, see that Træfik load-balances between the two instances of your services by running twice the following command:
```shell
curl -H Host:whoami.docker.localhost http://127.0.0.1
```
The output will show alternatively one of the followings:
```yaml
Hostname: 8656c8ddca6c
IP: 172.27.0.3
#...
```
```yaml
Hostname: 8458f154e1f1
IP: 172.27.0.4
# ...
```
### 4 — Enjoy Træfik's Magic
Now that you have a basic understanding of how Træfik can automatically create the routes to your services and load balance them, it might be time to dive into [the documentation](https://docs.traefik.io/) and let Træfik work for you! Whatever your infrastructure is, there is probably [an available Træfik backend](https://docs.traefik.io/configuration/backends/available) that will do the job.
Our recommendation would be to see for yourself how simple it is to enable HTTPS with [Træfik's let's encrypt integration](https://docs.traefik.io/user-guide/examples/#lets-encrypt-support) using the dedicated [user guide](https://docs.traefik.io/user-guide/docker-and-lets-encrypt/).

View File

@@ -0,0 +1,18 @@
version: '3'
services:
#The reverse proxy service (Træfik)
reverse-proxy:
image: traefik #The official Traefik docker image
command: --api --docker #Enables the web UI and tells Træfik to listen to docker
ports:
- "80:80" #The HTTP port
- "8080:8080" #The Web UI (enabled by --api)
volumes:
- /var/run/docker.sock:/var/run/docker.sock #So that Traefik can listen to the Docker events
#A container that exposes a simple API
whoami:
image: emilevauge/whoami #A container that exposes an API to show it's IP address
labels:
- "traefik.frontend.rule=Host:whoami.docker.localhost"

View File

@@ -12,6 +12,7 @@ import (
"github.com/containous/traefik/log" "github.com/containous/traefik/log"
"github.com/containous/traefik/safe" "github.com/containous/traefik/safe"
"github.com/go-kit/kit/metrics"
"github.com/vulcand/oxy/roundrobin" "github.com/vulcand/oxy/roundrobin"
) )
@@ -19,9 +20,9 @@ var singleton *HealthCheck
var once sync.Once var once sync.Once
// GetHealthCheck returns the health check which is guaranteed to be a singleton. // GetHealthCheck returns the health check which is guaranteed to be a singleton.
func GetHealthCheck() *HealthCheck { func GetHealthCheck(metrics metricsRegistry) *HealthCheck {
once.Do(func() { once.Do(func() {
singleton = newHealthCheck() singleton = newHealthCheck(metrics)
}) })
return singleton return singleton
} }
@@ -42,14 +43,15 @@ func (opt Options) String() string {
// BackendHealthCheck HealthCheck configuration for a backend // BackendHealthCheck HealthCheck configuration for a backend
type BackendHealthCheck struct { type BackendHealthCheck struct {
Options Options
name string
disabledURLs []*url.URL disabledURLs []*url.URL
requestTimeout time.Duration requestTimeout time.Duration
} }
//HealthCheck struct //HealthCheck struct
type HealthCheck struct { type HealthCheck struct {
mutex sync.Mutex
Backends map[string]*BackendHealthCheck Backends map[string]*BackendHealthCheck
metrics metricsRegistry
cancel context.CancelFunc cancel context.CancelFunc
} }
@@ -60,77 +62,90 @@ type LoadBalancer interface {
Servers() []*url.URL Servers() []*url.URL
} }
func newHealthCheck() *HealthCheck { func newHealthCheck(metrics metricsRegistry) *HealthCheck {
return &HealthCheck{ return &HealthCheck{
Backends: make(map[string]*BackendHealthCheck), Backends: make(map[string]*BackendHealthCheck),
metrics: metrics,
} }
} }
// metricsRegistry is a local interface in the healthcheck package, exposing only the required metrics
// necessary for the healthcheck package. This makes it easier for the tests.
type metricsRegistry interface {
BackendServerUpGauge() metrics.Gauge
}
// NewBackendHealthCheck Instantiate a new BackendHealthCheck // NewBackendHealthCheck Instantiate a new BackendHealthCheck
func NewBackendHealthCheck(options Options) *BackendHealthCheck { func NewBackendHealthCheck(options Options, backendName string) *BackendHealthCheck {
return &BackendHealthCheck{ return &BackendHealthCheck{
Options: options, Options: options,
name: backendName,
requestTimeout: 5 * time.Second, requestTimeout: 5 * time.Second,
} }
} }
//SetBackendsConfiguration set backends configuration //SetBackendsConfiguration set backends configuration
func (hc *HealthCheck) SetBackendsConfiguration(parentCtx context.Context, backends map[string]*BackendHealthCheck) { func (hc *HealthCheck) SetBackendsConfiguration(parentCtx context.Context, backends map[string]*BackendHealthCheck) {
hc.mutex.Lock()
hc.Backends = backends hc.Backends = backends
if hc.cancel != nil { if hc.cancel != nil {
hc.cancel() hc.cancel()
} }
ctx, cancel := context.WithCancel(parentCtx) ctx, cancel := context.WithCancel(parentCtx)
hc.cancel = cancel hc.cancel = cancel
hc.mutex.Unlock()
for backendID, backend := range backends { for _, backend := range backends {
currentBackendID := backendID
currentBackend := backend currentBackend := backend
safe.Go(func() { safe.Go(func() {
hc.execute(ctx, currentBackendID, currentBackend) hc.execute(ctx, currentBackend)
}) })
} }
} }
func (hc *HealthCheck) execute(ctx context.Context, backendID string, backend *BackendHealthCheck) { func (hc *HealthCheck) execute(ctx context.Context, backend *BackendHealthCheck) {
log.Debugf("Initial healthcheck for currentBackend %s ", backendID) log.Debugf("Initial health check for backend: %q", backend.name)
checkBackend(backend) hc.checkBackend(backend)
ticker := time.NewTicker(backend.Interval) ticker := time.NewTicker(backend.Interval)
defer ticker.Stop() defer ticker.Stop()
for { for {
select { select {
case <-ctx.Done(): case <-ctx.Done():
log.Debug("Stopping all current Healthcheck goroutines") log.Debug("Stopping current health check goroutines of backend: %s", backend.name)
return return
case <-ticker.C: case <-ticker.C:
log.Debugf("Refreshing healthcheck for currentBackend %s ", backendID) log.Debugf("Refreshing health check for backend: %s", backend.name)
checkBackend(backend) hc.checkBackend(backend)
} }
} }
} }
func checkBackend(currentBackend *BackendHealthCheck) { func (hc *HealthCheck) checkBackend(backend *BackendHealthCheck) {
enabledURLs := currentBackend.LB.Servers() enabledURLs := backend.LB.Servers()
var newDisabledURLs []*url.URL var newDisabledURLs []*url.URL
for _, url := range currentBackend.disabledURLs { for _, url := range backend.disabledURLs {
if checkHealth(url, currentBackend) { serverUpMetricValue := float64(0)
log.Debugf("HealthCheck is up [%s]: Upsert in server list", url.String()) if err := checkHealth(url, backend); err == nil {
currentBackend.LB.UpsertServer(url, roundrobin.Weight(1)) log.Warnf("Health check up: Returning to server list. Backend: %q URL: %q", backend.name, url.String())
backend.LB.UpsertServer(url, roundrobin.Weight(1))
serverUpMetricValue = 1
} else { } else {
log.Warnf("HealthCheck is still failing [%s]", url.String()) log.Warnf("Health check still failing. Backend: %q URL: %q Reason: %s", backend.name, url.String(), err)
newDisabledURLs = append(newDisabledURLs, url) newDisabledURLs = append(newDisabledURLs, url)
} }
labelValues := []string{"backend", backend.name, "url", url.String()}
hc.metrics.BackendServerUpGauge().With(labelValues...).Set(serverUpMetricValue)
} }
currentBackend.disabledURLs = newDisabledURLs backend.disabledURLs = newDisabledURLs
for _, url := range enabledURLs { for _, url := range enabledURLs {
if !checkHealth(url, currentBackend) { serverUpMetricValue := float64(1)
log.Warnf("HealthCheck has failed [%s]: Remove from server list", url.String()) if err := checkHealth(url, backend); err != nil {
currentBackend.LB.RemoveServer(url) log.Warnf("Health check failed: Remove from server list. Backend: %q URL: %q Reason: %s", backend.name, url.String(), err)
currentBackend.disabledURLs = append(currentBackend.disabledURLs, url) backend.LB.RemoveServer(url)
backend.disabledURLs = append(backend.disabledURLs, url)
serverUpMetricValue = 0
} }
labelValues := []string{"backend", backend.name, "url", url.String()}
hc.metrics.BackendServerUpGauge().With(labelValues...).Set(serverUpMetricValue)
} }
} }
@@ -148,21 +163,28 @@ func (backend *BackendHealthCheck) newRequest(serverURL *url.URL) (*http.Request
return http.NewRequest(http.MethodGet, u.String(), nil) return http.NewRequest(http.MethodGet, u.String(), nil)
} }
func checkHealth(serverURL *url.URL, backend *BackendHealthCheck) bool { // checkHealth returns a nil error in case it was successful and otherwise
// a non-nil error with a meaningful description why the health check failed.
func checkHealth(serverURL *url.URL, backend *BackendHealthCheck) error {
client := http.Client{ client := http.Client{
Timeout: backend.requestTimeout, Timeout: backend.requestTimeout,
Transport: backend.Options.Transport, Transport: backend.Options.Transport,
} }
req, err := backend.newRequest(serverURL) req, err := backend.newRequest(serverURL)
if err != nil { if err != nil {
log.Errorf("Failed to create HTTP request [%s] for healthcheck: %s", serverURL, err) return fmt.Errorf("failed to create HTTP request: %s", err)
return false
} }
resp, err := client.Do(req) resp, err := client.Do(req)
if err == nil { if err == nil {
defer resp.Body.Close() defer resp.Body.Close()
} }
return err == nil && resp.StatusCode == http.StatusOK
switch {
case err != nil:
return fmt.Errorf("HTTP request failed: %s", err)
case resp.StatusCode != http.StatusOK:
return fmt.Errorf("received non-200 status code: %v", resp.StatusCode)
}
return nil
} }

View File

@@ -27,6 +27,7 @@ func TestSetBackendsConfiguration(t *testing.T) {
healthSequence []bool healthSequence []bool
wantNumRemovedServers int wantNumRemovedServers int
wantNumUpsertedServers int wantNumUpsertedServers int
wantGaugeValue float64
}{ }{
{ {
desc: "healthy server staying healthy", desc: "healthy server staying healthy",
@@ -34,6 +35,7 @@ func TestSetBackendsConfiguration(t *testing.T) {
healthSequence: []bool{true}, healthSequence: []bool{true},
wantNumRemovedServers: 0, wantNumRemovedServers: 0,
wantNumUpsertedServers: 0, wantNumUpsertedServers: 0,
wantGaugeValue: 1,
}, },
{ {
desc: "healthy server becoming sick", desc: "healthy server becoming sick",
@@ -41,6 +43,7 @@ func TestSetBackendsConfiguration(t *testing.T) {
healthSequence: []bool{false}, healthSequence: []bool{false},
wantNumRemovedServers: 1, wantNumRemovedServers: 1,
wantNumUpsertedServers: 0, wantNumUpsertedServers: 0,
wantGaugeValue: 0,
}, },
{ {
desc: "sick server becoming healthy", desc: "sick server becoming healthy",
@@ -48,6 +51,7 @@ func TestSetBackendsConfiguration(t *testing.T) {
healthSequence: []bool{true}, healthSequence: []bool{true},
wantNumRemovedServers: 0, wantNumRemovedServers: 0,
wantNumUpsertedServers: 1, wantNumUpsertedServers: 1,
wantGaugeValue: 1,
}, },
{ {
desc: "sick server staying sick", desc: "sick server staying sick",
@@ -55,6 +59,7 @@ func TestSetBackendsConfiguration(t *testing.T) {
healthSequence: []bool{false}, healthSequence: []bool{false},
wantNumRemovedServers: 0, wantNumRemovedServers: 0,
wantNumUpsertedServers: 0, wantNumUpsertedServers: 0,
wantGaugeValue: 0,
}, },
{ {
desc: "healthy server toggling to sick and back to healthy", desc: "healthy server toggling to sick and back to healthy",
@@ -62,6 +67,7 @@ func TestSetBackendsConfiguration(t *testing.T) {
healthSequence: []bool{false, true}, healthSequence: []bool{false, true},
wantNumRemovedServers: 1, wantNumRemovedServers: 1,
wantNumUpsertedServers: 1, wantNumUpsertedServers: 1,
wantGaugeValue: 1,
}, },
} }
@@ -81,7 +87,7 @@ func TestSetBackendsConfiguration(t *testing.T) {
Path: "/path", Path: "/path",
Interval: healthCheckInterval, Interval: healthCheckInterval,
LB: lb, LB: lb,
}) }, "backendName")
serverURL := testhelpers.MustParseURL(ts.URL) serverURL := testhelpers.MustParseURL(ts.URL)
if test.startHealthy { if test.startHealthy {
lb.servers = append(lb.servers, serverURL) lb.servers = append(lb.servers, serverURL)
@@ -89,13 +95,15 @@ func TestSetBackendsConfiguration(t *testing.T) {
backend.disabledURLs = append(backend.disabledURLs, serverURL) backend.disabledURLs = append(backend.disabledURLs, serverURL)
} }
collectingMetrics := testhelpers.NewCollectingHealthCheckMetrics()
check := HealthCheck{ check := HealthCheck{
Backends: make(map[string]*BackendHealthCheck), Backends: make(map[string]*BackendHealthCheck),
metrics: collectingMetrics,
} }
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
wg.Add(1) wg.Add(1)
go func() { go func() {
check.execute(ctx, "id", backend) check.execute(ctx, backend)
wg.Done() wg.Done()
}() }()
@@ -118,6 +126,10 @@ func TestSetBackendsConfiguration(t *testing.T) {
if lb.numUpsertedServers != test.wantNumUpsertedServers { if lb.numUpsertedServers != test.wantNumUpsertedServers {
t.Errorf("got %d upserted servers, wanted %d", lb.numUpsertedServers, test.wantNumUpsertedServers) t.Errorf("got %d upserted servers, wanted %d", lb.numUpsertedServers, test.wantNumUpsertedServers)
} }
if collectingMetrics.Gauge.GaugeValue != test.wantGaugeValue {
t.Errorf("got %v ServerUp Gauge, want %v", collectingMetrics.Gauge.GaugeValue, test.wantGaugeValue)
}
}) })
} }
} }
@@ -168,7 +180,7 @@ func TestNewRequest(t *testing.T) {
Options{ Options{
Path: test.path, Path: test.path,
Port: test.port, Port: test.port,
}) }, "backendName")
u := &url.URL{ u := &url.URL{
Scheme: "http", Scheme: "http",

View File

@@ -1,18 +1,19 @@
package integration package integration
import ( import (
"crypto/md5"
"crypto/rand"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"net"
"net/http" "net/http"
"net/http/httptest"
"os" "os"
"strings" "strings"
"time" "time"
"github.com/containous/traefik/integration/try" "github.com/containous/traefik/integration/try"
"github.com/containous/traefik/middlewares/accesslog"
"github.com/go-check/check" "github.com/go-check/check"
"github.com/mattn/go-shellwords"
checker "github.com/vdemeester/shakers" checker "github.com/vdemeester/shakers"
) )
@@ -24,57 +25,634 @@ const (
// AccessLogSuite // AccessLogSuite
type AccessLogSuite struct{ BaseSuite } type AccessLogSuite struct{ BaseSuite }
type accessLogValue struct {
formatOnly bool
code string
user string
frontendName string
backendName string
}
func (s *AccessLogSuite) SetUpSuite(c *check.C) {
s.createComposeProject(c, "access_log")
s.composeProject.Start(c)
s.composeProject.Container(c, "server0")
s.composeProject.Container(c, "server1")
s.composeProject.Container(c, "server2")
s.composeProject.Container(c, "server3")
}
func (s *AccessLogSuite) TearDownTest(c *check.C) {
displayTraefikLogFile(c, traefikTestLogFile)
os.Remove(traefikTestAccessLogFile)
}
func (s *AccessLogSuite) TestAccessLog(c *check.C) { func (s *AccessLogSuite) TestAccessLog(c *check.C) {
// Ensure working directory is clean // Ensure working directory is clean
os.Remove(traefikTestAccessLogFile) ensureWorkingDirectoryIsClean()
os.Remove(traefikTestLogFile)
// Start Traefik // Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml")) cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c) defer display(c)
err := cmd.Start() err := cmd.Start()
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
defer cmd.Process.Kill() defer cmd.Process.Kill()
defer os.Remove(traefikTestAccessLogFile) waitForTraefik(c, "server1")
defer os.Remove(traefikTestLogFile)
err = try.Do(1*time.Second, func() error { checkStatsForLogFile(c)
if _, err := os.Stat(traefikTestLogFile); err != nil {
return fmt.Errorf("could not get stats for log file: %s", err)
}
return nil
})
c.Assert(err, checker.IsNil)
// Verify Traefik started OK // Verify Traefik started OK
checkTraefikStarted(c)
// Make some requests
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
c.Assert(err, checker.IsNil)
req.Host = "frontend1.docker.local"
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
c.Assert(err, checker.IsNil)
req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
c.Assert(err, checker.IsNil)
req.Host = "frontend2.docker.local"
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
c.Assert(err, checker.IsNil)
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
c.Assert(err, checker.IsNil)
// Verify access.log output as expected
count := checkAccessLogOutput(c)
c.Assert(count, checker.GreaterOrEqualThan, 3)
// Verify no other Traefik problems
checkNoOtherTraefikProblems(c)
}
func (s *AccessLogSuite) TestAccessLogAuthFrontend(c *check.C) {
// Ensure working directory is clean
ensureWorkingDirectoryIsClean()
expected := []accessLogValue{
{
formatOnly: false,
code: "401",
user: "-",
frontendName: "Auth for frontend-Host-frontend-auth-docker-local",
backendName: "-",
},
}
// Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
checkStatsForLogFile(c)
s.composeProject.Container(c, "authFrontend")
waitForTraefik(c, "authFrontend")
// Verify Traefik started OK
checkTraefikStarted(c)
// Test auth frontend
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8006/", nil)
c.Assert(err, checker.IsNil)
req.Host = "frontend.auth.docker.local"
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized), try.HasBody())
c.Assert(err, checker.IsNil)
// Verify access.log output as expected
count := checkAccessLogExactValuesOutput(c, expected)
c.Assert(count, checker.GreaterOrEqualThan, len(expected))
// Verify no other Traefik problems
checkNoOtherTraefikProblems(c)
}
func (s *AccessLogSuite) TestAccessLogAuthEntrypoint(c *check.C) {
// Ensure working directory is clean
ensureWorkingDirectoryIsClean()
expected := []accessLogValue{
{
formatOnly: false,
code: "401",
user: "-",
frontendName: "Auth for entrypoint",
backendName: "-",
},
}
// Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
checkStatsForLogFile(c)
s.composeProject.Container(c, "authEntrypoint")
waitForTraefik(c, "authEntrypoint")
// Verify Traefik started OK
checkTraefikStarted(c)
// Test auth entrypoint
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8004/", nil)
c.Assert(err, checker.IsNil)
req.Host = "entrypoint.auth.docker.local"
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized), try.HasBody())
c.Assert(err, checker.IsNil)
// Verify access.log output as expected
count := checkAccessLogExactValuesOutput(c, expected)
c.Assert(count, checker.GreaterOrEqualThan, len(expected))
// Verify no other Traefik problems
checkNoOtherTraefikProblems(c)
}
func (s *AccessLogSuite) TestAccessLogAuthEntrypointSuccess(c *check.C) {
// Ensure working directory is clean
ensureWorkingDirectoryIsClean()
expected := []accessLogValue{
{
formatOnly: false,
code: "200",
user: "test",
frontendName: "Host-entrypoint-auth-docker",
backendName: "http://172.17.0",
},
}
// Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
checkStatsForLogFile(c)
s.composeProject.Container(c, "authEntrypoint")
waitForTraefik(c, "authEntrypoint")
// Verify Traefik started OK
checkTraefikStarted(c)
// Test auth entrypoint
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8004/", nil)
c.Assert(err, checker.IsNil)
req.Host = "entrypoint.auth.docker.local"
req.SetBasicAuth("test", "test")
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
c.Assert(err, checker.IsNil)
// Verify access.log output as expected
count := checkAccessLogExactValuesOutput(c, expected)
c.Assert(count, checker.GreaterOrEqualThan, len(expected))
// Verify no other Traefik problems
checkNoOtherTraefikProblems(c)
}
func (s *AccessLogSuite) TestAccessLogDigestAuthEntrypoint(c *check.C) {
ensureWorkingDirectoryIsClean()
expected := []accessLogValue{
{
formatOnly: false,
code: "401",
user: "-",
frontendName: "Auth for entrypoint",
backendName: "-",
},
{
formatOnly: false,
code: "200",
user: "test",
frontendName: "Host-entrypoint-digest-auth-docker",
backendName: "http://172.17.0",
},
}
// Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
checkStatsForLogFile(c)
s.composeProject.Container(c, "digestAuthEntrypoint")
waitForTraefik(c, "digestAuthEntrypoint")
// Verify Traefik started OK
checkTraefikStarted(c)
// Test auth entrypoint
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8008/", nil)
c.Assert(err, checker.IsNil)
req.Host = "entrypoint.digest.auth.docker.local"
resp, err := try.ResponseUntilStatusCode(req, 500*time.Millisecond, http.StatusUnauthorized)
c.Assert(err, checker.IsNil)
digestParts := digestParts(resp)
digestParts["uri"] = "/"
digestParts["method"] = http.MethodGet
digestParts["username"] = "test"
digestParts["password"] = "test"
req.Header.Set("Authorization", getDigestAuthorization(digestParts))
req.Header.Set("Content-Type", "application/json")
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
c.Assert(err, checker.IsNil)
// Verify access.log output as expected
count := checkAccessLogExactValuesOutput(c, expected)
c.Assert(count, checker.GreaterOrEqualThan, len(expected))
// Verify no other Traefik problems
checkNoOtherTraefikProblems(c)
}
// Thanks to mvndaai for digest authentication
// https://stackoverflow.com/questions/39474284/how-do-you-do-a-http-post-with-digest-authentication-in-golang/39481441#39481441
func digestParts(resp *http.Response) map[string]string {
result := map[string]string{}
if len(resp.Header["Www-Authenticate"]) > 0 {
wantedHeaders := []string{"nonce", "realm", "qop", "opaque"}
responseHeaders := strings.Split(resp.Header["Www-Authenticate"][0], ",")
for _, r := range responseHeaders {
for _, w := range wantedHeaders {
if strings.Contains(r, w) {
result[w] = strings.Split(r, `"`)[1]
}
}
}
}
return result
}
func getMD5(data string) string {
digest := md5.New()
digest.Write([]byte(data))
return fmt.Sprintf("%x", digest.Sum(nil))
}
func getCnonce() string {
b := make([]byte, 8)
io.ReadFull(rand.Reader, b)
return fmt.Sprintf("%x", b)[:16]
}
func getDigestAuthorization(digestParts map[string]string) string {
d := digestParts
ha1 := getMD5(d["username"] + ":" + d["realm"] + ":" + d["password"])
ha2 := getMD5(d["method"] + ":" + d["uri"])
nonceCount := "00000001"
cnonce := getCnonce()
response := getMD5(fmt.Sprintf("%s:%s:%s:%s:%s:%s", ha1, d["nonce"], nonceCount, cnonce, d["qop"], ha2))
authorization := fmt.Sprintf(`Digest username="%s", realm="%s", nonce="%s", uri="%s", cnonce="%s", nc=%s, qop=%s, response="%s", opaque="%s", algorithm="MD5"`,
d["username"], d["realm"], d["nonce"], d["uri"], cnonce, nonceCount, d["qop"], response, d["opaque"])
return authorization
}
func (s *AccessLogSuite) TestAccessLogEntrypointRedirect(c *check.C) {
ensureWorkingDirectoryIsClean()
expected := []accessLogValue{
{
formatOnly: false,
code: "302",
user: "-",
frontendName: "entrypoint redirect for frontend-",
backendName: "-",
},
{
formatOnly: true,
},
}
// Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
checkStatsForLogFile(c)
s.composeProject.Container(c, "entrypointRedirect")
waitForTraefik(c, "entrypointRedirect")
// Verify Traefik started OK
checkTraefikStarted(c)
// Test entrypoint redirect
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8001/test", nil)
c.Assert(err, checker.IsNil)
req.Host = ""
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
c.Assert(err, checker.IsNil)
// Verify access.log output as expected
count := checkAccessLogExactValuesOutput(c, expected)
c.Assert(count, checker.GreaterOrEqualThan, len(expected))
// Verify no other Traefik problems
checkNoOtherTraefikProblems(c)
}
func (s *AccessLogSuite) TestAccessLogFrontendRedirect(c *check.C) {
ensureWorkingDirectoryIsClean()
expected := []accessLogValue{
{
formatOnly: false,
code: "302",
user: "-",
frontendName: "frontend redirect for frontend-Path-",
backendName: "-",
},
{
formatOnly: true,
},
}
// Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
checkStatsForLogFile(c)
s.composeProject.Container(c, "frontendRedirect")
waitForTraefik(c, "frontendRedirect")
// Verify Traefik started OK
checkTraefikStarted(c)
// Test frontend redirect
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8005/test", nil)
c.Assert(err, checker.IsNil)
req.Host = ""
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
c.Assert(err, checker.IsNil)
// Verify access.log output as expected
count := checkAccessLogExactValuesOutput(c, expected)
c.Assert(count, checker.GreaterOrEqualThan, len(expected))
// Verify no other Traefik problems
checkNoOtherTraefikProblems(c)
}
func (s *AccessLogSuite) TestAccessLogRateLimit(c *check.C) {
ensureWorkingDirectoryIsClean()
expected := []accessLogValue{
{
formatOnly: true,
},
{
formatOnly: true,
},
{
formatOnly: false,
code: "429",
user: "-",
frontendName: "rate limit for frontend-Host-ratelimit",
backendName: "/",
},
}
// Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
checkStatsForLogFile(c)
s.composeProject.Container(c, "rateLimit")
waitForTraefik(c, "rateLimit")
// Verify Traefik started OK
checkTraefikStarted(c)
// Test rate limit
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8007/", nil)
c.Assert(err, checker.IsNil)
req.Host = "ratelimit.docker.local"
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
c.Assert(err, checker.IsNil)
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.HasBody())
c.Assert(err, checker.IsNil)
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusTooManyRequests), try.HasBody())
c.Assert(err, checker.IsNil)
// Verify access.log output as expected
count := checkAccessLogExactValuesOutput(c, expected)
c.Assert(count, checker.GreaterOrEqualThan, len(expected))
// Verify no other Traefik problems
checkNoOtherTraefikProblems(c)
}
func (s *AccessLogSuite) TestAccessLogBackendNotFound(c *check.C) {
ensureWorkingDirectoryIsClean()
expected := []accessLogValue{
{
formatOnly: false,
code: "404",
user: "-",
frontendName: "backend not found",
backendName: "/",
},
}
// Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
waitForTraefik(c, "server1")
checkStatsForLogFile(c)
// Verify Traefik started OK
checkTraefikStarted(c)
// Test rate limit
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
c.Assert(err, checker.IsNil)
req.Host = "backendnotfound.docker.local"
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound), try.HasBody())
c.Assert(err, checker.IsNil)
// Verify access.log output as expected
count := checkAccessLogExactValuesOutput(c, expected)
c.Assert(count, checker.GreaterOrEqualThan, len(expected))
// Verify no other Traefik problems
checkNoOtherTraefikProblems(c)
}
func (s *AccessLogSuite) TestAccessLogEntrypointWhitelist(c *check.C) {
ensureWorkingDirectoryIsClean()
expected := []accessLogValue{
{
formatOnly: false,
code: "403",
user: "-",
frontendName: "ipwhitelister for entrypoint httpWhitelistReject",
backendName: "-",
},
}
// Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
checkStatsForLogFile(c)
s.composeProject.Container(c, "entrypointWhitelist")
waitForTraefik(c, "entrypointWhitelist")
// Verify Traefik started OK
checkTraefikStarted(c)
// Test rate limit
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8002/", nil)
c.Assert(err, checker.IsNil)
req.Host = "entrypoint.whitelist.docker.local"
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusForbidden), try.HasBody())
c.Assert(err, checker.IsNil)
// Verify access.log output as expected
count := checkAccessLogExactValuesOutput(c, expected)
c.Assert(count, checker.GreaterOrEqualThan, len(expected))
// Verify no other Traefik problems
checkNoOtherTraefikProblems(c)
}
func (s *AccessLogSuite) TestAccessLogFrontendWhitelist(c *check.C) {
ensureWorkingDirectoryIsClean()
expected := []accessLogValue{
{
formatOnly: false,
code: "403",
user: "-",
frontendName: "ipwhitelister for frontend-Host-frontend-whitelist",
backendName: "-",
},
}
// Start Traefik
cmd, display := s.traefikCmd(withConfigFile("fixtures/access_log_config.toml"))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
checkStatsForLogFile(c)
s.composeProject.Container(c, "frontendWhitelist")
waitForTraefik(c, "frontendWhitelist")
// Verify Traefik started OK
checkTraefikStarted(c)
// Test rate limit
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
c.Assert(err, checker.IsNil)
req.Host = "frontend.whitelist.docker.local"
err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusForbidden), try.HasBody())
c.Assert(err, checker.IsNil)
// Verify access.log output as expected
count := checkAccessLogExactValuesOutput(c, expected)
c.Assert(count, checker.GreaterOrEqualThan, len(expected))
// Verify no other Traefik problems
checkNoOtherTraefikProblems(c)
}
func checkNoOtherTraefikProblems(c *check.C) {
traefikLog, err := ioutil.ReadFile(traefikTestLogFile) traefikLog, err := ioutil.ReadFile(traefikTestLogFile)
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
if len(traefikLog) > 0 { if len(traefikLog) > 0 {
fmt.Printf("%s\n", string(traefikLog)) fmt.Printf("%s\n", string(traefikLog))
c.Assert(traefikLog, checker.HasLen, 0) c.Assert(traefikLog, checker.HasLen, 0)
} }
}
// Start test servers func checkAccessLogOutput(c *check.C) int {
ts1 := startAccessLogServer(8081) lines := extractLines(c)
defer ts1.Close()
ts2 := startAccessLogServer(8082)
defer ts2.Close()
ts3 := startAccessLogServer(8083)
defer ts3.Close()
// Make some requests
err = try.GetRequest("http://127.0.0.1:8000/test1", 500*time.Millisecond)
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://127.0.0.1:8000/test2", 500*time.Millisecond)
c.Assert(err, checker.IsNil)
err = try.GetRequest("http://127.0.0.1:8000/test2", 500*time.Millisecond)
c.Assert(err, checker.IsNil)
// Verify access.log output as expected
accessLog, err := ioutil.ReadFile(traefikTestAccessLogFile)
c.Assert(err, checker.IsNil)
lines := strings.Split(string(accessLog), "\n")
count := 0 count := 0
for i, line := range lines { for i, line := range lines {
if len(line) > 0 { if len(line) > 0 {
@@ -82,40 +660,109 @@ func (s *AccessLogSuite) TestAccessLog(c *check.C) {
CheckAccessLogFormat(c, line, i) CheckAccessLogFormat(c, line, i)
} }
} }
c.Assert(count, checker.GreaterOrEqualThan, 3) return count
}
// Verify no other Traefik problems func checkAccessLogExactValuesOutput(c *check.C, values []accessLogValue) int {
traefikLog, err = ioutil.ReadFile(traefikTestLogFile) lines := extractLines(c)
count := 0
for i, line := range lines {
fmt.Printf(line)
fmt.Println()
if len(line) > 0 {
count++
if values[i].formatOnly {
CheckAccessLogFormat(c, line, i)
} else {
checkAccessLogExactValues(c, line, i, values[i])
}
}
}
return count
}
func extractLines(c *check.C) []string {
accessLog, err := ioutil.ReadFile(traefikTestAccessLogFile)
c.Assert(err, checker.IsNil)
lines := strings.Split(string(accessLog), "\n")
return lines
}
func checkStatsForLogFile(c *check.C) {
err := try.Do(1*time.Second, func() error {
if _, errStat := os.Stat(traefikTestLogFile); errStat != nil {
return fmt.Errorf("could not get stats for log file: %s", errStat)
}
return nil
})
c.Assert(err, checker.IsNil)
}
func ensureWorkingDirectoryIsClean() {
os.Remove(traefikTestAccessLogFile)
os.Remove(traefikTestLogFile)
}
func checkTraefikStarted(c *check.C) []byte {
traefikLog, err := ioutil.ReadFile(traefikTestLogFile)
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
if len(traefikLog) > 0 { if len(traefikLog) > 0 {
fmt.Printf("%s\n", string(traefikLog)) fmt.Printf("%s\n", string(traefikLog))
c.Assert(traefikLog, checker.HasLen, 0) c.Assert(traefikLog, checker.HasLen, 0)
} }
return traefikLog
} }
func CheckAccessLogFormat(c *check.C, line string, i int) { func CheckAccessLogFormat(c *check.C, line string, i int) {
tokens, err := shellwords.Parse(line) results, err := accesslog.ParseAccessLog(line)
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
c.Assert(tokens, checker.HasLen, 14) c.Assert(results, checker.HasLen, 14)
c.Assert(tokens[6], checker.Matches, `^(-|\d{3})$`) c.Assert(results[accesslog.OriginStatus], checker.Matches, `^(-|\d{3})$`)
c.Assert(tokens[10], checker.Equals, fmt.Sprintf("%d", i+1)) c.Assert(results[accesslog.RequestCount], checker.Equals, fmt.Sprintf("%d", i+1))
c.Assert(tokens[11], checker.HasPrefix, "frontend") c.Assert(results[accesslog.FrontendName], checker.HasPrefix, "\"Host-")
c.Assert(tokens[12], checker.HasPrefix, "http://127.0.0.1:808") c.Assert(results[accesslog.BackendURL], checker.HasPrefix, "\"http://")
c.Assert(tokens[13], checker.Matches, `^\d+ms$`) c.Assert(results[accesslog.Duration], checker.Matches, `^\d+ms$`)
} }
func startAccessLogServer(port int) (ts *httptest.Server) { func checkAccessLogExactValues(c *check.C, line string, i int, v accessLogValue) {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { results, err := accesslog.ParseAccessLog(line)
fmt.Fprintf(w, "Received query %s!\n", r.URL.Path[1:]) c.Assert(err, checker.IsNil)
}) c.Assert(results, checker.HasLen, 14)
if listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port)); err != nil { if len(v.user) > 0 {
panic(err) c.Assert(results[accesslog.ClientUsername], checker.Equals, v.user)
} else { }
ts = &httptest.Server{ c.Assert(results[accesslog.OriginStatus], checker.Equals, v.code)
Listener: listener, c.Assert(results[accesslog.RequestCount], checker.Equals, fmt.Sprintf("%d", i+1))
Config: &http.Server{Handler: handler}, c.Assert(results[accesslog.FrontendName], checker.Matches, `^"?`+v.frontendName+`.*$`)
} c.Assert(results[accesslog.BackendURL], checker.Matches, `^"?`+v.backendName+`.*$`)
ts.Start() c.Assert(results[accesslog.Duration], checker.Matches, `^\d+ms$`)
}
func waitForTraefik(c *check.C, containerName string) {
// Wait for Traefik to turn ready.
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080/api", nil)
c.Assert(err, checker.IsNil)
err = try.Request(req, 2*time.Second, try.StatusCodeIs(http.StatusOK), try.BodyContains(containerName))
c.Assert(err, checker.IsNil)
}
func displayTraefikLogFile(c *check.C, path string) {
if c.Failed() {
if _, err := os.Stat(path); !os.IsNotExist(err) {
content, errRead := ioutil.ReadFile(path)
fmt.Printf("%s: Traefik logs: \n", c.TestName())
if errRead == nil {
fmt.Println(content)
} else {
fmt.Println(errRead)
}
} else {
fmt.Printf("%s: No Traefik logs.\n", c.TestName())
}
errRemove := os.Remove(path)
if errRemove != nil {
fmt.Println(errRemove)
}
} }
return
} }

View File

@@ -32,6 +32,9 @@ const (
// Wildcard domain to check // Wildcard domain to check
wildcardDomain = "*.acme.wtf" wildcardDomain = "*.acme.wtf"
// Traefik default certificate
traefikDefaultDomain = "TRAEFIK DEFAULT CERT"
) )
func (s *AcmeSuite) SetUpSuite(c *check.C) { func (s *AcmeSuite) SetUpSuite(c *check.C) {
@@ -41,7 +44,7 @@ func (s *AcmeSuite) SetUpSuite(c *check.C) {
s.boulderIP = s.composeProject.Container(c, "boulder").NetworkSettings.IPAddress s.boulderIP = s.composeProject.Container(c, "boulder").NetworkSettings.IPAddress
// wait for boulder // wait for boulder
err := try.GetRequest("http://"+s.boulderIP+":4000/directory", 120*time.Second, try.StatusCodeIs(http.StatusOK)) err := try.GetRequest("http://"+s.boulderIP+":4001/directory", 120*time.Second, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
} }
@@ -52,26 +55,46 @@ func (s *AcmeSuite) TearDownSuite(c *check.C) {
} }
} }
// Test OnDemand option with none provided certificate // Test ACME provider with certificate at start
func (s *AcmeSuite) TestOnDemandRetrieveAcmeCertificate(c *check.C) { func (s *AcmeSuite) TestACMEProviderAtStart(c *check.C) {
testCase := AcmeTestCase{ testCase := AcmeTestCase{
traefikConfFilePath: "fixtures/acme/acme.toml", traefikConfFilePath: "fixtures/provideracme/acme.toml",
onDemand: true, onDemand: false,
domainToCheck: acmeDomain} domainToCheck: acmeDomain}
s.retrieveAcmeCertificate(c, testCase) s.retrieveAcmeCertificate(c, testCase)
} }
// Test OnHostRule option with none provided certificate // Test ACME provider with certificate at start
func (s *AcmeSuite) TestOnHostRuleRetrieveAcmeCertificate(c *check.C) { func (s *AcmeSuite) TestACMEProviderAtStartInSAN(c *check.C) {
testCase := AcmeTestCase{ testCase := AcmeTestCase{
traefikConfFilePath: "fixtures/acme/acme.toml", traefikConfFilePath: "fixtures/provideracme/acme_insan.toml",
onDemand: false,
domainToCheck: "acme.wtf"}
s.retrieveAcmeCertificate(c, testCase)
}
// Test ACME provider with certificate at start
func (s *AcmeSuite) TestACMEProviderOnHost(c *check.C) {
testCase := AcmeTestCase{
traefikConfFilePath: "fixtures/provideracme/acme_onhost.toml",
onDemand: false, onDemand: false,
domainToCheck: acmeDomain} domainToCheck: acmeDomain}
s.retrieveAcmeCertificate(c, testCase) s.retrieveAcmeCertificate(c, testCase)
} }
// Test ACME provider with certificate at start and no ACME challenge
func (s *AcmeSuite) TestACMEProviderOnHostWithNoACMEChallenge(c *check.C) {
testCase := AcmeTestCase{
traefikConfFilePath: "fixtures/acme/no_challenge_acme.toml",
onDemand: false,
domainToCheck: traefikDefaultDomain}
s.retrieveAcmeCertificate(c, testCase)
}
// Test OnDemand option with none provided certificate and challenge HTTP-01 // Test OnDemand option with none provided certificate and challenge HTTP-01
func (s *AcmeSuite) TestOnDemandRetrieveAcmeCertificateHTTP01(c *check.C) { func (s *AcmeSuite) TestOnDemandRetrieveAcmeCertificateHTTP01(c *check.C) {
testCase := AcmeTestCase{ testCase := AcmeTestCase{
@@ -142,6 +165,19 @@ func (s *AcmeSuite) TestOnHostRuleRetrieveAcmeCertificateWithDynamicWildcard(c *
s.retrieveAcmeCertificate(c, testCase) s.retrieveAcmeCertificate(c, testCase)
} }
// Test Let's encrypt down
func (s *AcmeSuite) TestNoValidLetsEncryptServer(c *check.C) {
cmd, display := s.traefikCmd(withConfigFile("fixtures/acme/wrong_acme.toml"))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
// Expected traefik works
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 10*time.Second, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
}
// Doing an HTTPS request and test the response certificate // Doing an HTTPS request and test the response certificate
func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase AcmeTestCase) { func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase AcmeTestCase) {
file := s.adaptFile(c, testCase.traefikConfFilePath, struct { file := s.adaptFile(c, testCase.traefikConfFilePath, struct {
@@ -170,8 +206,8 @@ func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase AcmeTestCase) {
// wait for traefik (generating acme account take some seconds) // wait for traefik (generating acme account take some seconds)
err = try.Do(90*time.Second, func() error { err = try.Do(90*time.Second, func() error {
_, err := client.Get("https://127.0.0.1:5001") _, errGet := client.Get("https://127.0.0.1:5001")
return err return errGet
}) })
c.Assert(err, checker.IsNil) c.Assert(err, checker.IsNil)
@@ -203,7 +239,7 @@ func (s *AcmeSuite) retrieveAcmeCertificate(c *check.C, testCase AcmeTestCase) {
cn := resp.TLS.PeerCertificates[0].Subject.CommonName cn := resp.TLS.PeerCertificates[0].Subject.CommonName
if cn != testCase.domainToCheck { if cn != testCase.domainToCheck {
return fmt.Errorf("domain %s found in place of %s", cn, testCase.domainToCheck) return fmt.Errorf("domain %s found instead of %s", cn, testCase.domainToCheck)
} }
return nil return nil

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