mirror of
https://github.com/containous/traefik.git
synced 2025-09-22 13:44:25 +03:00
Compare commits
146 Commits
v1.7.0-rc1
...
v1.7.3
Author | SHA1 | Date | |
---|---|---|---|
|
c875819a2e | ||
|
6d4cf0d892 | ||
|
78a9d20691 | ||
|
7c2409b5a7 | ||
|
0335f6fba9 | ||
|
2c7b7cd6ca | ||
|
5632952665 | ||
|
7eeac63139 | ||
|
1b54f4d32a | ||
|
e8e9dd9400 | ||
|
b722748ec3 | ||
|
609b2630d7 | ||
|
5bdf8a5ea3 | ||
|
7a2592b2fa | ||
|
546bebc860 | ||
|
ad51f4f2a5 | ||
|
a777c3553c | ||
|
157580c232 | ||
|
a9deeb321b | ||
|
ec86149b1e | ||
|
31f92001e2 | ||
|
d69977c229 | ||
|
44e06a1a1e | ||
|
4cb1ae4626 | ||
|
f04813fa02 | ||
|
742029d8a4 | ||
|
f74526a36e | ||
|
61e1836472 | ||
|
8d8e509fe6 | ||
|
147e79ea07 | ||
|
9e26f0b058 | ||
|
8cc3c4a6b7 | ||
|
1d8bdd4384 | ||
|
7033b996c6 | ||
|
0c76a8ac89 | ||
|
d4311f9cf5 | ||
|
6a50a6fd5a | ||
|
29473ef356 | ||
|
1f1ecb15f6 | ||
|
38d655636d | ||
|
9ab5cbf235 | ||
|
f63873cc73 | ||
|
c2938ff138 | ||
|
ab2c98d931 | ||
|
0ae8cd9a9d | ||
|
f3aefe282c | ||
|
a80cca95a2 | ||
|
c52f4b043d | ||
|
253060b4f3 | ||
|
36966da701 | ||
|
bb7c4aaf7e | ||
|
c68ebaa2ca | ||
|
538424b01c | ||
|
48e7a87741 | ||
|
74ace58ae1 | ||
|
913d8737cc | ||
|
b98f5ed8b1 | ||
|
e4bb506ace | ||
|
0f0ba099c9 | ||
|
f400292be7 | ||
|
efc6560d83 | ||
|
56488d435f | ||
|
f586950528 | ||
|
a302731cd1 | ||
|
ef753838e7 | ||
|
acb79d6f73 | ||
|
157c796294 | ||
|
0861c59bec | ||
|
e4a7375d34 | ||
|
6bbac65f7e | ||
|
845f1a7377 | ||
|
9c8e518423 | ||
|
bd3b787fd5 | ||
|
27e4a8a227 | ||
|
cf2d7497e4 | ||
|
df41cd925e | ||
|
e46de74328 | ||
|
feeb7f81a6 | ||
|
2beb5236d0 | ||
|
f062ee80c8 | ||
|
a7bb768e98 | ||
|
07be89d6e9 | ||
|
d81c4e6d1a | ||
|
870755e90d | ||
|
bd3c8c3cde | ||
|
278b3180c3 | ||
|
bb2686a08f | ||
|
202783ca7d | ||
|
308904110a | ||
|
60b4095c75 | ||
|
d04b4fa2cc | ||
|
2d449f63e0 | ||
|
7ff6e6b66f | ||
|
bb33128552 | ||
|
86add29838 | ||
|
70712a0f62 | ||
|
4db937b571 | ||
|
ad6f41c77a | ||
|
e6040e55f5 | ||
|
b4ac3d4470 | ||
|
d62f7e2082 | ||
|
cfe2f1a1e6 | ||
|
7732e2307e | ||
|
8c733abef3 | ||
|
4d79c2a6d2 | ||
|
ed0c7d9c49 | ||
|
fb4717d5f3 | ||
|
09b489a614 | ||
|
402f7011d4 | ||
|
838dd8c19f | ||
|
91cafd1752 | ||
|
eea60b6baa | ||
|
baf8d63cb4 | ||
|
967e4208da | ||
|
ba3a579d07 | ||
|
7d2b7cd7f1 | ||
|
73b4df4e18 | ||
|
37aa902cef | ||
|
bafb583666 | ||
|
aabebb2185 | ||
|
c8ae97fd38 | ||
|
d50b6a34bc | ||
|
853be929bc | ||
|
a1911a9608 | ||
|
ff2e2d5026 | ||
|
a953d3ad89 | ||
|
9ce444b91a | ||
|
ae8be89767 | ||
|
5774d100c1 | ||
|
dbe720f0f1 | ||
|
5afc8f2b12 | ||
|
c7e008f57a | ||
|
14b7152bf0 | ||
|
3ef6bf2118 | ||
|
f0ab2721a5 | ||
|
2721c2017c | ||
|
a7c158f0e1 | ||
|
7ff9193cf5 | ||
|
031451abab | ||
|
8d75aba7eb | ||
|
027093a5a5 | ||
|
bdc0e3bfcf | ||
|
b2a57ca1f3 | ||
|
6ef0e6791b | ||
|
9374d6b3b9 | ||
|
f173ff02e3 |
@@ -16,6 +16,7 @@ env:
|
||||
|
||||
script:
|
||||
- echo "Skipping tests... (Tests are executed on SemaphoreCI)"
|
||||
- if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then make docs-verify; fi
|
||||
|
||||
before_deploy:
|
||||
- >
|
||||
|
354
CHANGELOG.md
354
CHANGELOG.md
@@ -1,5 +1,359 @@
|
||||
# Change Log
|
||||
|
||||
## [v1.7.3](https://github.com/containous/traefik/tree/v1.7.3) (2018-10-15)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.2...v1.7.3)
|
||||
|
||||
**Enhancements:**
|
||||
- Improve the CLI help ([#3996](https://github.com/containous/traefik/pull/3996) by [dduportal](https://github.com/dduportal))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** DNS challenge Cloudflare auth zone ([#4042](https://github.com/containous/traefik/pull/4042) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** ACME DNS challenges ([#3998](https://github.com/containous/traefik/pull/3998) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Don't initalize ACME provider if storage is empty ([#3988](https://github.com/containous/traefik/pull/3988) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Fix: acme DNS providers ([#4021](https://github.com/containous/traefik/pull/4021) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Prevent some malformed errors in LE. ([#4015](https://github.com/containous/traefik/pull/4015) by [ldez](https://github.com/ldez))
|
||||
- **[authentication,consulcatalog,docker,ecs,etcd,kv,marathon,mesos,rancher]** Add the AuthResponseHeaders to the labels ([#3973](https://github.com/containous/traefik/pull/3973) by [Crypto89](https://github.com/Crypto89))
|
||||
- **[docker]** usebindportip can fall back on the container ip / port ([#4018](https://github.com/containous/traefik/pull/4018) by [geraldcroes](https://github.com/geraldcroes))
|
||||
- **[k8s]** Avoid flapping of multiple Ingress definitions ([#3862](https://github.com/containous/traefik/pull/3862) by [rtreffer](https://github.com/rtreffer))
|
||||
- **[middleware,server]** Log stack on panic ([#4033](https://github.com/containous/traefik/pull/4033) by [ldez](https://github.com/ldez))
|
||||
- **[middleware,server]** Fix recover from panic handler ([#4031](https://github.com/containous/traefik/pull/4031) by [mmatur](https://github.com/mmatur))
|
||||
- **[server,websocket]** Fix update oxy ([#4009](https://github.com/containous/traefik/pull/4009) by [mmatur](https://github.com/mmatur))
|
||||
|
||||
**Documentation:**
|
||||
- **[docker]** Add tags label to Docker provider documentation ([#3896](https://github.com/containous/traefik/pull/3896) by [artheus](https://github.com/artheus))
|
||||
- **[docker]** Added two examples with labels in docker-compose.yml ([#3891](https://github.com/containous/traefik/pull/3891) by [pascalandy](https://github.com/pascalandy))
|
||||
- **[k8s]** Move buffering annotation documentation to service ([#3991](https://github.com/containous/traefik/pull/3991) by [ldez](https://github.com/ldez))
|
||||
- Fix a typo ([#3995](https://github.com/containous/traefik/pull/3995) by [arnydo](https://github.com/arnydo))
|
||||
|
||||
## [v1.7.2](https://github.com/containous/traefik/tree/v1.7.2) (2018-10-04)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.1...v1.7.2)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme,cluster,kv]** TLS, ACME, cluster and several entrypoints. ([#3962](https://github.com/containous/traefik/pull/3962) by [ldez](https://github.com/ldez))
|
||||
- **[cluster,kv]** Correctly initialize kv store if storage key missing ([#3958](https://github.com/containous/traefik/pull/3958) by [jfrabaute](https://github.com/jfrabaute))
|
||||
- **[cluster,kv]** Return an error if kv store CA cert is invalid ([#3956](https://github.com/containous/traefik/pull/3956) by [jfrabaute](https://github.com/jfrabaute))
|
||||
- **[file]** Do not Errorf during file watcher verification test loop. ([#3938](https://github.com/containous/traefik/pull/3938) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[k8s]** Add Template-ability check to Kubernetes API Fields ([#3964](https://github.com/containous/traefik/pull/3964) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[logs]** Colored logs on windows. ([#3966](https://github.com/containous/traefik/pull/3966) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Whitelist log for deprecated configuration. ([#3963](https://github.com/containous/traefik/pull/3963) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Trimming whitespace in XFF for IP whitelisting ([#3971](https://github.com/containous/traefik/pull/3971) by [olmoser](https://github.com/olmoser))
|
||||
- **[rules]** Rule parsing error. ([#3976](https://github.com/containous/traefik/pull/3976) by [ldez](https://github.com/ldez))
|
||||
- Global configuration log at start ([#3954](https://github.com/containous/traefik/pull/3954) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- **[logs]** Document the default accessLog format ([#3942](https://github.com/containous/traefik/pull/3942) by [dfredell](https://github.com/dfredell))
|
||||
|
||||
## [v1.7.1](https://github.com/containous/traefik/tree/v1.7.1) (2018-09-28)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.0...v1.7.1)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme,cluster]** Don't remove static certs from config when cluster mode ([#3946](https://github.com/containous/traefik/pull/3946) by [Juliens](https://github.com/Juliens))
|
||||
- **[acme]** Fix TLS ALPN cluster mode. ([#3934](https://github.com/containous/traefik/pull/3934) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Don't challenge ACME when host rule on another entry point ([#3923](https://github.com/containous/traefik/pull/3923) by [Juliens](https://github.com/Juliens))
|
||||
- **[tls]** Use the first static certificate as a fallback when no default is given ([#3948](https://github.com/containous/traefik/pull/3948) by [Juliens](https://github.com/Juliens))
|
||||
|
||||
## [v1.7.0](https://github.com/containous/traefik/tree/v1.7.0) (2018-09-24)
|
||||
[Commits](https://github.com/containous/traefik/compare/v1.7.0-rc1...v1.7.0)
|
||||
[Commits pre RC](https://github.com/containous/traefik/compare/v1.6.0-rc1...v1.7.0-rc1)
|
||||
|
||||
**Enhancements:**
|
||||
- **[acme]** Simplify get acme client ([#3499](https://github.com/containous/traefik/pull/3499) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Simplify acme e2e tests. ([#3534](https://github.com/containous/traefik/pull/3534) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Add option to select algorithm to generate ACME certificates ([#3319](https://github.com/containous/traefik/pull/3319) by [mmatur](https://github.com/mmatur))
|
||||
- **[acme]** Enable to override certificates in key-value store when using storeconfig ([#3202](https://github.com/containous/traefik/pull/3202) by [thomasjpfan](https://github.com/thomasjpfan))
|
||||
- **[acme]** ACME TLS ALPN ([#3553](https://github.com/containous/traefik/pull/3553) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Remove acme provider dependency in server ([#3225](https://github.com/containous/traefik/pull/3225) by [Juliens](https://github.com/Juliens))
|
||||
- **[acme]** Use official Pebble Image. ([#3708](https://github.com/containous/traefik/pull/3708) by [ldez](https://github.com/ldez))
|
||||
- **[api,cluster]** Improved cluster api to include the current leader node ([#3100](https://github.com/containous/traefik/pull/3100) by [aantono](https://github.com/aantono))
|
||||
- **[authentication,consul,consulcatalog,docker,ecs,kv,marathon,mesos,rancher]** Auth support in frontends ([#3559](https://github.com/containous/traefik/pull/3559) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[authentication,k8s]** Auth support in frontends for k8s and file ([#3460](https://github.com/containous/traefik/pull/3460) by [Zatte](https://github.com/Zatte))
|
||||
- **[authentication,middleware]** Add xforwarded method ([#3424](https://github.com/containous/traefik/pull/3424) by [erik-sjoestedt](https://github.com/erik-sjoestedt))
|
||||
- **[authentication,middleware]** Forward auth headers ([#3521](https://github.com/containous/traefik/pull/3521) by [hwhelan-CB](https://github.com/hwhelan-CB))
|
||||
- **[consul,etcd,tls]** Improve TLS integration tests ([#3679](https://github.com/containous/traefik/pull/3679) by [mmatur](https://github.com/mmatur))
|
||||
- **[consulcatalog,docker,ecs,file,k8s,kv,marathon,mesos,rancher]** Add SSLForceHost support. ([#3246](https://github.com/containous/traefik/pull/3246) by [ldez](https://github.com/ldez))
|
||||
- **[consulcatalog]** Multiple frontends for consulcatalog ([#3796](https://github.com/containous/traefik/pull/3796) by [hsmade](https://github.com/hsmade))
|
||||
- **[consulcatalog]** Add support for stale reads from Consul catalog ([#3523](https://github.com/containous/traefik/pull/3523) by [marenzo](https://github.com/marenzo))
|
||||
- **[docker]** Add a default value for the docker.network configuration ([#3471](https://github.com/containous/traefik/pull/3471) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[ecs]** Support for AWS ECS Fargate ([#3379](https://github.com/containous/traefik/pull/3379) by [mmatur](https://github.com/mmatur))
|
||||
- **[ecs]** Add support for ECS constraints ([#3537](https://github.com/containous/traefik/pull/3537) by [andrewstucki](https://github.com/andrewstucki))
|
||||
- **[ecs]** Add segment support for ECS ([#3817](https://github.com/containous/traefik/pull/3817) by [mmatur](https://github.com/mmatur))
|
||||
- **[ecs]** Support `traefik.backend` for ECS ([#3510](https://github.com/containous/traefik/pull/3510) by [hwhelan-CB](https://github.com/hwhelan-CB))
|
||||
- **[ecs]** Allow binding ECS container port ([#3533](https://github.com/containous/traefik/pull/3533) by [andrewstucki](https://github.com/andrewstucki))
|
||||
- **[healthcheck,consul,consulcatalog,docker,ecs,kv,marathon,mesos,rancher]** Override health check scheme ([#3315](https://github.com/containous/traefik/pull/3315) by [ldez](https://github.com/ldez))
|
||||
- **[healthcheck]** Support 3xx HTTP status codes for health check ([#3364](https://github.com/containous/traefik/pull/3364) by [SniperCZE](https://github.com/SniperCZE))
|
||||
- **[healthcheck]** Support all 2xx HTTP status code for health check. ([#3362](https://github.com/containous/traefik/pull/3362) by [ldez](https://github.com/ldez))
|
||||
- **[healthcheck]** Add HTTP headers to healthcheck. ([#3047](https://github.com/containous/traefik/pull/3047) by [zetaab](https://github.com/zetaab))
|
||||
- **[k8s]** Add more k8s tests ([#3491](https://github.com/containous/traefik/pull/3491) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Substitude hardcoded "<namespace>/<name>" with k8s ListerGetter ([#3470](https://github.com/containous/traefik/pull/3470) by [yue9944882](https://github.com/yue9944882))
|
||||
- **[k8s]** Custom frontend name for test helper ([#3444](https://github.com/containous/traefik/pull/3444) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Add annotation to allow modifiers to be used properly in kubernetes ([#3481](https://github.com/containous/traefik/pull/3481) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Create Global Backend Ingress ([#3404](https://github.com/containous/traefik/pull/3404) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Specify backend servers' weight via annotation for kubernetes ([#3112](https://github.com/containous/traefik/pull/3112) by [yue9944882](https://github.com/yue9944882))
|
||||
- **[k8s]** Support multi-port services. ([#3121](https://github.com/containous/traefik/pull/3121) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[k8s]** Mapping ExternalNames to custom ports ([#3231](https://github.com/containous/traefik/pull/3231) by [gildas](https://github.com/gildas))
|
||||
- **[k8s]** Allow any kubernetes ingressClass value ([#3516](https://github.com/containous/traefik/pull/3516) by [rtreffer](https://github.com/rtreffer))
|
||||
- **[k8s]** Enable Ingress Status updates ([#3324](https://github.com/containous/traefik/pull/3324) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Add possibility to set a protocol ([#3648](https://github.com/containous/traefik/pull/3648) by [SantoDE](https://github.com/SantoDE))
|
||||
- **[k8s]** Remove unnecessary loop ([#3799](https://github.com/containous/traefik/pull/3799) by [ZloyDyadka](https://github.com/ZloyDyadka))
|
||||
- **[kv]** Use index-based syntax in KV tests. ([#3352](https://github.com/containous/traefik/pull/3352) by [ldez](https://github.com/ldez))
|
||||
- **[logs,middleware]** Make accesslogs.logTheRoundTrip async to get lost performance ([#3152](https://github.com/containous/traefik/pull/3152) by [ryarnyah](https://github.com/ryarnyah))
|
||||
- **[logs,middleware]** Added duration filter for logs ([#3463](https://github.com/containous/traefik/pull/3463) by [rodrigodiez](https://github.com/rodrigodiez))
|
||||
- **[marathon]** Sane default and configurable Marathon request timeouts ([#3286](https://github.com/containous/traefik/pull/3286) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[marathon]** Adding compatibility for marathon 1.5 ([#3505](https://github.com/containous/traefik/pull/3505) by [TrevinTeacutter](https://github.com/TrevinTeacutter))
|
||||
- **[mesos]** Segments Labels: Mesos ([#3383](https://github.com/containous/traefik/pull/3383) by [drewkerrigan](https://github.com/drewkerrigan))
|
||||
- **[metrics]** Metrics: Add support for InfluxDB Database / RetentionPolicy and HTTP client ([#3391](https://github.com/containous/traefik/pull/3391) by [drewkerrigan](https://github.com/drewkerrigan))
|
||||
- **[middleware,consulcatalog,docker,ecs,kv,marathon,mesos,rancher]** Pass the TLS Cert infos in headers ([#3826](https://github.com/containous/traefik/pull/3826) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[middleware,server]** Extreme Makeover: server refactoring ([#3461](https://github.com/containous/traefik/pull/3461) by [ldez](https://github.com/ldez))
|
||||
- **[middleware,tracing]** Added integration support for DataDog APM Tracing ([#3517](https://github.com/containous/traefik/pull/3517) by [aantono](https://github.com/aantono))
|
||||
- **[middleware,tracing]** Create a custom logger for jaeger ([#3541](https://github.com/containous/traefik/pull/3541) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware]** Performance enhancements for the rules matchers. ([#3563](https://github.com/containous/traefik/pull/3563) by [ShaneSaww](https://github.com/ShaneSaww))
|
||||
- **[middleware]** Extract internal router creation from server ([#3204](https://github.com/containous/traefik/pull/3204) by [Juliens](https://github.com/Juliens))
|
||||
- **[rules]** CNAME flattening ([#3403](https://github.com/containous/traefik/pull/3403) by [gamalan](https://github.com/gamalan))
|
||||
- **[servicefabric]** Add HTTP headers to healthcheck. ([#3205](https://github.com/containous/traefik/pull/3205) by [ldez](https://github.com/ldez))
|
||||
- **[tls]** Support TLS MinVersion and CipherSuite as CLI option. ([#3107](https://github.com/containous/traefik/pull/3107) by [ldez](https://github.com/ldez))
|
||||
- **[tls]** Improve TLS Handshake ([#3512](https://github.com/containous/traefik/pull/3512) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[webui]** Add some missing elements in the WebUI ([#3327](https://github.com/containous/traefik/pull/3327) by [ldez](https://github.com/ldez))
|
||||
- Call functions to enable block/mutex pprof profiles. ([#3564](https://github.com/containous/traefik/pull/3564) by [timoreimann](https://github.com/timoreimann))
|
||||
- Minor changes ([#3554](https://github.com/containous/traefik/pull/3554) by [ldez](https://github.com/ldez))
|
||||
- Generated assets file are only mandatory in main ([#3386](https://github.com/containous/traefik/pull/3386) by [Juliens](https://github.com/Juliens))
|
||||
- h2c server ([#3387](https://github.com/containous/traefik/pull/3387) by [Juliens](https://github.com/Juliens))
|
||||
- Fix backend reuse ([#3312](https://github.com/containous/traefik/pull/3312) by [arnested](https://github.com/arnested))
|
||||
- Upgrade GRPC dependencies ([#3342](https://github.com/containous/traefik/pull/3342) by [gottwald](https://github.com/gottwald))
|
||||
- Implement h2c with backend ([#3371](https://github.com/containous/traefik/pull/3371) by [Juliens](https://github.com/Juliens))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme,cluster]** StoreConfig always initializes the account if it is missing ([#3844](https://github.com/containous/traefik/pull/3844) by [geraldcroes](https://github.com/geraldcroes))
|
||||
- **[acme,provider]** Create init method on provider interface ([#3580](https://github.com/containous/traefik/pull/3580) by [Juliens](https://github.com/Juliens))
|
||||
- **[acme]** Does not generate ACME certificate if domain is checked by dynamic certificate ([#3238](https://github.com/containous/traefik/pull/3238) by [Juliens](https://github.com/Juliens))
|
||||
- **[acme]** Ensure only certificates from ACME enabled entrypoint are used ([#3880](https://github.com/containous/traefik/pull/3880) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[acme]** Fix acme account deletion without provider change ([#3664](https://github.com/containous/traefik/pull/3664) by [zyclonite](https://github.com/zyclonite))
|
||||
- **[acme]** Fix some DNS providers issues ([#3915](https://github.com/containous/traefik/pull/3915) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Fix LEGO update ([#3895](https://github.com/containous/traefik/pull/3895) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Set a keyType to ACME if the account is stored with no KeyType ([#3733](https://github.com/containous/traefik/pull/3733) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Fix ACME certificate for wildcard and root domains ([#3675](https://github.com/containous/traefik/pull/3675) by [nmengin](https://github.com/nmengin))
|
||||
- **[acme]** Update lego ([#3659](https://github.com/containous/traefik/pull/3659) by [mmatur](https://github.com/mmatur))
|
||||
- **[acme]** Bump LEGO version ([#3888](https://github.com/containous/traefik/pull/3888) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Serve TLS-Challenge certificate in first ([#3605](https://github.com/containous/traefik/pull/3605) by [nmengin](https://github.com/nmengin))
|
||||
- **[api,authentication,webui]** Auth section in web UI. ([#3628](https://github.com/containous/traefik/pull/3628) by [ldez](https://github.com/ldez))
|
||||
- **[api]** Remove TLS in API ([#3665](https://github.com/containous/traefik/pull/3665) by [mmatur](https://github.com/mmatur))
|
||||
- **[authentication,consulcatalog,docker,ecs,k8s,kv,marathon,mesos,rancher]** Auth Forward with certificates in templates. ([#3804](https://github.com/containous/traefik/pull/3804) by [ldez](https://github.com/ldez))
|
||||
- **[authentication,middleware,provider]** Don't pass the Authorization header to the backends ([#3606](https://github.com/containous/traefik/pull/3606) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[authentication,middleware]** Do not copy hop-by-hop headers to forward auth request ([#3907](https://github.com/containous/traefik/pull/3907) by [stffabi](https://github.com/stffabi))
|
||||
- **[authentication,middleware]** Remove hop-by-hop headers from forward auth response ([#3900](https://github.com/containous/traefik/pull/3900) by [stffabi](https://github.com/stffabi))
|
||||
- **[docker]** Uses both binded HostIP and HostPort when useBindPortIP=true ([#3638](https://github.com/containous/traefik/pull/3638) by [geraldcroes](https://github.com/geraldcroes))
|
||||
- **[ecs]** Fix 400 bad request on AWS ECS API ([#3629](https://github.com/containous/traefik/pull/3629) by [mmatur](https://github.com/mmatur))
|
||||
- **[k8s]** Fix Rewrite-target regex ([#3699](https://github.com/containous/traefik/pull/3699) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Don't merge kubernetes ingresses when priority is set ([#3743](https://github.com/containous/traefik/pull/3743) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Prevent unparsable strings from being rendered in the Kubernetes template ([#3753](https://github.com/containous/traefik/pull/3753) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Correct App-Root kubernetes behavior ([#3592](https://github.com/containous/traefik/pull/3592) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Add more K8s Unit Tests ([#3583](https://github.com/containous/traefik/pull/3583) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Fix rewrite-target Annotation behavior ([#3582](https://github.com/containous/traefik/pull/3582) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Fix panic setting ingress status ([#3492](https://github.com/containous/traefik/pull/3492) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[kv]** KV and authentication ([#3615](https://github.com/containous/traefik/pull/3615) by [ldez](https://github.com/ldez))
|
||||
- **[kv]** Add missing quotes around backendName in kv template ([#3885](https://github.com/containous/traefik/pull/3885) by [NatMarchand](https://github.com/NatMarchand))
|
||||
- **[kv]** Include missing key in error message for KV store ([#3779](https://github.com/containous/traefik/pull/3779) by [camelpunch](https://github.com/camelpunch))
|
||||
- **[logs]** Add logs when error is generated in error handler ([#3571](https://github.com/containous/traefik/pull/3571) by [Juliens](https://github.com/Juliens))
|
||||
- **[logs]** Add interface to Træfik logger ([#3889](https://github.com/containous/traefik/pull/3889) by [nmengin](https://github.com/nmengin))
|
||||
- **[metrics]** Avoid a panic during Prometheus registering ([#3717](https://github.com/containous/traefik/pull/3717) by [nmengin](https://github.com/nmengin))
|
||||
- **[middleware,tracing]** Fix tracing duplicated headers ([#3878](https://github.com/containous/traefik/pull/3878) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware,websocket]** Enable retry on websocket ([#3825](https://github.com/containous/traefik/pull/3825) by [Juliens](https://github.com/Juliens))
|
||||
- **[middleware]** Avoid retries when any data was written to the backend ([#3285](https://github.com/containous/traefik/pull/3285) by [marco-jantke](https://github.com/marco-jantke))
|
||||
- **[middleware]** Extend https redirection tests, and fix incorrect behavior ([#3742](https://github.com/containous/traefik/pull/3742) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[middleware]** Send 'Retry-After' to comply with RFC6585. ([#3593](https://github.com/containous/traefik/pull/3593) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Correct Entrypoint Redirect with Stripped or Added Path ([#3631](https://github.com/containous/traefik/pull/3631) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[middleware]** Fix error pages ([#3894](https://github.com/containous/traefik/pull/3894) by [Juliens](https://github.com/Juliens))
|
||||
- **[oxy]** Handle Te header when http2 ([#3824](https://github.com/containous/traefik/pull/3824) by [Juliens](https://github.com/Juliens))
|
||||
- **[server]** Avoid goroutine leak in server ([#3851](https://github.com/containous/traefik/pull/3851) by [nmengin](https://github.com/nmengin))
|
||||
- **[server]** Avoid panic during stop ([#3898](https://github.com/containous/traefik/pull/3898) by [nmengin](https://github.com/nmengin))
|
||||
- **[tracing]** Added default configuration for DataDog APM Tracer ([#3655](https://github.com/containous/traefik/pull/3655) by [aantono](https://github.com/aantono))
|
||||
- **[tracing]** Added support for Trace name truncation for traces ([#3689](https://github.com/containous/traefik/pull/3689) by [aantono](https://github.com/aantono))
|
||||
- **[websocket]** Handle shutdown of Hijacked connections ([#3636](https://github.com/containous/traefik/pull/3636) by [Juliens](https://github.com/Juliens))
|
||||
- **[webui]** Added Dashboard table item for Rate Limits ([#3893](https://github.com/containous/traefik/pull/3893) by [codecyclist](https://github.com/codecyclist))
|
||||
- Fix logger in Oxy ([#3913](https://github.com/containous/traefik/pull/3913) by [ldez](https://github.com/ldez))
|
||||
- H2C: Remove buggy line in init to make verbose switch working ([#3701](https://github.com/containous/traefik/pull/3701) by [dduportal](https://github.com/dduportal))
|
||||
- Updating oxy dependency ([#3700](https://github.com/containous/traefik/pull/3700) by [crholm](https://github.com/crholm))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** Update ACME documentation about TLS-ALPN challenge ([#3756](https://github.com/containous/traefik/pull/3756) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Fix some DNS provider link ([#3639](https://github.com/containous/traefik/pull/3639) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Fix documentation for route53 acme provider ([#3811](https://github.com/containous/traefik/pull/3811) by [A-Shleifman](https://github.com/A-Shleifman))
|
||||
- **[acme]** Update Namecheap status ([#3604](https://github.com/containous/traefik/pull/3604) by [stoinov](https://github.com/stoinov))
|
||||
- **[docker]** Fix style in examples/quickstart ([#3705](https://github.com/containous/traefik/pull/3705) by [korigod](https://github.com/korigod))
|
||||
- **[docker]** Change syntax in quick start guide ([#3726](https://github.com/containous/traefik/pull/3726) by [trotro](https://github.com/trotro))
|
||||
- **[docker]** Typo in docker-and-lets-encrypt.md ([#3724](https://github.com/containous/traefik/pull/3724) by [A-Shleifman](https://github.com/A-Shleifman))
|
||||
- **[docker]** Improve the wording in the documentation for Docker and fix title for Docker User Guide ([#3797](https://github.com/containous/traefik/pull/3797) by [dduportal](https://github.com/dduportal))
|
||||
- **[k8s]** Add a k8s guide section on traffic splitting via service weights. ([#3556](https://github.com/containous/traefik/pull/3556) by [timoreimann](https://github.com/timoreimann))
|
||||
- **[k8s]** Change code block of traefik-web-ui to match file ([#3542](https://github.com/containous/traefik/pull/3542) by [drewgwallace](https://github.com/drewgwallace))
|
||||
- **[k8s]** Fix typo which breaks k8s example manifest ([#3441](https://github.com/containous/traefik/pull/3441) by [GeertJohan](https://github.com/GeertJohan))
|
||||
- **[k8s]** Correct Modifier in Kubernetes Documentation ([#3610](https://github.com/containous/traefik/pull/3610) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Improve Connection Limit Kubernetes Documentation ([#3711](https://github.com/containous/traefik/pull/3711) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Add traefik prefix to k8s annotations ([#3682](https://github.com/containous/traefik/pull/3682) by [zifeo](https://github.com/zifeo))
|
||||
- **[k8s]** Update kubernetes docs to reflect https options ([#3807](https://github.com/containous/traefik/pull/3807) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Update kubernetes.md ([#3719](https://github.com/containous/traefik/pull/3719) by [kmaris](https://github.com/kmaris))
|
||||
- **[metrics]** Adding grafana dashboards based on prometheus metrics ([#3393](https://github.com/containous/traefik/pull/3393) by [deimosfr](https://github.com/deimosfr))
|
||||
- **[middleware,tracing]** Fix missing tracing backend in documentation ([#3706](https://github.com/containous/traefik/pull/3706) by [mmatur](https://github.com/mmatur))
|
||||
- **[provider]** Typo in auth labels. ([#3730](https://github.com/containous/traefik/pull/3730) by [ldez](https://github.com/ldez))
|
||||
- **[servicefabric]** Fix Service Fabric docs to use v1.6 labels ([#3209](https://github.com/containous/traefik/pull/3209) by [jjcollinge](https://github.com/jjcollinge))
|
||||
- **[tracing]** Simple documentation grammar update in tracing ([#3720](https://github.com/containous/traefik/pull/3720) by [loadstar81](https://github.com/loadstar81))
|
||||
- Replace unrendered emoji ([#3690](https://github.com/containous/traefik/pull/3690) by [korigod](https://github.com/korigod))
|
||||
- Make the "base domain" on all providers ([#3835](https://github.com/containous/traefik/pull/3835) by [dduportal](https://github.com/dduportal))
|
||||
- Prepare release v1.7.0-rc5 ([#3902](https://github.com/containous/traefik/pull/3902) by [dduportal](https://github.com/dduportal))
|
||||
- Prepare release v1.7.0-rc3 ([#3709](https://github.com/containous/traefik/pull/3709) by [mmatur](https://github.com/mmatur))
|
||||
- Prepare release v1.7.0-rc4 ([#3864](https://github.com/containous/traefik/pull/3864) by [Juliens](https://github.com/Juliens))
|
||||
- Prepare release v1.7.0-rc2 ([#3632](https://github.com/containous/traefik/pull/3632) by [nmengin](https://github.com/nmengin))
|
||||
- Prepare release v1.7.0-rc1 ([#3578](https://github.com/containous/traefik/pull/3578) by [mmatur](https://github.com/mmatur))
|
||||
|
||||
**Misc:**
|
||||
- **[webui]** Removed non-applicable default tests and fixed custom tests ([#3908](https://github.com/containous/traefik/pull/3908) by [codecyclist](https://github.com/codecyclist))
|
||||
- Merge v1.6.6 into v1.7 ([#3802](https://github.com/containous/traefik/pull/3802) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.5 into v1.7 ([#3595](https://github.com/containous/traefik/pull/3595) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.4 into master ([#3502](https://github.com/containous/traefik/pull/3502) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.3 into master ([#3439](https://github.com/containous/traefik/pull/3439) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.2 into master ([#3367](https://github.com/containous/traefik/pull/3367) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.1 into master ([#3326](https://github.com/containous/traefik/pull/3326) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.0 into master ([#3253](https://github.com/containous/traefik/pull/3253) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.0-rc6 into master ([#3203](https://github.com/containous/traefik/pull/3203) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.0-rc5 into master ([#3180](https://github.com/containous/traefik/pull/3180) by [ldez](https://github.com/ldez))
|
||||
- Merge v1.6.0-rc4 into master ([#3129](https://github.com/containous/traefik/pull/3129) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v1.7.0-rc5](https://github.com/containous/traefik/tree/v1.7.0-rc5) (2018-09-18)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.0-rc4...v1.7.0-rc5)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Ensure only certificates from ACME enabled entrypoint are used ([#3880](https://github.com/containous/traefik/pull/3880) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[acme]** Fix LEGO update ([#3895](https://github.com/containous/traefik/pull/3895) by [ldez](https://github.com/ldez))
|
||||
- **[acme]** Bump LEGO version ([#3888](https://github.com/containous/traefik/pull/3888) by [ldez](https://github.com/ldez))
|
||||
- **[authentication,middleware]** Remove hop-by-hop headers from forward auth response ([#3900](https://github.com/containous/traefik/pull/3900) by [stffabi](https://github.com/stffabi))
|
||||
- **[kv]** Add missing quotes around backendName in kv template ([#3885](https://github.com/containous/traefik/pull/3885) by [NatMarchand](https://github.com/NatMarchand))
|
||||
- **[logs]** Add interface to Træfik logger ([#3889](https://github.com/containous/traefik/pull/3889) by [nmengin](https://github.com/nmengin))
|
||||
- **[middleware,tracing]** Fix tracing duplicated headers ([#3878](https://github.com/containous/traefik/pull/3878) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware]** Fix error pages ([#3894](https://github.com/containous/traefik/pull/3894) by [Juliens](https://github.com/Juliens))
|
||||
- **[server]** Avoid panic during stop ([#3898](https://github.com/containous/traefik/pull/3898) by [nmengin](https://github.com/nmengin))
|
||||
|
||||
## [v1.7.0-rc4](https://github.com/containous/traefik/tree/v1.7.0-rc4) (2018-09-07)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.0-rc3...v1.7.0-rc4)
|
||||
|
||||
**Enhancements:**
|
||||
- **[acme]** Use official Pebble Image. ([#3708](https://github.com/containous/traefik/pull/3708) by [ldez](https://github.com/ldez))
|
||||
- **[consulcatalog]** Multiple frontends for consulcatalog ([#3796](https://github.com/containous/traefik/pull/3796) by [hsmade](https://github.com/hsmade))
|
||||
- **[ecs]** Add segment support for ECS ([#3817](https://github.com/containous/traefik/pull/3817) by [mmatur](https://github.com/mmatur))
|
||||
- **[k8s]** Remove unnecessary loop ([#3799](https://github.com/containous/traefik/pull/3799) by [ZloyDyadka](https://github.com/ZloyDyadka))
|
||||
- **[middleware,consulcatalog,docker,ecs,kv,marathon,mesos,rancher]** Pass the TLS Cert infos in headers ([#3826](https://github.com/containous/traefik/pull/3826) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme,cluster]** StoreConfig always initializes the account if it is missing ([#3844](https://github.com/containous/traefik/pull/3844) by [geraldcroes](https://github.com/geraldcroes))
|
||||
- **[acme]** Set a keyType to ACME if the account is stored with no KeyType ([#3733](https://github.com/containous/traefik/pull/3733) by [nmengin](https://github.com/nmengin))
|
||||
- **[authentication,consulcatalog,docker,ecs,k8s,kv,marathon,mesos,rancher]** Auth Forward with certificates in templates. ([#3804](https://github.com/containous/traefik/pull/3804) by [ldez](https://github.com/ldez))
|
||||
- **[k8s]** Prevent unparsable strings from being rendered in the Kubernetes template ([#3753](https://github.com/containous/traefik/pull/3753) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Don't merge kubernetes ingresses when priority is set ([#3743](https://github.com/containous/traefik/pull/3743) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[kv]** Include missing key in error message for KV store ([#3779](https://github.com/containous/traefik/pull/3779) by [camelpunch](https://github.com/camelpunch))
|
||||
- **[metrics]** Avoid a panic during Prometheus registering ([#3717](https://github.com/containous/traefik/pull/3717) by [nmengin](https://github.com/nmengin))
|
||||
- **[middleware,websocket]** Enable retry on websocket ([#3825](https://github.com/containous/traefik/pull/3825) by [Juliens](https://github.com/Juliens))
|
||||
- **[middleware]** Extend https redirection tests, and fix incorrect behavior ([#3742](https://github.com/containous/traefik/pull/3742) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[oxy]** Handle Te header when http2 ([#3824](https://github.com/containous/traefik/pull/3824) by [Juliens](https://github.com/Juliens))
|
||||
- **[server]** Avoid goroutine leak in server ([#3851](https://github.com/containous/traefik/pull/3851) by [nmengin](https://github.com/nmengin))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** Fix documentation for route53 acme provider ([#3811](https://github.com/containous/traefik/pull/3811) by [A-Shleifman](https://github.com/A-Shleifman))
|
||||
- **[acme]** Update ACME documentation about TLS-ALPN challenge ([#3756](https://github.com/containous/traefik/pull/3756) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Change syntax in quick start guide ([#3726](https://github.com/containous/traefik/pull/3726) by [trotro](https://github.com/trotro))
|
||||
- **[docker]** Improve the wording in the documentation for Docker and fix title for Docker User Guide ([#3797](https://github.com/containous/traefik/pull/3797) by [dduportal](https://github.com/dduportal))
|
||||
- **[docker]** Typo in docker-and-lets-encrypt.md ([#3724](https://github.com/containous/traefik/pull/3724) by [A-Shleifman](https://github.com/A-Shleifman))
|
||||
- **[k8s]** Update kubernetes docs to reflect https options ([#3807](https://github.com/containous/traefik/pull/3807) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Update kubernetes.md ([#3719](https://github.com/containous/traefik/pull/3719) by [kmaris](https://github.com/kmaris))
|
||||
- **[k8s]** Improve Connection Limit Kubernetes Documentation ([#3711](https://github.com/containous/traefik/pull/3711) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[provider]** Typo in auth labels. ([#3730](https://github.com/containous/traefik/pull/3730) by [ldez](https://github.com/ldez))
|
||||
- **[tracing]** Simple documentation grammar update in tracing ([#3720](https://github.com/containous/traefik/pull/3720) by [loadstar81](https://github.com/loadstar81))
|
||||
- Make the "base domain" on all providers ([#3835](https://github.com/containous/traefik/pull/3835) by [dduportal](https://github.com/dduportal))
|
||||
|
||||
**Misc:**
|
||||
- Merge v1.6.6 into v1.7 ([#3802](https://github.com/containous/traefik/pull/3802) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v1.6.6](https://github.com/containous/traefik/tree/v1.6.6) (2018-08-20)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.6.5...v1.6.6)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Avoid duplicated ACME resolution ([#3751](https://github.com/containous/traefik/pull/3751) by [nmengin](https://github.com/nmengin))
|
||||
- **[api]** Remove TLS in API ([#3788](https://github.com/containous/traefik/pull/3788) by [Juliens](https://github.com/Juliens))
|
||||
- **[cluster]** Remove unusable `--cluster` flag ([#3616](https://github.com/containous/traefik/pull/3616) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[ecs]** Fix bad condition in ECS provider ([#3609](https://github.com/containous/traefik/pull/3609) by [mmatur](https://github.com/mmatur))
|
||||
- Set keepalive on TCP socket so idleTimeout works ([#3740](https://github.com/containous/traefik/pull/3740) by [ajardan](https://github.com/ajardan))
|
||||
|
||||
**Documentation:**
|
||||
- A tiny rewording on the documentation API's page ([#3794](https://github.com/containous/traefik/pull/3794) by [dduportal](https://github.com/dduportal))
|
||||
- Adding warnings and solution about the configuration exposure ([#3790](https://github.com/containous/traefik/pull/3790) by [dduportal](https://github.com/dduportal))
|
||||
- Fix path to the debug pprof API ([#3608](https://github.com/containous/traefik/pull/3608) by [multani](https://github.com/multani))
|
||||
|
||||
**Misc:**
|
||||
- **[oxy,websocket]** Update oxy dependency ([#3777](https://github.com/containous/traefik/pull/3777) by [Juliens](https://github.com/Juliens))
|
||||
|
||||
## [v1.7.0-rc3](https://github.com/containous/traefik/tree/v1.7.0-rc3) (2018-08-01)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.0-rc2...v1.7.0-rc3)
|
||||
|
||||
**Enhancements:**
|
||||
- **[consul,etcd,tls]** Improve TLS integration tests ([#3679](https://github.com/containous/traefik/pull/3679) by [mmatur](https://github.com/mmatur))
|
||||
- **[k8s]** Add possibility to set a protocol ([#3648](https://github.com/containous/traefik/pull/3648) by [SantoDE](https://github.com/SantoDE))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Fix acme account deletion without provider change ([#3664](https://github.com/containous/traefik/pull/3664) by [zyclonite](https://github.com/zyclonite))
|
||||
- **[acme]** Update lego ([#3659](https://github.com/containous/traefik/pull/3659) by [mmatur](https://github.com/mmatur))
|
||||
- **[acme]** Fix ACME certificate for wildcard and root domains ([#3675](https://github.com/containous/traefik/pull/3675) by [nmengin](https://github.com/nmengin))
|
||||
- **[api]** Remove TLS in API ([#3665](https://github.com/containous/traefik/pull/3665) by [mmatur](https://github.com/mmatur))
|
||||
- **[docker]** Uses both binded HostIP and HostPort when useBindPortIP=true ([#3638](https://github.com/containous/traefik/pull/3638) by [geraldcroes](https://github.com/geraldcroes))
|
||||
- **[k8s]** Fix Rewrite-target regex ([#3699](https://github.com/containous/traefik/pull/3699) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[middleware]** Correct Entrypoint Redirect with Stripped or Added Path ([#3631](https://github.com/containous/traefik/pull/3631) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[tracing]** Added default configuration for DataDog APM Tracer ([#3655](https://github.com/containous/traefik/pull/3655) by [aantono](https://github.com/aantono))
|
||||
- **[tracing]** Added support for Trace name truncation for traces ([#3689](https://github.com/containous/traefik/pull/3689) by [aantono](https://github.com/aantono))
|
||||
- **[websocket]** Handle shutdown of Hijacked connections ([#3636](https://github.com/containous/traefik/pull/3636) by [Juliens](https://github.com/Juliens))
|
||||
- H2C: Remove buggy line in init to make verbose switch working ([#3701](https://github.com/containous/traefik/pull/3701) by [dduportal](https://github.com/dduportal))
|
||||
- Updating oxy dependency ([#3700](https://github.com/containous/traefik/pull/3700) by [crholm](https://github.com/crholm))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** Update Namecheap status ([#3604](https://github.com/containous/traefik/pull/3604) by [stoinov](https://github.com/stoinov))
|
||||
- **[acme]** Fix some DNS provider link ([#3639](https://github.com/containous/traefik/pull/3639) by [ldez](https://github.com/ldez))
|
||||
- **[docker]** Fix style in examples/quickstart ([#3705](https://github.com/containous/traefik/pull/3705) by [korigod](https://github.com/korigod))
|
||||
- **[k8s]** Add traefik prefix to k8s annotations ([#3682](https://github.com/containous/traefik/pull/3682) by [zifeo](https://github.com/zifeo))
|
||||
- **[middleware,tracing]** Fix missing tracing backend in documentation ([#3706](https://github.com/containous/traefik/pull/3706) by [mmatur](https://github.com/mmatur))
|
||||
- Replace unrendered emoji ([#3690](https://github.com/containous/traefik/pull/3690) by [korigod](https://github.com/korigod))
|
||||
|
||||
## [v1.7.0-rc2](https://github.com/containous/traefik/tree/v1.7.0-rc2) (2018-07-17)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.7.0-rc1...v1.7.0-rc2)
|
||||
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme,provider]** Create init method on provider interface ([#3580](https://github.com/containous/traefik/pull/3580) by [Juliens](https://github.com/Juliens))
|
||||
- **[acme]** Serve TLS-Challenge certificate in first ([#3605](https://github.com/containous/traefik/pull/3605) by [nmengin](https://github.com/nmengin))
|
||||
- **[api,authentication,webui]** Auth section in web UI. ([#3628](https://github.com/containous/traefik/pull/3628) by [ldez](https://github.com/ldez))
|
||||
- **[authentication,middleware,provider]** Don't pass the Authorization header to the backends ([#3606](https://github.com/containous/traefik/pull/3606) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[ecs]** Fix 400 bad request on AWS ECS API ([#3629](https://github.com/containous/traefik/pull/3629) by [mmatur](https://github.com/mmatur))
|
||||
- **[k8s]** Fix rewrite-target Annotation behavior ([#3582](https://github.com/containous/traefik/pull/3582) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Correct App-Root kubernetes behavior ([#3592](https://github.com/containous/traefik/pull/3592) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[k8s]** Add more K8s Unit Tests ([#3583](https://github.com/containous/traefik/pull/3583) by [dtomcej](https://github.com/dtomcej))
|
||||
- **[kv]** KV and authentication ([#3615](https://github.com/containous/traefik/pull/3615) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Send 'Retry-After' to comply with RFC6585. ([#3593](https://github.com/containous/traefik/pull/3593) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- **[k8s]** Correct Modifier in Kubernetes Documentation ([#3610](https://github.com/containous/traefik/pull/3610) by [dtomcej](https://github.com/dtomcej))
|
||||
|
||||
**Misc:**
|
||||
- Merge v1.6.5 into v1.7 ([#3595](https://github.com/containous/traefik/pull/3595) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v1.6.5](https://github.com/containous/traefik/tree/v1.6.5) (2018-07-09)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.6.4...v1.6.5)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Add a mutex on local store for HTTPChallenges ([#3579](https://github.com/containous/traefik/pull/3579) by [Juliens](https://github.com/Juliens))
|
||||
- **[consulcatalog]** Split the error handling from Consul Catalog (deadlock) ([#3560](https://github.com/containous/traefik/pull/3560) by [ortz](https://github.com/ortz))
|
||||
- **[docker]** segment labels: multiple frontends for one backend. ([#3511](https://github.com/containous/traefik/pull/3511) by [ldez](https://github.com/ldez))
|
||||
- **[kv]** Better support on same prefix at the same level in the KV ([#3532](https://github.com/containous/traefik/pull/3532) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||
- **[logs]** Add logs when error is generated in error handler ([#3567](https://github.com/containous/traefik/pull/3567) by [Juliens](https://github.com/Juliens))
|
||||
- **[middleware]** Create middleware to be able to handle HTTP pipelining correctly ([#3513](https://github.com/containous/traefik/pull/3513) by [mmatur](https://github.com/mmatur))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** The gandiv5 provider works with wildcard ([#3506](https://github.com/containous/traefik/pull/3506) by [manu5801](https://github.com/manu5801))
|
||||
- **[kv]** Update keyFile first/last line comment in kv-config.md ([#3558](https://github.com/containous/traefik/pull/3558) by [madnight](https://github.com/madnight))
|
||||
- Minor formatting issue in user-guide ([#3546](https://github.com/containous/traefik/pull/3546) by [Vanuan](https://github.com/Vanuan))
|
||||
|
||||
## [v1.7.0-rc1](https://github.com/containous/traefik/tree/v1.7.0-rc1) (2018-07-09)
|
||||
[All Commits](https://github.com/containous/traefik/compare/v1.6.0-rc1...v1.7.0-rc1)
|
||||
|
||||
|
@@ -13,7 +13,7 @@ You need to run the `binary` target. This will create binaries for Linux platfor
|
||||
$ make binary
|
||||
docker build -t "traefik-dev:no-more-godep-ever" -f build.Dockerfile .
|
||||
Sending build context to Docker daemon 295.3 MB
|
||||
Step 0 : FROM golang:1.10-alpine
|
||||
Step 0 : FROM golang:1.11-alpine
|
||||
---> 8c6473912976
|
||||
Step 1 : RUN go get github.com/golang/dep/cmd/dep
|
||||
[...]
|
||||
@@ -160,9 +160,11 @@ Integration tests must be run from the `integration/` directory and require the
|
||||
|
||||
The [documentation site](http://docs.traefik.io/) is built with [mkdocs](http://mkdocs.org/)
|
||||
|
||||
### Method 1: `Docker` and `make`
|
||||
### Building Documentation
|
||||
|
||||
You can test documentation using the `docs` target.
|
||||
#### Method 1: `Docker` and `make`
|
||||
|
||||
You can build the documentation and serve it locally with livereloading, using the `docs` target:
|
||||
|
||||
```bash
|
||||
$ make docs
|
||||
@@ -177,11 +179,18 @@ docker run --rm -v /home/user/go/github/containous/traefik:/mkdocs -p 8000:8000
|
||||
|
||||
And go to [http://127.0.0.1:8000](http://127.0.0.1:8000).
|
||||
|
||||
### Method 2: `mkdocs`
|
||||
If you only want to build the documentation without serving it locally, you can use the following command:
|
||||
|
||||
```bash
|
||||
$ make docs-build
|
||||
...
|
||||
```
|
||||
|
||||
#### Method 2: `mkdocs`
|
||||
|
||||
First make sure you have python and pip installed
|
||||
|
||||
```shell
|
||||
```bash
|
||||
$ python --version
|
||||
Python 2.7.2
|
||||
$ pip --version
|
||||
@@ -190,22 +199,42 @@ pip 1.5.2
|
||||
|
||||
Then install mkdocs with pip
|
||||
|
||||
```shell
|
||||
```bash
|
||||
pip install --user -r requirements.txt
|
||||
```
|
||||
|
||||
To test documentation locally run `mkdocs serve` in the root directory, this should start a server locally to preview your changes.
|
||||
To build documentation locally and serve it locally,
|
||||
run `mkdocs serve` in the root directory,
|
||||
this should start a server locally to preview your changes.
|
||||
|
||||
```shell
|
||||
```bash
|
||||
$ mkdocs serve
|
||||
INFO - Building documentation...
|
||||
WARNING - Config value: 'theme'. Warning: The theme 'united' will be removed in an upcoming MkDocs release. See http://www.mkdocs.org/about/release-notes/ for more details
|
||||
INFO - Cleaning site directory
|
||||
[I 160505 22:31:24 server:281] Serving on http://127.0.0.1:8000
|
||||
[I 160505 22:31:24 handlers:59] Start watching changes
|
||||
[I 160505 22:31:24 handlers:61] Start detecting changes
|
||||
```
|
||||
|
||||
### Verify Documentation
|
||||
|
||||
You can verify that the documentation meets some expectations, as checking for dead links, html markup validity.
|
||||
|
||||
```bash
|
||||
$ make docs-verify
|
||||
docker build -t traefik-docs-verify ./script/docs-verify-docker-image ## Build Validator image
|
||||
...
|
||||
docker run --rm -v /home/travis/build/containous/traefik:/app traefik-docs-verify ## Check for dead links and w3c compliance
|
||||
=== Checking HTML content...
|
||||
Running ["HtmlCheck", "ImageCheck", "ScriptCheck", "LinkCheck"] on /app/site/basics/index.html on *.html...
|
||||
```
|
||||
|
||||
If you recently changed the documentation, do not forget to clean it to have it rebuilt:
|
||||
|
||||
```bash
|
||||
$ make docs-clean docs-verify
|
||||
...
|
||||
```
|
||||
|
||||
## How to Write a Good Issue
|
||||
|
||||
|
111
Gopkg.lock
generated
111
Gopkg.lock
generated
@@ -7,6 +7,12 @@
|
||||
revision = "056a55f54a6cc77b440b31a56a5e7c3982d32811"
|
||||
version = "v0.22.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "code.cloudfoundry.org/clock"
|
||||
packages = ["."]
|
||||
revision = "02e53af36e6c978af692887ed449b74026d76fec"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/ArthurHlt/go-eureka-client"
|
||||
@@ -80,6 +86,11 @@
|
||||
packages = ["."]
|
||||
revision = "e039e20e500c2c025d9145be375e27cf42a94174"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/Microsoft/ApplicationInsights-Go"
|
||||
packages = ["appinsights"]
|
||||
revision = "98ac7ca026c26818888600ea0d966987aa56f043"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/Microsoft/go-winio"
|
||||
packages = ["."]
|
||||
@@ -158,6 +169,23 @@
|
||||
revision = "a494eba1efa1f38338393727dff63389a6a66534"
|
||||
version = "v0.6.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/aliyun/alibaba-cloud-sdk-go"
|
||||
packages = [
|
||||
"sdk",
|
||||
"sdk/auth",
|
||||
"sdk/auth/credentials",
|
||||
"sdk/auth/signers",
|
||||
"sdk/endpoints",
|
||||
"sdk/errors",
|
||||
"sdk/requests",
|
||||
"sdk/responses",
|
||||
"sdk/utils",
|
||||
"services/alidns"
|
||||
]
|
||||
revision = "cad214d7d71fba7883fcf3b7e550ba782c15b400"
|
||||
version = "1.27.7"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/aokoli/goutils"
|
||||
packages = ["."]
|
||||
@@ -231,6 +259,12 @@
|
||||
packages = ["."]
|
||||
revision = "2ea60e5f094469f9e65adb9cd103795b73ae743e"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/cloudflare/cloudflare-go"
|
||||
packages = ["."]
|
||||
revision = "1f9007fbecae20711133c60519338c41cef1ffb4"
|
||||
version = "v0.8.5"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/codahale/hdrhistogram"
|
||||
@@ -249,8 +283,8 @@
|
||||
".",
|
||||
"parse"
|
||||
]
|
||||
revision = "b4c2f060875361c070ed2bc300c5929b82f5fa2e"
|
||||
version = "v1.1.2"
|
||||
revision = "aad81c7ac7f49671a59b9ede8ab22436e132a302"
|
||||
version = "v1.3.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -267,8 +301,8 @@
|
||||
[[projects]]
|
||||
name = "github.com/containous/traefik-extra-service-fabric"
|
||||
packages = ["."]
|
||||
revision = "08668650856571f3529bde394a36a5c9cf16a991"
|
||||
version = "v1.2.0"
|
||||
revision = "6e90a9eef2ac9d320e55d6e994d169673a8d8b0f"
|
||||
version = "v1.3.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/coreos/bbolt"
|
||||
@@ -305,6 +339,12 @@
|
||||
revision = "48702e0da86bd25e76cfef347e2adeb434a0d0a6"
|
||||
version = "v14"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/cpu/goacmedns"
|
||||
packages = ["."]
|
||||
revision = "565ecf2a84df654865cc102705ac160a3b04fc01"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/davecgh/go-spew"
|
||||
packages = ["spew"]
|
||||
@@ -523,8 +563,8 @@
|
||||
[[projects]]
|
||||
name = "github.com/exoscale/egoscale"
|
||||
packages = ["."]
|
||||
revision = "e4fedc381fbddb7fef4d7060388a726c6de37c88"
|
||||
version = "v0.9.7"
|
||||
revision = "d8dfca6802ad5c1a5300e52fa68067e791322035"
|
||||
version = "v0.11.4"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/fatih/color"
|
||||
@@ -603,6 +643,12 @@
|
||||
packages = ["."]
|
||||
revision = "1d0bd113de87027671077d3c71eb3ac5d7dbba72"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/go-resty/resty"
|
||||
packages = ["."]
|
||||
revision = "d4920dcf5b7689548a6db640278a9b35a5b48ec6"
|
||||
version = "v1.9.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/go-stack/stack"
|
||||
packages = ["."]
|
||||
@@ -689,7 +735,7 @@
|
||||
branch = "master"
|
||||
name = "github.com/gorilla/websocket"
|
||||
packages = ["."]
|
||||
revision = "eb925808374e5ca90c83401a40d711dc08c0c0f6"
|
||||
revision = "66b9c49e59c6c48f0ffce28c2d8b8a5678502c6d"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/gravitational/trace"
|
||||
@@ -747,6 +793,15 @@
|
||||
packages = ["."]
|
||||
revision = "3959339b333561bf62a38b424fd41517c2c90f40"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/iij/doapi"
|
||||
packages = [
|
||||
".",
|
||||
"protocol"
|
||||
]
|
||||
revision = "8803795a9b7b938fa88ddbd63a77893beee14cd8"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/imdario/mergo"
|
||||
packages = ["."]
|
||||
@@ -763,6 +818,12 @@
|
||||
revision = "2d474a3089bcfce6b472779be9470a1f0ef3d5e4"
|
||||
version = "v1.3.7"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/jjcollinge/logrus-appinsights"
|
||||
packages = ["."]
|
||||
revision = "9b66602d496a139e4722bdde32f0f1ac1c12d4a8"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/jjcollinge/servicefabric"
|
||||
packages = ["."]
|
||||
@@ -790,6 +851,12 @@
|
||||
revision = "59fac5042749a5afb9af70e813da1dd5474f0167"
|
||||
version = "1.0.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/konsorten/go-windows-terminal-sequences"
|
||||
packages = ["."]
|
||||
revision = "b729f2633dfe35f4d1d8a32385f6685610ce1cb5"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/kr/logfmt"
|
||||
@@ -817,6 +884,12 @@
|
||||
packages = ["."]
|
||||
revision = "1113af38e5916529ad7317b0fe12e273e6e92af5"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/linode/linodego"
|
||||
packages = ["."]
|
||||
revision = "d0d31d8ca62fa3f7e4526ca0ce95de81e4ed001e"
|
||||
version = "v0.5.1"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/mailgun/minheap"
|
||||
packages = ["."]
|
||||
@@ -1118,14 +1191,14 @@
|
||||
[[projects]]
|
||||
name = "github.com/satori/go.uuid"
|
||||
packages = ["."]
|
||||
revision = "879c5887cd475cd7864858769793b2ceb0d44feb"
|
||||
version = "v1.1.0"
|
||||
revision = "f58768cc1a7a7e77a3bd49e98cdd21419399b6a3"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/sirupsen/logrus"
|
||||
packages = ["."]
|
||||
revision = "d682213848ed68c0a260ca37d6dd5ace8423f5ba"
|
||||
version = "v1.0.4"
|
||||
revision = "a67f783a3814b8729bd2dac5780b5f78f8dbd64d"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/spf13/pflag"
|
||||
@@ -1249,7 +1322,7 @@
|
||||
"roundrobin",
|
||||
"utils"
|
||||
]
|
||||
revision = "adbef6bedf021985587c3c18c9d4b84b2d78f67c"
|
||||
revision = "7d94d212f808222b72fd0b8bb171bfcd4e27ffca"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/vulcand/predicate"
|
||||
@@ -1282,6 +1355,8 @@
|
||||
"log",
|
||||
"platform/config/env",
|
||||
"providers/dns",
|
||||
"providers/dns/acmedns",
|
||||
"providers/dns/alidns",
|
||||
"providers/dns/auroradns",
|
||||
"providers/dns/azure",
|
||||
"providers/dns/bluecat",
|
||||
@@ -1291,6 +1366,7 @@
|
||||
"providers/dns/dnsimple",
|
||||
"providers/dns/dnsmadeeasy",
|
||||
"providers/dns/dnspod",
|
||||
"providers/dns/dreamhost",
|
||||
"providers/dns/duckdns",
|
||||
"providers/dns/dyn",
|
||||
"providers/dns/exec",
|
||||
@@ -1301,10 +1377,14 @@
|
||||
"providers/dns/gcloud",
|
||||
"providers/dns/glesys",
|
||||
"providers/dns/godaddy",
|
||||
"providers/dns/hostingde",
|
||||
"providers/dns/iij",
|
||||
"providers/dns/lightsail",
|
||||
"providers/dns/linode",
|
||||
"providers/dns/linodev4",
|
||||
"providers/dns/namecheap",
|
||||
"providers/dns/namedotcom",
|
||||
"providers/dns/netcup",
|
||||
"providers/dns/nifcloud",
|
||||
"providers/dns/ns1",
|
||||
"providers/dns/otc",
|
||||
@@ -1314,10 +1394,11 @@
|
||||
"providers/dns/rfc2136",
|
||||
"providers/dns/route53",
|
||||
"providers/dns/sakuracloud",
|
||||
"providers/dns/stackpath",
|
||||
"providers/dns/vegadns",
|
||||
"providers/dns/vultr"
|
||||
]
|
||||
revision = "e0d512138c43e3f056a41cd7a5beff662ec130d3"
|
||||
revision = "160d6fe60303699067faad57dc0b1e147ac499ef"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
@@ -1352,6 +1433,7 @@
|
||||
"ipv4",
|
||||
"ipv6",
|
||||
"proxy",
|
||||
"publicsuffix",
|
||||
"trace",
|
||||
"websocket"
|
||||
]
|
||||
@@ -1362,6 +1444,7 @@
|
||||
name = "golang.org/x/oauth2"
|
||||
packages = [
|
||||
".",
|
||||
"clientcredentials",
|
||||
"google",
|
||||
"internal",
|
||||
"jws",
|
||||
@@ -1738,6 +1821,6 @@
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "a3429eccf578f09c7c521a32f81df1af5f0a24cb8c9702b7b768f3db34153216"
|
||||
inputs-digest = "059f9d29d78e7a800b676c529197fd627de968837b01c663a8a00ee72c36271b"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
@@ -54,7 +54,7 @@
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/containous/flaeg"
|
||||
version = "1.0.1"
|
||||
version = "1.3.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
@@ -66,7 +66,7 @@
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/containous/traefik-extra-service-fabric"
|
||||
version = "1.2.0"
|
||||
version = "1.3.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/coreos/go-systemd"
|
||||
|
19
Makefile
19
Makefile
@@ -1,4 +1,4 @@
|
||||
.PHONY: all
|
||||
.PHONY: all docs-verify docs docs-clean docs-build
|
||||
|
||||
TRAEFIK_ENVS := \
|
||||
-e OS_ARCH_ARG \
|
||||
@@ -22,6 +22,7 @@ REPONAME := $(shell echo $(REPO) | tr '[:upper:]' '[:lower:]')
|
||||
TRAEFIK_IMAGE := $(if $(REPONAME),$(REPONAME),"containous/traefik")
|
||||
INTEGRATION_OPTS := $(if $(MAKE_DOCKER_HOST),-e "DOCKER_HOST=$(MAKE_DOCKER_HOST)", -e "TEST_CONTAINER=1" -v "/var/run/docker.sock:/var/run/docker.sock")
|
||||
TRAEFIK_DOC_IMAGE := traefik-docs
|
||||
TRAEFIK_DOC_VERIFY_IMAGE := $(TRAEFIK_DOC_IMAGE)-verify
|
||||
|
||||
DOCKER_BUILD_ARGS := $(if $(DOCKER_VERSION), "--build-arg=DOCKER_VERSION=$(DOCKER_VERSION)",)
|
||||
DOCKER_RUN_OPTS := $(TRAEFIK_ENVS) $(TRAEFIK_MOUNT) "$(TRAEFIK_DEV_IMAGE)"
|
||||
@@ -94,11 +95,23 @@ image-dirty: binary ## build a docker traefik image
|
||||
image: clear-static binary ## clean up static directory and build a docker traefik image
|
||||
docker build -t $(TRAEFIK_IMAGE) .
|
||||
|
||||
docs-image:
|
||||
docker build -t $(TRAEFIK_DOC_IMAGE) -f docs.Dockerfile .
|
||||
|
||||
docs: docs-image
|
||||
docker run $(DOCKER_RUN_DOC_OPTS) $(TRAEFIK_DOC_IMAGE) mkdocs serve
|
||||
|
||||
docs-image:
|
||||
docker build -t $(TRAEFIK_DOC_IMAGE) -f docs.Dockerfile .
|
||||
docs-build: site
|
||||
|
||||
docs-verify: site
|
||||
docker build -t $(TRAEFIK_DOC_VERIFY_IMAGE) ./script/docs-verify-docker-image ## Build Validator image
|
||||
docker run --rm -v $(CURDIR):/app $(TRAEFIK_DOC_VERIFY_IMAGE) ## Check for dead links and w3c compliance
|
||||
|
||||
site: docs-image
|
||||
docker run $(DOCKER_RUN_DOC_OPTS) $(TRAEFIK_DOC_IMAGE) mkdocs build
|
||||
|
||||
docs-clean:
|
||||
rm -rf $(CURDIR)/site
|
||||
|
||||
clear-static:
|
||||
rm -rf static
|
||||
|
10
README.md
10
README.md
@@ -9,7 +9,7 @@
|
||||
[](https://microbadger.com/images/traefik)
|
||||
[](https://github.com/containous/traefik/blob/master/LICENSE.md)
|
||||
[](https://slack.traefik.io)
|
||||
[](https://twitter.com/intent/follow?screen_name=traefikproxy)
|
||||
[](https://twitter.com/intent/follow?screen_name=traefik)
|
||||
|
||||
|
||||
Træfik is a modern HTTP reverse proxy and load balancer that makes deploying microservices easy.
|
||||
@@ -164,12 +164,10 @@ Each version is supported until the next one is released (e.g. 1.1.x will be sup
|
||||
|
||||
We use [Semantic Versioning](http://semver.org/)
|
||||
|
||||
## Plumbing
|
||||
## Mailing lists
|
||||
|
||||
- [Oxy](https://github.com/vulcand/oxy): an awesome proxy library made by Mailgun folks
|
||||
- [Gorilla mux](https://github.com/gorilla/mux): famous request router
|
||||
- [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
|
||||
- General announcements, new releases: mail at news+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/news)
|
||||
- Security announcements: mail at security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
|
||||
|
||||
## Credits
|
||||
|
||||
|
87
acme/acme.go
87
acme/acme.go
@@ -9,8 +9,10 @@ import (
|
||||
fmtlog "log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/BurntSushi/ty/fun"
|
||||
@@ -63,6 +65,8 @@ type ACME struct {
|
||||
jobs *channels.InfiniteChannel
|
||||
TLSConfig *tls.Config `description:"TLS config in case wildcard certs are used"`
|
||||
dynamicCerts *safe.Safe
|
||||
resolvingDomains map[string]struct{}
|
||||
resolvingDomainsMutex sync.RWMutex
|
||||
}
|
||||
|
||||
func (a *ACME) init() error {
|
||||
@@ -75,6 +79,10 @@ func (a *ACME) init() error {
|
||||
}
|
||||
|
||||
a.jobs = channels.NewInfiniteChannel()
|
||||
|
||||
// Init the currently resolved domain map
|
||||
a.resolvingDomains = make(map[string]struct{})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -119,7 +127,6 @@ func (a *ACME) CreateClusterConfig(leadership *cluster.Leadership, tlsConfig *tl
|
||||
|
||||
a.checkOnDemandDomain = checkOnDemandDomain
|
||||
a.dynamicCerts = certs
|
||||
a.challengeTLSProvider = &challengeTLSProvider{store: a.store}
|
||||
|
||||
tlsConfig.GetCertificate = a.getCertificate
|
||||
a.TLSConfig = tlsConfig
|
||||
@@ -149,6 +156,7 @@ func (a *ACME) CreateClusterConfig(leadership *cluster.Leadership, tlsConfig *tl
|
||||
}
|
||||
|
||||
a.store = datastore
|
||||
a.challengeTLSProvider = &challengeTLSProvider{store: a.store}
|
||||
|
||||
ticker := time.NewTicker(24 * time.Hour)
|
||||
leadership.Pool.AddGoCtx(func(ctx context.Context) {
|
||||
@@ -183,7 +191,8 @@ func (a *ACME) leadershipListener(elected bool) error {
|
||||
account := object.(*Account)
|
||||
account.Init()
|
||||
// Reset Account values if caServer changed, thus registration URI can be updated
|
||||
if account != nil && account.Registration != nil && !strings.HasPrefix(account.Registration.URI, a.CAServer) {
|
||||
if account != nil && account.Registration != nil && !isAccountMatchingCaServer(account.Registration.URI, a.CAServer) {
|
||||
log.Info("Account URI does not match the current CAServer. The account will be reset")
|
||||
account.reset()
|
||||
}
|
||||
|
||||
@@ -200,6 +209,9 @@ func (a *ACME) leadershipListener(elected bool) error {
|
||||
}
|
||||
|
||||
needRegister = true
|
||||
} else if len(account.KeyType) == 0 {
|
||||
// Set the KeyType if not already defined in the account
|
||||
account.KeyType = acmeprovider.GetKeyType(a.KeyType)
|
||||
}
|
||||
|
||||
a.client, err = a.buildACMEClient(account)
|
||||
@@ -230,19 +242,33 @@ func (a *ACME) leadershipListener(elected bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func isAccountMatchingCaServer(accountURI string, serverURI string) bool {
|
||||
aru, err := url.Parse(accountURI)
|
||||
if err != nil {
|
||||
log.Infof("Unable to parse account.Registration URL : %v", err)
|
||||
return false
|
||||
}
|
||||
cau, err := url.Parse(serverURI)
|
||||
if err != nil {
|
||||
log.Infof("Unable to parse CAServer URL : %v", err)
|
||||
return false
|
||||
}
|
||||
return cau.Hostname() == aru.Hostname()
|
||||
}
|
||||
|
||||
func (a *ACME) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||
domain := types.CanonicalDomain(clientHello.ServerName)
|
||||
account := a.store.Get().(*Account)
|
||||
|
||||
if providedCertificate := a.getProvidedCertificate(domain); providedCertificate != nil {
|
||||
return providedCertificate, nil
|
||||
}
|
||||
|
||||
if challengeCert, ok := a.challengeTLSProvider.getCertificate(domain); ok {
|
||||
log.Debugf("ACME got challenge %s", domain)
|
||||
return challengeCert, nil
|
||||
}
|
||||
|
||||
if providedCertificate := a.getProvidedCertificate(domain); providedCertificate != nil {
|
||||
return providedCertificate, nil
|
||||
}
|
||||
|
||||
if domainCert, ok := account.DomainsCertificate.getCertificateForDomain(domain); ok {
|
||||
log.Debugf("ACME got domain cert %s", domain)
|
||||
return domainCert.tlsCert, nil
|
||||
@@ -518,6 +544,10 @@ func (a *ACME) LoadCertificateForDomains(domains []string) {
|
||||
if len(uncheckedDomains) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
a.addResolvingDomains(uncheckedDomains)
|
||||
defer a.removeResolvingDomains(uncheckedDomains)
|
||||
|
||||
certificate, err := a.getDomainsCertificates(uncheckedDomains)
|
||||
if err != nil {
|
||||
log.Errorf("Error getting ACME certificates %+v : %v", uncheckedDomains, err)
|
||||
@@ -549,6 +579,24 @@ func (a *ACME) LoadCertificateForDomains(domains []string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (a *ACME) addResolvingDomains(resolvingDomains []string) {
|
||||
a.resolvingDomainsMutex.Lock()
|
||||
defer a.resolvingDomainsMutex.Unlock()
|
||||
|
||||
for _, domain := range resolvingDomains {
|
||||
a.resolvingDomains[domain] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *ACME) removeResolvingDomains(resolvingDomains []string) {
|
||||
a.resolvingDomainsMutex.Lock()
|
||||
defer a.resolvingDomainsMutex.Unlock()
|
||||
|
||||
for _, domain := range resolvingDomains {
|
||||
delete(a.resolvingDomains, domain)
|
||||
}
|
||||
}
|
||||
|
||||
// Get provided certificate which check a domains list (Main and SANs)
|
||||
// from static and dynamic provided certificates
|
||||
func (a *ACME) getProvidedCertificate(domains string) *tls.Certificate {
|
||||
@@ -584,6 +632,9 @@ func searchProvidedCertificateForDomains(domain string, certs map[string]*tls.Ce
|
||||
// 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 {
|
||||
a.resolvingDomainsMutex.RLock()
|
||||
defer a.resolvingDomainsMutex.RUnlock()
|
||||
|
||||
log.Debugf("Looking for provided certificate to validate %s...", domains)
|
||||
allCerts := make(map[string]*tls.Certificate)
|
||||
|
||||
@@ -606,6 +657,13 @@ func (a *ACME) getUncheckedDomains(domains []string, account *Account) []string
|
||||
}
|
||||
}
|
||||
|
||||
// Get currently resolved domains
|
||||
for domain := range a.resolvingDomains {
|
||||
if _, ok := allCerts[domain]; !ok {
|
||||
allCerts[domain] = &tls.Certificate{}
|
||||
}
|
||||
}
|
||||
|
||||
// Get Configuration Domains
|
||||
for i := 0; i < len(a.Domains); i++ {
|
||||
allCerts[a.Domains[i].Main] = &tls.Certificate{}
|
||||
@@ -634,16 +692,25 @@ func searchUncheckedDomains(domains []string, certs map[string]*tls.Certificate)
|
||||
}
|
||||
|
||||
func (a *ACME) getDomainsCertificates(domains []string) (*Certificate, error) {
|
||||
domains = fun.Map(types.CanonicalDomain, domains).([]string)
|
||||
log.Debugf("Loading ACME certificates %s...", domains)
|
||||
var cleanDomains []string
|
||||
for _, domain := range domains {
|
||||
canonicalDomain := types.CanonicalDomain(domain)
|
||||
cleanDomain := acme.UnFqdn(canonicalDomain)
|
||||
if canonicalDomain != cleanDomain {
|
||||
log.Warnf("FQDN detected, please remove the trailing dot: %s", canonicalDomain)
|
||||
}
|
||||
cleanDomains = append(cleanDomains, cleanDomain)
|
||||
}
|
||||
|
||||
log.Debugf("Loading ACME certificates %s...", cleanDomains)
|
||||
bundle := true
|
||||
|
||||
certificate, err := a.client.ObtainCertificate(domains, bundle, nil, OSCPMustStaple)
|
||||
certificate, err := a.client.ObtainCertificate(cleanDomains, bundle, nil, OSCPMustStaple)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot obtain certificates: %+v", err)
|
||||
}
|
||||
|
||||
log.Debugf("Loaded ACME certificates %s", domains)
|
||||
log.Debugf("Loaded ACME certificates %s", cleanDomains)
|
||||
return &Certificate{
|
||||
Domain: certificate.Domain,
|
||||
CertURL: certificate.CertURL,
|
||||
|
@@ -331,9 +331,12 @@ func TestAcme_getUncheckedCertificates(t *testing.T) {
|
||||
mm["*.containo.us"] = &tls.Certificate{}
|
||||
mm["traefik.acme.io"] = &tls.Certificate{}
|
||||
|
||||
a := ACME{TLSConfig: &tls.Config{NameToCertificate: mm}}
|
||||
dm := make(map[string]struct{})
|
||||
dm["*.traefik.wtf"] = struct{}{}
|
||||
|
||||
domains := []string{"traefik.containo.us", "trae.containo.us"}
|
||||
a := ACME{TLSConfig: &tls.Config{NameToCertificate: mm}, resolvingDomains: dm}
|
||||
|
||||
domains := []string{"traefik.containo.us", "trae.containo.us", "foo.traefik.wtf"}
|
||||
uncheckedDomains := a.getUncheckedDomains(domains, nil)
|
||||
assert.Empty(t, uncheckedDomains)
|
||||
domains = []string{"traefik.acme.io", "trae.acme.io"}
|
||||
@@ -351,6 +354,9 @@ func TestAcme_getUncheckedCertificates(t *testing.T) {
|
||||
account := Account{DomainsCertificate: domainsCertificates}
|
||||
uncheckedDomains = a.getUncheckedDomains(domains, &account)
|
||||
assert.Empty(t, uncheckedDomains)
|
||||
domains = []string{"traefik.containo.us", "trae.containo.us", "traefik.wtf"}
|
||||
uncheckedDomains = a.getUncheckedDomains(domains, nil)
|
||||
assert.Len(t, uncheckedDomains, 1)
|
||||
}
|
||||
|
||||
func TestAcme_getProvidedCertificate(t *testing.T) {
|
||||
|
@@ -2,12 +2,15 @@ package anonymize
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/traefik/acme"
|
||||
"github.com/containous/traefik/api"
|
||||
"github.com/containous/traefik/configuration"
|
||||
"github.com/containous/traefik/middlewares"
|
||||
"github.com/containous/traefik/provider"
|
||||
acmeprovider "github.com/containous/traefik/provider/acme"
|
||||
"github.com/containous/traefik/provider/boltdb"
|
||||
@@ -25,8 +28,11 @@ import (
|
||||
"github.com/containous/traefik/provider/mesos"
|
||||
"github.com/containous/traefik/provider/rancher"
|
||||
"github.com/containous/traefik/provider/zk"
|
||||
"github.com/containous/traefik/safe"
|
||||
traefiktls "github.com/containous/traefik/tls"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/elazarl/go-bindata-assetfs"
|
||||
"github.com/thoas/stats"
|
||||
)
|
||||
|
||||
func TestDo_globalConfiguration(t *testing.T) {
|
||||
@@ -54,7 +60,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
{CertFile: "CertFile 2", KeyFile: "KeyFile 2"},
|
||||
},
|
||||
ClientCA: traefiktls.ClientCA{
|
||||
Files: []string{"foo ClientCAFiles 1", "foo ClientCAFiles 2", "foo ClientCAFiles 3"},
|
||||
Files: traefiktls.FilesOrContents{"foo ClientCAFiles 1", "foo ClientCAFiles 2", "foo ClientCAFiles 3"},
|
||||
Optional: false,
|
||||
},
|
||||
},
|
||||
@@ -99,7 +105,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
{CertFile: "CertFile 2", KeyFile: "KeyFile 2"},
|
||||
},
|
||||
ClientCA: traefiktls.ClientCA{
|
||||
Files: []string{"fii ClientCAFiles 1", "fii ClientCAFiles 2", "fii ClientCAFiles 3"},
|
||||
Files: traefiktls.FilesOrContents{"fii ClientCAFiles 1", "fii ClientCAFiles 2", "fii ClientCAFiles 3"},
|
||||
Optional: false,
|
||||
},
|
||||
},
|
||||
@@ -181,13 +187,42 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
config.MaxIdleConnsPerHost = 666
|
||||
config.IdleTimeout = flaeg.Duration(666 * time.Second)
|
||||
config.InsecureSkipVerify = true
|
||||
config.RootCAs = traefiktls.RootCAs{"RootCAs 1", "RootCAs 2", "RootCAs 3"}
|
||||
config.RootCAs = traefiktls.FilesOrContents{"RootCAs 1", "RootCAs 2", "RootCAs 3"}
|
||||
config.Retry = &configuration.Retry{
|
||||
Attempts: 666,
|
||||
}
|
||||
config.HealthCheck = &configuration.HealthCheckConfig{
|
||||
Interval: flaeg.Duration(666 * time.Second),
|
||||
}
|
||||
config.API = &api.Handler{
|
||||
EntryPoint: "traefik",
|
||||
Dashboard: true,
|
||||
Debug: true,
|
||||
CurrentConfigurations: &safe.Safe{},
|
||||
Statistics: &types.Statistics{
|
||||
RecentErrors: 666,
|
||||
},
|
||||
Stats: &stats.Stats{
|
||||
Uptime: time.Now(),
|
||||
Pid: 666,
|
||||
ResponseCounts: map[string]int{"foo": 1},
|
||||
TotalResponseCounts: map[string]int{"bar": 1},
|
||||
TotalResponseTime: time.Now(),
|
||||
},
|
||||
StatsRecorder: &middlewares.StatsRecorder{},
|
||||
DashboardAssets: &assetfs.AssetFS{
|
||||
Asset: func(path string) ([]byte, error) {
|
||||
return nil, nil
|
||||
},
|
||||
AssetDir: func(path string) ([]string, error) {
|
||||
return nil, nil
|
||||
},
|
||||
AssetInfo: func(path string) (os.FileInfo, error) {
|
||||
return nil, nil
|
||||
},
|
||||
Prefix: "fii",
|
||||
},
|
||||
}
|
||||
config.RespondingTimeouts = &configuration.RespondingTimeouts{
|
||||
ReadTimeout: flaeg.Duration(666 * time.Second),
|
||||
WriteTimeout: flaeg.Duration(666 * time.Second),
|
||||
@@ -213,7 +248,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
MustMatch: true,
|
||||
},
|
||||
},
|
||||
Trace: true,
|
||||
Trace: true,
|
||||
DebugLogGeneratedTemplate: true,
|
||||
},
|
||||
Endpoint: "docker Endpoint",
|
||||
@@ -244,7 +279,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
MustMatch: true,
|
||||
},
|
||||
},
|
||||
Trace: true,
|
||||
Trace: true,
|
||||
DebugLogGeneratedTemplate: true,
|
||||
},
|
||||
Directory: "file Directory",
|
||||
@@ -309,7 +344,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
MustMatch: true,
|
||||
},
|
||||
},
|
||||
Trace: true,
|
||||
Trace: true,
|
||||
DebugLogGeneratedTemplate: true,
|
||||
},
|
||||
Endpoint: "",
|
||||
@@ -349,7 +384,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
MustMatch: true,
|
||||
},
|
||||
},
|
||||
Trace: true,
|
||||
Trace: true,
|
||||
DebugLogGeneratedTemplate: true,
|
||||
},
|
||||
Endpoint: "ConsulCatalog Endpoint",
|
||||
@@ -374,7 +409,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
MustMatch: true,
|
||||
},
|
||||
},
|
||||
Trace: true,
|
||||
Trace: true,
|
||||
DebugLogGeneratedTemplate: true,
|
||||
},
|
||||
Endpoint: "k8s Endpoint",
|
||||
@@ -400,7 +435,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
MustMatch: true,
|
||||
},
|
||||
},
|
||||
Trace: true,
|
||||
Trace: true,
|
||||
DebugLogGeneratedTemplate: true,
|
||||
},
|
||||
Endpoint: "mesos Endpoint",
|
||||
@@ -429,7 +464,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
MustMatch: true,
|
||||
},
|
||||
},
|
||||
Trace: true,
|
||||
Trace: true,
|
||||
DebugLogGeneratedTemplate: true,
|
||||
},
|
||||
Endpoint: "eureka Endpoint",
|
||||
@@ -452,7 +487,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
MustMatch: true,
|
||||
},
|
||||
},
|
||||
Trace: true,
|
||||
Trace: true,
|
||||
DebugLogGeneratedTemplate: true,
|
||||
},
|
||||
Domain: "ecs Domain",
|
||||
@@ -481,7 +516,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
MustMatch: true,
|
||||
},
|
||||
},
|
||||
Trace: true,
|
||||
Trace: true,
|
||||
DebugLogGeneratedTemplate: true,
|
||||
},
|
||||
APIConfiguration: rancher.APIConfiguration{
|
||||
@@ -519,7 +554,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
MustMatch: true,
|
||||
},
|
||||
},
|
||||
Trace: true,
|
||||
Trace: true,
|
||||
DebugLogGeneratedTemplate: true,
|
||||
},
|
||||
AccessKeyID: "dynamodb AccessKeyID",
|
||||
@@ -546,7 +581,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
MustMatch: true,
|
||||
},
|
||||
},
|
||||
Trace: true,
|
||||
Trace: true,
|
||||
DebugLogGeneratedTemplate: true,
|
||||
},
|
||||
Endpoint: "etcd Endpoint",
|
||||
@@ -578,7 +613,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
MustMatch: true,
|
||||
},
|
||||
},
|
||||
Trace: true,
|
||||
Trace: true,
|
||||
DebugLogGeneratedTemplate: true,
|
||||
},
|
||||
Endpoint: "zk Endpoint",
|
||||
@@ -610,7 +645,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
MustMatch: true,
|
||||
},
|
||||
},
|
||||
Trace: true,
|
||||
Trace: true,
|
||||
DebugLogGeneratedTemplate: true,
|
||||
},
|
||||
Endpoint: "boltdb Endpoint",
|
||||
@@ -642,7 +677,7 @@ func TestDo_globalConfiguration(t *testing.T) {
|
||||
MustMatch: true,
|
||||
},
|
||||
},
|
||||
Trace: true,
|
||||
Trace: true,
|
||||
DebugLogGeneratedTemplate: true,
|
||||
},
|
||||
Endpoint: "consul Endpoint",
|
||||
|
@@ -23,7 +23,7 @@ type Handler struct {
|
||||
Statistics *types.Statistics `description:"Enable more detailed statistics" export:"true"`
|
||||
Stats *thoas_stats.Stats `json:"-"`
|
||||
StatsRecorder *middlewares.StatsRecorder `json:"-"`
|
||||
DashboardAssets *assetfs.AssetFS
|
||||
DashboardAssets *assetfs.AssetFS `json:"-"`
|
||||
}
|
||||
|
||||
var (
|
||||
|
@@ -209,8 +209,30 @@ var _templatesConsul_catalogTmpl = []byte(`[backends]
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $auth := getAuth $service.TraefikLabels }}
|
||||
{{ $tlsClientCert := getPassTLSClientCert $service.TraefikLabels }}
|
||||
{{if $tlsClientCert }}
|
||||
[frontends."frontend-{{ $service.ServiceName }}".passTLSClientCert]
|
||||
pem = {{ $tlsClientCert.PEM }}
|
||||
{{ $infos := $tlsClientCert.Infos }}
|
||||
{{if $infos }}
|
||||
[frontends."frontend-{{ $service.ServiceName }}".passTLSClientCert.infos]
|
||||
notAfter = {{ $infos.NotAfter }}
|
||||
notBefore = {{ $infos.NotBefore }}
|
||||
sans = {{ $infos.Sans }}
|
||||
{{ $subject := $infos.Subject }}
|
||||
{{if $subject }}
|
||||
[frontends."frontend-{{ $service.ServiceName }}".passTLSClientCert.infos.subject]
|
||||
country = {{ $subject.Country }}
|
||||
province = {{ $subject.Province }}
|
||||
locality = {{ $subject.Locality }}
|
||||
organization = {{ $subject.Organization }}
|
||||
commonName = {{ $subject.CommonName }}
|
||||
serialNumber = {{ $subject.SerialNumber }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $auth := getAuth $service.TraefikLabels }}
|
||||
{{if $auth }}
|
||||
[frontends."frontend-{{ $service.ServiceName }}".auth]
|
||||
headerField = "{{ $auth.HeaderField }}"
|
||||
@@ -219,19 +241,25 @@ var _templatesConsul_catalogTmpl = []byte(`[backends]
|
||||
[frontends."frontend-{{ $service.ServiceName }}".auth.forward]
|
||||
address = "{{ $auth.Forward.Address }}"
|
||||
trustForwardHeader = {{ $auth.Forward.TrustForwardHeader }}
|
||||
{{if $auth.Forward.AuthResponseHeaders }}
|
||||
authResponseHeaders = [{{range $auth.Forward.AuthResponseHeaders }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
|
||||
{{if $auth.Forward.TLS }}
|
||||
[frontends."frontend-{{ $service.ServiceName }}".auth.forward.tls]
|
||||
ca = "{{ $auth.Forward.TLS.CA }}"
|
||||
caOptional = {{ $auth.Forward.TLS.CAOptional }}
|
||||
cert = "{{ $auth.Forward.TLS.Cert }}"
|
||||
key = "{{ $auth.Forward.TLS.Key }}"
|
||||
cert = """{{ $auth.Forward.TLS.Cert }}"""
|
||||
key = """{{ $auth.Forward.TLS.Key }}"""
|
||||
insecureSkipVerify = {{ $auth.Forward.TLS.InsecureSkipVerify }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if $auth.Basic }}
|
||||
[frontends."frontend-{{ $service.ServiceName }}".auth.basic]
|
||||
removeHeader = {{ $auth.Basic.RemoveHeader }}
|
||||
{{if $auth.Basic.Users }}
|
||||
users = [{{range $auth.Basic.Users }}
|
||||
"{{.}}",
|
||||
@@ -242,6 +270,7 @@ var _templatesConsul_catalogTmpl = []byte(`[backends]
|
||||
|
||||
{{if $auth.Digest }}
|
||||
[frontends."frontend-{{ $service.ServiceName }}".auth.digest]
|
||||
removeHeader = {{ $auth.Digest.RemoveHeader }}
|
||||
{{if $auth.Digest.Users }}
|
||||
users = [{{range $auth.Digest.Users }}
|
||||
"{{.}}",
|
||||
@@ -657,6 +686,29 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $tlsClientCert := getPassTLSClientCert $container.SegmentLabels }}
|
||||
{{if $tlsClientCert }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert]
|
||||
pem = {{ $tlsClientCert.PEM }}
|
||||
{{ $infos := $tlsClientCert.Infos }}
|
||||
{{if $infos }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos]
|
||||
notAfter = {{ $infos.NotAfter }}
|
||||
notBefore = {{ $infos.NotBefore }}
|
||||
sans = {{ $infos.Sans }}
|
||||
{{ $subject := $infos.Subject }}
|
||||
{{if $subject }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos.subject]
|
||||
country = {{ $subject.Country }}
|
||||
province = {{ $subject.Province }}
|
||||
locality = {{ $subject.Locality }}
|
||||
organization = {{ $subject.Organization }}
|
||||
commonName = {{ $subject.CommonName }}
|
||||
serialNumber = {{ $subject.SerialNumber }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $auth := getAuth $container.SegmentLabels }}
|
||||
{{if $auth }}
|
||||
[frontends."frontend-{{ $frontendName }}".auth]
|
||||
@@ -666,19 +718,25 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
|
||||
[frontends."frontend-{{ $frontendName }}".auth.forward]
|
||||
address = "{{ $auth.Forward.Address }}"
|
||||
trustForwardHeader = {{ $auth.Forward.TrustForwardHeader }}
|
||||
{{if $auth.Forward.AuthResponseHeaders }}
|
||||
authResponseHeaders = [{{range $auth.Forward.AuthResponseHeaders }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
|
||||
{{if $auth.Forward.TLS }}
|
||||
[frontends."frontend-{{ $frontendName }}".auth.forward.tls]
|
||||
ca = "{{ $auth.Forward.TLS.CA }}"
|
||||
caOptional = {{ $auth.Forward.TLS.CAOptional }}
|
||||
cert = "{{ $auth.Forward.TLS.Cert }}"
|
||||
key = "{{ $auth.Forward.TLS.Key }}"
|
||||
cert = """{{ $auth.Forward.TLS.Cert }}"""
|
||||
key = """{{ $auth.Forward.TLS.Key }}"""
|
||||
insecureSkipVerify = {{ $auth.Forward.TLS.InsecureSkipVerify }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if $auth.Basic }}
|
||||
[frontends."frontend-{{ $frontendName }}".auth.basic]
|
||||
removeHeader = {{ $auth.Basic.RemoveHeader }}
|
||||
{{if $auth.Basic.Users }}
|
||||
users = [{{range $auth.Basic.Users }}
|
||||
"{{.}}",
|
||||
@@ -689,6 +747,7 @@ var _templatesDockerTmpl = []byte(`{{$backendServers := .Servers}}
|
||||
|
||||
{{if $auth.Digest }}
|
||||
[frontends."frontend-{{ $frontendName }}".auth.digest]
|
||||
removeHeader = {{ $auth.Digest.RemoveHeader }}
|
||||
{{if $auth.Digest.Users }}
|
||||
users = [{{range $auth.Digest.Users }}
|
||||
"{{.}}",
|
||||
@@ -883,13 +942,13 @@ var _templatesEcsTmpl = []byte(`[backends]
|
||||
{{range $serviceName, $instances := .Services }}
|
||||
{{ $firstInstance := index $instances 0 }}
|
||||
|
||||
{{ $circuitBreaker := getCircuitBreaker $firstInstance.TraefikLabels }}
|
||||
{{ $circuitBreaker := getCircuitBreaker $firstInstance.SegmentLabels }}
|
||||
{{if $circuitBreaker }}
|
||||
[backends."backend-{{ $serviceName }}".circuitBreaker]
|
||||
expression = "{{ $circuitBreaker.Expression }}"
|
||||
{{end}}
|
||||
|
||||
{{ $loadBalancer := getLoadBalancer $firstInstance.TraefikLabels }}
|
||||
{{ $loadBalancer := getLoadBalancer $firstInstance.SegmentLabels }}
|
||||
{{if $loadBalancer }}
|
||||
[backends."backend-{{ $serviceName }}".loadBalancer]
|
||||
method = "{{ $loadBalancer.Method }}"
|
||||
@@ -900,14 +959,14 @@ var _templatesEcsTmpl = []byte(`[backends]
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $maxConn := getMaxConn $firstInstance.TraefikLabels }}
|
||||
{{ $maxConn := getMaxConn $firstInstance.SegmentLabels }}
|
||||
{{if $maxConn }}
|
||||
[backends."backend-{{ $serviceName }}".maxConn]
|
||||
extractorFunc = "{{ $maxConn.ExtractorFunc }}"
|
||||
amount = {{ $maxConn.Amount }}
|
||||
{{end}}
|
||||
|
||||
{{ $healthCheck := getHealthCheck $firstInstance.TraefikLabels }}
|
||||
{{ $healthCheck := getHealthCheck $firstInstance.SegmentLabels }}
|
||||
{{if $healthCheck }}
|
||||
[backends."backend-{{ $serviceName }}".healthCheck]
|
||||
scheme = "{{ $healthCheck.Scheme }}"
|
||||
@@ -923,7 +982,7 @@ var _templatesEcsTmpl = []byte(`[backends]
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $buffering := getBuffering $firstInstance.TraefikLabels }}
|
||||
{{ $buffering := getBuffering $firstInstance.SegmentLabels }}
|
||||
{{if $buffering }}
|
||||
[backends."backend-{{ $serviceName }}".buffering]
|
||||
maxRequestBodyBytes = {{ $buffering.MaxRequestBodyBytes }}
|
||||
@@ -945,38 +1004,69 @@ var _templatesEcsTmpl = []byte(`[backends]
|
||||
{{range $serviceName, $instances := .Services }}
|
||||
{{range $instance := filterFrontends $instances }}
|
||||
|
||||
[frontends."frontend-{{ $serviceName }}"]
|
||||
backend = "backend-{{ $serviceName }}"
|
||||
priority = {{ getPriority $instance.TraefikLabels }}
|
||||
passHostHeader = {{ getPassHostHeader $instance.TraefikLabels }}
|
||||
passTLSCert = {{ getPassTLSCert $instance.TraefikLabels }}
|
||||
{{ $frontendName := getFrontendName $instance }}
|
||||
|
||||
entryPoints = [{{range getEntryPoints $instance.TraefikLabels }}
|
||||
[frontends."frontend-{{ $frontendName }}"]
|
||||
backend = "backend-{{ $serviceName }}"
|
||||
priority = {{ getPriority $instance.SegmentLabels }}
|
||||
passHostHeader = {{ getPassHostHeader $instance.SegmentLabels }}
|
||||
passTLSCert = {{ getPassTLSCert $instance.SegmentLabels }}
|
||||
|
||||
entryPoints = [{{range getEntryPoints $instance.SegmentLabels }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $auth := getAuth $instance.TraefikLabels }}
|
||||
{{ $tlsClientCert := getPassTLSClientCert $instance.SegmentLabels }}
|
||||
{{if $tlsClientCert }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert]
|
||||
pem = {{ $tlsClientCert.PEM }}
|
||||
{{ $infos := $tlsClientCert.Infos }}
|
||||
{{if $infos }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos]
|
||||
notAfter = {{ $infos.NotAfter }}
|
||||
notBefore = {{ $infos.NotBefore }}
|
||||
sans = {{ $infos.Sans }}
|
||||
{{ $subject := $infos.Subject }}
|
||||
{{if $subject }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos.subject]
|
||||
country = {{ $subject.Country }}
|
||||
province = {{ $subject.Province }}
|
||||
locality = {{ $subject.Locality }}
|
||||
organization = {{ $subject.Organization }}
|
||||
commonName = {{ $subject.CommonName }}
|
||||
serialNumber = {{ $subject.SerialNumber }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $auth := getAuth $instance.SegmentLabels }}
|
||||
{{if $auth }}
|
||||
[frontends."frontend-{{ $serviceName }}".auth]
|
||||
[frontends."frontend-{{ $frontendName }}".auth]
|
||||
headerField = "{{ $auth.HeaderField }}"
|
||||
|
||||
{{if $auth.Forward }}
|
||||
[frontends."frontend-{{ $serviceName }}".auth.forward]
|
||||
[frontends."frontend-{{ $frontendName }}".auth.forward]
|
||||
address = "{{ $auth.Forward.Address }}"
|
||||
trustForwardHeader = {{ $auth.Forward.TrustForwardHeader }}
|
||||
{{if $auth.Forward.AuthResponseHeaders }}
|
||||
authResponseHeaders = [{{range $auth.Forward.AuthResponseHeaders }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
|
||||
{{if $auth.Forward.TLS }}
|
||||
[frontends."frontend-{{ $serviceName }}".auth.forward.tls]
|
||||
[frontends."frontend-{{ $frontendName }}".auth.forward.tls]
|
||||
ca = "{{ $auth.Forward.TLS.CA }}"
|
||||
caOptional = {{ $auth.Forward.TLS.CAOptional }}
|
||||
cert = "{{ $auth.Forward.TLS.Cert }}"
|
||||
key = "{{ $auth.Forward.TLS.Key }}"
|
||||
cert = """{{ $auth.Forward.TLS.Cert }}"""
|
||||
key = """{{ $auth.Forward.TLS.Key }}"""
|
||||
insecureSkipVerify = {{ $auth.Forward.TLS.InsecureSkipVerify }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if $auth.Basic }}
|
||||
[frontends."frontend-{{ $serviceName }}".auth.basic]
|
||||
[frontends."frontend-{{ $frontendName }}".auth.basic]
|
||||
removeHeader = {{ $auth.Basic.RemoveHeader }}
|
||||
{{if $auth.Basic.Users }}
|
||||
users = [{{range $auth.Basic.Users }}
|
||||
"{{.}}",
|
||||
@@ -986,7 +1076,8 @@ var _templatesEcsTmpl = []byte(`[backends]
|
||||
{{end}}
|
||||
|
||||
{{if $auth.Digest }}
|
||||
[frontends."frontend-{{ $serviceName }}".auth.digest]
|
||||
[frontends."frontend-{{ $frontendName }}".auth.digest]
|
||||
removeHeader = {{ $auth.Digest.RemoveHeader }}
|
||||
{{if $auth.Digest.Users }}
|
||||
users = [{{range $auth.Digest.Users }}
|
||||
"{{.}}",
|
||||
@@ -996,29 +1087,29 @@ var _templatesEcsTmpl = []byte(`[backends]
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $whitelist := getWhiteList $instance.TraefikLabels }}
|
||||
{{ $whitelist := getWhiteList $instance.SegmentLabels }}
|
||||
{{if $whitelist }}
|
||||
[frontends."frontend-{{ $serviceName }}".whiteList]
|
||||
[frontends."frontend-{{ $frontendName }}".whiteList]
|
||||
sourceRange = [{{range $whitelist.SourceRange }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
useXForwardedFor = {{ $whitelist.UseXForwardedFor }}
|
||||
{{end}}
|
||||
|
||||
{{ $redirect := getRedirect $instance.TraefikLabels }}
|
||||
{{ $redirect := getRedirect $instance.SegmentLabels }}
|
||||
{{if $redirect }}
|
||||
[frontends."frontend-{{ $serviceName }}".redirect]
|
||||
[frontends."frontend-{{ $frontendName }}".redirect]
|
||||
entryPoint = "{{ $redirect.EntryPoint }}"
|
||||
regex = "{{ $redirect.Regex }}"
|
||||
replacement = "{{ $redirect.Replacement }}"
|
||||
permanent = {{ $redirect.Permanent }}
|
||||
{{end}}
|
||||
|
||||
{{ $errorPages := getErrorPages $instance.TraefikLabels }}
|
||||
{{ $errorPages := getErrorPages $instance.SegmentLabels }}
|
||||
{{if $errorPages }}
|
||||
[frontends."frontend-{{ $serviceName }}".errors]
|
||||
[frontends."frontend-{{ $frontendName }}".errors]
|
||||
{{range $pageName, $page := $errorPages }}
|
||||
[frontends."frontend-{{ $serviceName }}".errors."{{ $pageName }}"]
|
||||
[frontends."frontend-{{ $frontendName }}".errors."{{ $pageName }}"]
|
||||
status = [{{range $page.Status }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
@@ -1027,22 +1118,22 @@ var _templatesEcsTmpl = []byte(`[backends]
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $rateLimit := getRateLimit $instance.TraefikLabels }}
|
||||
{{ $rateLimit := getRateLimit $instance.SegmentLabels }}
|
||||
{{if $rateLimit }}
|
||||
[frontends."frontend-{{ $serviceName }}".rateLimit]
|
||||
[frontends."frontend-{{ $frontendName }}".rateLimit]
|
||||
extractorFunc = "{{ $rateLimit.ExtractorFunc }}"
|
||||
[frontends."frontend-{{ $serviceName }}".rateLimit.rateSet]
|
||||
[frontends."frontend-{{ $frontendName }}".rateLimit.rateSet]
|
||||
{{ range $limitName, $limit := $rateLimit.RateSet }}
|
||||
[frontends."frontend-{{ $serviceName }}".rateLimit.rateSet."{{ $limitName }}"]
|
||||
[frontends."frontend-{{ $frontendName }}".rateLimit.rateSet."{{ $limitName }}"]
|
||||
period = "{{ $limit.Period }}"
|
||||
average = {{ $limit.Average }}
|
||||
burst = {{ $limit.Burst }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $headers := getHeaders $instance.TraefikLabels }}
|
||||
{{ $headers := getHeaders $instance.SegmentLabels }}
|
||||
{{if $headers }}
|
||||
[frontends."frontend-{{ $serviceName }}".headers]
|
||||
[frontends."frontend-{{ $frontendName }}".headers]
|
||||
SSLRedirect = {{ $headers.SSLRedirect }}
|
||||
SSLTemporaryRedirect = {{ $headers.SSLTemporaryRedirect }}
|
||||
SSLHost = "{{ $headers.SSLHost }}"
|
||||
@@ -1074,28 +1165,28 @@ var _templatesEcsTmpl = []byte(`[backends]
|
||||
{{end}}
|
||||
|
||||
{{if $headers.CustomRequestHeaders }}
|
||||
[frontends."frontend-{{ $serviceName }}".headers.customRequestHeaders]
|
||||
[frontends."frontend-{{ $frontendName }}".headers.customRequestHeaders]
|
||||
{{range $k, $v := $headers.CustomRequestHeaders }}
|
||||
{{$k}} = "{{$v}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if $headers.CustomResponseHeaders }}
|
||||
[frontends."frontend-{{ $serviceName }}".headers.customResponseHeaders]
|
||||
[frontends."frontend-{{ $frontendName }}".headers.customResponseHeaders]
|
||||
{{range $k, $v := $headers.CustomResponseHeaders }}
|
||||
{{$k}} = "{{$v}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if $headers.SSLProxyHeaders }}
|
||||
[frontends."frontend-{{ $serviceName }}".headers.SSLProxyHeaders]
|
||||
[frontends."frontend-{{ $frontendName }}".headers.SSLProxyHeaders]
|
||||
{{range $k, $v := $headers.SSLProxyHeaders }}
|
||||
{{$k}} = "{{$v}}"
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
[frontends."frontend-{{ $serviceName }}".routes."route-frontend-{{ $serviceName }}"]
|
||||
[frontends."frontend-{{ $frontendName }}".routes."route-frontend-{{ $frontendName }}"]
|
||||
rule = "{{ getFrontendRule $instance }}"
|
||||
|
||||
{{end}}
|
||||
@@ -1217,6 +1308,7 @@ var _templatesKubernetesTmpl = []byte(`[backends]
|
||||
|
||||
{{if $frontend.Auth.Basic }}
|
||||
[frontends."{{ $frontendName }}".auth.basic]
|
||||
removeHeader = {{$frontend.Auth.Basic.RemoveHeader}}
|
||||
users = [{{range $frontend.Auth.Basic.Users }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
@@ -1224,6 +1316,7 @@ var _templatesKubernetesTmpl = []byte(`[backends]
|
||||
|
||||
{{if $frontend.Auth.Digest }}
|
||||
[frontends."{{ $frontendName }}".auth.digest]
|
||||
removeHeader = {{$frontend.Auth.Digest.RemoveHeader}}
|
||||
users = [{{range $frontend.Auth.Digest.Users }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
@@ -1238,8 +1331,8 @@ var _templatesKubernetesTmpl = []byte(`[backends]
|
||||
trustForwardHeader = {{ $frontend.Auth.Forward.TrustForwardHeader }}
|
||||
{{if $frontend.Auth.Forward.TLS }}
|
||||
[frontends."{{ $frontendName }}".auth.forward.tls]
|
||||
cert = "{{ $frontend.Auth.Forward.TLS.Cert }}"
|
||||
key = "{{ $frontend.Auth.Forward.TLS.Key }}"
|
||||
cert = """{{ $frontend.Auth.Forward.TLS.Cert }}"""
|
||||
key = """{{ $frontend.Auth.Forward.TLS.Key }}"""
|
||||
insecureSkipVerify = {{ $frontend.Auth.Forward.TLS.InsecureSkipVerify }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
@@ -1398,14 +1491,14 @@ var _templatesKvTmpl = []byte(`[backends]
|
||||
|
||||
{{ $healthCheck := getHealthCheck $backend }}
|
||||
{{if $healthCheck }}
|
||||
[backends.{{ $backendName }}.healthCheck]
|
||||
[backends."{{ $backendName }}".healthCheck]
|
||||
scheme = "{{ $healthCheck.Scheme }}"
|
||||
path = "{{ $healthCheck.Path }}"
|
||||
port = {{ $healthCheck.Port }}
|
||||
interval = "{{ $healthCheck.Interval }}"
|
||||
hostname = "{{ $healthCheck.Hostname }}"
|
||||
{{if $healthCheck.Headers }}
|
||||
[backends.{{ $backendName }}.healthCheck.headers]
|
||||
[backends."{{ $backendName }}".healthCheck.headers]
|
||||
{{range $k, $v := $healthCheck.Headers }}
|
||||
{{$k}} = "{{$v}}"
|
||||
{{end}}
|
||||
@@ -1414,7 +1507,7 @@ var _templatesKvTmpl = []byte(`[backends]
|
||||
|
||||
{{ $buffering := getBuffering $backend }}
|
||||
{{if $buffering }}
|
||||
[backends.{{ $backendName }}.buffering]
|
||||
[backends."{{ $backendName }}".buffering]
|
||||
maxRequestBodyBytes = {{ $buffering.MaxRequestBodyBytes }}
|
||||
memRequestBodyBytes = {{ $buffering.MemRequestBodyBytes }}
|
||||
maxResponseBodyBytes = {{ $buffering.MaxResponseBodyBytes }}
|
||||
@@ -1444,6 +1537,29 @@ var _templatesKvTmpl = []byte(`[backends]
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $tlsClientCert := getPassTLSClientCert $frontend }}
|
||||
{{if $tlsClientCert }}
|
||||
[frontends."{{ $frontendName }}".passTLSClientCert]
|
||||
pem = {{ $tlsClientCert.PEM }}
|
||||
{{ $infos := $tlsClientCert.Infos }}
|
||||
{{if $infos }}
|
||||
[frontends."{{ $frontendName }}".passTLSClientCert.infos]
|
||||
notAfter = {{ $infos.NotAfter }}
|
||||
notBefore = {{ $infos.NotBefore }}
|
||||
sans = {{ $infos.Sans }}
|
||||
{{ $subject := $infos.Subject }}
|
||||
{{if $subject }}
|
||||
[frontends."{{ $frontendName }}".passTLSClientCert.infos.subject]
|
||||
country = {{ $subject.Country }}
|
||||
province = {{ $subject.Province }}
|
||||
locality = {{ $subject.Locality }}
|
||||
organization = {{ $subject.Organization }}
|
||||
commonName = {{ $subject.CommonName }}
|
||||
serialNumber = {{ $subject.SerialNumber }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $auth := getAuth $frontend }}
|
||||
{{if $auth }}
|
||||
[frontends."{{ $frontendName }}".auth]
|
||||
@@ -1453,19 +1569,25 @@ var _templatesKvTmpl = []byte(`[backends]
|
||||
[frontends."{{ $frontendName }}".auth.forward]
|
||||
address = "{{ $auth.Forward.Address }}"
|
||||
trustForwardHeader = {{ $auth.Forward.TrustForwardHeader }}
|
||||
{{if $auth.Forward.AuthResponseHeaders }}
|
||||
authResponseHeaders = [{{range $auth.Forward.AuthResponseHeaders }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
|
||||
{{if $auth.Forward.TLS }}
|
||||
[frontends."{{ $frontendName }}".auth.forward.tls]
|
||||
ca = "{{ $auth.Forward.TLS.CA }}"
|
||||
caOptional = {{ $auth.Forward.TLS.CAOptional }}
|
||||
cert = "{{ $auth.Forward.TLS.Cert }}"
|
||||
key = "{{ $auth.Forward.TLS.Key }}"
|
||||
cert = """{{ $auth.Forward.TLS.Cert }}"""
|
||||
key = """{{ $auth.Forward.TLS.Key }}"""
|
||||
insecureSkipVerify = {{ $auth.Forward.TLS.InsecureSkipVerify }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if $auth.Basic }}
|
||||
[frontends."{{ $frontendName }}".auth.basic]
|
||||
removeHeader = {{ $auth.Basic.RemoveHeader }}
|
||||
{{if $auth.Basic.Users }}
|
||||
users = [{{range $auth.Basic.Users }}
|
||||
"{{.}}",
|
||||
@@ -1476,6 +1598,7 @@ var _templatesKvTmpl = []byte(`[backends]
|
||||
|
||||
{{if $auth.Digest }}
|
||||
[frontends."{{ $frontendName }}".auth.digest]
|
||||
removeHeader = {{ $auth.Digest.RemoveHeader }}
|
||||
{{if $auth.Digest.Users }}
|
||||
users = [{{range $auth.Digest.Users }}
|
||||
"{{.}}",
|
||||
@@ -1784,7 +1907,30 @@ var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $auth := getAuth $app.SegmentLabels }}
|
||||
{{ $tlsClientCert := getPassTLSClientCert $app.SegmentLabels }}
|
||||
{{if $tlsClientCert }}
|
||||
[frontends."{{ $frontendName }}".passTLSClientCert]
|
||||
pem = {{ $tlsClientCert.PEM }}
|
||||
{{ $infos := $tlsClientCert.Infos }}
|
||||
{{if $infos }}
|
||||
[frontends."{{ $frontendName }}".passTLSClientCert.infos]
|
||||
notAfter = {{ $infos.NotAfter }}
|
||||
notBefore = {{ $infos.NotBefore }}
|
||||
sans = {{ $infos.Sans }}
|
||||
{{ $subject := $infos.Subject }}
|
||||
{{if $subject }}
|
||||
[frontends."{{ $frontendName }}".passTLSClientCert.infos.subject]
|
||||
country = {{ $subject.Country }}
|
||||
province = {{ $subject.Province }}
|
||||
locality = {{ $subject.Locality }}
|
||||
organization = {{ $subject.Organization }}
|
||||
commonName = {{ $subject.CommonName }}
|
||||
serialNumber = {{ $subject.SerialNumber }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $auth := getAuth $app.SegmentLabels }}
|
||||
{{if $auth }}
|
||||
[frontends."{{ $frontendName }}".auth]
|
||||
headerField = "{{ $auth.HeaderField }}"
|
||||
@@ -1793,19 +1939,25 @@ var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }}
|
||||
[frontends."{{ $frontendName }}".auth.forward]
|
||||
address = "{{ $auth.Forward.Address }}"
|
||||
trustForwardHeader = {{ $auth.Forward.TrustForwardHeader }}
|
||||
{{if $auth.Forward.AuthResponseHeaders }}
|
||||
authResponseHeaders = [{{range $auth.Forward.AuthResponseHeaders }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
|
||||
{{if $auth.Forward.TLS }}
|
||||
[frontends."{{ $frontendName }}".auth.forward.tls]
|
||||
ca = "{{ $auth.Forward.TLS.CA }}"
|
||||
caOptional = {{ $auth.Forward.TLS.CAOptional }}
|
||||
cert = "{{ $auth.Forward.TLS.Cert }}"
|
||||
key = "{{ $auth.Forward.TLS.Key }}"
|
||||
cert = """{{ $auth.Forward.TLS.Cert }}"""
|
||||
key = """{{ $auth.Forward.TLS.Key }}"""
|
||||
insecureSkipVerify = {{ $auth.Forward.TLS.InsecureSkipVerify }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if $auth.Basic }}
|
||||
[frontends."{{ $frontendName }}".auth.basic]
|
||||
removeHeader = {{ $auth.Basic.RemoveHeader }}
|
||||
{{if $auth.Basic.Users }}
|
||||
users = [{{range $auth.Basic.Users }}
|
||||
"{{.}}",
|
||||
@@ -1816,6 +1968,7 @@ var _templatesMarathonTmpl = []byte(`{{ $apps := .Applications }}
|
||||
|
||||
{{if $auth.Digest }}
|
||||
[frontends."{{ $frontendName }}".auth.digest]
|
||||
removeHeader = {{ $auth.Digest.RemoveHeader }}
|
||||
{{if $auth.Digest.Users }}
|
||||
users = [{{range $auth.Digest.Users }}
|
||||
"{{.}}",
|
||||
@@ -2068,6 +2221,29 @@ var _templatesMesosTmpl = []byte(`[backends]
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $tlsClientCert := getPassTLSClientCert $app.TraefikLabels }}
|
||||
{{if $tlsClientCert }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert]
|
||||
pem = {{ $tlsClientCert.PEM }}
|
||||
{{ $infos := $tlsClientCert.Infos }}
|
||||
{{if $infos }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos]
|
||||
notAfter = {{ $infos.NotAfter }}
|
||||
notBefore = {{ $infos.NotBefore }}
|
||||
sans = {{ $infos.Sans }}
|
||||
{{ $subject := $infos.Subject }}
|
||||
{{if $subject }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos.subject]
|
||||
country = {{ $subject.Country }}
|
||||
province = {{ $subject.Province }}
|
||||
locality = {{ $subject.Locality }}
|
||||
organization = {{ $subject.Organization }}
|
||||
commonName = {{ $subject.CommonName }}
|
||||
serialNumber = {{ $subject.SerialNumber }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $auth := getAuth $app.TraefikLabels }}
|
||||
{{if $auth }}
|
||||
[frontends."frontend-{{ $frontendName }}".auth]
|
||||
@@ -2077,19 +2253,25 @@ var _templatesMesosTmpl = []byte(`[backends]
|
||||
[frontends."frontend-{{ $frontendName }}".auth.forward]
|
||||
address = "{{ $auth.Forward.Address }}"
|
||||
trustForwardHeader = {{ $auth.Forward.TrustForwardHeader }}
|
||||
{{if $auth.Forward.AuthResponseHeaders }}
|
||||
authResponseHeaders = [{{range $auth.Forward.AuthResponseHeaders }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
|
||||
{{if $auth.Forward.TLS }}
|
||||
[frontends."frontend-{{ $frontendName }}".auth.forward.tls]
|
||||
ca = "{{ $auth.Forward.TLS.CA }}"
|
||||
caOptional = {{ $auth.Forward.TLS.CAOptional }}
|
||||
cert = "{{ $auth.Forward.TLS.Cert }}"
|
||||
key = "{{ $auth.Forward.TLS.Key }}"
|
||||
cert = """{{ $auth.Forward.TLS.Cert }}"""
|
||||
key = """{{ $auth.Forward.TLS.Key }}"""
|
||||
insecureSkipVerify = {{ $auth.Forward.TLS.InsecureSkipVerify }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if $auth.Basic }}
|
||||
[frontends."frontend-{{ $frontendName }}".auth.basic]
|
||||
removeHeader = {{ $auth.Basic.RemoveHeader}}
|
||||
{{if $auth.Basic.Users }}
|
||||
users = [{{range $auth.Basic.Users }}
|
||||
"{{.}}",
|
||||
@@ -2100,6 +2282,7 @@ var _templatesMesosTmpl = []byte(`[backends]
|
||||
|
||||
{{if $auth.Digest }}
|
||||
[frontends."frontend-{{ $frontendName }}".auth.digest]
|
||||
removeHeader = {{ $auth.Digest.RemoveHeader}}
|
||||
{{if $auth.Digest.Users }}
|
||||
users = [{{range $auth.Digest.Users }}
|
||||
"{{.}}",
|
||||
@@ -2405,6 +2588,29 @@ var _templatesRancherTmpl = []byte(`{{ $backendServers := .Backends }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
|
||||
{{ $tlsClientCert := getPassTLSClientCert $service.SegmentLabels }}
|
||||
{{if $tlsClientCert }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert]
|
||||
pem = {{ $tlsClientCert.PEM }}
|
||||
{{ $infos := $tlsClientCert.Infos }}
|
||||
{{if $infos }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos]
|
||||
notAfter = {{ $infos.NotAfter }}
|
||||
notBefore = {{ $infos.NotBefore }}
|
||||
sans = {{ $infos.Sans }}
|
||||
{{ $subject := $infos.Subject }}
|
||||
{{if $subject }}
|
||||
[frontends."frontend-{{ $frontendName }}".passTLSClientCert.infos.subject]
|
||||
country = {{ $subject.Country }}
|
||||
province = {{ $subject.Province }}
|
||||
locality = {{ $subject.Locality }}
|
||||
organization = {{ $subject.Organization }}
|
||||
commonName = {{ $subject.CommonName }}
|
||||
serialNumber = {{ $subject.SerialNumber }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{ $auth := getAuth $service.SegmentLabels }}
|
||||
{{if $auth }}
|
||||
[frontends."frontend-{{ $frontendName }}".auth]
|
||||
@@ -2414,19 +2620,25 @@ var _templatesRancherTmpl = []byte(`{{ $backendServers := .Backends }}
|
||||
[frontends."frontend-{{ $frontendName }}".auth.forward]
|
||||
address = "{{ $auth.Forward.Address }}"
|
||||
trustForwardHeader = {{ $auth.Forward.TrustForwardHeader }}
|
||||
{{if $auth.Forward.AuthResponseHeaders }}
|
||||
authResponseHeaders = [{{range $auth.Forward.AuthResponseHeaders }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
|
||||
{{if $auth.Forward.TLS }}
|
||||
[frontends."frontend-{{ $frontendName }}".auth.forward.tls]
|
||||
ca = "{{ $auth.Forward.TLS.CA }}"
|
||||
caOptional = {{ $auth.Forward.TLS.CAOptional }}
|
||||
cert = "{{ $auth.Forward.TLS.Cert }}"
|
||||
key = "{{ $auth.Forward.TLS.Key }}"
|
||||
cert = """{{ $auth.Forward.TLS.Cert }}"""
|
||||
key = """{{ $auth.Forward.TLS.Key }}"""
|
||||
insecureSkipVerify = {{ $auth.Forward.TLS.InsecureSkipVerify }}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{if $auth.Basic }}
|
||||
[frontends."frontend-{{ $frontendName }}".auth.basic]
|
||||
removeHeader = {{ $auth.Basic.RemoveHeader }}
|
||||
{{if $auth.Basic.Users }}
|
||||
users = [{{range $auth.Basic.Users }}
|
||||
"{{.}}",
|
||||
@@ -2437,6 +2649,7 @@ var _templatesRancherTmpl = []byte(`{{ $backendServers := .Backends }}
|
||||
|
||||
{{if $auth.Digest }}
|
||||
[frontends."frontend-{{ $frontendName }}".auth.digest]
|
||||
removeHeader = {{ $auth.Digest.RemoveHeader }}
|
||||
{{if $auth.Digest.Users }}
|
||||
users = [{{range $auth.Digest.Users }}
|
||||
"{{.}}",
|
||||
|
@@ -1,11 +1,11 @@
|
||||
FROM golang:1.10-alpine
|
||||
FROM golang:1.11-alpine
|
||||
|
||||
RUN apk --update upgrade \
|
||||
&& apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
RUN go get github.com/containous/go-bindata/... \
|
||||
&& go get github.com/golang/lint/golint \
|
||||
&& go get golang.org/x/lint/golint \
|
||||
&& go get github.com/kisielk/errcheck \
|
||||
&& go get github.com/client9/misspell/cmd/misspell
|
||||
|
||||
|
@@ -78,7 +78,7 @@ func (d *Datastore) watchChanges() error {
|
||||
stopCh := make(chan struct{})
|
||||
kvCh, err := d.kv.Watch(d.lockKey, stopCh, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error while watching key %s: %v", d.lockKey, err)
|
||||
}
|
||||
safe.Go(func() {
|
||||
ctx, cancel := context.WithCancel(d.ctx)
|
||||
|
@@ -94,7 +94,7 @@ func NewCmd(traefikConfiguration *cmd.TraefikConfiguration, traefikPointersConfi
|
||||
Description: `Report an issue on Traefik bugtracker`,
|
||||
Config: traefikConfiguration,
|
||||
DefaultPointersConfig: traefikPointersConfiguration,
|
||||
Run: runCmd(traefikConfiguration),
|
||||
Run: runCmd(traefikConfiguration),
|
||||
Metadata: map[string]string{
|
||||
"parseAllSources": "true",
|
||||
},
|
||||
|
@@ -34,7 +34,7 @@ func Test_createReport(t *testing.T) {
|
||||
File: &file.Provider{
|
||||
Directory: "BAR",
|
||||
},
|
||||
RootCAs: tls.RootCAs{"fllf"},
|
||||
RootCAs: tls.FilesOrContents{"fllf"},
|
||||
},
|
||||
}
|
||||
|
||||
|
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/containous/traefik/configuration"
|
||||
"github.com/containous/traefik/middlewares/accesslog"
|
||||
"github.com/containous/traefik/middlewares/tracing"
|
||||
"github.com/containous/traefik/middlewares/tracing/datadog"
|
||||
"github.com/containous/traefik/middlewares/tracing/jaeger"
|
||||
"github.com/containous/traefik/middlewares/tracing/zipkin"
|
||||
"github.com/containous/traefik/ping"
|
||||
@@ -218,8 +219,9 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||
|
||||
// default Tracing
|
||||
defaultTracing := tracing.Tracing{
|
||||
Backend: "jaeger",
|
||||
ServiceName: "traefik",
|
||||
Backend: "jaeger",
|
||||
ServiceName: "traefik",
|
||||
SpanNameLimit: 0,
|
||||
Jaeger: &jaeger.Config{
|
||||
SamplingServerURL: "http://localhost:5778/sampling",
|
||||
SamplingType: "const",
|
||||
@@ -232,6 +234,11 @@ func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
|
||||
ID128Bit: true,
|
||||
Debug: false,
|
||||
},
|
||||
DataDog: &datadog.Config{
|
||||
LocalAgentHostPort: "localhost:8126",
|
||||
GlobalTag: "",
|
||||
Debug: false,
|
||||
},
|
||||
}
|
||||
|
||||
// default LifeCycle
|
||||
|
@@ -20,7 +20,7 @@ func NewCmd(traefikConfiguration *cmd.TraefikConfiguration, traefikPointersConfi
|
||||
Description: `Calls traefik /ping to check health (web provider must be enabled)`,
|
||||
Config: traefikConfiguration,
|
||||
DefaultPointersConfig: traefikPointersConfiguration,
|
||||
Run: runCmd(traefikConfiguration),
|
||||
Run: runCmd(traefikConfiguration),
|
||||
Metadata: map[string]string{
|
||||
"parseAllSources": "true",
|
||||
},
|
||||
|
@@ -85,8 +85,13 @@ func Run(kv *staert.KvSource, traefikConfiguration *cmd.TraefikConfiguration) fu
|
||||
}
|
||||
}
|
||||
|
||||
accountInitialized, err := keyExists(kv, traefikConfiguration.GlobalConfiguration.ACME.Storage)
|
||||
if err != nil && err != store.ErrKeyNotFound {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check to see if ACME account object is already in kv store
|
||||
if traefikConfiguration.GlobalConfiguration.ACME.OverrideCertificates {
|
||||
if traefikConfiguration.GlobalConfiguration.ACME.OverrideCertificates || !accountInitialized {
|
||||
|
||||
// Store the ACME Account into the KV Store
|
||||
// Certificates in KV Store will be overridden
|
||||
@@ -114,6 +119,15 @@ func Run(kv *staert.KvSource, traefikConfiguration *cmd.TraefikConfiguration) fu
|
||||
}
|
||||
}
|
||||
|
||||
func keyExists(source *staert.KvSource, key string) (bool, error) {
|
||||
list, err := source.List(key, nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return len(list) > 0, nil
|
||||
}
|
||||
|
||||
// migrateACMEData allows migrating data from acme.json file to KV store in function of the file format
|
||||
func migrateACMEData(fileName string) (*acme.Account, error) {
|
||||
|
||||
|
@@ -66,7 +66,7 @@ Complete documentation is available at https://traefik.io`,
|
||||
// add custom parsers
|
||||
f.AddParser(reflect.TypeOf(configuration.EntryPoints{}), &configuration.EntryPoints{})
|
||||
f.AddParser(reflect.TypeOf(configuration.DefaultEntryPoints{}), &configuration.DefaultEntryPoints{})
|
||||
f.AddParser(reflect.TypeOf(traefiktls.RootCAs{}), &traefiktls.RootCAs{})
|
||||
f.AddParser(reflect.TypeOf(traefiktls.FilesOrContents{}), &traefiktls.FilesOrContents{})
|
||||
f.AddParser(reflect.TypeOf(types.Constraints{}), &types.Constraints{})
|
||||
f.AddParser(reflect.TypeOf(kubernetes.Namespaces{}), &kubernetes.Namespaces{})
|
||||
f.AddParser(reflect.TypeOf(ecs.Clusters{}), &ecs.Clusters{})
|
||||
@@ -165,26 +165,37 @@ func runCmd(globalConfiguration *configuration.GlobalConfiguration, configFile s
|
||||
globalConfiguration.SetEffectiveConfiguration(configFile)
|
||||
globalConfiguration.ValidateConfiguration()
|
||||
|
||||
log.Infof("Traefik version %s built on %s", version.Version, version.BuildDate)
|
||||
|
||||
jsonConf, err := json.Marshal(globalConfiguration)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
log.Debugf("Global configuration loaded [struct] %#v", globalConfiguration)
|
||||
} else {
|
||||
log.Debugf("Global configuration loaded %s", string(jsonConf))
|
||||
}
|
||||
|
||||
if globalConfiguration.API != nil && globalConfiguration.API.Dashboard {
|
||||
globalConfiguration.API.DashboardAssets = &assetfs.AssetFS{Asset: genstatic.Asset, AssetInfo: genstatic.AssetInfo, AssetDir: genstatic.AssetDir, Prefix: "static"}
|
||||
}
|
||||
|
||||
jsonConf, _ := json.Marshal(globalConfiguration)
|
||||
log.Infof("Traefik version %s built on %s", version.Version, version.BuildDate)
|
||||
|
||||
if globalConfiguration.CheckNewVersion {
|
||||
checkNewVersion()
|
||||
}
|
||||
|
||||
stats(globalConfiguration)
|
||||
|
||||
log.Debugf("Global configuration loaded %s", string(jsonConf))
|
||||
|
||||
providerAggregator := configuration.NewProviderAggregator(globalConfiguration)
|
||||
|
||||
acmeprovider := globalConfiguration.InitACMEProvider()
|
||||
if acmeprovider != nil {
|
||||
providerAggregator.AddProvider(acmeprovider)
|
||||
acmeprovider, err := globalConfiguration.InitACMEProvider()
|
||||
if err != nil {
|
||||
log.Errorf("Unable to initialize ACME provider: %v", err)
|
||||
} else if acmeprovider != nil {
|
||||
err = providerAggregator.AddProvider(acmeprovider)
|
||||
if err != nil {
|
||||
log.Errorf("Unable to add ACME provider to the providers list: %v", err)
|
||||
acmeprovider = nil
|
||||
}
|
||||
}
|
||||
|
||||
entryPoints := map[string]server.EntryPoint{}
|
||||
@@ -196,22 +207,24 @@ func runCmd(globalConfiguration *configuration.GlobalConfiguration, configFile s
|
||||
|
||||
internalRouter := router.NewInternalRouterAggregator(*globalConfiguration, entryPointName)
|
||||
if acmeprovider != nil {
|
||||
if acmeprovider.HTTPChallenge != nil && acmeprovider.HTTPChallenge.EntryPoint == entryPointName {
|
||||
if acmeprovider.HTTPChallenge != nil && entryPointName == acmeprovider.HTTPChallenge.EntryPoint {
|
||||
internalRouter.AddRouter(acmeprovider)
|
||||
}
|
||||
|
||||
// TLS ALPN 01
|
||||
if acmeprovider.HTTPChallenge == nil && acmeprovider.DNSChallenge == nil && acmeprovider.TLSChallenge != nil {
|
||||
if acmeprovider.TLSChallenge != nil && acmeprovider.HTTPChallenge == nil && acmeprovider.DNSChallenge == nil {
|
||||
entryPoint.TLSALPNGetter = acmeprovider.GetTLSALPNCertificate
|
||||
}
|
||||
|
||||
if acmeprovider.EntryPoint == entryPointName && acmeprovider.OnDemand {
|
||||
if acmeprovider.OnDemand && entryPointName == acmeprovider.EntryPoint {
|
||||
entryPoint.OnDemandListener = acmeprovider.ListenRequest
|
||||
}
|
||||
|
||||
entryPoint.CertificateStore = traefiktls.NewCertificateStore()
|
||||
acmeprovider.SetCertificateStore(entryPoint.CertificateStore)
|
||||
|
||||
if entryPointName == acmeprovider.EntryPoint {
|
||||
entryPoint.CertificateStore = traefiktls.NewCertificateStore()
|
||||
acmeprovider.SetCertificateStore(entryPoint.CertificateStore)
|
||||
log.Debugf("Setting Acme Certificate store from Entrypoint: %s", entryPointName)
|
||||
}
|
||||
}
|
||||
|
||||
entryPoint.InternalRouter = internalRouter
|
||||
|
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/containous/traefik/api"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/middlewares/tracing"
|
||||
"github.com/containous/traefik/middlewares/tracing/datadog"
|
||||
"github.com/containous/traefik/middlewares/tracing/jaeger"
|
||||
"github.com/containous/traefik/middlewares/tracing/zipkin"
|
||||
"github.com/containous/traefik/ping"
|
||||
@@ -32,6 +33,8 @@ import (
|
||||
"github.com/containous/traefik/provider/zk"
|
||||
"github.com/containous/traefik/tls"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/pkg/errors"
|
||||
lego "github.com/xenolf/lego/acme"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -58,19 +61,19 @@ const (
|
||||
// GlobalConfiguration holds global configuration (with providers, etc.).
|
||||
// It's populated from the traefik configuration file passed as an argument to the binary.
|
||||
type GlobalConfiguration struct {
|
||||
LifeCycle *LifeCycle `description:"Timeouts influencing the server life cycle" export:"true"`
|
||||
GraceTimeOut flaeg.Duration `short:"g" description:"(Deprecated) Duration to give active requests a chance to finish before Traefik stops" export:"true"` // Deprecated
|
||||
Debug bool `short:"d" description:"Enable debug mode" export:"true"`
|
||||
CheckNewVersion bool `description:"Periodically check if a new version has been released" export:"true"`
|
||||
SendAnonymousUsage bool `description:"send periodically anonymous usage statistics" export:"true"`
|
||||
AccessLogsFile string `description:"(Deprecated) Access logs file" export:"true"` // Deprecated
|
||||
AccessLog *types.AccessLog `description:"Access log settings" export:"true"`
|
||||
TraefikLogsFile string `description:"(Deprecated) Traefik logs file. Stdout is used when omitted or empty" export:"true"` // Deprecated
|
||||
TraefikLog *types.TraefikLog `description:"Traefik log settings" export:"true"`
|
||||
Tracing *tracing.Tracing `description:"OpenTracing configuration" export:"true"`
|
||||
LogLevel string `short:"l" description:"Log level" export:"true"`
|
||||
EntryPoints EntryPoints `description:"Entrypoints definition using format: --entryPoints='Name:http Address::8000 Redirect.EntryPoint:https' --entryPoints='Name:https Address::4442 TLS:tests/traefik.crt,tests/traefik.key;prod/traefik.crt,prod/traefik.key'" export:"true"`
|
||||
Cluster *types.Cluster `description:"Enable clustering" export:"true"`
|
||||
LifeCycle *LifeCycle `description:"Timeouts influencing the server life cycle" export:"true"`
|
||||
GraceTimeOut flaeg.Duration `short:"g" description:"(Deprecated) Duration to give active requests a chance to finish before Traefik stops" export:"true"` // Deprecated
|
||||
Debug bool `short:"d" description:"Enable debug mode" export:"true"`
|
||||
CheckNewVersion bool `description:"Periodically check if a new version has been released" export:"true"`
|
||||
SendAnonymousUsage bool `description:"send periodically anonymous usage statistics" export:"true"`
|
||||
AccessLogsFile string `description:"(Deprecated) Access logs file" export:"true"` // Deprecated
|
||||
AccessLog *types.AccessLog `description:"Access log settings" export:"true"`
|
||||
TraefikLogsFile string `description:"(Deprecated) Traefik logs file. Stdout is used when omitted or empty" export:"true"` // Deprecated
|
||||
TraefikLog *types.TraefikLog `description:"Traefik log settings" export:"true"`
|
||||
Tracing *tracing.Tracing `description:"OpenTracing configuration" export:"true"`
|
||||
LogLevel string `short:"l" description:"Log level" export:"true"`
|
||||
EntryPoints EntryPoints `description:"Entrypoints definition using format: --entryPoints='Name:http Address::8000 Redirect.EntryPoint:https' --entryPoints='Name:https Address::4442 TLS:tests/traefik.crt,tests/traefik.key;prod/traefik.crt,prod/traefik.key'" export:"true"`
|
||||
Cluster *types.Cluster
|
||||
Constraints types.Constraints `description:"Filter services by constraint, matching with service tags" export:"true"`
|
||||
ACME *acme.ACME `description:"Enable ACME (Let's Encrypt): automatic SSL" export:"true"`
|
||||
DefaultEntryPoints DefaultEntryPoints `description:"Entrypoints to be used by frontends that do not specify any entrypoint" export:"true"`
|
||||
@@ -78,7 +81,7 @@ type GlobalConfiguration struct {
|
||||
MaxIdleConnsPerHost int `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used" export:"true"`
|
||||
IdleTimeout flaeg.Duration `description:"(Deprecated) maximum amount of time an idle (keep-alive) connection will remain idle before closing itself." export:"true"` // Deprecated
|
||||
InsecureSkipVerify bool `description:"Disable SSL certificate verification" export:"true"`
|
||||
RootCAs tls.RootCAs `description:"Add cert file for self-signed certificate"`
|
||||
RootCAs tls.FilesOrContents `description:"Add cert file for self-signed certificate"`
|
||||
Retry *Retry `description:"Enable retry sending request if network error" export:"true"`
|
||||
HealthCheck *HealthCheckConfig `description:"Health check parameters" export:"true"`
|
||||
RespondingTimeouts *RespondingTimeouts `description:"Timeouts for incoming requests to the Traefik instance" export:"true"`
|
||||
@@ -206,6 +209,11 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
|
||||
entryPoint.WhitelistSourceRange = nil
|
||||
}
|
||||
}
|
||||
|
||||
if entryPoint.TLS != nil && entryPoint.TLS.DefaultCertificate == nil && len(entryPoint.TLS.Certificates) > 0 {
|
||||
log.Infof("No tls.defaultCertificate given for %s: using the first item in tls.certificates as a fallback.", entryPointName)
|
||||
entryPoint.TLS.DefaultCertificate = &entryPoint.TLS.Certificates[0]
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure LifeCycle isn't nil to spare nil checks elsewhere.
|
||||
@@ -332,6 +340,10 @@ func (gc *GlobalConfiguration) initTracing() {
|
||||
log.Warn("Zipkin configuration will be ignored")
|
||||
gc.Tracing.Zipkin = nil
|
||||
}
|
||||
if gc.Tracing.DataDog != nil {
|
||||
log.Warn("DataDog configuration will be ignored")
|
||||
gc.Tracing.DataDog = nil
|
||||
}
|
||||
case zipkin.Name:
|
||||
if gc.Tracing.Zipkin == nil {
|
||||
gc.Tracing.Zipkin = &zipkin.Config{
|
||||
@@ -345,6 +357,26 @@ func (gc *GlobalConfiguration) initTracing() {
|
||||
log.Warn("Jaeger configuration will be ignored")
|
||||
gc.Tracing.Jaeger = nil
|
||||
}
|
||||
if gc.Tracing.DataDog != nil {
|
||||
log.Warn("DataDog configuration will be ignored")
|
||||
gc.Tracing.DataDog = nil
|
||||
}
|
||||
case datadog.Name:
|
||||
if gc.Tracing.DataDog == nil {
|
||||
gc.Tracing.DataDog = &datadog.Config{
|
||||
LocalAgentHostPort: "localhost:8126",
|
||||
GlobalTag: "",
|
||||
Debug: false,
|
||||
}
|
||||
}
|
||||
if gc.Tracing.Zipkin != nil {
|
||||
log.Warn("Zipkin configuration will be ignored")
|
||||
gc.Tracing.Zipkin = nil
|
||||
}
|
||||
if gc.Tracing.Jaeger != nil {
|
||||
log.Warn("Jaeger configuration will be ignored")
|
||||
gc.Tracing.Jaeger = nil
|
||||
}
|
||||
default:
|
||||
log.Warnf("Unknown tracer %q", gc.Tracing.Backend)
|
||||
return
|
||||
@@ -371,6 +403,17 @@ func (gc *GlobalConfiguration) initACMEProvider() {
|
||||
gc.ACME.HTTPChallenge = nil
|
||||
}
|
||||
|
||||
for _, domain := range gc.ACME.Domains {
|
||||
if domain.Main != lego.UnFqdn(domain.Main) {
|
||||
log.Warnf("FQDN detected, please remove the trailing dot: %s", domain.Main)
|
||||
}
|
||||
for _, san := range domain.SANs {
|
||||
if san != lego.UnFqdn(san) {
|
||||
log.Warnf("FQDN detected, please remove the trailing dot: %s", san)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: to remove in the future
|
||||
if len(gc.ACME.StorageFile) > 0 && len(gc.ACME.Storage) == 0 {
|
||||
log.Warn("ACME.StorageFile is deprecated, use ACME.Storage instead")
|
||||
@@ -389,8 +432,13 @@ func (gc *GlobalConfiguration) initACMEProvider() {
|
||||
}
|
||||
|
||||
// InitACMEProvider create an acme provider from the ACME part of globalConfiguration
|
||||
func (gc *GlobalConfiguration) InitACMEProvider() *acmeprovider.Provider {
|
||||
func (gc *GlobalConfiguration) InitACMEProvider() (*acmeprovider.Provider, error) {
|
||||
if gc.ACME != nil {
|
||||
if len(gc.ACME.Storage) == 0 {
|
||||
// Delete the ACME configuration to avoid starting ACME in cluster mode
|
||||
gc.ACME = nil
|
||||
return nil, errors.New("unable to initialize ACME provider with no storage location for the certificates")
|
||||
}
|
||||
// TODO: Remove when Provider ACME will replace totally ACME
|
||||
// If provider file, use Provider ACME instead of ACME
|
||||
if gc.Cluster == nil {
|
||||
@@ -414,10 +462,10 @@ func (gc *GlobalConfiguration) InitACMEProvider() *acmeprovider.Provider {
|
||||
provider.Store = store
|
||||
acme.ConvertToNewFormat(provider.Storage)
|
||||
gc.ACME = nil
|
||||
return provider
|
||||
return provider, nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func getSafeACMECAServer(caServerSrc string) string {
|
||||
|
@@ -5,10 +5,12 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/containous/flaeg"
|
||||
"github.com/containous/traefik/acme"
|
||||
"github.com/containous/traefik/middlewares/tracing"
|
||||
"github.com/containous/traefik/middlewares/tracing/jaeger"
|
||||
"github.com/containous/traefik/middlewares/tracing/zipkin"
|
||||
"github.com/containous/traefik/provider"
|
||||
acmeprovider "github.com/containous/traefik/provider/acme"
|
||||
"github.com/containous/traefik/provider/file"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -215,3 +217,52 @@ func TestSetEffectiveConfigurationTracing(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestInitACMEProvider(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
acmeConfiguration *acme.ACME
|
||||
expectedConfiguration *acmeprovider.Provider
|
||||
noError bool
|
||||
}{
|
||||
{
|
||||
desc: "No ACME configuration",
|
||||
acmeConfiguration: nil,
|
||||
expectedConfiguration: nil,
|
||||
noError: true,
|
||||
},
|
||||
{
|
||||
desc: "ACME configuration with storage",
|
||||
acmeConfiguration: &acme.ACME{Storage: "foo/acme.json"},
|
||||
expectedConfiguration: &acmeprovider.Provider{Configuration: &acmeprovider.Configuration{Storage: "foo/acme.json"}},
|
||||
noError: true,
|
||||
},
|
||||
{
|
||||
desc: "ACME configuration with no storage",
|
||||
acmeConfiguration: &acme.ACME{},
|
||||
expectedConfiguration: nil,
|
||||
noError: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
gc := &GlobalConfiguration{
|
||||
ACME: test.acmeConfiguration,
|
||||
}
|
||||
|
||||
configuration, err := gc.InitACMEProvider()
|
||||
|
||||
assert.True(t, (err == nil) == test.noError)
|
||||
|
||||
if test.expectedConfiguration == nil {
|
||||
assert.Nil(t, configuration)
|
||||
} else {
|
||||
assert.Equal(t, test.expectedConfiguration.Storage, configuration.Storage)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -106,14 +106,16 @@ 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, ","),
|
||||
Users: strings.Split(v, ","),
|
||||
RemoveHeader: toBool(result, "auth_basic_removeheader"),
|
||||
}
|
||||
}
|
||||
|
||||
var digest *types.Digest
|
||||
if v, ok := result["auth_digest_users"]; ok {
|
||||
digest = &types.Digest{
|
||||
Users: strings.Split(v, ","),
|
||||
Users: strings.Split(v, ","),
|
||||
RemoveHeader: toBool(result, "auth_digest_removeheader"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,7 +234,8 @@ func makeEntryPointTLS(result map[string]string) (*tls.TLS, error) {
|
||||
|
||||
if configTLS != nil {
|
||||
if len(result["ca"]) > 0 {
|
||||
files := strings.Split(result["ca"], ",")
|
||||
files := tls.FilesOrContents{}
|
||||
files.Set(result["ca"])
|
||||
optional := toBool(result, "ca_optional")
|
||||
configTLS.ClientCA = tls.ClientCA{
|
||||
Files: files,
|
||||
|
@@ -33,7 +33,9 @@ func Test_parseEntryPointsConfiguration(t *testing.T) {
|
||||
"ProxyProtocol.TrustedIPs:192.168.0.1 " +
|
||||
"ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24 " +
|
||||
"Auth.Basic.Users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0 " +
|
||||
"Auth.Basic.RemoveHeader:true " +
|
||||
"Auth.Digest.Users:test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e " +
|
||||
"Auth.Digest.RemoveHeader:true " +
|
||||
"Auth.HeaderField:X-WebAuth-User " +
|
||||
"Auth.Forward.Address:https://authserver.com/auth " +
|
||||
"Auth.Forward.AuthResponseHeaders:X-Auth,X-Test,X-Secret " +
|
||||
@@ -49,7 +51,9 @@ func Test_parseEntryPointsConfiguration(t *testing.T) {
|
||||
expectedResult: map[string]string{
|
||||
"address": ":8000",
|
||||
"auth_basic_users": "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
"auth_basic_removeheader": "true",
|
||||
"auth_digest_users": "test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e",
|
||||
"auth_digest_removeheader": "true",
|
||||
"auth_forward_address": "https://authserver.com/auth",
|
||||
"auth_forward_authresponseheaders": "X-Auth,X-Test,X-Secret",
|
||||
"auth_forward_tls_ca": "path/to/local.crt",
|
||||
@@ -63,19 +67,19 @@ func Test_parseEntryPointsConfiguration(t *testing.T) {
|
||||
"ca_optional": "true",
|
||||
"compress": "true",
|
||||
"forwardedheaders_trustedips": "10.0.0.3/24,20.0.0.3/24",
|
||||
"name": "foo",
|
||||
"proxyprotocol_trustedips": "192.168.0.1",
|
||||
"redirect_entrypoint": "https",
|
||||
"redirect_permanent": "true",
|
||||
"redirect_regex": "http://localhost/(.*)",
|
||||
"redirect_replacement": "http://mydomain/$1",
|
||||
"tls": "goo,gii",
|
||||
"tls_acme": "TLS",
|
||||
"tls_ciphersuites": "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
||||
"tls_minversion": "VersionTLS11",
|
||||
"whitelistsourcerange": "10.42.0.0/16,152.89.1.33/32,afed:be44::/16",
|
||||
"whitelist_sourcerange": "10.42.0.0/16,152.89.1.33/32,afed:be44::/16",
|
||||
"whitelist_usexforwardedfor": "true",
|
||||
"name": "foo",
|
||||
"proxyprotocol_trustedips": "192.168.0.1",
|
||||
"redirect_entrypoint": "https",
|
||||
"redirect_permanent": "true",
|
||||
"redirect_regex": "http://localhost/(.*)",
|
||||
"redirect_replacement": "http://mydomain/$1",
|
||||
"tls": "goo,gii",
|
||||
"tls_acme": "TLS",
|
||||
"tls_ciphersuites": "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
||||
"tls_minversion": "VersionTLS11",
|
||||
"whitelistsourcerange": "10.42.0.0/16,152.89.1.33/32,afed:be44::/16",
|
||||
"whitelist_sourcerange": "10.42.0.0/16,152.89.1.33/32,afed:be44::/16",
|
||||
"whitelist_usexforwardedfor": "true",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -190,7 +194,9 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||
"ProxyProtocol.TrustedIPs:192.168.0.1 " +
|
||||
"ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24 " +
|
||||
"Auth.Basic.Users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0 " +
|
||||
"Auth.Basic.RemoveHeader:true " +
|
||||
"Auth.Digest.Users:test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e " +
|
||||
"Auth.Digest.RemoveHeader:true " +
|
||||
"Auth.HeaderField:X-WebAuth-User " +
|
||||
"Auth.Forward.Address:https://authserver.com/auth " +
|
||||
"Auth.Forward.AuthResponseHeaders:X-Auth,X-Test,X-Secret " +
|
||||
@@ -220,7 +226,7 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||
},
|
||||
},
|
||||
ClientCA: tls.ClientCA{
|
||||
Files: []string{"car"},
|
||||
Files: tls.FilesOrContents{"car"},
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
@@ -232,12 +238,14 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||
},
|
||||
Auth: &types.Auth{
|
||||
Basic: &types.Basic{
|
||||
RemoveHeader: true,
|
||||
Users: types.Users{
|
||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
},
|
||||
},
|
||||
Digest: &types.Digest{
|
||||
RemoveHeader: true,
|
||||
Users: types.Users{
|
||||
"test:traefik:a2688e031edb4be6a3797f3882655c05",
|
||||
"test2:traefik:518845800f9e2bfb1f1f740ec24f074e",
|
||||
@@ -330,7 +338,7 @@ func TestEntryPoints_Set(t *testing.T) {
|
||||
},
|
||||
},
|
||||
ClientCA: tls.ClientCA{
|
||||
Files: []string{"car"},
|
||||
Files: tls.FilesOrContents{"car"},
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
|
@@ -2,7 +2,6 @@ package configuration
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/provider"
|
||||
@@ -12,82 +11,101 @@ import (
|
||||
|
||||
// ProviderAggregator aggregate providers
|
||||
type ProviderAggregator struct {
|
||||
providers []provider.Provider
|
||||
providers []provider.Provider
|
||||
constraints types.Constraints
|
||||
}
|
||||
|
||||
// NewProviderAggregator return an aggregate of all the providers configured in GlobalConfiguration
|
||||
func NewProviderAggregator(gc *GlobalConfiguration) ProviderAggregator {
|
||||
provider := ProviderAggregator{}
|
||||
provider := ProviderAggregator{
|
||||
constraints: gc.Constraints,
|
||||
}
|
||||
if gc.Docker != nil {
|
||||
provider.providers = append(provider.providers, gc.Docker)
|
||||
provider.quietAddProvider(gc.Docker)
|
||||
}
|
||||
if gc.Marathon != nil {
|
||||
provider.providers = append(provider.providers, gc.Marathon)
|
||||
provider.quietAddProvider(gc.Marathon)
|
||||
}
|
||||
if gc.File != nil {
|
||||
provider.providers = append(provider.providers, gc.File)
|
||||
provider.quietAddProvider(gc.File)
|
||||
}
|
||||
if gc.Rest != nil {
|
||||
provider.providers = append(provider.providers, gc.Rest)
|
||||
provider.quietAddProvider(gc.Rest)
|
||||
}
|
||||
if gc.Consul != nil {
|
||||
provider.providers = append(provider.providers, gc.Consul)
|
||||
provider.quietAddProvider(gc.Consul)
|
||||
}
|
||||
if gc.ConsulCatalog != nil {
|
||||
provider.providers = append(provider.providers, gc.ConsulCatalog)
|
||||
provider.quietAddProvider(gc.ConsulCatalog)
|
||||
}
|
||||
if gc.Etcd != nil {
|
||||
provider.providers = append(provider.providers, gc.Etcd)
|
||||
provider.quietAddProvider(gc.Etcd)
|
||||
}
|
||||
if gc.Zookeeper != nil {
|
||||
provider.providers = append(provider.providers, gc.Zookeeper)
|
||||
provider.quietAddProvider(gc.Zookeeper)
|
||||
}
|
||||
if gc.Boltdb != nil {
|
||||
provider.providers = append(provider.providers, gc.Boltdb)
|
||||
provider.quietAddProvider(gc.Boltdb)
|
||||
}
|
||||
if gc.Kubernetes != nil {
|
||||
provider.providers = append(provider.providers, gc.Kubernetes)
|
||||
provider.quietAddProvider(gc.Kubernetes)
|
||||
}
|
||||
if gc.Mesos != nil {
|
||||
provider.providers = append(provider.providers, gc.Mesos)
|
||||
provider.quietAddProvider(gc.Mesos)
|
||||
}
|
||||
if gc.Eureka != nil {
|
||||
provider.providers = append(provider.providers, gc.Eureka)
|
||||
provider.quietAddProvider(gc.Eureka)
|
||||
}
|
||||
if gc.ECS != nil {
|
||||
provider.providers = append(provider.providers, gc.ECS)
|
||||
provider.quietAddProvider(gc.ECS)
|
||||
}
|
||||
if gc.Rancher != nil {
|
||||
provider.providers = append(provider.providers, gc.Rancher)
|
||||
provider.quietAddProvider(gc.Rancher)
|
||||
}
|
||||
if gc.DynamoDB != nil {
|
||||
provider.providers = append(provider.providers, gc.DynamoDB)
|
||||
provider.quietAddProvider(gc.DynamoDB)
|
||||
}
|
||||
if gc.ServiceFabric != nil {
|
||||
provider.providers = append(provider.providers, gc.ServiceFabric)
|
||||
provider.quietAddProvider(gc.ServiceFabric)
|
||||
}
|
||||
return provider
|
||||
}
|
||||
|
||||
func (p *ProviderAggregator) quietAddProvider(provider provider.Provider) {
|
||||
err := p.AddProvider(provider)
|
||||
if err != nil {
|
||||
log.Errorf("Error initializing provider %T: %v", provider, err)
|
||||
}
|
||||
}
|
||||
|
||||
// AddProvider add a provider in the providers map
|
||||
func (p *ProviderAggregator) AddProvider(provider provider.Provider) {
|
||||
func (p *ProviderAggregator) AddProvider(provider provider.Provider) error {
|
||||
err := provider.Init(p.constraints)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.providers = append(p.providers, provider)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Init the provider
|
||||
func (p ProviderAggregator) Init(_ types.Constraints) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Provide call the provide method of every providers
|
||||
func (p ProviderAggregator) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool, constraints types.Constraints) error {
|
||||
func (p ProviderAggregator) Provide(configurationChan chan<- types.ConfigMessage, pool *safe.Pool) 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.Debugf("Unable to marshal provider conf %T with error: %v", p, err)
|
||||
}
|
||||
log.Infof("Starting provider %v %s", providerType, jsonConf)
|
||||
log.Infof("Starting provider %T %s", p, jsonConf)
|
||||
currentProvider := p
|
||||
safe.Go(func() {
|
||||
err := currentProvider.Provide(configurationChan, pool, constraints)
|
||||
err := currentProvider.Provide(configurationChan, pool)
|
||||
if err != nil {
|
||||
log.Errorf("Error starting provider %v: %s", providerType, err)
|
||||
log.Errorf("Error starting provider %T: %v", p, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@@ -1,11 +1,10 @@
|
||||
FROM alpine
|
||||
FROM alpine:3.7
|
||||
|
||||
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.local/bin
|
||||
|
||||
COPY requirements.txt /mkdocs/
|
||||
WORKDIR /mkdocs
|
||||
VOLUME /mkdocs
|
||||
|
||||
RUN apk --update upgrade \
|
||||
&& apk --no-cache --no-progress add py-pip \
|
||||
&& rm -rf /var/cache/apk/* \
|
||||
&& pip install --user -r requirements.txt
|
||||
RUN apk --no-cache --no-progress add py-pip \
|
||||
&& pip install --user -r requirements.txt
|
||||
|
@@ -122,7 +122,7 @@ In order to use regular expressions with Host and Path matchers, you must declar
|
||||
The variable has no special meaning; however, it is required by the [gorilla/mux](https://github.com/gorilla/mux) dependency which embeds the regular expression and defines the syntax.
|
||||
|
||||
You can optionally enable `passHostHeader` to forward client `Host` header to the backend.
|
||||
You can also optionally enable `passTLSCert` to forward TLS Client certificates to the backend.
|
||||
You can also optionally configure the `passTLSClientCert` option to pass the Client certificates to the backend in a specific header.
|
||||
|
||||
##### Path Matcher Usage Guidelines
|
||||
|
||||
@@ -157,7 +157,8 @@ Here is an example of frontends definition:
|
||||
[frontends.frontend2]
|
||||
backend = "backend1"
|
||||
passHostHeader = true
|
||||
passTLSCert = true
|
||||
[frontends.frontend2.passTLSClientCert]
|
||||
pem = true
|
||||
priority = 10
|
||||
entrypoints = ["https"] # overrides defaultEntryPoints
|
||||
[frontends.frontend2.routes.test_1]
|
||||
|
@@ -102,29 +102,23 @@ entryPoint = "https"
|
||||
#
|
||||
# KeyType = "RSA4096"
|
||||
|
||||
# Domains list.
|
||||
# Only domains defined here can generate wildcard certificates.
|
||||
#
|
||||
# [[acme.domains]]
|
||||
# main = "local1.com"
|
||||
# sans = ["test1.local1.com", "test2.local1.com"]
|
||||
# [[acme.domains]]
|
||||
# main = "local2.com"
|
||||
# [[acme.domains]]
|
||||
# main = "*.local3.com"
|
||||
# sans = ["local3.com", "test1.test1.local3.com"]
|
||||
|
||||
# Use a HTTP-01 ACME challenge.
|
||||
# Use a TLS-ALPN-01 ACME challenge.
|
||||
#
|
||||
# Optional (but recommended)
|
||||
#
|
||||
[acme.httpChallenge]
|
||||
[acme.tlsChallenge]
|
||||
|
||||
# Use a HTTP-01 ACME challenge.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
# [acme.httpChallenge]
|
||||
|
||||
# EntryPoint to use for the HTTP-01 challenges.
|
||||
#
|
||||
# Required
|
||||
#
|
||||
entryPoint = "http"
|
||||
# entryPoint = "http"
|
||||
|
||||
# Use a DNS-01 ACME challenge rather than HTTP-01 challenge.
|
||||
# Note: mandatory for wildcard certificate generation.
|
||||
@@ -147,6 +141,18 @@ entryPoint = "https"
|
||||
# Default: 0
|
||||
#
|
||||
# delayBeforeCheck = 0
|
||||
|
||||
# Domains list.
|
||||
# Only domains defined here can generate wildcard certificates.
|
||||
#
|
||||
# [[acme.domains]]
|
||||
# main = "local1.com"
|
||||
# sans = ["test1.local1.com", "test2.local1.com"]
|
||||
# [[acme.domains]]
|
||||
# main = "local2.com"
|
||||
# [[acme.domains]]
|
||||
# main = "*.local3.com"
|
||||
# sans = ["local3.com", "test1.test1.local3.com"]
|
||||
```
|
||||
|
||||
### `caServer`
|
||||
@@ -164,7 +170,7 @@ caServer = "https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
|
||||
### ACME Challenge
|
||||
|
||||
#### TLS Challenge
|
||||
#### `tlsChallenge`
|
||||
|
||||
Use the `TLS-ALPN-01` challenge to generate and renew ACME certificates by provisioning a TLS certificate.
|
||||
|
||||
@@ -245,44 +251,50 @@ Useful if internal networks block external DNS queries.
|
||||
|
||||
Here is a list of supported `provider`s, that can automate the DNS verification, along with the required environment variables and their [wildcard & root domain support](/configuration/acme/#wildcard-domains) for each. Do not hesitate to complete it.
|
||||
|
||||
| Provider Name | Provider Code | Environment Variables | Wildcard & Root Domain Support |
|
||||
|--------------------------------------------------------|----------------|-----------------------------------------------------------------------------------------------------------------------------|--------------------------------|
|
||||
| [Auroradns](https://www.pcextreme.com/aurora/dns) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` | Not tested yet |
|
||||
| [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP` | Not tested yet |
|
||||
| [Blue Cat](https://www.bluecatnetworks.com/) | `bluecat` | `BLUECAT_SERVER_URL`, `BLUECAT_USER_NAME`, `BLUECAT_PASSWORD`, `BLUECAT_CONFIG_NAME`, `BLUECAT_DNS_VIEW` | Not tested yet |
|
||||
| [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CLOUDFLARE_EMAIL`, `CLOUDFLARE_API_KEY` - The `Global API Key` needs to be used, not the `Origin CA Key` | YES |
|
||||
| [CloudXNS](https://www.cloudxns.net) | `cloudxns` | `CLOUDXNS_API_KEY`, `CLOUDXNS_SECRET_KEY` | Not tested yet |
|
||||
| [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` | YES |
|
||||
| [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` | Not tested yet |
|
||||
| [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` | Not tested yet |
|
||||
| [DNSPod](http://www.dnspod.net/) | `dnspod` | `DNSPOD_API_KEY` | Not tested yet |
|
||||
| [Duck DNS](https://www.duckdns.org/) | `duckdns` | `DUCKDNS_TOKEN` | Not tested yet |
|
||||
| [Dyn](https://dyn.com) | `dyn` | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` | Not tested yet |
|
||||
| External Program | `exec` | `EXEC_PATH` | Not tested yet |
|
||||
| [Exoscale](https://www.exoscale.ch) | `exoscale` | `EXOSCALE_API_KEY`, `EXOSCALE_API_SECRET`, `EXOSCALE_ENDPOINT` | YES |
|
||||
| [Fast DNS](https://www.akamai.com/) | `fastdns` | `AKAMAI_CLIENT_TOKEN`, `AKAMAI_CLIENT_SECRET`, `AKAMAI_ACCESS_TOKEN` | Not tested yet |
|
||||
| [Gandi](https://www.gandi.net) | `gandi` | `GANDI_API_KEY` | Not tested yet |
|
||||
| [Gandi V5](http://doc.livedns.gandi.net) | `gandiv5` | `GANDIV5_API_KEY` | YES |
|
||||
| [Glesys](https://glesys.com/) | `glesys` | `GLESYS_API_USER`, `GLESYS_API_KEY`, `GLESYS_DOMAIN` | Not tested yet |
|
||||
| [GoDaddy](https://godaddy.com/domains) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` | Not tested yet |
|
||||
| [Google Cloud DNS](https://cloud.google.com/dns/docs/) | `gcloud` | `GCE_PROJECT`, `GCE_SERVICE_ACCOUNT_FILE` | YES |
|
||||
| [Lightsail](https://aws.amazon.com/lightsail/) | `lightsail` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `DNS_ZONE` | Not tested yet |
|
||||
| [Linode](https://www.linode.com) | `linode` | `LINODE_API_KEY` | Not tested yet |
|
||||
| manual | - | none, but you need to run Træfik interactively, turn on `acmeLogging` to see instructions and press <kbd>Enter</kbd>. | YES |
|
||||
| [Namecheap](https://www.namecheap.com) | `namecheap` | `NAMECHEAP_API_USER`, `NAMECHEAP_API_KEY` | Not tested yet |
|
||||
| [name.com](https://www.name.com/) | `namedotcom` | `NAMECOM_USERNAME`, `NAMECOM_API_TOKEN`, `NAMECOM_SERVER` | Not tested yet |
|
||||
| [NIFCloud](https://cloud.nifty.com/service/dns.htm) | `nifcloud` | `NIFCLOUD_ACCESS_KEY_ID`, `NIFCLOUD_SECRET_ACCESS_KEY` | Not tested yet |
|
||||
| [Ns1](https://ns1.com/) | `ns1` | `NS1_API_KEY` | Not tested yet |
|
||||
| [Open Telekom Cloud](https://cloud.telekom.de/en/) | `otc` | `OTC_DOMAIN_NAME`, `OTC_USER_NAME`, `OTC_PASSWORD`, `OTC_PROJECT_NAME`, `OTC_IDENTITY_ENDPOINT` | Not tested yet |
|
||||
| [OVH](https://www.ovh.com) | `ovh` | `OVH_ENDPOINT`, `OVH_APPLICATION_KEY`, `OVH_APPLICATION_SECRET`, `OVH_CONSUMER_KEY` | YES |
|
||||
| [PowerDNS](https://www.powerdns.com) | `pdns` | `PDNS_API_KEY`, `PDNS_API_URL` | Not tested yet |
|
||||
| [Rackspace](https://www.rackspace.com/cloud/dns) | `rackspace` | `RACKSPACE_USER`, `RACKSPACE_API_KEY` | Not tested yet |
|
||||
| [RFC2136](https://tools.ietf.org/html/rfc2136) | `rfc2136` | `RFC2136_TSIG_KEY`, `RFC2136_TSIG_SECRET`, `RFC2136_TSIG_ALGORITHM`, `RFC2136_NAMESERVER` | Not tested yet |
|
||||
| [Route 53](https://aws.amazon.com/route53/) | `route53` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_REGION`, `AWS_HOSTED_ZONE_ID` or a configured user/instance IAM profile. | YES |
|
||||
| [Sakura Cloud](https://cloud.sakura.ad.jp/) | `sakuracloud` | `SAKURACLOUD_ACCESS_TOKEN`, `SAKURACLOUD_ACCESS_TOKEN_SECRET` | Not tested yet |
|
||||
| [VegaDNS](https://github.com/shupp/VegaDNS-API) | `vegadns` | `SECRET_VEGADNS_KEY`, `SECRET_VEGADNS_SECRET`, `VEGADNS_URL` | Not tested yet |
|
||||
| [VULTR](https://www.vultr.com) | `vultr` | `VULTR_API_KEY` | Not tested yet |
|
||||
|
||||
| Provider Name | Provider Code | Environment Variables | Wildcard & Root Domain Support |
|
||||
|--------------------------------------------------------|----------------|---------------------------------------------------------------------------------------------------------------------------------|--------------------------------|
|
||||
| [Alibaba Cloud](https://www.vultr.com) | `alidns` | `ALICLOUD_ACCESS_KEY`, `ALICLOUD_SECRET_KEY`, `ALICLOUD_REGION_ID` | Not tested yet |
|
||||
| [Auroradns](https://www.pcextreme.com/aurora/dns) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` | Not tested yet |
|
||||
| [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP` | Not tested yet |
|
||||
| [Blue Cat](https://www.bluecatnetworks.com/) | `bluecat` | `BLUECAT_SERVER_URL`, `BLUECAT_USER_NAME`, `BLUECAT_PASSWORD`, `BLUECAT_CONFIG_NAME`, `BLUECAT_DNS_VIEW` | Not tested yet |
|
||||
| [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CF_API_EMAIL`, `CF_API_KEY` - The `Global API Key` needs to be used, not the `Origin CA Key` | YES |
|
||||
| [CloudXNS](https://www.cloudxns.net) | `cloudxns` | `CLOUDXNS_API_KEY`, `CLOUDXNS_SECRET_KEY` | Not tested yet |
|
||||
| [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` | YES |
|
||||
| [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` | Not tested yet |
|
||||
| [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` | Not tested yet |
|
||||
| [DNSPod](http://www.dnspod.net/) | `dnspod` | `DNSPOD_API_KEY` | Not tested yet |
|
||||
| [DreamHost](https://www.dreamhost.com/) | `dreamhost` | `DREAMHOST_API_KEY` | YES |
|
||||
| [Duck DNS](https://www.duckdns.org/) | `duckdns` | `DUCKDNS_TOKEN` | Not tested yet |
|
||||
| [Dyn](https://dyn.com) | `dyn` | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` | Not tested yet |
|
||||
| External Program | `exec` | `EXEC_PATH` | Not tested yet |
|
||||
| [Exoscale](https://www.exoscale.ch) | `exoscale` | `EXOSCALE_API_KEY`, `EXOSCALE_API_SECRET`, `EXOSCALE_ENDPOINT` | YES |
|
||||
| [Fast DNS](https://www.akamai.com/) | `fastdns` | `AKAMAI_CLIENT_TOKEN`, `AKAMAI_CLIENT_SECRET`, `AKAMAI_ACCESS_TOKEN` | Not tested yet |
|
||||
| [Gandi](https://www.gandi.net) | `gandi` | `GANDI_API_KEY` | Not tested yet |
|
||||
| [Gandi v5](http://doc.livedns.gandi.net) | `gandiv5` | `GANDIV5_API_KEY` | YES |
|
||||
| [Glesys](https://glesys.com/) | `glesys` | `GLESYS_API_USER`, `GLESYS_API_KEY`, `GLESYS_DOMAIN` | Not tested yet |
|
||||
| [GoDaddy](https://godaddy.com/domains) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` | Not tested yet |
|
||||
| [Google Cloud DNS](https://cloud.google.com/dns/docs/) | `gcloud` | `GCE_PROJECT`, `GCE_SERVICE_ACCOUNT_FILE` | YES |
|
||||
| [hosting.de](https://www.hosting.de) | `hostingde` | `HOSTINGDE_API_KEY`, `HOSTINGDE_ZONE_NAME` | Not tested yet |
|
||||
| [IIJ](https://www.iij.ad.jp/) | `iij` | `IIJ_API_ACCESS_KEY`, `IIJ_API_SECRET_KEY`, `IIJ_DO_SERVICE_CODE` | Not tested yet |
|
||||
| [Lightsail](https://aws.amazon.com/lightsail/) | `lightsail` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `DNS_ZONE` | Not tested yet |
|
||||
| [Linode](https://www.linode.com) | `linode` | `LINODE_API_KEY` | Not tested yet |
|
||||
| [Linode v4](https://www.linode.com) | `linodev4` | `LINODE_TOKEN` | Not tested yet |
|
||||
| manual | - | none, but you need to run Træfik interactively, turn on `acmeLogging` to see instructions and press <kbd>Enter</kbd>. | YES |
|
||||
| [Namecheap](https://www.namecheap.com) | `namecheap` | `NAMECHEAP_API_USER`, `NAMECHEAP_API_KEY` | YES |
|
||||
| [name.com](https://www.name.com/) | `namedotcom` | `NAMECOM_USERNAME`, `NAMECOM_API_TOKEN`, `NAMECOM_SERVER` | Not tested yet |
|
||||
| [Netcup](https://www.netcup.eu/) | `netcup` | `NETCUP_CUSTOMER_NUMBER`, `NETCUP_API_KEY`, `NETCUP_API_PASSWORD` | Not tested yet |
|
||||
| [NIFCloud](https://cloud.nifty.com/service/dns.htm) | `nifcloud` | `NIFCLOUD_ACCESS_KEY_ID`, `NIFCLOUD_SECRET_ACCESS_KEY` | Not tested yet |
|
||||
| [Ns1](https://ns1.com/) | `ns1` | `NS1_API_KEY` | Not tested yet |
|
||||
| [Open Telekom Cloud](https://cloud.telekom.de) | `otc` | `OTC_DOMAIN_NAME`, `OTC_USER_NAME`, `OTC_PASSWORD`, `OTC_PROJECT_NAME`, `OTC_IDENTITY_ENDPOINT` | Not tested yet |
|
||||
| [OVH](https://www.ovh.com) | `ovh` | `OVH_ENDPOINT`, `OVH_APPLICATION_KEY`, `OVH_APPLICATION_SECRET`, `OVH_CONSUMER_KEY` | YES |
|
||||
| [PowerDNS](https://www.powerdns.com) | `pdns` | `PDNS_API_KEY`, `PDNS_API_URL` | Not tested yet |
|
||||
| [Rackspace](https://www.rackspace.com/cloud/dns) | `rackspace` | `RACKSPACE_USER`, `RACKSPACE_API_KEY` | Not tested yet |
|
||||
| [RFC2136](https://tools.ietf.org/html/rfc2136) | `rfc2136` | `RFC2136_TSIG_KEY`, `RFC2136_TSIG_SECRET`, `RFC2136_TSIG_ALGORITHM`, `RFC2136_NAMESERVER` | Not tested yet |
|
||||
| [Route 53](https://aws.amazon.com/route53/) | `route53` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `[AWS_REGION]`, `[AWS_HOSTED_ZONE_ID]` or a configured user/instance IAM profile. | YES |
|
||||
| [Sakura Cloud](https://cloud.sakura.ad.jp/) | `sakuracloud` | `SAKURACLOUD_ACCESS_TOKEN`, `SAKURACLOUD_ACCESS_TOKEN_SECRET` | Not tested yet |
|
||||
| [Stackpath](https://www.stackpath.com/) | `stackpath` | `STACKPATH_CLIENT_ID`, `STACKPATH_CLIENT_SECRET`, `STACKPATH_STACK_ID` | Not tested yet |
|
||||
| [VegaDNS](https://github.com/shupp/VegaDNS-API) | `vegadns` | `SECRET_VEGADNS_KEY`, `SECRET_VEGADNS_SECRET`, `VEGADNS_URL` | Not tested yet |
|
||||
| [VULTR](https://www.vultr.com) | `vultr` | `VULTR_API_KEY` | Not tested yet |
|
||||
|
||||
### `domains`
|
||||
|
||||
@@ -371,7 +383,7 @@ For example, the rule `Host:test1.traefik.io,test2.traefik.io` will request a ce
|
||||
|
||||
!!! warning
|
||||
`onHostRule` option can not be used to generate wildcard certificates.
|
||||
Refer to [wildcard generation](/configuration/acme/#wildcard-domain) for further information.
|
||||
Refer to [wildcard generation](/configuration/acme/#wildcard-domains) for further information.
|
||||
|
||||
### `storage`
|
||||
|
||||
|
@@ -4,6 +4,9 @@
|
||||
|
||||
```toml
|
||||
# API definition
|
||||
# Warning: Enabling API will expose Træfik's configuration.
|
||||
# It is not recommended in production,
|
||||
# unless secured by authentication and authorizations
|
||||
[api]
|
||||
# Name of the related entry point
|
||||
#
|
||||
@@ -12,7 +15,7 @@
|
||||
#
|
||||
entryPoint = "traefik"
|
||||
|
||||
# Enabled Dashboard
|
||||
# Enable Dashboard
|
||||
#
|
||||
# Optional
|
||||
# Default: true
|
||||
@@ -21,7 +24,7 @@
|
||||
|
||||
# Enable debug mode.
|
||||
# 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.
|
||||
#
|
||||
# Optional
|
||||
@@ -30,7 +33,7 @@
|
||||
debug = true
|
||||
```
|
||||
|
||||
For more customization, see [entry points](/configuration/entrypoints/) documentation and [examples](/user-guide/examples/#ping-health-check).
|
||||
For more customization, see [entry points](/configuration/entrypoints/) documentation and the examples below.
|
||||
|
||||
## Web UI
|
||||
|
||||
@@ -38,6 +41,22 @@ For more customization, see [entry points](/configuration/entrypoints/) document
|
||||
|
||||

|
||||
|
||||
## Security
|
||||
|
||||
Enabling the API will expose all configuration elements,
|
||||
including sensitive data.
|
||||
|
||||
It is not recommended in production,
|
||||
unless secured by authentication and authorizations.
|
||||
|
||||
A good sane default (but not exhaustive) set of recommendations
|
||||
would be to apply the following protection mechanism:
|
||||
|
||||
* _At application level:_ enabling HTTP [Basic Authentication](#authentication)
|
||||
* _At transport level:_ NOT exposing publicly the API's port,
|
||||
keeping it restricted over internal networks
|
||||
(restricted networks as in https://en.wikipedia.org/wiki/Principle_of_least_privilege).
|
||||
|
||||
## API
|
||||
|
||||
| Path | Method | Description |
|
||||
|
@@ -31,7 +31,7 @@ exposedByDefault = false
|
||||
#
|
||||
stale = false
|
||||
|
||||
# Default domain used.
|
||||
# Default base domain used for the frontend rules.
|
||||
#
|
||||
# Optional
|
||||
#
|
||||
@@ -94,60 +94,84 @@ Additional settings can be defined using Consul Catalog tags.
|
||||
!!! note
|
||||
The default prefix is `traefik`.
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `<prefix>.enable=false` | Disables this container in Træfik. |
|
||||
| `<prefix>.protocol=https` | Overrides the default `http` protocol. |
|
||||
| `<prefix>.weight=10` | Assigns this weight to the container. |
|
||||
| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `<prefix>.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend. ex: `NetworkErrorRatio() > 0.` |
|
||||
| `<prefix>.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. |
|
||||
| `<prefix>.backend.healthcheck.interval=1s` | Defines the health check interval. |
|
||||
| `<prefix>.backend.healthcheck.port=8080` | Sets a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. |
|
||||
| `<prefix>.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
|
||||
| `<prefix>.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `<prefix>.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm. |
|
||||
| `<prefix>.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions. |
|
||||
| `<prefix>.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions. |
|
||||
| `<prefix>.backend.loadbalancer.sticky=true` | Enables backend sticky sessions. (DEPRECATED) |
|
||||
| `<prefix>.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `<prefix>.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `<prefix>.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). |
|
||||
| `<prefix>.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. |
|
||||
| `<prefix>.frontend.auth.basic.usersfile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `<prefix>.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. |
|
||||
| `<prefix>.frontend.auth.digest.usersfile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `<prefix>.frontend.auth.forward.address=https://example.com`| Sets the URL of the authentication server. |
|
||||
| `<prefix>.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. |
|
||||
| `<prefix>.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). |
|
||||
| `<prefix>.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||
| `<prefix>.frontend.auth.forward.tls.insecureSkipVerify=true`| If set to true invalid SSL certificates are accepted. |
|
||||
| `<prefix>.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||
| `<prefix>.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. |
|
||||
| `<prefix>.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. |
|
||||
| `<prefix>.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `<prefix>.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `<prefix>.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `<prefix>.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `<prefix>.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
||||
| `<prefix>.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
||||
| `<prefix>.frontend.priority=10` | Overrides default frontend priority. |
|
||||
| `<prefix>.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `<prefix>.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `<prefix>.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `<prefix>.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `<prefix>.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS). |
|
||||
| `<prefix>.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `<prefix>.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `<prefix>.frontend.redirect.permanent=true` | Returns 301 instead of 302. |
|
||||
| `<prefix>.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{{.ServiceName}}.{{.Domain}}`. |
|
||||
| `<prefix>.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `<prefix>.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
| Label | Description |
|
||||
|----------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `<prefix>.enable=false` | Disables this container in Træfik. |
|
||||
| `<prefix>.protocol=https` | Overrides the default `http` protocol. |
|
||||
| `<prefix>.weight=10` | Assigns this weight to the container. |
|
||||
| `traefik.backend.buffering.maxRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.maxResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memRequestBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.memResponseBodyBytes=0` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.buffering.retryExpression=EXPR` | See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `<prefix>.backend.circuitbreaker.expression=EXPR` | Creates a [circuit breaker](/basics/#backends) to be used against the backend. ex: `NetworkErrorRatio() > 0.` |
|
||||
| `<prefix>.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. |
|
||||
| `<prefix>.backend.healthcheck.interval=1s` | Defines the health check interval. |
|
||||
| `<prefix>.backend.healthcheck.port=8080` | Sets a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. |
|
||||
| `<prefix>.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
|
||||
| `<prefix>.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `<prefix>.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm. |
|
||||
| `<prefix>.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions. |
|
||||
| `<prefix>.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions. |
|
||||
| `<prefix>.backend.loadbalancer.sticky=true` | Enables backend sticky sessions. (DEPRECATED) |
|
||||
| `<prefix>.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `<prefix>.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `<prefix>.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). |
|
||||
| `<prefix>.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
|
||||
| `<prefix>.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. |
|
||||
| `<prefix>.frontend.auth.basic.usersfile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `<prefix>.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
|
||||
| `<prefix>.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. |
|
||||
| `<prefix>.frontend.auth.digest.usersfile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `<prefix>.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. |
|
||||
| `<prefix>.frontend.auth.forward.authResponseHeaders=EXPR` | Sets the forward authentication authResponseHeaders in CSV format: `X-Auth-User,X-Auth-Header` |
|
||||
| `<prefix>.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. |
|
||||
| `<prefix>.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). |
|
||||
| `<prefix>.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||
| `<prefix>.frontend.auth.forward.tls.insecureSkipVerify=true` | If set to true invalid SSL certificates are accepted. |
|
||||
| `<prefix>.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||
| `<prefix>.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. |
|
||||
| `<prefix>.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. |
|
||||
| `<prefix>.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `<prefix>.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `<prefix>.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `<prefix>.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `<prefix>.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
||||
| `<prefix>.frontend.passTLSClientCert.infos.notAfter=true` | Add the noAfter field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `<prefix>.frontend.passTLSClientCert.infos.notBefore=true` | Add the noBefore field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `<prefix>.frontend.passTLSClientCert.infos.sans=true` | Add the sans field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `<prefix>.frontend.passTLSClientCert.infos.subject.commonName=true` | Add the subject.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `<prefix>.frontend.passTLSClientCert.infos.subject.country=true` | Add the subject.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `<prefix>.frontend.passTLSClientCert.infos.subject.locality=true` | Add the subject.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `<prefix>.frontend.passTLSClientCert.infos.subject.organization=true`| Add the subject.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `<prefix>.frontend.passTLSClientCert.infos.subject.province=true` | Add the subject.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `<prefix>.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Add the subject.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `<prefix>.frontend.passTLSClientCert.pem=true` | Pass the escaped pem in the `X-Forwarded-Ssl-Client-Cert` header. |
|
||||
| `<prefix>.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
||||
| `<prefix>.frontend.priority=10` | Overrides default frontend priority. |
|
||||
| `<prefix>.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `<prefix>.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `<prefix>.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `<prefix>.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `<prefix>.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS). |
|
||||
| `<prefix>.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `<prefix>.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `<prefix>.frontend.redirect.permanent=true` | Returns 301 instead of 302. |
|
||||
| `<prefix>.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{{.ServiceName}}.{{.Domain}}`. |
|
||||
| `<prefix>.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `<prefix>.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
|
||||
### Multiple frontends for a single service
|
||||
|
||||
If you need to support multiple frontends for a service, for example when having multiple `rules` that can't be combined, specify them as follows:
|
||||
|
||||
```
|
||||
<prefix>.frontends.A.rule=Host:A:PathPrefix:/A
|
||||
<prefix>.frontends.B.rule=Host:B:PathPrefix:/
|
||||
```
|
||||
|
||||
`A` and `B` here are just arbitrary names, they can be anything. You can use any setting that applies to `<prefix>.frontend` from the table above.
|
||||
|
||||
### Custom Headers
|
||||
|
||||
@@ -187,7 +211,7 @@ Additional settings can be defined using Consul Catalog tags.
|
||||
| `<prefix>.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `<prefix>.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
|
||||
|
||||
|
||||
### Examples
|
||||
|
||||
If you want that Træfik uses Consul tags correctly you need to defined them like that:
|
||||
|
@@ -19,7 +19,7 @@ Træfik can be configured to use Docker as a provider.
|
||||
#
|
||||
endpoint = "unix:///var/run/docker.sock"
|
||||
|
||||
# Default domain used.
|
||||
# Default base domain used for the frontend rules.
|
||||
# Can be overridden by setting the "traefik.domain" label on a container.
|
||||
#
|
||||
# Required
|
||||
@@ -57,7 +57,9 @@ watch = true
|
||||
exposedByDefault = true
|
||||
|
||||
# Use the IP address from the binded port instead of the inner network one.
|
||||
# For specific use-case :)
|
||||
#
|
||||
# In case no IP address is attached to the binded port (or in case
|
||||
# there is no bind), the inner network one will be used as a fallback.
|
||||
#
|
||||
# Optional
|
||||
# Default: false
|
||||
@@ -110,7 +112,7 @@ To enable constraints see [provider-specific constraints section](/configuration
|
||||
#
|
||||
endpoint = "tcp://127.0.0.1:2375"
|
||||
|
||||
# Default domain used.
|
||||
# Default base domain used for the frontend rules.
|
||||
# Can be overridden by setting the "traefik.domain" label on a services.
|
||||
#
|
||||
# Optional
|
||||
@@ -207,65 +209,79 @@ services:
|
||||
|
||||
Labels can be used on containers to override default behavior.
|
||||
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.docker.network` | Overrides the default docker network to use for connections to the container. [1] |
|
||||
| `traefik.domain` | Sets the default domain for the frontend rules. |
|
||||
| `traefik.enable=false` | Disables this container in Træfik. |
|
||||
| `traefik.port=80` | Registers this port. Useful when the container exposes multiples ports. |
|
||||
| `traefik.protocol=https` | Overrides the default `http` protocol |
|
||||
| `traefik.weight=10` | Assigns this weight to the container |
|
||||
| `traefik.backend=foo` | Gives 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` | Creates a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. |
|
||||
| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. |
|
||||
| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. |
|
||||
| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
|
||||
| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | Enables backend sticky sessions (DEPRECATED) |
|
||||
| `traefik.backend.loadbalancer.swarm=true` | Uses Swarm's inbuilt load balancer (only relevant under Swarm Mode). |
|
||||
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` [2] (DEPRECATED). |
|
||||
| `traefik.frontend.auth.basic.users=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` [2]. |
|
||||
| `traefik.frontend.auth.basic.usersfile=/path/.htpasswd` | Sets the basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `traefik.frontend.auth.digest.users=EXPR` | Sets the digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. |
|
||||
| `traefik.frontend.auth.digest.usersfile=/path/.htdigest` | Sets the digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `traefik.frontend.auth.forward.address=https://example.com`| Sets the URL of the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). |
|
||||
| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true`| If set to true invalid SSL certificates are accepted. |
|
||||
| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. |
|
||||
| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header user to pass the authenticated user to the application. |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
||||
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) |
|
||||
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. |
|
||||
| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{containerName}.{domain}` or `Host:{service}.{project_name}.{domain}` if you are using `docker-compose`. |
|
||||
| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access.<br>If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
| Label | Description |
|
||||
|---------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.docker.network` | Overrides the default docker network to use for connections to the container. [1] |
|
||||
| `traefik.domain` | Sets the default base domain for the frontend rules. For more information, check the [Container Labels section's of the user guide "Let's Encrypt & Docker"](/user-guide/docker-and-lets-encrypt/#container-labels) |
|
||||
| `traefik.enable=false` | Disables this container in Træfik. |
|
||||
| `traefik.port=80` | Registers this port. Useful when the container exposes multiples ports. |
|
||||
| `traefik.tags=foo,bar,myTag` | Adds Træfik tags to the Docker container/service to be used in [constraints](/configuration/commons/#constraints). |
|
||||
| `traefik.protocol=https` | Overrides the default `http` protocol |
|
||||
| `traefik.weight=10` | Assigns this weight to the container |
|
||||
| `traefik.backend=foo` | Gives 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` | Creates a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. |
|
||||
| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. |
|
||||
| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. |
|
||||
| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
|
||||
| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | Enables backend sticky sessions (DEPRECATED) |
|
||||
| `traefik.backend.loadbalancer.swarm=true` | Uses Swarm's inbuilt load balancer (only relevant under Swarm Mode). |
|
||||
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` [2] (DEPRECATED). |
|
||||
| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
|
||||
| `traefik.frontend.auth.basic.users=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` [2]. |
|
||||
| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets the basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
|
||||
| `traefik.frontend.auth.digest.users=EXPR` | Sets the digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. |
|
||||
| `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets the digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. |
|
||||
| `traefik.frontend.auth.forward.authResponseHeaders=EXPR` | Sets the forward authentication authResponseHeaders in CSV format: `X-Auth-User,X-Auth-Header` |
|
||||
| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). |
|
||||
| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true` | If set to true invalid SSL certificates are accepted. |
|
||||
| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. |
|
||||
| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header user to pass the authenticated user to the application. |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.notAfter=true` | Add the noAfter field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.notBefore=true` | Add the noBefore field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.sans=true` | Add the sans field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.commonName=true` | Add the subject.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.country=true` | Add the subject.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.locality=true` | Add the subject.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.organization=true`| Add the subject.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.province=true` | Add the subject.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Add the subject.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.pem=true` | Pass the escaped pem in the `X-Forwarded-Ssl-Client-Cert` header. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend (DEPRECATED). |
|
||||
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) |
|
||||
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. |
|
||||
| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{containerName}.{domain}` or `Host:{service}.{project_name}.{domain}` if you are using `docker-compose`. |
|
||||
| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access.<br>If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
|
||||
[1] `traefik.docker.network`:
|
||||
If a container is linked to several networks, be sure to set the proper network name (you can check with `docker inspect <container_id>`) otherwise it will randomly pick one (depending on how docker is returning them).
|
||||
@@ -318,44 +334,57 @@ You can define as many segments as ports exposed in a container.
|
||||
|
||||
Segment labels override the default behavior.
|
||||
|
||||
| Label | Description |
|
||||
|---------------------------------------------------------------------------|---------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
||||
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
||||
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic.usersfile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersfile` |
|
||||
| `traefik.<segment_name>.frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` |
|
||||
| `traefik.<segment_name>.frontend.auth.digest.usersfile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersfile` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.address=https://example.com`| Same as `traefik.frontend.auth.forward.address` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.insecureSkipVerify=true`| Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify`|
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` |
|
||||
| `traefik.<segment_name>.frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` |
|
||||
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
||||
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
||||
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
|
||||
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
|
||||
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
|
||||
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
|
||||
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
|
||||
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------------------------------|------------------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
||||
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
||||
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` |
|
||||
| `traefik.<segment_name>.frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` |
|
||||
| `traefik.<segment_name>.frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` |
|
||||
| `traefik.<segment_name>.frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.authResponseHeaders=EXPR` | Same as `traefik.frontend.auth.forward.authResponseHeaders` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.insecureSkipVerify=true` | Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` |
|
||||
| `traefik.<segment_name>.frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` |
|
||||
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notAfter=true` | Same as `traefik.frontend.passTLSClientCert.infos.notAfter` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notBefore=true` | Same as `traefik.frontend.passTLSClientCert.infos.notBefore` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.sans=true` | Same as `traefik.frontend.passTLSClientCert.infos.sans` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.commonName=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.commonName` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.country=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.country` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.locality=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.locality` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.organization=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.organization`|
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.province=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.province` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.serialNumber`|
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.pem=true` | Same as `traefik.frontend.passTLSClientCert.infos.pem` |
|
||||
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
||||
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
|
||||
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
|
||||
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
|
||||
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
|
||||
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
|
||||
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
|
||||
|
||||
#### Custom Headers
|
||||
|
||||
@@ -400,3 +429,25 @@ Segment labels override the default behavior.
|
||||
When running inside a container, Træfik will need network access through:
|
||||
|
||||
`docker network connect <network> <traefik-container>`
|
||||
|
||||
## usebindportip
|
||||
|
||||
The default behavior of Træfik is to route requests to the IP/Port of the matching container.
|
||||
When setting `usebindportip` to true, you tell Træfik to use the IP/Port attached to the container's binding instead of the inner network IP/Port.
|
||||
|
||||
When used in conjunction with the `traefik.port` label (that tells Træfik to route requests to a specific port), Træfik tries to find a binding with `traefik.port` port to select the container. If it can't find such a binding, Træfik falls back on the internal network IP of the container, but still uses the `traefik.port` that is set in the label.
|
||||
|
||||
Below is a recap of the behavior of `usebindportip` in different situations.
|
||||
|
||||
| traefik.port label | Container's binding | Routes to |
|
||||
|--------------------|----------------------------------------------------|----------------|
|
||||
| - | - | IntIP:IntPort |
|
||||
| - | ExtPort:IntPort | IntIP:IntPort |
|
||||
| - | ExtIp:ExtPort:IntPort | ExtIp:ExtPort |
|
||||
| LblPort | - | IntIp:LblPort |
|
||||
| LblPort | ExtIp:ExtPort:LblPort | ExtIp:ExtPort |
|
||||
| LblPort | ExtIp:ExtPort:OtherPort | IntIp:LblPort |
|
||||
| LblPort | ExtIp1:ExtPort1:IntPort1 & ExtIp2:LblPort:IntPort2 | ExtIp2:LblPort |
|
||||
|
||||
!!! note
|
||||
In the above table, ExtIp stands for "external IP found in the binding", IntIp stands for "internal network container's IP", ExtPort stands for "external Port found in the binding", and IntPort stands for "internal network container's port."
|
@@ -32,7 +32,7 @@ clusters = ["default"]
|
||||
#
|
||||
watch = true
|
||||
|
||||
# Default domain used.
|
||||
# Default base domain used for the frontend rules.
|
||||
# Can be overridden by setting the "traefik.domain" label.
|
||||
#
|
||||
# Optional
|
||||
@@ -136,63 +136,77 @@ Træfik needs the following policy to read ECS information:
|
||||
|
||||
Labels can be used on task containers to override default behaviour:
|
||||
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.domain` | Sets the default domain for frontend rules. |
|
||||
| `traefik.enable=false` | Disables this container in Træfik. |
|
||||
| `traefik.port=80` | Overrides the default `port` value. Overrides `NetworkBindings` from Docker Container |
|
||||
| `traefik.protocol=https` | Overrides the default `http` protocol |
|
||||
| `traefik.weight=10` | Assigns this weight to the container |
|
||||
| `traefik.backend=foo` | Gives 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` | Creates a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. |
|
||||
| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. (Default: 30s) |
|
||||
| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. |
|
||||
| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
|
||||
| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie manually name for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | Enables backend sticky sessions (DEPRECATED) |
|
||||
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). |
|
||||
| `traefik.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. |
|
||||
| `traefik.frontend.auth.basic.usersfile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. |
|
||||
| `traefik.frontend.auth.digest.usersfile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `traefik.frontend.auth.forward.address=https://example.com`| Sets the URL of the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). |
|
||||
| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true`| If set to true invalid SSL certificates are accepted. |
|
||||
| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. |
|
||||
| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
||||
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) |
|
||||
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. |
|
||||
| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{instance_name}.{domain}`. |
|
||||
| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
| Label | Description |
|
||||
|---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.domain` | Sets the default base domain for frontend rules. |
|
||||
| `traefik.enable=false` | Disables this container in Træfik. |
|
||||
| `traefik.port=80` | Overrides the default `port` value. Overrides `NetworkBindings` from Docker Container |
|
||||
| `traefik.protocol=https` | Overrides the default `http` protocol |
|
||||
| `traefik.weight=10` | Assigns this weight to the container |
|
||||
| `traefik.backend=foo` | Gives 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` | Creates a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. |
|
||||
| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. (Default: 30s) |
|
||||
| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. |
|
||||
| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
|
||||
| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie manually name for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | Enables backend sticky sessions (DEPRECATED) |
|
||||
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). |
|
||||
| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
|
||||
| `traefik.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. |
|
||||
| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
|
||||
| `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. |
|
||||
| `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. |
|
||||
| `traefik.frontend.auth.forward.authResponseHeaders=EXPR` | Sets the forward authentication authResponseHeaders in CSV format: `X-Auth-User,X-Auth-Header` |
|
||||
| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). |
|
||||
| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true` | If set to true invalid SSL certificates are accepted. |
|
||||
| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. |
|
||||
| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. |
|
||||
| `traefik.frontend.auth.removeHeader=true` | If set to true, removes the Authorization header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.notAfter=true` | Add the noAfter field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.notBefore=true` | Add the noBefore field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.sans=true` | Add the sans field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.commonName=true` | Add the subject.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.country=true` | Add the subject.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.locality=true` | Add the subject.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.organization=true`| Add the subject.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.province=true` | Add the subject.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Add the subject.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.pem=true` | Pass the escaped pem in the `X-Forwarded-Ssl-Client-Cert` header. |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
||||
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) |
|
||||
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. |
|
||||
| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{instance_name}.{domain}`. |
|
||||
| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
|
||||
### Custom Headers
|
||||
|
||||
@@ -225,3 +239,96 @@ Labels can be used on task containers to override default behaviour:
|
||||
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
|
||||
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
|
||||
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
|
||||
|
||||
### Containers with Multiple Ports (segment labels)
|
||||
|
||||
Segment labels are used to define routes to an application exposing multiple ports.
|
||||
A segment is a group of labels that apply to a port exposed by an application.
|
||||
You can define as many segments as ports exposed in an application.
|
||||
|
||||
Segment labels override the default behavior.
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------------------------------------|-------------------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
||||
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
||||
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` |
|
||||
| `traefik.<segment_name>.frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` |
|
||||
| `traefik.<segment_name>.frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` |
|
||||
| `traefik.<segment_name>.frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.authResponseHeaders=EXPR` | Same as `traefik.frontend.auth.forward.authResponseHeaders` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.insecureSkipVerify=true` | Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` |
|
||||
| `traefik.<segment_name>.frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` |
|
||||
| `traefik.<segment_name>.frontend.auth.removeHeader=true` | Same as `traefik.frontend.auth.removeHeader` |
|
||||
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notAfter=true` | Same as `traefik.frontend.passTLSClientCert.infos.notAfter` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notBefore=true` | Same as `traefik.frontend.passTLSClientCert.infos.notBefore` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.sans=true` | Same as `traefik.frontend.passTLSClientCert.infos.sans` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.commonName=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.commonName` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.country=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.country` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.locality=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.locality` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.organization=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.organization` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.province=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.province` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.serialNumber=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.serialNumber` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.pem=true` | Same as `traefik.frontend.passTLSClientCert.infos.pem` |
|
||||
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
||||
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
|
||||
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
|
||||
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
|
||||
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
|
||||
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
|
||||
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
|
||||
|
||||
#### Custom Headers
|
||||
|
||||
| Label | Description |
|
||||
|----------------------------------------------------------------------|----------------------------------------------------------|
|
||||
| `traefik.<segment_name>.frontend.headers.customRequestHeaders=EXPR ` | Same as `traefik.frontend.headers.customRequestHeaders` |
|
||||
| `traefik.<segment_name>.frontend.headers.customResponseHeaders=EXPR` | Same as `traefik.frontend.headers.customResponseHeaders` |
|
||||
|
||||
#### Security Headers
|
||||
|
||||
| Label | Description |
|
||||
|-------------------------------------------------------------------------|--------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.frontend.headers.allowedHosts=EXPR` | Same as `traefik.frontend.headers.allowedHosts` |
|
||||
| `traefik.<segment_name>.frontend.headers.browserXSSFilter=true` | Same as `traefik.frontend.headers.browserXSSFilter` |
|
||||
| `traefik.<segment_name>.frontend.headers.contentSecurityPolicy=VALUE` | Same as `traefik.frontend.headers.contentSecurityPolicy` |
|
||||
| `traefik.<segment_name>.frontend.headers.contentTypeNosniff=true` | Same as `traefik.frontend.headers.contentTypeNosniff` |
|
||||
| `traefik.<segment_name>.frontend.headers.customBrowserXSSValue=VALUE` | Same as `traefik.frontend.headers.customBrowserXSSValue` |
|
||||
| `traefik.<segment_name>.frontend.headers.customFrameOptionsValue=VALUE` | Same as `traefik.frontend.headers.customFrameOptionsValue` |
|
||||
| `traefik.<segment_name>.frontend.headers.forceSTSHeader=false` | Same as `traefik.frontend.headers.forceSTSHeader` |
|
||||
| `traefik.<segment_name>.frontend.headers.frameDeny=false` | Same as `traefik.frontend.headers.frameDeny` |
|
||||
| `traefik.<segment_name>.frontend.headers.hostsProxyHeaders=EXPR` | Same as `traefik.frontend.headers.hostsProxyHeaders` |
|
||||
| `traefik.<segment_name>.frontend.headers.isDevelopment=false` | Same as `traefik.frontend.headers.isDevelopment` |
|
||||
| `traefik.<segment_name>.frontend.headers.publicKey=VALUE` | Same as `traefik.frontend.headers.publicKey` |
|
||||
| `traefik.<segment_name>.frontend.headers.referrerPolicy=VALUE` | Same as `traefik.frontend.headers.referrerPolicy` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLRedirect=true` | Same as `traefik.frontend.headers.SSLRedirect` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLTemporaryRedirect=true` | Same as `traefik.frontend.headers.SSLTemporaryRedirect` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLHost=HOST` | Same as `traefik.frontend.headers.SSLHost` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLForceHost=true` | Same as `traefik.frontend.headers.SSLForceHost` |
|
||||
| `traefik.<segment_name>.frontend.headers.SSLProxyHeaders=EXPR` | Same as `traefik.frontend.headers.SSLProxyHeaders=EXPR` |
|
||||
| `traefik.<segment_name>.frontend.headers.STSSeconds=315360000` | Same as `traefik.frontend.headers.STSSeconds=315360000` |
|
||||
| `traefik.<segment_name>.frontend.headers.STSIncludeSubdomains=true` | Same as `traefik.frontend.headers.STSIncludeSubdomains=true` |
|
||||
| `traefik.<segment_name>.frontend.headers.STSPreload=true` | Same as `traefik.frontend.headers.STSPreload=true` |
|
||||
|
@@ -53,7 +53,6 @@ Træfik can be configured with a file.
|
||||
entryPoints = ["http", "https"]
|
||||
backend = "backend1"
|
||||
passHostHeader = true
|
||||
passTLSCert = true
|
||||
priority = 42
|
||||
|
||||
# Use frontends.frontend1.auth.basic below instead
|
||||
@@ -61,16 +60,33 @@ Træfik can be configured with a file.
|
||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
]
|
||||
|
||||
[frontends.frontend1.passTLSClientCert]
|
||||
# Pass the escaped pem in a `X-Forwarded-Ssl-Client-Cert` header
|
||||
pem = true
|
||||
# Pass the escaped client cert infos selected below in a `X-Forwarded-Ssl-Client-Cert-Infos` header
|
||||
# The unescaped header is like `Subject="C=%s,ST=%s,L=%s,O=%s,CN=%s",NB=%d,NA=%d,SAN=%s`
|
||||
# It there is more than one certificates, their are separated by a `;`
|
||||
[frontends.frontend-server.passTLSClientCert.infos]
|
||||
notBefore = true
|
||||
notAfter = true
|
||||
[frontends.frontend-server.passTLSClientCert.infos.subject]
|
||||
country = true
|
||||
province = true
|
||||
locality = true
|
||||
organization = true
|
||||
commonName = true
|
||||
serialNumber = true
|
||||
[frontends.frontend1.auth]
|
||||
headerField = "X-WebAuth-User"
|
||||
[frontends.frontend1.auth.basic]
|
||||
removeHeader = true
|
||||
users = [
|
||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
]
|
||||
usersFile = "/path/to/.htpasswd"
|
||||
[frontends.frontend1.auth.digest]
|
||||
removeHeader = true
|
||||
users = [
|
||||
"test:traefik:a2688e031edb4be6a3797f3882655c05",
|
||||
"test2:traefik:518845800f9e2bfb1f1f740ec24f074e",
|
||||
|
@@ -108,7 +108,7 @@ The endpoint may be specified to override the environment variable values inside
|
||||
|
||||
When the environment variables are not found, Traefik will try to connect to the Kubernetes API server with an external-cluster client.
|
||||
In this case, the endpoint is required.
|
||||
Specifically, it may be set to the URL used by `kubectl proxy` to connect to a Kubernetes cluster using the granted autentication and authorization of the associated kubeconfig.
|
||||
Specifically, it may be set to the URL used by `kubectl proxy` to connect to a Kubernetes cluster using the granted authentication and authorization of the associated kubeconfig.
|
||||
|
||||
### `labelselector`
|
||||
|
||||
@@ -127,7 +127,13 @@ This will give more flexibility in cloud/dynamic environments.
|
||||
|
||||
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.
|
||||
|
||||
There are 2 ways to configure Traefik to use https to communicate with backend pods:
|
||||
|
||||
1. If the service port defined in the ingress spec is 443 (note that you can still use `targetPort` to use a different port on your pod).
|
||||
2. If the service port defined in the ingress spec has a name that starts with `https` (such as `https-api`, `https-web` or just `https`).
|
||||
|
||||
If either of those configuration options exist, then the backend communication protocol is assumed to be TLS, and will connect via TLS automatically.
|
||||
|
||||
!!! note
|
||||
Please note that by enabling TLS communication between traefik and your pods, you will have to have trusted certificates that have the proper trust chain and IP subject name.
|
||||
@@ -140,26 +146,26 @@ If the service port defined in the ingress spec is 443, then the backend communi
|
||||
|
||||
The following general annotations are applicable on the Ingress object:
|
||||
|
||||
| Annotation | Description |
|
||||
|---------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.ingress.kubernetes.io/buffering: <YML>` | (3) See [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.ingress.kubernetes.io/error-pages: <YML>` | (1) See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.ingress.kubernetes.io/frontend-entry-points: http,https` | Override the default frontend endpoints. |
|
||||
| `traefik.ingress.kubernetes.io/pass-tls-cert: "true"` | Override the default frontend PassTLSCert value. Default: `false`. |
|
||||
| `traefik.ingress.kubernetes.io/preserve-host: "true"` | Forward client `Host` header to the backend. |
|
||||
| `traefik.ingress.kubernetes.io/priority: "3"` | Override the default frontend rule priority. |
|
||||
| `traefik.ingress.kubernetes.io/rate-limit: <YML>` | (2) See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.ingress.kubernetes.io/redirect-entry-point: https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS). |
|
||||
| `traefik.ingress.kubernetes.io/redirect-permanent: "true"` | Return 301 instead of 302. |
|
||||
| `traefik.ingress.kubernetes.io/redirect-regex: ^http://localhost/(.*)` | Redirect to another URL for that frontend. Must be set with `traefik.ingress.kubernetes.io/redirect-replacement`. |
|
||||
| `traefik.ingress.kubernetes.io/redirect-replacement: http://mydomain/$1` | Redirect to another URL for that frontend. Must be set with `traefik.ingress.kubernetes.io/redirect-regex`. |
|
||||
| `traefik.ingress.kubernetes.io/rewrite-target: /users` | Replaces each matched Ingress path with the specified one, and adds the old path to the `X-Replaced-Path` header. |
|
||||
| `traefik.ingress.kubernetes.io/rule-type: PathPrefixStrip` | Override the default frontend rule type. Only path related matchers can be used [(`Path`, `PathPrefix`, `PathStrip`, `PathPrefixStrip`)](/basics/#path-matcher-usage-guidelines). Note: ReplacePath is deprecated in this annotation, use the `traefik.ingress.kubernetes.io/request-modifier` annotation instead. Default: `PathPrefix`. |
|
||||
| `traefik.ingress.kubernetes.io/request-modifier: AddPath: /users` | Add a [request modifier](/basics/#modifiers) to the backend request. |
|
||||
| `traefik.ingress.kubernetes.io/whitelist-source-range: "1.2.3.0/24, fe80::/16"` | A comma-separated list of IP ranges permitted for access (6). |
|
||||
| `ingress.kubernetes.io/whitelist-x-forwarded-for: "true"` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
| `traefik.ingress.kubernetes.io/app-root: "/index.html"` | Redirects all requests for `/` to the defined path. (4) |
|
||||
| `traefik.ingress.kubernetes.io/service-weights: <YML>` | Set ingress backend weights specified as percentage or decimal numbers in YAML. (5) |
|
||||
| Annotation | Description |
|
||||
|---------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.ingress.kubernetes.io/error-pages: <YML>` | (1) See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.ingress.kubernetes.io/frontend-entry-points: http,https` | Override the default frontend endpoints. |
|
||||
| `traefik.ingress.kubernetes.io/pass-tls-cert: "true"` | Override the default frontend PassTLSCert value. Default: `false`. |
|
||||
| `traefik.ingress.kubernetes.io/preserve-host: "true"` | Forward client `Host` header to the backend. |
|
||||
| `traefik.ingress.kubernetes.io/priority: "3"` | Override the default frontend rule priority. |
|
||||
| `traefik.ingress.kubernetes.io/rate-limit: <YML>` | (2) See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.ingress.kubernetes.io/redirect-entry-point: https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS). |
|
||||
| `traefik.ingress.kubernetes.io/redirect-permanent: "true"` | Return 301 instead of 302. |
|
||||
| `traefik.ingress.kubernetes.io/redirect-regex: ^http://localhost/(.*)` | Redirect to another URL for that frontend. Must be set with `traefik.ingress.kubernetes.io/redirect-replacement`. |
|
||||
| `traefik.ingress.kubernetes.io/redirect-replacement: http://mydomain/$1` | Redirect to another URL for that frontend. Must be set with `traefik.ingress.kubernetes.io/redirect-regex`. |
|
||||
| `traefik.ingress.kubernetes.io/rewrite-target: /users` | Replaces each matched Ingress path with the specified one, and adds the old path to the `X-Replaced-Path` header. |
|
||||
| `traefik.ingress.kubernetes.io/rule-type: PathPrefixStrip` | Overrides the default frontend rule type. Only path-related matchers can be specified [(`Path`, `PathPrefix`, `PathStrip`, `PathPrefixStrip`)](/basics/#path-matcher-usage-guidelines). |
|
||||
| `traefik.ingress.kubernetes.io/request-modifier: AddPrefix: /users` | Adds a [request modifier](/basics/#modifiers) to the backend request. |
|
||||
| `traefik.ingress.kubernetes.io/whitelist-source-range: "1.2.3.0/24, fe80::/16"` | A comma-separated list of IP ranges permitted for access (6). |
|
||||
| `ingress.kubernetes.io/whitelist-x-forwarded-for: "true"` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
| `traefik.ingress.kubernetes.io/app-root: "/index.html"` | Redirects all requests for `/` to the defined path. (4) |
|
||||
| `traefik.ingress.kubernetes.io/service-weights: <YML>` | Set ingress backend weights specified as percentage or decimal numbers in YAML. (5)
|
||||
| `ingress.kubernetes.io/protocol: <NAME>` | Set the protocol Traefik will use to communicate with pods.
|
||||
|
||||
|
||||
<1> `traefik.ingress.kubernetes.io/error-pages` example:
|
||||
@@ -193,20 +199,14 @@ rateset:
|
||||
burst: 18
|
||||
```
|
||||
|
||||
<3> `traefik.ingress.kubernetes.io/buffering` example:
|
||||
|
||||
```yaml
|
||||
maxrequestbodybytes: 10485760
|
||||
memrequestbodybytes: 2097153
|
||||
maxresponsebodybytes: 10485761
|
||||
memresponsebodybytes: 2097152
|
||||
retryexpression: IsNetworkError() && Attempts() <= 2
|
||||
```
|
||||
<3> `traefik.ingress.kubernetes.io/rule-type`
|
||||
Note: `ReplacePath` is deprecated in this annotation, use the `traefik.ingress.kubernetes.io/request-modifier` annotation instead. Default: `PathPrefix`.
|
||||
|
||||
<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.
|
||||
This annotation may not be combined with other redirect annotations.
|
||||
Trying to do so will result in the other redirects being ignored.
|
||||
This annotation can be used in combination with `traefik.ingress.kubernetes.io/redirect-permanent` to configure whether the `app-root` redirect is a 301 or a 302.
|
||||
|
||||
<5> `traefik.ingress.kubernetes.io/service-weights`:
|
||||
Service weights enable to split traffic across multiple backing services in a fine-grained manner.
|
||||
@@ -249,14 +249,25 @@ The following annotations are applicable on the Service object associated with a
|
||||
|
||||
| Annotation | Description |
|
||||
|--------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.ingress.kubernetes.io/buffering: <YML>` | (1) See the [buffering](/configuration/commons/#buffering) section. |
|
||||
| `traefik.backend.loadbalancer.sticky: "true"` | Enable backend sticky sessions (DEPRECATED). |
|
||||
| `traefik.ingress.kubernetes.io/affinity: "true"` | Enable backend sticky sessions. |
|
||||
| `traefik.ingress.kubernetes.io/circuit-breaker-expression: <expression>` | Set the circuit breaker expression for the backend. |
|
||||
| `traefik.ingress.kubernetes.io/load-balancer-method: drr` | Override the default `wrr` load balancer algorithm. |
|
||||
| `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. |
|
||||
| `traefik.ingress.kubernetes.io/max-conn-amount: "10"` | Sets the maximum number of simultaneous connections to the backend.<br>Must be used in conjunction with the label below to take effect. |
|
||||
| `traefik.ingress.kubernetes.io/max-conn-extractor-func: client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.ingress.kubernetes.io/session-cookie-name: <NAME>` | Manually set the cookie name for sticky sessions. |
|
||||
|
||||
<1> `traefik.ingress.kubernetes.io/buffering` example:
|
||||
|
||||
```yaml
|
||||
maxrequestbodybytes: 10485760
|
||||
memrequestbodybytes: 2097153
|
||||
maxresponsebodybytes: 10485761
|
||||
memresponsebodybytes: 2097152
|
||||
retryexpression: IsNetworkError() && Attempts() <= 2
|
||||
```
|
||||
|
||||
!!! note
|
||||
`traefik.ingress.kubernetes.io/` and `ingress.kubernetes.io/` are supported prefixes.
|
||||
|
||||
@@ -303,6 +314,7 @@ The source of the authentication is a Secret object that contains the credential
|
||||
|----------------------------------------------------------------------|-------|--------|---------|-------------------------------------------------------------------------------------------------------------|
|
||||
| `ingress.kubernetes.io/auth-type: basic` | x | x | x | Contains the authentication type: `basic`, `digest`, `forward`. |
|
||||
| `ingress.kubernetes.io/auth-secret: mysecret` | x | x | | Name of Secret containing the username and password with access to the paths defined in the Ingress object. |
|
||||
| `ingress.kubernetes.io/auth-remove-header: true` | x | x | | If set to `true` removes the `Authorization` header. |
|
||||
| `ingress.kubernetes.io/auth-header-field: X-WebAuth-User` | x | x | | Pass Authenticated user to application via headers. |
|
||||
| `ingress.kubernetes.io/auth-url: https://example.com` | | | x | [The URL of the authentication server](/configuration/entrypoints/#forward-authentication). |
|
||||
| `ingress.kubernetes.io/auth-trust-headers: false` | | | x | Trust `X-Forwarded-*` headers. |
|
||||
|
@@ -31,7 +31,7 @@ endpoint = "http://127.0.0.1:8080"
|
||||
#
|
||||
watch = true
|
||||
|
||||
# Default domain used.
|
||||
# Default base domain used for the frontend rules.
|
||||
# Can be overridden by setting the "traefik.domain" label on an application.
|
||||
#
|
||||
# Required
|
||||
@@ -193,64 +193,78 @@ They may be specified on one of two levels: Application or service.
|
||||
|
||||
The following labels can be defined on Marathon applications. They adjust the behavior for the entire application.
|
||||
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.domain` | Sets the default domain used for the frontend rules. |
|
||||
| `traefik.enable=false` | Disables this container in Træfik. |
|
||||
| `traefik.port=80` | Registers this port. Useful when the container exposes multiples ports. |
|
||||
| `traefik.portIndex=1` | Registers port by index in the application's ports array. Useful when the application exposes multiple ports. |
|
||||
| `traefik.protocol=https` | Overrides the default `http` protocol. |
|
||||
| `traefik.weight=10` | Assigns this weight to the container. |
|
||||
| `traefik.backend=foo` | Gives 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` | Creates a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. |
|
||||
| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. (Default: 30s) |
|
||||
| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. |
|
||||
| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
|
||||
| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | Enables backend sticky sessions (DEPRECATED) |
|
||||
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). |
|
||||
| `traefik.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. |
|
||||
| `traefik.frontend.auth.basic.usersfile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. |
|
||||
| `traefik.frontend.auth.digest.usersfile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `traefik.frontend.auth.forward.address=https://example.com`| Sets the URL of the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). |
|
||||
| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true`| If set to true invalid SSL certificates are accepted. |
|
||||
| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. |
|
||||
| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
||||
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) |
|
||||
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. |
|
||||
| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{sub_domain}.{domain}`. |
|
||||
| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
| Label | Description |
|
||||
|---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.domain` | Sets the default base domain used for the frontend rules. |
|
||||
| `traefik.enable=false` | Disables this container in Træfik. |
|
||||
| `traefik.port=80` | Registers this port. Useful when the container exposes multiples ports. |
|
||||
| `traefik.portIndex=1` | Registers port by index in the application's ports array. Useful when the application exposes multiple ports. |
|
||||
| `traefik.protocol=https` | Overrides the default `http` protocol. |
|
||||
| `traefik.weight=10` | Assigns this weight to the container. |
|
||||
| `traefik.backend=foo` | Gives 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` | Creates a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. |
|
||||
| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. (Default: 30s) |
|
||||
| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. |
|
||||
| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
|
||||
| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | Enables backend sticky sessions (DEPRECATED) |
|
||||
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). |
|
||||
| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
|
||||
| `traefik.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. |
|
||||
| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
|
||||
| `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. |
|
||||
| `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. |
|
||||
| `traefik.frontend.auth.forward.authResponseHeaders=EXPR` | Sets the forward authentication authResponseHeaders in CSV format: `X-Auth-User,X-Auth-Header` |
|
||||
| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). |
|
||||
| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true` | If set to true invalid SSL certificates are accepted. |
|
||||
| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. |
|
||||
| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. |
|
||||
| `traefik.frontend.auth.removeHeader=true` | If set to true, removes the Authorization header. |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.notAfter=true` | Add the noAfter field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.notBefore=true` | Add the noBefore field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.sans=true` | Add the sans field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.commonName=true` | Add the subject.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.country=true` | Add the subject.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.locality=true` | Add the subject.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.organization=true`| Add the subject.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.province=true` | Add the subject.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Add the subject.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.pem=true` | Pass the escaped pem in the `X-Forwarded-Ssl-Client-Cert` header. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
||||
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) |
|
||||
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. |
|
||||
| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{sub_domain}.{domain}`. |
|
||||
| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
|
||||
#### Custom Headers
|
||||
|
||||
@@ -293,33 +307,59 @@ You can define as many segments as ports exposed in an application.
|
||||
|
||||
Segment labels override the default behavior.
|
||||
|
||||
| Label | Description |
|
||||
|---------------------------------------------------------------------------|-------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||
| `traefik.<segment_name>.portIndex=1` | Same as `traefik.portIndex` |
|
||||
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
||||
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
||||
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
||||
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
||||
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
||||
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
|
||||
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
|
||||
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
|
||||
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
|
||||
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
|
||||
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------------------------------|------------------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||
| `traefik.<segment_name>.portIndex=1` | Same as `traefik.portIndex` |
|
||||
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
||||
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
||||
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` |
|
||||
| `traefik.<segment_name>.frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` |
|
||||
| `traefik.<segment_name>.frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` |
|
||||
| `traefik.<segment_name>.frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.authResponseHeaders=EXPR` | Same as `traefik.frontend.auth.forward.authResponseHeaders` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.insecureSkipVerify=true` | Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` |
|
||||
| `traefik.<segment_name>.frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` |
|
||||
| `traefik.<segment_name>.frontend.auth.removeHeader=true` | Same as `traefik.frontend.auth.removeHeader` |
|
||||
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notAfter=true` | Same as `traefik.frontend.passTLSClientCert.infos.notAfter` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notBefore=true` | Same as `traefik.frontend.passTLSClientCert.infos.notBefore` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.sans=true` | Same as `traefik.frontend.passTLSClientCert.infos.sans` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.commonName=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.commonName` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.country=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.country` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.locality=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.locality` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.organization=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.organization`|
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.province=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.province` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.serialNumber`|
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.pem=true` | Same as `traefik.frontend.passTLSClientCert.infos.pem` |
|
||||
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
||||
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
|
||||
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
|
||||
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
|
||||
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
|
||||
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
|
||||
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
|
||||
|
||||
#### Custom Headers
|
||||
|
||||
|
@@ -27,7 +27,7 @@ endpoint = "http://127.0.0.1:8080"
|
||||
#
|
||||
watch = true
|
||||
|
||||
# Default domain used.
|
||||
# Default base domain used for the frontend rules.
|
||||
# Can be overridden by setting the "traefik.domain" label on an application.
|
||||
#
|
||||
# Required
|
||||
@@ -106,64 +106,78 @@ domain = "mesos.localhost"
|
||||
|
||||
The following labels can be defined on Mesos tasks. They adjust the behavior for the entire application.
|
||||
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.domain` | Sets the default domain for the frontend rules. |
|
||||
| `traefik.enable=false` | Disables this container in Træfik. |
|
||||
| `traefik.port=80` | Registers this port. Useful when the application exposes multiple ports. |
|
||||
| `traefik.portName=web` | Registers port by name in the application's ports array. Useful when the application exposes multiple ports. |
|
||||
| `traefik.portIndex=1` | Registers port by index in the application's ports array. Useful when the application exposes multiple ports. |
|
||||
| `traefik.protocol=https` | Overrides the default `http` protocol |
|
||||
| `traefik.weight=10` | Assigns this weight to the container |
|
||||
| `traefik.backend=foo` | Gives 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` | Creates a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. |
|
||||
| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. (Default: 30s) |
|
||||
| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. |
|
||||
| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
|
||||
| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie manually name for sticky sessions |
|
||||
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). |
|
||||
| `traefik.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. |
|
||||
| `traefik.frontend.auth.basic.usersfile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. |
|
||||
| `traefik.frontend.auth.digest.usersfile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `traefik.frontend.auth.forward.address=https://example.com`| Sets the URL of the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). |
|
||||
| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true`| If set to true invalid SSL certificates are accepted. |
|
||||
| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. |
|
||||
| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
||||
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) |
|
||||
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. |
|
||||
| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{discovery_name}.{domain}`. |
|
||||
| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
| Label | Description |
|
||||
|---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.domain` | Sets the default base domain for the frontend rules. |
|
||||
| `traefik.enable=false` | Disables this container in Træfik. |
|
||||
| `traefik.port=80` | Registers this port. Useful when the application exposes multiple ports. |
|
||||
| `traefik.portName=web` | Registers port by name in the application's ports array. Useful when the application exposes multiple ports. |
|
||||
| `traefik.portIndex=1` | Registers port by index in the application's ports array. Useful when the application exposes multiple ports. |
|
||||
| `traefik.protocol=https` | Overrides the default `http` protocol |
|
||||
| `traefik.weight=10` | Assigns this weight to the container |
|
||||
| `traefik.backend=foo` | Gives 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` | Creates a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. |
|
||||
| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. (Default: 30s) |
|
||||
| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. |
|
||||
| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
|
||||
| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie manually name for sticky sessions |
|
||||
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). |
|
||||
| `traefik.frontend.auth.basic.users=EXPR` | Sets basic authentication to this frontend in CSV format: `User:Hash,User:Hash`. |
|
||||
| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
|
||||
| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
|
||||
| `traefik.frontend.auth.digest.users=EXPR` | Sets digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. |
|
||||
| `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. |
|
||||
| `traefik.frontend.auth.forward.authResponseHeaders=EXPR` | Sets the forward authentication authResponseHeaders in CSV format: `X-Auth-User,X-Auth-Header` |
|
||||
| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). |
|
||||
| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true` | If set to true invalid SSL certificates are accepted. |
|
||||
| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. |
|
||||
| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. |
|
||||
| `traefik.frontend.auth.removeHeader=true` | If set to true, removes the Authorization header. |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.notAfter=true` | Add the noAfter field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.notBefore=true` | Add the noBefore field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.sans=true` | Add the sans field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.commonName=true` | Add the subject.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.country=true` | Add the subject.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.locality=true` | Add the subject.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.organization=true`| Add the subject.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.province=true` | Add the subject.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Add the subject.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.pem=true` | Pass the escaped pem in the `X-Forwarded-Ssl-Client-Cert` header. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
||||
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) |
|
||||
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. |
|
||||
| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{discovery_name}.{domain}`. |
|
||||
| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access. If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
|
||||
### Custom Headers
|
||||
|
||||
@@ -207,34 +221,60 @@ Additionally, if a segment name matches a named port, that port will be used unl
|
||||
|
||||
Segment labels override the default behavior.
|
||||
|
||||
| Label | Description |
|
||||
|---------------------------------------------------------------------------|-------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||
| `traefik.<segment_name>.portIndex=1` | Same as `traefik.portIndex` |
|
||||
| `traefik.<segment_name>.portName=web` | Same as `traefik.portName` |
|
||||
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
||||
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
||||
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
||||
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
||||
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
||||
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
|
||||
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
|
||||
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
|
||||
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
|
||||
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
|
||||
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------------------------------|------------------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||
| `traefik.<segment_name>.portIndex=1` | Same as `traefik.portIndex` |
|
||||
| `traefik.<segment_name>.portName=web` | Same as `traefik.portName` |
|
||||
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
||||
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
||||
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` |
|
||||
| `traefik.<segment_name>.frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` |
|
||||
| `traefik.<segment_name>.frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` |
|
||||
| `traefik.<segment_name>.frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.authResponseHeaders=EXPR` | Same as `traefik.frontend.auth.forward.authResponseHeaders` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.insecureSkipVerify=true` | Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` |
|
||||
| `traefik.<segment_name>.frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` |
|
||||
| `traefik.<segment_name>.frontend.auth.removeHeader=true` | Same as `traefik.frontend.auth.removeHeader` |
|
||||
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notAfter=true` | Same as `traefik.frontend.passTLSClientCert.infos.notAfter` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notBefore=true` | Same as `traefik.frontend.passTLSClientCert.infos.notBefore` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.sans=true` | Same as `traefik.frontend.passTLSClientCert.infos.sans` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.commonName=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.commonName` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.country=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.country` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.locality=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.locality` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.organization=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.organization`|
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.province=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.province` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.serialNumber`|
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.pem=true` | Same as `traefik.frontend.passTLSClientCert.infos.pem` |
|
||||
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
||||
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
|
||||
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
|
||||
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
|
||||
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
|
||||
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
|
||||
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
|
||||
|
||||
#### Custom Headers
|
||||
|
||||
|
@@ -12,7 +12,7 @@ Træfik can be configured to use Rancher as a provider.
|
||||
# Enable Rancher Provider.
|
||||
[rancher]
|
||||
|
||||
# Default domain used.
|
||||
# Default base domain used for the frontend rules.
|
||||
# Can be overridden by setting the "traefik.domain" label on an service.
|
||||
#
|
||||
# Required
|
||||
@@ -138,63 +138,76 @@ secretKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
|
||||
Labels can be used on task containers to override default behavior:
|
||||
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.domain` | Sets the default domain for the frontend rules. |
|
||||
| `traefik.enable=false` | Disables this container in Træfik. |
|
||||
| `traefik.port=80` | Registers this port. Useful when the container exposes multiple ports. |
|
||||
| `traefik.protocol=https` | Overrides the default `http` protocol. |
|
||||
| `traefik.weight=10` | Assigns this weight to the container. |
|
||||
| `traefik.backend=foo` | Gives 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` | Creates a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. |
|
||||
| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. |
|
||||
| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. |
|
||||
| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
|
||||
| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | Enables backend sticky sessions (DEPRECATED) |
|
||||
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). |
|
||||
| `traefik.frontend.auth.basic.users=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` . |
|
||||
| `traefik.frontend.auth.basic.usersfile=/path/.htpasswd` | Sets the basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `traefik.frontend.auth.digest.users=EXPR` | Sets the digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. |
|
||||
| `traefik.frontend.auth.digest.usersfile=/path/.htdigest` | Sets the digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `traefik.frontend.auth.forward.address=https://example.com`| Sets the URL of the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). |
|
||||
| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true`| If set to true invalid SSL certificates are accepted. |
|
||||
| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. |
|
||||
| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
||||
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) |
|
||||
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. |
|
||||
| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{containerName}.{domain}` or `Host:{service}.{project_name}.{domain}` if you are using `docker-compose`. |
|
||||
| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access.<br>If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
| Label | Description |
|
||||
|---------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `traefik.domain` | Sets the default base domain for the frontend rules. |
|
||||
| `traefik.enable=false` | Disables this container in Træfik. |
|
||||
| `traefik.port=80` | Registers this port. Useful when the container exposes multiple ports. |
|
||||
| `traefik.protocol=https` | Overrides the default `http` protocol. |
|
||||
| `traefik.weight=10` | Assigns this weight to the container. |
|
||||
| `traefik.backend=foo` | Gives 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` | Creates a [circuit breaker](/basics/#backends) to be used against the backend |
|
||||
| `traefik.backend.healthcheck.path=/health` | Enables health check for the backend, hitting the container at `path`. |
|
||||
| `traefik.backend.healthcheck.interval=1s` | Defines the health check interval. |
|
||||
| `traefik.backend.healthcheck.port=8080` | Sets a different port for the health check. |
|
||||
| `traefik.backend.healthcheck.scheme=http` | Overrides the server URL scheme. |
|
||||
| `traefik.backend.healthcheck.hostname=foobar.com` | Defines the health check hostname. |
|
||||
| `traefik.backend.healthcheck.headers=EXPR` | Defines the health check request headers <br>Format: <code>HEADER:value||HEADER2:value2</code> |
|
||||
| `traefik.backend.loadbalancer.method=drr` | Overrides the default `wrr` load balancer algorithm |
|
||||
| `traefik.backend.loadbalancer.stickiness=true` | Enables backend sticky sessions |
|
||||
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Sets the cookie name manually for sticky sessions |
|
||||
| `traefik.backend.loadbalancer.sticky=true` | Enables backend sticky sessions (DEPRECATED) |
|
||||
| `traefik.backend.maxconn.amount=10` | Sets a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
|
||||
| `traefik.backend.maxconn.extractorfunc=client.ip` | Sets the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
|
||||
| `traefik.frontend.auth.basic=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` (DEPRECATED). |
|
||||
| `traefik.frontend.auth.basic.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
|
||||
| `traefik.frontend.auth.basic.users=EXPR` | Sets the basic authentication to this frontend in CSV format: `User:Hash,User:Hash` . |
|
||||
| `traefik.frontend.auth.basic.usersFile=/path/.htpasswd` | Sets the basic authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `traefik.frontend.auth.digest.removeHeader=true` | If set to `true`, removes the `Authorization` header. |
|
||||
| `traefik.frontend.auth.digest.users=EXPR` | Sets the digest authentication to this frontend in CSV format: `User:Realm:Hash,User:Realm:Hash`. |
|
||||
| `traefik.frontend.auth.digest.usersFile=/path/.htdigest` | Sets the digest authentication with an external file; if users and usersFile are provided, both are merged, with external file contents having precedence. |
|
||||
| `traefik.frontend.auth.forward.address=https://example.com` | Sets the URL of the authentication server. |
|
||||
| `traefik.frontend.auth.forward.authResponseHeaders=EXPR` | Sets the forward authentication authResponseHeaders in CSV format: `X-Auth-User,X-Auth-Header` |
|
||||
| `traefik.frontend.auth.forward.tls.ca=/path/ca.pem` | Sets the Certificate Authority (CA) for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.caOptional=true` | Checks the certificates if present but do not force to be signed by a specified Certificate Authority (CA). |
|
||||
| `traefik.frontend.auth.forward.tls.cert=/path/server.pem` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.tls.insecureSkipVerify=true` | If set to true invalid SSL certificates are accepted. |
|
||||
| `traefik.frontend.auth.forward.tls.key=/path/server.key` | Sets the Certificate for the TLS connection with the authentication server. |
|
||||
| `traefik.frontend.auth.forward.trustForwardHeader=true` | Trusts X-Forwarded-* headers. |
|
||||
| `traefik.frontend.auth.headerField=X-WebAuth-User` | Sets the header used to pass the authenticated user to the application. |
|
||||
| `traefik.frontend.entryPoints=http,https` | Assigns this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
|
||||
| `traefik.frontend.errors.<name>.backend=NAME` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.query=PATH` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.errors.<name>.status=RANGE` | See [custom error pages](/configuration/commons/#custom-error-pages) section. |
|
||||
| `traefik.frontend.passHostHeader=true` | Forwards client `Host` header to the backend. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.notAfter=true` | Add the noAfter field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.notBefore=true` | Add the noBefore field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.sans=true` | Add the sans field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.commonName=true` | Add the subject.commonName field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.country=true` | Add the subject.country field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.locality=true` | Add the subject.locality field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.organization=true`| Add the subject.organization field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.province=true` | Add the subject.province field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Add the subject.serialNumber field in a escaped client infos in the `X-Forwarded-Ssl-Client-Cert-Infos` header. |
|
||||
| `traefik.frontend.passTLSClientCert.pem=true` | Pass the escaped pem in the `X-Forwarded-Ssl-Client-Cert` header. |
|
||||
| `traefik.frontend.passTLSCert=true` | Forwards TLS Client certificates to the backend. |
|
||||
| `traefik.frontend.priority=10` | Overrides default frontend priority |
|
||||
| `traefik.frontend.rateLimit.extractorFunc=EXP` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.period=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.average=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.rateLimit.rateSet.<name>.burst=6` | See [rate limiting](/configuration/commons/#rate-limiting) section. |
|
||||
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint to this frontend (e.g. HTTPS) |
|
||||
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
|
||||
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirects to another URL to this frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
|
||||
| `traefik.frontend.redirect.permanent=true` | Returns 301 instead of 302. |
|
||||
| `traefik.frontend.rule=EXPR` | Overrides the default frontend rule. Default: `Host:{containerName}.{domain}` or `Host:{service}.{project_name}.{domain}` if you are using `docker-compose`. |
|
||||
| `traefik.frontend.whiteList.sourceRange=RANGE` | Sets a list of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access.<br>If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
|
||||
| `traefik.frontend.whiteList.useXForwardedFor=true` | Uses `X-Forwarded-For` header as valid source of IP for the white list. |
|
||||
|
||||
#### Custom Headers
|
||||
|
||||
@@ -236,44 +249,57 @@ You can define as many segments as ports exposed in a container.
|
||||
|
||||
Segment labels override the default behavior.
|
||||
|
||||
| Label | Description |
|
||||
|---------------------------------------------------------------------------|---------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
||||
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
||||
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic.usersfile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersfile` |
|
||||
| `traefik.<segment_name>.frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` |
|
||||
| `traefik.<segment_name>.frontend.auth.digest.usersfile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersfile` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.address=https://example.com`| Same as `traefik.frontend.auth.forward.address` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.insecureSkipVerify=true`| Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify`|
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` |
|
||||
| `traefik.<segment_name>.frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` |
|
||||
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
||||
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
||||
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
|
||||
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
|
||||
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
|
||||
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
|
||||
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
|
||||
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
|
||||
| Label | Description |
|
||||
|------------------------------------------------------------------------------------|------------------------------------------------------------------------|
|
||||
| `traefik.<segment_name>.backend=BACKEND` | Same as `traefik.backend` |
|
||||
| `traefik.<segment_name>.domain=DOMAIN` | Same as `traefik.domain` |
|
||||
| `traefik.<segment_name>.port=PORT` | Same as `traefik.port` |
|
||||
| `traefik.<segment_name>.protocol=http` | Same as `traefik.protocol` |
|
||||
| `traefik.<segment_name>.weight=10` | Same as `traefik.weight` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic=EXPR` | Same as `traefik.frontend.auth.basic` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic.removeHeader=true` | Same as `traefik.frontend.auth.basic.removeHeader` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic.users=EXPR` | Same as `traefik.frontend.auth.basic.users` |
|
||||
| `traefik.<segment_name>.frontend.auth.basic.usersFile=/path/.htpasswd` | Same as `traefik.frontend.auth.basic.usersFile` |
|
||||
| `traefik.<segment_name>.frontend.auth.digest.removeHeader=true` | Same as `traefik.frontend.auth.digest.removeHeader` |
|
||||
| `traefik.<segment_name>.frontend.auth.digest.users=EXPR` | Same as `traefik.frontend.auth.digest.users` |
|
||||
| `traefik.<segment_name>.frontend.auth.digest.usersFile=/path/.htdigest` | Same as `traefik.frontend.auth.digest.usersFile` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.address=https://example.com` | Same as `traefik.frontend.auth.forward.address` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.authResponseHeaders=EXPR` | Same as `traefik.frontend.auth.forward.authResponseHeaders` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.ca=/path/ca.pem` | Same as `traefik.frontend.auth.forward.tls.ca` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.caOptional=true` | Same as `traefik.frontend.auth.forward.tls.caOptional` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.cert=/path/server.pem` | Same as `traefik.frontend.auth.forward.tls.cert` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.insecureSkipVerify=true` | Same as `traefik.frontend.auth.forward.tls.insecureSkipVerify` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.tls.key=/path/server.key` | Same as `traefik.frontend.auth.forward.tls.key` |
|
||||
| `traefik.<segment_name>.frontend.auth.forward.trustForwardHeader=true` | Same as `traefik.frontend.auth.forward.trustForwardHeader` |
|
||||
| `traefik.<segment_name>.frontend.auth.headerField=X-WebAuth-User` | Same as `traefik.frontend.auth.headerField` |
|
||||
| `traefik.<segment_name>.frontend.entryPoints=https` | Same as `traefik.frontend.entryPoints` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.backend=NAME` | Same as `traefik.frontend.errors.<name>.backend` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.query=PATH` | Same as `traefik.frontend.errors.<name>.query` |
|
||||
| `traefik.<segment_name>.frontend.errors.<name>.status=RANGE` | Same as `traefik.frontend.errors.<name>.status` |
|
||||
| `traefik.<segment_name>.frontend.passHostHeader=true` | Same as `traefik.frontend.passHostHeader` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notAfter=true` | Same as `traefik.frontend.passTLSClientCert.infos.notAfter` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.notBefore=true` | Same as `traefik.frontend.passTLSClientCert.infos.notBefore` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.sans=true` | Same as `traefik.frontend.passTLSClientCert.infos.sans` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.commonName=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.commonName` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.country=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.country` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.locality=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.locality` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.organization=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.organization`|
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.province=true` | Same as `traefik.frontend.passTLSClientCert.infos.subject.province` |
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.infos.subject.serialNumber=true`| Same as `traefik.frontend.passTLSClientCert.infos.subject.serialNumber`|
|
||||
| `traefik.<segment_name>.frontend.passTLSClientCert.pem=true` | Same as `traefik.frontend.passTLSClientCert.infos.pem` |
|
||||
| `traefik.<segment_name>.frontend.passTLSCert=true` | Same as `traefik.frontend.passTLSCert` |
|
||||
| `traefik.<segment_name>.frontend.priority=10` | Same as `traefik.frontend.priority` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.extractorFunc=EXP` | Same as `traefik.frontend.rateLimit.extractorFunc` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.period=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.period` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.average=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.average` |
|
||||
| `traefik.<segment_name>.frontend.rateLimit.rateSet.<name>.burst=6` | Same as `traefik.frontend.rateLimit.rateSet.<name>.burst` |
|
||||
| `traefik.<segment_name>.frontend.redirect.entryPoint=https` | Same as `traefik.frontend.redirect.entryPoint` |
|
||||
| `traefik.<segment_name>.frontend.redirect.regex=^http://localhost/(.*)` | Same as `traefik.frontend.redirect.regex` |
|
||||
| `traefik.<segment_name>.frontend.redirect.replacement=http://mydomain/$1` | Same as `traefik.frontend.redirect.replacement` |
|
||||
| `traefik.<segment_name>.frontend.redirect.permanent=true` | Same as `traefik.frontend.redirect.permanent` |
|
||||
| `traefik.<segment_name>.frontend.rule=EXP` | Same as `traefik.frontend.rule` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.sourceRange=RANGE` | Same as `traefik.frontend.whiteList.sourceRange` |
|
||||
| `traefik.<segment_name>.frontend.whiteList.useXForwardedFor=true` | Same as `traefik.frontend.whiteList.useXForwardedFor` |
|
||||
|
||||
#### Custom Headers
|
||||
|
||||
|
@@ -18,7 +18,7 @@
|
||||
|
||||
# Enable debug mode.
|
||||
# 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/.
|
||||
# The log level will be set to DEBUG unless `logLevel` is specified.
|
||||
#
|
||||
# Optional
|
||||
|
@@ -5,6 +5,11 @@
|
||||
### TOML
|
||||
|
||||
```toml
|
||||
defaultEntryPoints = ["http", "https"]
|
||||
|
||||
# ...
|
||||
# ...
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
@@ -40,12 +45,14 @@
|
||||
[entryPoints.http.auth]
|
||||
headerField = "X-WebAuth-User"
|
||||
[entryPoints.http.auth.basic]
|
||||
removeHeader = true
|
||||
users = [
|
||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
]
|
||||
usersFile = "/path/to/.htpasswd"
|
||||
[entryPoints.http.auth.digest]
|
||||
removeHeader = true
|
||||
users = [
|
||||
"test:traefik:a2688e031edb4be6a3797f3882655c05",
|
||||
"test2:traefik:518845800f9e2bfb1f1f740ec24f074e",
|
||||
@@ -127,7 +134,9 @@ ProxyProtocol.TrustedIPs:192.168.0.1
|
||||
ProxyProtocol.Insecure:true
|
||||
ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24
|
||||
Auth.Basic.Users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0
|
||||
Auth.Basic.Removeheader:true
|
||||
Auth.Digest.Users:test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e
|
||||
Auth.Digest.Removeheader:true
|
||||
Auth.HeaderField:X-WebAuth-User
|
||||
Auth.Forward.Address:https://authserver.com/auth
|
||||
Auth.Forward.AuthResponseHeaders:X-Auth,X-Test,X-Secret
|
||||
@@ -275,18 +284,32 @@ Users can be specified directly in the TOML file, or indirectly by referencing a
|
||||
usersFile = "/path/to/.htpasswd"
|
||||
```
|
||||
|
||||
Optionally, you can pass authenticated user to application via headers
|
||||
Optionally, you can:
|
||||
|
||||
- pass authenticated user to application via headers
|
||||
|
||||
```toml
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
[entryPoints.http.auth]
|
||||
headerField = "X-WebAuth-User" # <--
|
||||
headerField = "X-WebAuth-User" # <-- header for the authenticated user
|
||||
[entryPoints.http.auth.basic]
|
||||
users = ["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"]
|
||||
```
|
||||
|
||||
- remove the Authorization header
|
||||
|
||||
```toml
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
[entryPoints.http.auth]
|
||||
[entryPoints.http.auth.basic]
|
||||
removeHeader = true # <-- remove the Authorization header
|
||||
users = ["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"]
|
||||
```
|
||||
|
||||
### Digest Authentication
|
||||
|
||||
You can use `htdigest` to generate them.
|
||||
@@ -304,18 +327,32 @@ Users can be specified directly in the TOML file, or indirectly by referencing a
|
||||
usersFile = "/path/to/.htdigest"
|
||||
```
|
||||
|
||||
Optionally, you can pass authenticated user to application via headers
|
||||
Optionally, you can!
|
||||
|
||||
- pass authenticated user to application via headers.
|
||||
|
||||
```toml
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
[entryPoints.http.auth]
|
||||
headerField = "X-WebAuth-User" # <--
|
||||
headerField = "X-WebAuth-User" # <-- header for the authenticated user
|
||||
[entryPoints.http.auth.digest]
|
||||
users = ["test:traefik:a2688e031edb4be6a3797f3882655c05", "test2:traefik:518845800f9e2bfb1f1f740ec24f074e"]
|
||||
```
|
||||
|
||||
- remove the Authorization header.
|
||||
|
||||
```toml
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":80"
|
||||
[entryPoints.http.auth]
|
||||
[entryPoints.http.auth.digest]
|
||||
removeHeader = true # <-- remove the Authorization header
|
||||
users = ["test:traefik:a2688e031edb4be6a3797f3882655c05", "test2:traefik:518845800f9e2bfb1f1f740ec24f074e"]
|
||||
```
|
||||
|
||||
### Forward Authentication
|
||||
|
||||
This configuration will first forward the request to `http://authserver.com/auth`.
|
||||
|
@@ -60,12 +60,14 @@ For more information about the CLI, see the documentation about [Traefik command
|
||||
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"
|
||||
@@ -90,6 +92,7 @@ traefikLogsFile = "log/traefik.log"
|
||||
```
|
||||
|
||||
To customize the log level:
|
||||
|
||||
```toml
|
||||
# Log level
|
||||
#
|
||||
@@ -109,17 +112,20 @@ 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"
|
||||
@@ -127,6 +133,7 @@ format = "json"
|
||||
```
|
||||
|
||||
To write the logs in async, specify `bufferingSize` as the format (must be >0):
|
||||
|
||||
```toml
|
||||
[accessLog]
|
||||
filePath = "/path/to/access.log"
|
||||
@@ -141,6 +148,7 @@ bufferingSize = 100
|
||||
```
|
||||
|
||||
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"
|
||||
@@ -171,6 +179,7 @@ format = "json"
|
||||
```
|
||||
|
||||
To customize logs format:
|
||||
|
||||
```toml
|
||||
[accessLog]
|
||||
filePath = "/path/to/access.log"
|
||||
@@ -218,7 +227,8 @@ format = "json"
|
||||
# ...
|
||||
```
|
||||
|
||||
#### List of all available fields
|
||||
|
||||
### List of all available fields
|
||||
|
||||
```ini
|
||||
StartUTC
|
||||
@@ -266,6 +276,15 @@ Deprecated way (before 1.4):
|
||||
accessLogsFile = "log/access.log"
|
||||
```
|
||||
|
||||
### CLF - Common Log Format
|
||||
|
||||
By default, Træfik use the CLF (`common`) as access log format.
|
||||
|
||||
```html
|
||||
<remote_IP_address> - <client_user_name_if_available> [<timestamp>] "<request_method> <request_path> <request_protocol>" <origin_server_HTTP_status> <origin_server_content_size> "<request_referrer>" "<request_user_agent>" <number_of_requests_received_since_Traefik_started> "<Traefik_frontend_name>" "<Traefik_backend_URL>" <request_duration_in_ms>ms
|
||||
```
|
||||
|
||||
|
||||
## Log Rotation
|
||||
|
||||
Traefik will close and reopen its log files, assuming they're configured, on receipt of a USR1 signal.
|
||||
|
@@ -1,10 +1,10 @@
|
||||
# Tracing
|
||||
|
||||
Tracing system allows developers to visualize call flows in there infrastructures.
|
||||
The tracing system allows developers to visualize call flows in their infrastructure.
|
||||
|
||||
We use [OpenTracing](http://opentracing.io). It is an open standard designed for distributed tracing.
|
||||
|
||||
Træfik supports two backends: Jaeger and Zipkin.
|
||||
Træfik supports three tracing backends: Jaeger, Zipkin and DataDog.
|
||||
|
||||
## Jaeger
|
||||
|
||||
@@ -22,6 +22,13 @@ Træfik supports two backends: Jaeger and Zipkin.
|
||||
# Default: "traefik"
|
||||
#
|
||||
serviceName = "traefik"
|
||||
|
||||
# Span name limit allows for name truncation in case of very long Frontend/Backend names
|
||||
# This can prevent certain tracing providers to drop traces that exceed their length limits
|
||||
#
|
||||
# Default: 0 - no truncation will occur
|
||||
#
|
||||
spanNameLimit = 0
|
||||
|
||||
[tracing.jaeger]
|
||||
# Sampling Server URL is the address of jaeger-agent's HTTP sampling server
|
||||
@@ -72,6 +79,13 @@ Træfik supports two backends: Jaeger and Zipkin.
|
||||
# Default: "traefik"
|
||||
#
|
||||
serviceName = "traefik"
|
||||
|
||||
# Span name limit allows for name truncation in case of very long Frontend/Backend names
|
||||
# This can prevent certain tracing providers to drop traces that exceed their length limits
|
||||
#
|
||||
# Default: 0 - no truncation will occur
|
||||
#
|
||||
spanNameLimit = 150
|
||||
|
||||
[tracing.zipkin]
|
||||
# Zipking HTTP endpoint used to send data
|
||||
@@ -115,6 +129,13 @@ Træfik supports two backends: Jaeger and Zipkin.
|
||||
# Default: "traefik"
|
||||
#
|
||||
serviceName = "traefik"
|
||||
|
||||
# Span name limit allows for name truncation in case of very long Frontend/Backend names
|
||||
# This can prevent certain tracing providers to drop traces that exceed their length limits
|
||||
#
|
||||
# Default: 0 - no truncation will occur
|
||||
#
|
||||
spanNameLimit = 100
|
||||
|
||||
[tracing.datadog]
|
||||
# Local Agent Host Port instructs reporter to send spans to datadog-tracing-agent at this address
|
||||
|
@@ -3,11 +3,11 @@
|
||||
</p>
|
||||
|
||||
[](https://semaphoreci.com/containous/traefik)
|
||||
[](https://docs.traefik.io)
|
||||
[](/)
|
||||
[](https://goreportcard.com/report/github.com/containous/traefik)
|
||||
[](https://github.com/containous/traefik/blob/master/LICENSE.md)
|
||||
[](https://slack.traefik.io)
|
||||
[](https://twitter.com/intent/follow?screen_name=traefikproxy)
|
||||
[](https://twitter.com/intent/follow?screen_name=traefik)
|
||||
|
||||
|
||||
Træfik is a modern HTTP reverse proxy and load balancer that makes deploying microservices easy.
|
||||
@@ -44,7 +44,7 @@ _(But if you'd rather configure some of your routes manually, Træfik supports t
|
||||
- Keeps access logs (JSON, CLF)
|
||||
- Fast
|
||||
- Exposes a Rest API
|
||||
- Packaged as a single binary file (made with :heart: with go) and available as a [tiny](https://microbadger.com/images/traefik) [official](https://hub.docker.com/r/_/traefik/) docker image
|
||||
- Packaged as a single binary file (made with ❤️ with go) and available as a [tiny](https://microbadger.com/images/traefik) [official](https://hub.docker.com/r/_/traefik/) docker image
|
||||
|
||||
|
||||
## Supported Providers
|
||||
@@ -86,6 +86,10 @@ services:
|
||||
- /var/run/docker.sock:/var/run/docker.sock # So that Traefik can listen to the Docker events
|
||||
```
|
||||
|
||||
!!! warning
|
||||
Enabling the Web UI with the `--api` flag might expose configuration elements. You can read more about this on the [API/Dashboard's Security section](/configuration/api#security).
|
||||
|
||||
|
||||
**That's it. Now you can launch Træfik!**
|
||||
|
||||
Start your `reverse-proxy` with the following command:
|
||||
@@ -138,7 +142,7 @@ IP: 172.27.0.3
|
||||
Run more instances of your `whoami` service with the following command:
|
||||
|
||||
```shell
|
||||
docker-compose up -d --scale whoami=2
|
||||
docker-compose 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.
|
||||
@@ -199,3 +203,19 @@ Using the tiny Docker image:
|
||||
```shell
|
||||
docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/etc/traefik/traefik.toml traefik
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
### Security Advisories
|
||||
|
||||
We strongly advise you to join our mailing list to be aware of the latest announcements from our security team. You can subscribe sending a mail to security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
|
||||
|
||||
### CVE
|
||||
|
||||
Reported vulnerabilities can be found on
|
||||
[cve.mitre.org](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=traefik).
|
||||
|
||||
### Report a Vulnerability
|
||||
|
||||
We want to keep Træfik safe for everyone.
|
||||
If you've discovered a security vulnerability in Træfik, we appreciate your help in disclosing it to us in a responsible manner, using [this form](https://security.traefik.io).
|
||||
|
@@ -26,8 +26,8 @@ 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).**
|
||||
**In cluster mode, ACME certificates have to be stored in [a KV Store entry](/configuration/acme/#as-a-key-value-store-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.
|
||||
The others instances will get ACME certificate from the KV Store entry.
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# Docker & Traefik
|
||||
# Let's Encrypt & Docker
|
||||
|
||||
In this use case, we want to use Træfik as a _layer-7_ load balancer with SSL termination for a set of micro-services used to run a web application.
|
||||
|
||||
@@ -8,7 +8,7 @@ In addition, we want to use Let's Encrypt to automatically generate and renew SS
|
||||
|
||||
## Setting Up
|
||||
|
||||
In order for this to work, you'll need a server with a public IP address, with Docker installed on it.
|
||||
In order for this to work, you'll need a server with a public IP address, with Docker and docker-compose installed on it.
|
||||
|
||||
In this example, we're using the fictitious domain _my-awesome-app.org_.
|
||||
|
||||
@@ -232,7 +232,7 @@ Finally but not unimportantly, we tell Træfik to route **to** port `9000`, sinc
|
||||
`Service labels` allow managing many routes for the same container.
|
||||
|
||||
When both `container labels` and `service labels` are defined, `container labels` are just used as default values for missing `service labels` but no frontend/backend are going to be defined only with these labels.
|
||||
Obviously, labels `traefik.frontend.rule` and `traefik.port` described above, will only be used to complete information set in `service labels` during the container frontends/bakends creation.
|
||||
Obviously, labels `traefik.frontend.rule` and `traefik.port` described above, will only be used to complete information set in `service labels` during the container frontends/backends creation.
|
||||
|
||||
In the example, two service names are defined : `basic` and `admin`.
|
||||
They allow creating two frontends and two backends.
|
||||
|
@@ -223,7 +223,7 @@ 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).
|
||||
More information about wildcard certificates are available [in this section](/configuration/acme/#wildcard-domains).
|
||||
|
||||
### onHostRule option and provided certificates (with HTTP challenge)
|
||||
|
||||
@@ -311,7 +311,6 @@ The `consul` provider contains the configuration.
|
||||
[frontends.frontend2]
|
||||
backend = "backend1"
|
||||
passHostHeader = true
|
||||
passTLSCert = true
|
||||
entrypoints = ["https"] # overrides defaultEntryPoints
|
||||
[frontends.frontend2.routes.test_1]
|
||||
rule = "Host:{subdomain:[a-z]+}.localhost"
|
||||
@@ -330,3 +329,87 @@ providersThrottleDuration = "5s"
|
||||
[respondingTimeouts]
|
||||
idleTimeout = "360s"
|
||||
```
|
||||
|
||||
## Using labels in docker-compose.yml
|
||||
|
||||
Pay attention to the **labels** section:
|
||||
|
||||
```
|
||||
home:
|
||||
image: abiosoft/caddy:0.10.14
|
||||
networks:
|
||||
- ntw_front
|
||||
volumes:
|
||||
- ./www/home/srv/:/srv/
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 2
|
||||
#placement:
|
||||
# constraints: [node.role==manager]
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
max_attempts: 5
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.20'
|
||||
memory: 9M
|
||||
reservations:
|
||||
cpus: '0.05'
|
||||
memory: 9M
|
||||
labels:
|
||||
- "traefik.frontend.rule=PathPrefixStrip:/"
|
||||
- "traefik.backend=home"
|
||||
- "traefik.port=2015"
|
||||
- "traefik.weight=10"
|
||||
- "traefik.enable=true"
|
||||
- "traefik.passHostHeader=true"
|
||||
- "traefik.docker.network=ntw_front"
|
||||
- "traefik.frontend.entryPoints=http"
|
||||
- "traefik.backend.loadbalancer.swarm=true"
|
||||
- "traefik.backend.loadbalancer.method=drr"
|
||||
```
|
||||
|
||||
Something more tricky using `regex`.
|
||||
|
||||
In this case a slash is added to `siteexample.io/portainer` and redirect to `siteexample.io/portainer/`. For more details: https://github.com/containous/traefik/issues/563
|
||||
|
||||
The double sign `$$` are variables managed by the docker compose file ([documentation](https://docs.docker.com/compose/compose-file/#variable-substitution)).
|
||||
|
||||
```
|
||||
portainer:
|
||||
image: portainer/portainer:1.16.5
|
||||
networks:
|
||||
- ntw_front
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
placement:
|
||||
constraints: [node.role==manager]
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
max_attempts: 5
|
||||
resources:
|
||||
limits:
|
||||
cpus: '0.33'
|
||||
memory: 20M
|
||||
reservations:
|
||||
cpus: '0.05'
|
||||
memory: 10M
|
||||
labels:
|
||||
- "traefik.frontend.rule=PathPrefixStrip:/portainer"
|
||||
- "traefik.backend=portainer"
|
||||
- "traefik.port=9000"
|
||||
- "traefik.weight=10"
|
||||
- "traefik.enable=true"
|
||||
- "traefik.passHostHeader=true"
|
||||
- "traefik.docker.network=ntw_front"
|
||||
- "traefik.frontend.entryPoints=http"
|
||||
- "traefik.backend.loadbalancer.swarm=true"
|
||||
- "traefik.backend.loadbalancer.method=drr"
|
||||
# https://github.com/containous/traefik/issues/563#issuecomment-421360934
|
||||
- "traefik.frontend.redirect.regex=^(.*)/portainer$$"
|
||||
- "traefik.frontend.redirect.replacement=$$1/portainer/"
|
||||
- "traefik.frontend.rule=PathPrefix:/portainer;ReplacePathRegex: ^/portainer/(.*) /$$1"
|
||||
```
|
||||
|
@@ -45,9 +45,7 @@ We don't need specific configuration to use gRPC in Træfik, we just need to use
|
||||
|
||||
This section explains how to use Traefik as reverse proxy for gRPC application with self-signed certificates.
|
||||
|
||||
<p align="center">
|
||||
<img src="/img/grpc.svg" alt="gRPC architecture" title="gRPC architecture" />
|
||||
</p>
|
||||

|
||||
|
||||
### gRPC Server certificate
|
||||
|
||||
|
@@ -17,7 +17,7 @@ The config files used in this guide can be found in the [examples directory](htt
|
||||
|
||||
### Role Based Access Control configuration (Kubernetes 1.6+ only)
|
||||
|
||||
Kubernetes introduces [Role Based Access Control (RBAC)](https://kubernetes.io/docs/admin/authorization/rbac/) in 1.6+ to allow fine-grained control of Kubernetes resources and API.
|
||||
Kubernetes introduces [Role Based Access Control (RBAC)](https://kubernetes.io/docs/reference/access-authn-authz/rbac/) in 1.6+ to allow fine-grained control of Kubernetes resources and API.
|
||||
|
||||
If your cluster is configured with RBAC, you will need to authorize Træfik to use the Kubernetes API. There are two ways to set up the proper permission: Via namespace-specific RoleBindings or a single, global ClusterRoleBinding.
|
||||
|
||||
@@ -453,8 +453,8 @@ kubectl create secret generic mysecret --from-file auth --namespace=monitoring
|
||||
|
||||
C. Attach the following annotations to the Ingress object:
|
||||
|
||||
- `ingress.kubernetes.io/auth-type: "basic"`
|
||||
- `ingress.kubernetes.io/auth-secret: "mysecret"`
|
||||
- `traefik.ingress.kubernetes.io/auth-type: "basic"`
|
||||
- `traefik.ingress.kubernetes.io/auth-secret: "mysecret"`
|
||||
|
||||
They specify basic authentication and reference the Secret `mysecret` containing the credentials.
|
||||
|
||||
@@ -468,8 +468,8 @@ metadata:
|
||||
namespace: monitoring
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: traefik
|
||||
ingress.kubernetes.io/auth-type: "basic"
|
||||
ingress.kubernetes.io/auth-secret: "mysecret"
|
||||
traefik.ingress.kubernetes.io/auth-type: "basic"
|
||||
traefik.ingress.kubernetes.io/auth-secret: "mysecret"
|
||||
spec:
|
||||
rules:
|
||||
- host: dashboard.prometheus.example.com
|
||||
@@ -742,6 +742,45 @@ You should now be able to visit the websites in your browser.
|
||||
- [cheeses.minikube/cheddar](http://cheeses.minikube/cheddar/)
|
||||
- [cheeses.minikube/wensleydale](http://cheeses.minikube/wensleydale/)
|
||||
|
||||
## Multiple Ingress Definitions for the Same Host (or Host+Path)
|
||||
|
||||
Træfik will merge multiple Ingress definitions for the same host/path pair into one definition.
|
||||
|
||||
Let's say the number of cheese services is growing.
|
||||
It is now time to move the cheese services to a dedicated cheese namespace to simplify the managements of cheese and non-cheese services.
|
||||
|
||||
Simply deploy a new Ingress Object with the same host an path into the cheese namespace:
|
||||
|
||||
```yaml
|
||||
apiVersion: extensions/v1beta1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: cheese
|
||||
namespace: cheese
|
||||
annotations:
|
||||
kubernetes.io/ingress.class: traefik
|
||||
traefik.frontend.rule.type: PathPrefixStrip
|
||||
spec:
|
||||
rules:
|
||||
- host: cheese.minikube
|
||||
http:
|
||||
paths:
|
||||
- path: /cheddar
|
||||
backend:
|
||||
serviceName: cheddar
|
||||
servicePort: http
|
||||
```
|
||||
|
||||
Træfik will now look for cheddar service endpoints (ports on healthy pods) in both the cheese and the default namespace.
|
||||
Deploying cheddar into the cheese namespace and afterwards shutting down cheddar in the default namespace is enough to migrate the traffic.
|
||||
|
||||
!!! note
|
||||
The kubernetes documentation does not specify this merging behavior.
|
||||
|
||||
!!! note
|
||||
Merging ingress definitions can cause problems if the annotations differ or if the services handle requests differently.
|
||||
Be careful and extra cautious when running multiple overlapping ingress definitions.
|
||||
|
||||
## Specifying Routing Priorities
|
||||
|
||||
Sometimes you need to specify priority for ingress routes, especially when handling wildcard routes.
|
||||
|
@@ -56,7 +56,7 @@ whoami4:
|
||||
|
||||
### Upload the configuration in the Key-value store
|
||||
|
||||
We should now fill the store with the Træfik global configuration, as we do with a [TOML file configuration](/toml).
|
||||
We should now fill the store with the Træfik global configuration.
|
||||
To do that, we can send the Key-value pairs via [curl commands](https://www.consul.io/intro/getting-started/kv.html) or via the [Web UI](https://www.consul.io/intro/getting-started/ui.html).
|
||||
|
||||
Fortunately, Træfik allows automation of this process using the `storeconfig` subcommand.
|
||||
@@ -445,4 +445,4 @@ Then remove the line `storageFile = "acme.json"` from your TOML config file.
|
||||
|
||||
That's it!
|
||||
|
||||

|
||||

|
||||
|
@@ -30,7 +30,7 @@ Following is the order by which Traefik tries to identify the port (the first on
|
||||
|
||||
## Applications with multiple ports
|
||||
|
||||
Some Marathon applications may expose multiple ports. Traefik supports creating one so-called _service_ per port using [specific labels](/configuration/backends/marathon#service-level).
|
||||
Some Marathon applications may expose multiple ports. Traefik supports creating one so-called _segment_ per port using [segment labels](/configuration/backends/marathon#applications-with-multiple-ports-segment-labels).
|
||||
|
||||
For instance, assume that a Marathon application exposes a web API on port 80 and an admin interface on port 8080. It would then be possible to make each service available by specifying the following Marathon labels:
|
||||
|
||||
|
@@ -330,4 +330,4 @@ X-Forwarded-Proto: http
|
||||
X-Forwarded-Server: 77fc29c69fe4
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
@@ -178,4 +178,4 @@ X-Forwarded-Proto: http
|
||||
X-Forwarded-Server: 8fbc39271b4c
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
@@ -50,7 +50,7 @@ start_boulder() {
|
||||
# Script usage
|
||||
show_usage() {
|
||||
echo
|
||||
echo "USAGE : manage_acme_docker_environment.sh [--start|--stop|--restart]"
|
||||
echo "USAGE : manage_acme_docker_environment.sh [--dev|--start|--stop|--restart]"
|
||||
echo
|
||||
}
|
||||
|
||||
|
@@ -1,18 +1,18 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
#The reverse proxy service (Træfik)
|
||||
# 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
|
||||
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)
|
||||
- "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
|
||||
- /var/run/docker.sock:/var/run/docker.sock # So that Traefik can listen to the Docker events
|
||||
|
||||
#A container that exposes a simple API
|
||||
# A container that exposes a simple API
|
||||
whoami:
|
||||
image: emilevauge/whoami #A container that exposes an API to show it's IP address
|
||||
image: emilevauge/whoami # A container that exposes an API to show it's IP address
|
||||
labels:
|
||||
- "traefik.frontend.rule=Host:whoami.docker.localhost"
|
||||
- "traefik.frontend.rule=Host:whoami.docker.localhost"
|
||||
|
@@ -38,7 +38,6 @@ func init() {
|
||||
if strings.Contains(e, "http2debug=1") || strings.Contains(e, "http2debug=2") {
|
||||
http2VerboseLogs = true
|
||||
}
|
||||
http2VerboseLogs = true
|
||||
}
|
||||
|
||||
// Server implements net.Handler and enables h2c. Users who want h2c just need
|
||||
|
@@ -118,7 +118,7 @@ func (hc *HealthCheck) execute(ctx context.Context, backend *BackendConfig) {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
log.Debug("Stopping current health check goroutines of backend: %s", backend.name)
|
||||
log.Debugf("Stopping current health check goroutines of backend: %s", backend.name)
|
||||
return
|
||||
case <-ticker.C:
|
||||
log.Debugf("Refreshing health check for backend: %s", backend.name)
|
||||
|
@@ -79,7 +79,7 @@ func setupPebbleRootCA() (*http.Transport, error) {
|
||||
}
|
||||
|
||||
func (s *AcmeSuite) SetUpSuite(c *check.C) {
|
||||
s.createComposeProject(c, "peddle")
|
||||
s.createComposeProject(c, "pebble")
|
||||
s.composeProject.Start(c)
|
||||
|
||||
s.fakeDNSServer = startFakeDNSServer()
|
||||
@@ -91,7 +91,7 @@ func (s *AcmeSuite) SetUpSuite(c *check.C) {
|
||||
c.Fatal(err)
|
||||
}
|
||||
|
||||
// wait for peddle
|
||||
// wait for pebble
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, s.getAcmeURL(), nil)
|
||||
|
||||
client := &http.Client{
|
||||
@@ -122,7 +122,7 @@ func (s *AcmeSuite) TearDownSuite(c *check.C) {
|
||||
|
||||
func (s *AcmeSuite) TestHTTP01DomainsAtStart(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme-base.toml",
|
||||
traefikConfFilePath: "fixtures/acme/acme_base.toml",
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
@@ -140,7 +140,7 @@ func (s *AcmeSuite) TestHTTP01DomainsAtStart(c *check.C) {
|
||||
|
||||
func (s *AcmeSuite) TestHTTP01DomainsInSANAtStart(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme-base.toml",
|
||||
traefikConfFilePath: "fixtures/acme/acme_base.toml",
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
@@ -159,7 +159,7 @@ func (s *AcmeSuite) TestHTTP01DomainsInSANAtStart(c *check.C) {
|
||||
|
||||
func (s *AcmeSuite) TestHTTP01OnHostRule(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme-base.toml",
|
||||
traefikConfFilePath: "fixtures/acme/acme_base.toml",
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
@@ -175,7 +175,7 @@ func (s *AcmeSuite) TestHTTP01OnHostRule(c *check.C) {
|
||||
|
||||
func (s *AcmeSuite) TestHTTP01OnHostRuleECDSA(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme-base.toml",
|
||||
traefikConfFilePath: "fixtures/acme/acme_base.toml",
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
@@ -192,7 +192,7 @@ func (s *AcmeSuite) TestHTTP01OnHostRuleECDSA(c *check.C) {
|
||||
|
||||
func (s *AcmeSuite) TestHTTP01OnHostRuleInvalidAlgo(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme-base.toml",
|
||||
traefikConfFilePath: "fixtures/acme/acme_base.toml",
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
@@ -257,7 +257,7 @@ func (s *AcmeSuite) TestHTTP01OnHostRuleDynamicCertificatesWithWildcard(c *check
|
||||
|
||||
func (s *AcmeSuite) TestHTTP01OnDemand(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme-base.toml",
|
||||
traefikConfFilePath: "fixtures/acme/acme_base.toml",
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
@@ -287,6 +287,22 @@ func (s *AcmeSuite) TestHTTP01OnDemandStaticCertificatesWithWildcard(c *check.C)
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
}
|
||||
|
||||
func (s *AcmeSuite) TestHTTP01OnDemandStaticCertificatesWithWildcardMultipleEntrypoints(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme_tls_multiple_entrypoints.toml",
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
OnDemand: true,
|
||||
},
|
||||
},
|
||||
expectedCommonName: acmeDomain,
|
||||
expectedAlgorithm: x509.RSA,
|
||||
}
|
||||
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
}
|
||||
|
||||
func (s *AcmeSuite) TestHTTP01OnDemandDynamicCertificatesWithWildcard(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme_tls_dynamic.toml",
|
||||
@@ -305,7 +321,7 @@ func (s *AcmeSuite) TestHTTP01OnDemandDynamicCertificatesWithWildcard(c *check.C
|
||||
|
||||
func (s *AcmeSuite) TestTLSALPN01OnHostRule(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme-base.toml",
|
||||
traefikConfFilePath: "fixtures/acme/acme_base.toml",
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
TLSChallenge: &acme.TLSChallenge{},
|
||||
@@ -321,7 +337,7 @@ func (s *AcmeSuite) TestTLSALPN01OnHostRule(c *check.C) {
|
||||
|
||||
func (s *AcmeSuite) TestTLSALPN01OnDemand(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme-base.toml",
|
||||
traefikConfFilePath: "fixtures/acme/acme_base.toml",
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
TLSChallenge: &acme.TLSChallenge{},
|
||||
@@ -337,7 +353,7 @@ func (s *AcmeSuite) TestTLSALPN01OnDemand(c *check.C) {
|
||||
|
||||
func (s *AcmeSuite) TestTLSALPN01DomainsAtStart(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme-base.toml",
|
||||
traefikConfFilePath: "fixtures/acme/acme_base.toml",
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
TLSChallenge: &acme.TLSChallenge{},
|
||||
@@ -355,7 +371,7 @@ func (s *AcmeSuite) TestTLSALPN01DomainsAtStart(c *check.C) {
|
||||
|
||||
func (s *AcmeSuite) TestTLSALPN01DomainsInSANAtStart(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme-base.toml",
|
||||
traefikConfFilePath: "fixtures/acme/acme_base.toml",
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
TLSChallenge: &acme.TLSChallenge{},
|
||||
@@ -372,9 +388,27 @@ func (s *AcmeSuite) TestTLSALPN01DomainsInSANAtStart(c *check.C) {
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
}
|
||||
|
||||
func (s *AcmeSuite) TestTLSALPN01DomainsWithProvidedWildcardDomainAtStart(c *check.C) {
|
||||
testCase := acmeTestCase{
|
||||
traefikConfFilePath: "fixtures/acme/acme_tls.toml",
|
||||
template: templateModel{
|
||||
Acme: acme.Configuration{
|
||||
TLSChallenge: &acme.TLSChallenge{},
|
||||
Domains: types.Domains{types.Domain{
|
||||
Main: acmeDomain,
|
||||
}},
|
||||
},
|
||||
},
|
||||
expectedCommonName: wildcardDomain,
|
||||
expectedAlgorithm: x509.RSA,
|
||||
}
|
||||
|
||||
s.retrieveAcmeCertificate(c, testCase)
|
||||
}
|
||||
|
||||
// Test Let's encrypt down
|
||||
func (s *AcmeSuite) TestNoValidLetsEncryptServer(c *check.C) {
|
||||
file := s.adaptFile(c, "fixtures/acme/acme-base.toml", templateModel{
|
||||
file := s.adaptFile(c, "fixtures/acme/acme_base.toml", templateModel{
|
||||
Acme: acme.Configuration{
|
||||
CAServer: "http://wrongurl:4001/directory",
|
||||
HTTPChallenge: &acme.HTTPChallenge{EntryPoint: "http"},
|
||||
|
@@ -674,3 +674,49 @@ func (s *ConsulCatalogSuite) TestMaintenanceMode(c *check.C) {
|
||||
err = try.Request(req, 10*time.Second, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
func (s *ConsulCatalogSuite) TestMultipleFrontendRule(c *check.C) {
|
||||
cmd, display := s.traefikCmd(
|
||||
withConfigFile("fixtures/consul_catalog/simple.toml"),
|
||||
"--consulCatalog",
|
||||
"--consulCatalog.endpoint="+s.consulIP+":8500",
|
||||
"--consulCatalog.domain=consul.localhost")
|
||||
defer display(c)
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// Wait for Traefik to turn ready.
|
||||
err = try.GetRequest("http://127.0.0.1:8000/", 2*time.Second, try.StatusCodeIs(http.StatusNotFound))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
whoami := s.composeProject.Container(c, "whoami1")
|
||||
|
||||
err = s.registerService("test", whoami.NetworkSettings.IPAddress, 80,
|
||||
[]string{
|
||||
"traefik.frontends.service1.rule=Host:whoami1.consul.localhost",
|
||||
"traefik.frontends.service2.rule=Host:whoami2.consul.localhost",
|
||||
})
|
||||
c.Assert(err, checker.IsNil, check.Commentf("Error registering service"))
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = "test.consul.localhost"
|
||||
|
||||
err = try.Request(req, 10*time.Second, 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 = "whoami1.consul.localhost"
|
||||
|
||||
err = try.Request(req, 10*time.Second, 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 = "whoami2.consul.localhost"
|
||||
|
||||
err = try.Request(req, 10*time.Second, try.StatusCodeIs(http.StatusOK), try.HasBody())
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
@@ -585,21 +585,14 @@ func (s *ConsulSuite) TestSNIDynamicTlsConfig(c *check.C) {
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// wait for traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8081/api/providers", 60*time.Second, try.BodyContains("MIIEpQIBAAKCAQEA1RducBK6EiFDv3TYB8ZcrfKWRVaSfHzWicO3J5WdST9oS7hG"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
client := &http.Client{Transport: tr1}
|
||||
req.Host = tr1.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr1.TLSClientConfig.ServerName)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
var resp *http.Response
|
||||
resp, err = client.Do(req)
|
||||
|
||||
err = try.RequestWithTransport(req, 30*time.Second, tr1, try.HasCn(tr1.TLSClientConfig.ServerName))
|
||||
c.Assert(err, checker.IsNil)
|
||||
cn := resp.TLS.PeerCertificates[0].Subject.CommonName
|
||||
c.Assert(cn, checker.Equals, "snitest.com")
|
||||
|
||||
// now we configure the second keypair in consul and the request for host "snitest.org" will use the second keypair
|
||||
for key, value := range tlsconfigure2 {
|
||||
@@ -614,18 +607,12 @@ func (s *ConsulSuite) TestSNIDynamicTlsConfig(c *check.C) {
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// waiting for traefik to pull configuration
|
||||
err = try.GetRequest("http://127.0.0.1:8081/api/providers", 30*time.Second, try.BodyContains("MIIEogIBAAKCAQEAvG9kL+vF57+MICehzbqcQAUlAOSl5r"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
client = &http.Client{Transport: tr2}
|
||||
req.Host = tr2.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr2.TLSClientConfig.ServerName)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
resp, err = client.Do(req)
|
||||
|
||||
err = try.RequestWithTransport(req, 30*time.Second, tr2, try.HasCn(tr2.TLSClientConfig.ServerName))
|
||||
c.Assert(err, checker.IsNil)
|
||||
cn = resp.TLS.PeerCertificates[0].Subject.CommonName
|
||||
c.Assert(cn, checker.Equals, "snitest.org")
|
||||
}
|
||||
|
@@ -532,21 +532,14 @@ func (s *Etcd3Suite) TestSNIDynamicTlsConfig(c *check.C) {
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Træfik
|
||||
err = try.GetRequest("http://127.0.0.1:8081/api/providers", 60*time.Second, try.BodyContains(string("MIIEpQIBAAKCAQEA1RducBK6EiFDv3TYB8ZcrfKWRVaSfHzWicO3J5WdST9oS7h")))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
client := &http.Client{Transport: tr1}
|
||||
req.Host = tr1.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr1.TLSClientConfig.ServerName)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
var resp *http.Response
|
||||
resp, err = client.Do(req)
|
||||
|
||||
err = try.RequestWithTransport(req, 30*time.Second, tr1, try.HasCn(tr1.TLSClientConfig.ServerName))
|
||||
c.Assert(err, checker.IsNil)
|
||||
cn := resp.TLS.PeerCertificates[0].Subject.CommonName
|
||||
c.Assert(cn, checker.Equals, "snitest.com")
|
||||
|
||||
// now we configure the second keypair in etcd and the request for host "snitest.org" will use the second keypair
|
||||
|
||||
@@ -562,20 +555,14 @@ func (s *Etcd3Suite) TestSNIDynamicTlsConfig(c *check.C) {
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// waiting for Træfik to pull configuration
|
||||
err = try.GetRequest("http://127.0.0.1:8081/api/providers", 30*time.Second, try.BodyContains("MIIEogIBAAKCAQEAvG9kL+vF57+MICehzbqcQAUlAOSl5r"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
client = &http.Client{Transport: tr2}
|
||||
req.Host = tr2.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr2.TLSClientConfig.ServerName)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
resp, err = client.Do(req)
|
||||
|
||||
err = try.RequestWithTransport(req, 30*time.Second, tr2, try.HasCn(tr2.TLSClientConfig.ServerName))
|
||||
c.Assert(err, checker.IsNil)
|
||||
cn = resp.TLS.PeerCertificates[0].Subject.CommonName
|
||||
c.Assert(cn, checker.Equals, "snitest.org")
|
||||
}
|
||||
|
||||
func (s *Etcd3Suite) TestDeleteSNIDynamicTlsConfig(c *check.C) {
|
||||
@@ -646,21 +633,14 @@ func (s *Etcd3Suite) TestDeleteSNIDynamicTlsConfig(c *check.C) {
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Træfik
|
||||
err = try.GetRequest(traefikWebEtcdURL+"api/providers", 60*time.Second, try.BodyContains(string("MIIEpQIBAAKCAQEA1RducBK6EiFDv3TYB8ZcrfKWRVaSfHzWicO3J5WdST9oS7h")))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
client := &http.Client{Transport: tr1}
|
||||
req.Host = tr1.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr1.TLSClientConfig.ServerName)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
var resp *http.Response
|
||||
resp, err = client.Do(req)
|
||||
|
||||
err = try.RequestWithTransport(req, 30*time.Second, tr1, try.HasCn(tr1.TLSClientConfig.ServerName))
|
||||
c.Assert(err, checker.IsNil)
|
||||
cn := resp.TLS.PeerCertificates[0].Subject.CommonName
|
||||
c.Assert(cn, checker.Equals, "snitest.com")
|
||||
|
||||
// now we delete the tls cert/key pairs,so the endpoint show use default cert/key pair
|
||||
for key := range tlsconfigure1 {
|
||||
@@ -668,18 +648,12 @@ func (s *Etcd3Suite) TestDeleteSNIDynamicTlsConfig(c *check.C) {
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
// waiting for Træfik to pull configuration
|
||||
err = try.GetRequest(traefikWebEtcdURL+"api/providers", 30*time.Second, try.BodyNotContains("MIIEpQIBAAKCAQEA1RducBK6EiFDv3TYB8ZcrfKWRVaSfHzWicO3J5WdST9oS7h"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
client = &http.Client{Transport: tr1}
|
||||
req.Host = tr1.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr1.TLSClientConfig.ServerName)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
resp, err = client.Do(req)
|
||||
|
||||
err = try.RequestWithTransport(req, 30*time.Second, tr1, try.HasCn("TRAEFIK DEFAULT CERT"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
cn = resp.TLS.PeerCertificates[0].Subject.CommonName
|
||||
c.Assert(cn, checker.Equals, "TRAEFIK DEFAULT CERT")
|
||||
}
|
||||
|
@@ -315,13 +315,13 @@ func (s *EtcdSuite) TestCertificatesContentWithSNIConfigHandshake(c *check.C) {
|
||||
snitestOrgKey, err := ioutil.ReadFile("fixtures/https/snitest.org.key")
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
globalConfig := map[string]string{
|
||||
"/traefik/entrypoints/https/address": ":4443",
|
||||
"/traefik/entrypoints/https/tls/certificates/0/certfile": string(snitestComCert),
|
||||
"/traefik/entrypoints/https/tls/certificates/0/keyfile": string(snitestComKey),
|
||||
"/traefik/entrypoints/https/tls/certificates/1/certfile": string(snitestOrgCert),
|
||||
"/traefik/entrypoints/https/tls/certificates/1/keyfile": string(snitestOrgKey),
|
||||
"/traefik/defaultentrypoints/0": "https",
|
||||
globalConfig := map[string][]byte{
|
||||
"/traefik/entrypoints/https/address": []byte(":4443"),
|
||||
"/traefik/entrypoints/https/tls/certificates/0/certfile": snitestComCert,
|
||||
"/traefik/entrypoints/https/tls/certificates/0/keyfile": snitestComKey,
|
||||
"/traefik/entrypoints/https/tls/certificates/1/certfile": snitestOrgCert,
|
||||
"/traefik/entrypoints/https/tls/certificates/1/keyfile": snitestOrgKey,
|
||||
"/traefik/defaultentrypoints/0": []byte("https"),
|
||||
}
|
||||
|
||||
backend1 := map[string]string{
|
||||
@@ -351,7 +351,7 @@ func (s *EtcdSuite) TestCertificatesContentWithSNIConfigHandshake(c *check.C) {
|
||||
"/traefik/frontends/frontend2/routes/test_2/rule": "Host:snitest.org",
|
||||
}
|
||||
for key, value := range globalConfig {
|
||||
err := s.kv.Put(key, []byte(value), nil)
|
||||
err := s.kv.Put(key, value, nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
for key, value := range backend1 {
|
||||
@@ -548,21 +548,14 @@ func (s *EtcdSuite) TestSNIDynamicTlsConfig(c *check.C) {
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Træfik
|
||||
err = try.GetRequest("http://127.0.0.1:8081/api/providers", 60*time.Second, try.BodyContains(string("MIIEpQIBAAKCAQEA1RducBK6EiFDv3TYB8ZcrfKWRVaSfHzWicO3J5WdST9oS7h")))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
client := &http.Client{Transport: tr1}
|
||||
req.Host = tr1.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr1.TLSClientConfig.ServerName)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
var resp *http.Response
|
||||
resp, err = client.Do(req)
|
||||
|
||||
err = try.RequestWithTransport(req, 30*time.Second, tr1, try.HasCn(tr1.TLSClientConfig.ServerName))
|
||||
c.Assert(err, checker.IsNil)
|
||||
cn := resp.TLS.PeerCertificates[0].Subject.CommonName
|
||||
c.Assert(cn, checker.Equals, "snitest.com")
|
||||
|
||||
// now we configure the second keypair in etcd and the request for host "snitest.org" will use the second keypair
|
||||
|
||||
@@ -578,18 +571,12 @@ func (s *EtcdSuite) TestSNIDynamicTlsConfig(c *check.C) {
|
||||
})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// waiting for Træfik to pull configuration
|
||||
err = try.GetRequest("http://127.0.0.1:8081/api/providers", 30*time.Second, try.BodyContains("MIIEogIBAAKCAQEAvG9kL+vF57+MICehzbqcQAUlAOSl5r"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
client = &http.Client{Transport: tr2}
|
||||
req.Host = tr2.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr2.TLSClientConfig.ServerName)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
resp, err = client.Do(req)
|
||||
|
||||
err = try.RequestWithTransport(req, 30*time.Second, tr2, try.HasCn(tr2.TLSClientConfig.ServerName))
|
||||
c.Assert(err, checker.IsNil)
|
||||
cn = resp.TLS.PeerCertificates[0].Subject.CommonName
|
||||
c.Assert(cn, checker.Equals, "snitest.org")
|
||||
}
|
||||
|
@@ -27,6 +27,10 @@ defaultEntryPoints = ["http", "https"]
|
||||
entryPoint = "{{ .Acme.HTTPChallenge.EntryPoint }}"
|
||||
{{end}}
|
||||
|
||||
{{if .Acme.TLSChallenge }}
|
||||
[acme.tlsChallenge]
|
||||
{{end}}
|
||||
|
||||
{{range .Acme.Domains}}
|
||||
[[acme.domains]]
|
||||
main = "{{ .Main }}"
|
||||
|
59
integration/fixtures/acme/acme_tls_multiple_entrypoints.toml
Normal file
59
integration/fixtures/acme/acme_tls_multiple_entrypoints.toml
Normal file
@@ -0,0 +1,59 @@
|
||||
logLevel = "DEBUG"
|
||||
|
||||
defaultEntryPoints = ["http", "https"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = "{{ .PortHTTP }}"
|
||||
[entryPoints.https]
|
||||
address = "{{ .PortHTTPS }}"
|
||||
[entryPoints.https.tls]
|
||||
[entryPoints.traefik]
|
||||
address = ":9000"
|
||||
[entryPoints.traefik.tls]
|
||||
[[entryPoints.traefik.tls.certificates]]
|
||||
certFile = "fixtures/acme/ssl/wildcard.crt"
|
||||
keyFile = "fixtures/acme/ssl/wildcard.key"
|
||||
|
||||
[acme]
|
||||
email = "test@traefik.io"
|
||||
storage = "/tmp/acme.json"
|
||||
entryPoint = "https"
|
||||
acmeLogging = true
|
||||
onDemand = {{ .Acme.OnDemand }}
|
||||
onHostRule = {{ .Acme.OnHostRule }}
|
||||
keyType = "{{ .Acme.KeyType }}"
|
||||
caServer = "{{ .Acme.CAServer }}"
|
||||
|
||||
{{if .Acme.HTTPChallenge }}
|
||||
[acme.httpChallenge]
|
||||
entryPoint = "{{ .Acme.HTTPChallenge.EntryPoint }}"
|
||||
{{end}}
|
||||
|
||||
{{if .Acme.TLSChallenge }}
|
||||
[acme.tlsChallenge]
|
||||
{{end}}
|
||||
|
||||
{{range .Acme.Domains}}
|
||||
[[acme.domains]]
|
||||
main = "{{ .Main }}"
|
||||
sans = [{{range .SANs }}
|
||||
"{{.}}",
|
||||
{{end}}]
|
||||
{{end}}
|
||||
|
||||
[api]
|
||||
|
||||
[file]
|
||||
|
||||
[backends]
|
||||
[backends.backend]
|
||||
[backends.backend.servers.server1]
|
||||
url = "http://127.0.0.1:9010"
|
||||
weight = 1
|
||||
|
||||
[frontends]
|
||||
[frontends.frontend]
|
||||
backend = "backend"
|
||||
[frontends.frontend.routes.test]
|
||||
rule = "Host:traefik.acme.wtf"
|
@@ -2,7 +2,7 @@
|
||||
[backends]
|
||||
[backends.backend2]
|
||||
[backends.backend2.servers.server1]
|
||||
url = "http://172.17.0.2:80"
|
||||
url = "http://172.17.0.123:80"
|
||||
weight = 1
|
||||
|
||||
[frontends]
|
||||
|
70
integration/fixtures/https/https_redirect.toml
Normal file
70
integration/fixtures/https/https_redirect.toml
Normal file
@@ -0,0 +1,70 @@
|
||||
logLevel = "DEBUG"
|
||||
|
||||
defaultEntryPoints = ["http", "https"]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.http]
|
||||
address = ":8888"
|
||||
[entryPoints.http.redirect]
|
||||
entryPoint = "https"
|
||||
[entryPoints.https]
|
||||
address = ":8443"
|
||||
[entryPoints.https.tls]
|
||||
|
||||
[api]
|
||||
|
||||
[file]
|
||||
|
||||
[backends]
|
||||
[backends.backend1]
|
||||
[backends.backend1.servers.server1]
|
||||
url = "http://127.0.0.1:80"
|
||||
weight = 1
|
||||
|
||||
[frontends]
|
||||
|
||||
[frontends.frontend1]
|
||||
backend = "backend1"
|
||||
[frontends.frontend1.routes.test_1]
|
||||
rule = "Host: example.com; PathPrefixStrip: /api"
|
||||
[frontends.frontend2]
|
||||
backend = "backend1"
|
||||
[frontends.frontend2.routes.test_1]
|
||||
rule = "Host: example2.com; PathPrefixStrip: /api/"
|
||||
|
||||
[frontends.frontend3]
|
||||
backend = "backend1"
|
||||
[frontends.frontend3.routes.test_1]
|
||||
rule = "Host: test.com; AddPrefix: /foo"
|
||||
[frontends.frontend4]
|
||||
backend = "backend1"
|
||||
[frontends.frontend4.routes.test_1]
|
||||
rule = "Host: test2.com; AddPrefix: /foo/"
|
||||
|
||||
[frontends.frontend5]
|
||||
backend = "backend1"
|
||||
[frontends.frontend5.routes.test_1]
|
||||
rule = "Host: foo.com; PathPrefixStripRegex: /{id:[a-z]+}"
|
||||
[frontends.frontend6]
|
||||
backend = "backend1"
|
||||
[frontends.frontend6.routes.test_1]
|
||||
rule = "Host: foo2.com; PathPrefixStripRegex: /{id:[a-z]+}/"
|
||||
|
||||
[frontends.frontend7]
|
||||
backend = "backend1"
|
||||
[frontends.frontend7.routes.test_1]
|
||||
rule = "Host: bar.com; ReplacePathRegex: /api /"
|
||||
[frontends.frontend8]
|
||||
backend = "backend1"
|
||||
[frontends.frontend8.routes.test_1]
|
||||
rule = "Host: bar2.com; ReplacePathRegex: /api/ /"
|
||||
|
||||
[frontends.frontend9]
|
||||
backend = "backend1"
|
||||
[frontends.frontend9.routes.test_1]
|
||||
rule = "Host: pow.com; ReplacePath: /api"
|
||||
[frontends.frontend10]
|
||||
backend = "backend1"
|
||||
[frontends.frontend10.routes.test_1]
|
||||
rule = "Host: pow2.com; ReplacePath: /api/"
|
||||
|
21
integration/fixtures/tlsclientheaders/root.pem
Normal file
21
integration/fixtures/tlsclientheaders/root.pem
Normal file
@@ -0,0 +1,21 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDhDCCAmygAwIBAgIJAK4Ed0WF/YNQMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNV
|
||||
BAYTAkZSMQ8wDQYDVQQIDAZGUkFOQ0UxETAPBgNVBAcMCFRPVUxPVVNFMRMwEQYD
|
||||
VQQKDApjb250YWlub3VzMQ8wDQYDVQQDDAZzZXJ2ZXIwHhcNMTgwMzIxMTMzOTM4
|
||||
WhcNMjEwMTA4MTMzOTM4WjBXMQswCQYDVQQGEwJGUjEPMA0GA1UECAwGRlJBTkNF
|
||||
MREwDwYDVQQHDAhUT1VMT1VTRTETMBEGA1UECgwKY29udGFpbm91czEPMA0GA1UE
|
||||
AwwGc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2DfZMdW1
|
||||
QKmdOTPULt6WUMVFU3PUcovq4cVtvNAzAshduC/7nHZx60uFzVKLYnOfZ+5VYfOS
|
||||
zfVXPvltmBSWga1Yj6CuzfDZwY1nkcoL+22yBD6x4w2nB7aFaPNgj6M4ALVEZRKX
|
||||
lMow+a0c0mOr1kLHm99MT/oabcdI+wbAp8VnLz9DF6SD7iDjIOb4RjvmcyetBzwu
|
||||
1rQYti0bFHOnLCxiz0asXly0zspFajWkbGkvBdvEoP2qOHMeTV604PaBwpIMX/ly
|
||||
ymGgYUctHeC16ptDRDDj7Spmu7ec2NzjgNW+MOth6EkFlhYgg1OEIXP+IFJ5LbS8
|
||||
1t/Y+fDUoc6+IQIDAQABo1MwUTAdBgNVHQ4EFgQUYeZvrzWyLI3TjmTIJYpSTjTb
|
||||
/XUwHwYDVR0jBBgwFoAUYeZvrzWyLI3TjmTIJYpSTjTb/XUwDwYDVR0TAQH/BAUw
|
||||
AwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAYQL8d/WQxu7rE58GC7le53FNzujMNZ+h
|
||||
1kdS35LrTXPv5b6QTi5oUGi5LCesP4HnCpGdMFodyydhY8rhIDZWEFgkJZOLZhdt
|
||||
sAyRONdI/Ms/NGQO2oJD+TlV92e4k3ey4WJyXIFHXE2Apb77VlsiHp8pI/iF/R5t
|
||||
h4o4OADG7k6Fjf/wx7A18ru2eoH+PcwA8i6sQaQ1qEwxC0b3rh2TwaCpFQVcmMv5
|
||||
5jKPRBN0UC0PyHwqFZsSg1folhMAIBAjUsHgA6WleN9zMCyLAIn0LSai1CpFby6o
|
||||
d6xu6pp8pwot8YTL0yS5T0X9aNhK2/uDoP50ei6eWI3uuPa8NJxbyA==
|
||||
-----END CERTIFICATE-----
|
27
integration/fixtures/tlsclientheaders/server.key
Normal file
27
integration/fixtures/tlsclientheaders/server.key
Normal file
@@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAo0eupztBxEchz/9BbegBzKX35YUt0S2Xzp/mFM+hXQylWDHB
|
||||
z3wED7R89v3sY6ePTk/tAT5l6uKjmQ/zRlQFf7QtVWKUtYq8rjuFn9/EeC+233mx
|
||||
kVP7QAcuT6T8PzoUgysW6Tx3zz18VDRMnPhx1fjA1jAq3+IU03BpbFz7CkYCxkG/
|
||||
1wWHmsB16LH2bMxJrzapph2nSDnUkoATugSJec+DxTtX1hdjAaJK/JsIwioA/Lyy
|
||||
6YgE2oX7uRZBou0bA3y0TDnFoIVAVqISYWfszTGDlwL+SD/P9GYa19GZk4imdp8j
|
||||
LD/+J9eLuHG75rkROvE4xbSPbGIGkZOEYTmjHwIDAQABAoIBAGok81kroHlkdIqu
|
||||
uW4lYOYVDq5agYp2RTXBpOTqhU/kJKjMz91+FXXQM1ytfbra9sJGGyCv27lyVD/w
|
||||
qomRnXGDQ+U6DMpnwnjRoPBpm2M2QX/NsK11FuRsxqJn8sN3klYi8OX2tTw4EFb9
|
||||
GMECkZ4z88hJz9VzN26sqRwU5e2qw45Fhk+Jl6RBsiBfMGNGsmI5n1yIgvQd2PoM
|
||||
wVxHI+bb3rWL7zE7wy2rb2c+J0P2gy7fZlFN2ZLkC5RjTqdzD2P4erp4gcpgffuO
|
||||
0Epu7ZzuJ0UKCBXJOkhjlM79opLK6IBpF1YgxVCoMPbQVYAHP9hSwuz6hgc0ocwa
|
||||
+6PqzSECgYEA1kTSFN8tHq2VMFgwPyguppSmeJJdIcnMYdicJNkv9YXeIt4mAk9c
|
||||
Qm5eMLoqRJL94fdRDGb7QIqcfSrQODHy5dmqrTZd+TeSc4VRC1gZ7RPg5ja8b0dR
|
||||
DoktPizIYzWrNEaEjhWojqYXT5DOOmNgDbOYrlR6Qdrd6VOmQkIgHz0CgYEAwxSf
|
||||
NMe6LasWg9PYgLeVBcNc9oOjGvczOmNULngte8LpiJm4yzI0gMer20VdCtXYsyR1
|
||||
Zs/rItzSQuvr+3v5qW2NfJ/TaJkZ+bcc/fGJ2LcnM2Kfjfih8DSy5/MBzNM4cqw2
|
||||
arHVvQlAvfOSB8WoFzdXOS41Z+BumLsZE3/mMYsCgYBGNTKpCB+ep730o1DbwOzY
|
||||
RGjvpPXDNn4zqWgwYsHmL0EEJ8pIg3x1f/h4+ucSpR9vRTxXVf8JvOFd2gN0BlnS
|
||||
mqnkK6ZLHLxuAcb2cp28IwFULac8xx92JdifQMlASLuaW2jfrZUXeLC2r3oDg8Bb
|
||||
fPeQV7nfjjmcVH5rw4MG+QKBgQCi4RH4oJZLUSEQWo3XEvDjCfYRgWFqv2FPa+W6
|
||||
ku7u+ZPBURAg4D9EEvLjtmt0A47WLCe1+v3JcvQ/mfnDVQTkOKs8lbmPCN3OSNx1
|
||||
DvnYLzwUxFCR2jljdKy3y4cCPI1R+YXJ2ceq+RHMR5Ty1k59a+BwxqsimxncfcL3
|
||||
K//H9wKBgQChT3kvF9Igcdna8g+JneGD6RHXJX1o80QrO+eWma4NozEOmXqA7R7r
|
||||
+GwAyqy9GFM7pwUhHmhJAxILMBxR84EY7kCBvi1VlZ3JbT7w0gjjOqPHklvbsPj9
|
||||
BruA5xPMq1gzCOgejQIRoODtpH1S6Fi/YMTO6eq75qw6minHWi4dPw==
|
||||
-----END RSA PRIVATE KEY-----
|
19
integration/fixtures/tlsclientheaders/server.pem
Normal file
19
integration/fixtures/tlsclientheaders/server.pem
Normal file
@@ -0,0 +1,19 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDKjCCAhICCQDKAJTeuq3LHjANBgkqhkiG9w0BAQsFADBXMQswCQYDVQQGEwJG
|
||||
UjEPMA0GA1UECAwGRlJBTkNFMREwDwYDVQQHDAhUT1VMT1VTRTETMBEGA1UECgwK
|
||||
Y29udGFpbm91czEPMA0GA1UEAwwGc2VydmVyMB4XDTE4MDMyMTEzNDM0MVoXDTIx
|
||||
MDEwODEzNDM0MVowVzELMAkGA1UEBhMCRlIxDzANBgNVBAgMBkZSQU5DRTERMA8G
|
||||
A1UEBwwIVE9VTE9VU0UxEzARBgNVBAoMCmNvbnRhaW5vdXMxDzANBgNVBAMMBnNl
|
||||
cnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKNHrqc7QcRHIc//
|
||||
QW3oAcyl9+WFLdEtl86f5hTPoV0MpVgxwc98BA+0fPb97GOnj05P7QE+Zerio5kP
|
||||
80ZUBX+0LVVilLWKvK47hZ/fxHgvtt95sZFT+0AHLk+k/D86FIMrFuk8d889fFQ0
|
||||
TJz4cdX4wNYwKt/iFNNwaWxc+wpGAsZBv9cFh5rAdeix9mzMSa82qaYdp0g51JKA
|
||||
E7oEiXnPg8U7V9YXYwGiSvybCMIqAPy8sumIBNqF+7kWQaLtGwN8tEw5xaCFQFai
|
||||
EmFn7M0xg5cC/kg/z/RmGtfRmZOIpnafIyw//ifXi7hxu+a5ETrxOMW0j2xiBpGT
|
||||
hGE5ox8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAPYDdGyNWp7R9j2oxZEbQS4lb
|
||||
+2Ol1r6PFo/zmpB6GK3CSNo65a0DtW/ITeQi97MMgGS1D3wnaFPrwxtp0mEn7HjU
|
||||
uDcufHBqqBsjYC3NEtt+yyxNeYddLD/GdFXw4d6wNRdRaFCq5N1CPQzF4VTdoSLD
|
||||
xsOq/WAHHc2cyZyOprAqm2UXyWXxn4yWZqzDsZ41/v2f3uMNxeqyIEtNZVzTKQBu
|
||||
wWw+jlQKGu0T8Ex1f0jaKI1OPtN5dzaIfO8acHcuNdmnE+hVsoqe17Dckxsj1ORf
|
||||
8ZcZ4qvULVouGINQBP4fcl5jv6TOm1U+ZSk01FcHPmiDEMB6Utyy4ZLHPbmKYg==
|
||||
-----END CERTIFICATE-----
|
24
integration/fixtures/tlsclientheaders/simple.toml
Normal file
24
integration/fixtures/tlsclientheaders/simple.toml
Normal file
@@ -0,0 +1,24 @@
|
||||
logLevel = "DEBUG"
|
||||
defaultEntryPoints = ["https"]
|
||||
debug = true
|
||||
rootCAs = [ """{{ .RootCertContent }}""" ]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.https]
|
||||
address = ":8443"
|
||||
|
||||
[entryPoints.https.tls]
|
||||
|
||||
[entryPoints.https.tls.ClientCA]
|
||||
files = [ """{{ .RootCertContent }}""" ]
|
||||
optional = false
|
||||
|
||||
[[entryPoints.https.tls.certificates]]
|
||||
certFile = """{{ .ServerCertContent }}"""
|
||||
keyFile = """{{ .ServerKeyContent }}"""
|
||||
|
||||
[api]
|
||||
|
||||
[docker]
|
||||
endpoint = "unix:///var/run/docker.sock"
|
||||
watch = true
|
@@ -19,5 +19,6 @@ logLevel = "DEBUG"
|
||||
[frontends]
|
||||
[frontends.frontend1]
|
||||
backend = "backend1"
|
||||
passHostHeader = true
|
||||
[frontends.frontend1.routes.test_1]
|
||||
rule = "PathPrefix:/ws"
|
||||
|
@@ -24,5 +24,6 @@ insecureSkipVerify=true
|
||||
[frontends]
|
||||
[frontends.frontend1]
|
||||
backend = "backend1"
|
||||
passHostHeader = true
|
||||
[frontends.frontend1.routes.test_1]
|
||||
rule = "Path:/echo,/ws"
|
||||
|
@@ -66,7 +66,7 @@ func (s *HTTPSSuite) TestWithSNIConfigRoute(c *check.C) {
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 500*time.Millisecond, try.BodyContains("Host:snitest.org"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 1*time.Second, try.BodyContains("Host:snitest.org"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
backend1 := startTestServer("9010", http.StatusNoContent)
|
||||
@@ -92,27 +92,23 @@ func (s *HTTPSSuite) TestWithSNIConfigRoute(c *check.C) {
|
||||
},
|
||||
}
|
||||
|
||||
client := &http.Client{Transport: tr1}
|
||||
req, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = "snitest.com"
|
||||
req.Header.Set("Host", "snitest.com")
|
||||
req.Host = tr1.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr1.TLSClientConfig.ServerName)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
resp, err := client.Do(req)
|
||||
c.Assert(err, checker.IsNil)
|
||||
// Expected a 204 (from backend1)
|
||||
c.Assert(resp.StatusCode, checker.Equals, http.StatusNoContent)
|
||||
|
||||
client = &http.Client{Transport: tr2}
|
||||
err = try.RequestWithTransport(req, 30*time.Second, tr1, try.HasCn(tr1.TLSClientConfig.ServerName), try.StatusCodeIs(http.StatusNoContent))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = "snitest.org"
|
||||
req.Header.Set("Host", "snitest.org")
|
||||
req.Host = tr2.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr2.TLSClientConfig.ServerName)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
resp, err = client.Do(req)
|
||||
|
||||
err = try.RequestWithTransport(req, 30*time.Second, tr2, try.HasCn(tr2.TLSClientConfig.ServerName), try.StatusCodeIs(http.StatusResetContent))
|
||||
c.Assert(err, checker.IsNil)
|
||||
// Expected a 205 (from backend2)
|
||||
c.Assert(resp.StatusCode, checker.Equals, http.StatusResetContent)
|
||||
}
|
||||
|
||||
// TestWithSNIStrictNotMatchedRequest involves a client sending a SNI hostname of
|
||||
@@ -561,28 +557,25 @@ func (s *HTTPSSuite) TestWithSNIDynamicConfigRouteWithNoChange(c *check.C) {
|
||||
err = try.GetRequest(backend2.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusResetContent))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
client := &http.Client{Transport: tr1}
|
||||
req, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = tr1.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr1.TLSClientConfig.ServerName)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
resp, err := client.Do(req)
|
||||
c.Assert(err, checker.IsNil)
|
||||
// snitest.org certificate must be used yet
|
||||
c.Assert(resp.TLS.PeerCertificates[0].Subject.CommonName, check.Equals, tr1.TLSClientConfig.ServerName)
|
||||
// Expected a 204 (from backend1)
|
||||
c.Assert(resp.StatusCode, checker.Equals, http.StatusResetContent)
|
||||
|
||||
client = &http.Client{Transport: tr2}
|
||||
// snitest.org certificate must be used yet && Expected a 204 (from backend1)
|
||||
err = try.RequestWithTransport(req, 30*time.Second, tr1, try.HasCn(tr1.TLSClientConfig.ServerName), try.StatusCodeIs(http.StatusResetContent))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = tr2.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr2.TLSClientConfig.ServerName)
|
||||
resp, err = client.Do(req)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
|
||||
// snitest.com certificate does not exist, default certificate has to be used && Expected a 205 (from backend2)
|
||||
err = try.RequestWithTransport(req, 30*time.Second, tr2, try.HasCn("TRAEFIK DEFAULT CERT"), try.StatusCodeIs(http.StatusNoContent))
|
||||
c.Assert(err, checker.IsNil)
|
||||
// snitest.com certificate does not exist, default certificate has to be used
|
||||
c.Assert(resp.TLS.PeerCertificates[0].Subject.CommonName, checker.Not(check.Equals), tr2.TLSClientConfig.ServerName)
|
||||
// Expected a 205 (from backend2)
|
||||
c.Assert(resp.StatusCode, checker.Equals, http.StatusNoContent)
|
||||
}
|
||||
|
||||
// TestWithSNIDynamicConfigRouteWithChange involves a client sending HTTPS requests with
|
||||
@@ -633,57 +626,26 @@ func (s *HTTPSSuite) TestWithSNIDynamicConfigRouteWithChange(c *check.C) {
|
||||
err = try.GetRequest(backend2.URL, 500*time.Millisecond, try.StatusCodeIs(http.StatusResetContent))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// Change certificates configuration file content
|
||||
modifyCertificateConfFileContent(c, tr1.TLSClientConfig.ServerName, dynamicConfFileName, "https")
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
client := &http.Client{Transport: tr1}
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = tr1.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr1.TLSClientConfig.ServerName)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
|
||||
// Change certificates configuration file content
|
||||
modifyCertificateConfFileContent(c, tr1.TLSClientConfig.ServerName, dynamicConfFileName, "https")
|
||||
var resp *http.Response
|
||||
err = try.Do(30*time.Second, func() error {
|
||||
resp, err = client.Do(req)
|
||||
|
||||
// /!\ If connection is not closed, SSLHandshake will only be done during the first trial /!\
|
||||
req.Close = true
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cn := resp.TLS.PeerCertificates[0].Subject.CommonName
|
||||
if cn != tr1.TLSClientConfig.ServerName {
|
||||
return fmt.Errorf("domain %s found in place of %s", cn, tr1.TLSClientConfig.ServerName)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
err = try.RequestWithTransport(req, 30*time.Second, tr1, try.HasCn(tr1.TLSClientConfig.ServerName), try.StatusCodeIs(http.StatusNotFound))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(resp.StatusCode, checker.Equals, http.StatusNotFound)
|
||||
client = &http.Client{Transport: tr2}
|
||||
req.Host = tr2.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr2.TLSClientConfig.ServerName)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
|
||||
err = try.Do(60*time.Second, func() error {
|
||||
resp, err = client.Do(req)
|
||||
|
||||
// /!\ If connection is not closed, SSLHandshake will only be done during the first trial /!\
|
||||
req.Close = true
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cn := resp.TLS.PeerCertificates[0].Subject.CommonName
|
||||
if cn == tr2.TLSClientConfig.ServerName {
|
||||
return fmt.Errorf("domain %s found in place of default one", tr2.TLSClientConfig.ServerName)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
err = try.RequestWithTransport(req, 30*time.Second, tr2, try.HasCn("TRAEFIK DEFAULT CERT"), try.StatusCodeIs(http.StatusNotFound))
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(resp.StatusCode, checker.Equals, http.StatusNotFound)
|
||||
}
|
||||
|
||||
// TestWithSNIDynamicConfigRouteWithTlsConfigurationDeletion involves a client sending HTTPS requests with
|
||||
@@ -725,82 +687,146 @@ func (s *HTTPSSuite) TestWithSNIDynamicConfigRouteWithTlsConfigurationDeletion(c
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:4443/", nil)
|
||||
client := &http.Client{Transport: tr2}
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = tr2.TLSClientConfig.ServerName
|
||||
req.Header.Set("Host", tr2.TLSClientConfig.ServerName)
|
||||
req.Header.Set("Accept", "*/*")
|
||||
|
||||
var resp *http.Response
|
||||
err = try.Do(30*time.Second, func() error {
|
||||
resp, err = client.Do(req)
|
||||
|
||||
// /!\ If connection is not closed, SSLHandshake will only be done during the first trial /!\
|
||||
req.Close = true
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cn := resp.TLS.PeerCertificates[0].Subject.CommonName
|
||||
if cn != tr2.TLSClientConfig.ServerName {
|
||||
return fmt.Errorf("domain %s found in place of %s", cn, tr2.TLSClientConfig.ServerName)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
err = try.RequestWithTransport(req, 30*time.Second, tr2, try.HasCn(tr2.TLSClientConfig.ServerName), try.StatusCodeIs(http.StatusResetContent))
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(resp.StatusCode, checker.Equals, http.StatusResetContent)
|
||||
|
||||
// Change certificates configuration file content
|
||||
modifyCertificateConfFileContent(c, "", dynamicConfFileName, "https02")
|
||||
|
||||
err = try.Do(60*time.Second, func() error {
|
||||
resp, err = client.Do(req)
|
||||
|
||||
// /!\ If connection is not closed, SSLHandshake will only be done during the first trial /!\
|
||||
req.Close = true
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cn := resp.TLS.PeerCertificates[0].Subject.CommonName
|
||||
if cn == tr2.TLSClientConfig.ServerName {
|
||||
return fmt.Errorf("domain %s found instead of the default one", tr2.TLSClientConfig.ServerName)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
err = try.RequestWithTransport(req, 30*time.Second, tr2, try.HasCn("TRAEFIK DEFAULT CERT"), try.StatusCodeIs(http.StatusNotFound))
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(resp.StatusCode, checker.Equals, http.StatusNotFound)
|
||||
}
|
||||
|
||||
// modifyCertificateConfFileContent replaces the content of a HTTPS configuration file.
|
||||
func modifyCertificateConfFileContent(c *check.C, certFileName, confFileName, entryPoint string) {
|
||||
f, err := os.OpenFile("./"+confFileName, os.O_WRONLY, os.ModeExclusive)
|
||||
file, err := os.OpenFile("./"+confFileName, os.O_WRONLY, os.ModeExclusive)
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer func() {
|
||||
f.Close()
|
||||
file.Close()
|
||||
}()
|
||||
f.Truncate(0)
|
||||
err = file.Truncate(0)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// If certificate file is not provided, just truncate the configuration file
|
||||
if len(certFileName) > 0 {
|
||||
tlsConf := types.Configuration{
|
||||
TLS: []*traefiktls.Configuration{
|
||||
{
|
||||
Certificate: &traefiktls.Certificate{
|
||||
CertFile: traefiktls.FileOrContent("fixtures/https/" + certFileName + ".cert"),
|
||||
KeyFile: traefiktls.FileOrContent("fixtures/https/" + certFileName + ".key"),
|
||||
},
|
||||
EntryPoints: []string{entryPoint},
|
||||
TLS: []*traefiktls.Configuration{{
|
||||
Certificate: &traefiktls.Certificate{
|
||||
CertFile: traefiktls.FileOrContent("fixtures/https/" + certFileName + ".cert"),
|
||||
KeyFile: traefiktls.FileOrContent("fixtures/https/" + certFileName + ".key"),
|
||||
},
|
||||
},
|
||||
EntryPoints: []string{entryPoint},
|
||||
}},
|
||||
}
|
||||
|
||||
var confBuffer bytes.Buffer
|
||||
e := toml.NewEncoder(&confBuffer)
|
||||
err := e.Encode(tlsConf)
|
||||
err := toml.NewEncoder(&confBuffer).Encode(tlsConf)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
_, err = f.Write(confBuffer.Bytes())
|
||||
_, err = file.Write(confBuffer.Bytes())
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *HTTPSSuite) TestEntrypointHttpsRedirectAndPathModification(c *check.C) {
|
||||
cmd, display := s.traefikCmd(withConfigFile("fixtures/https/https_redirect.toml"))
|
||||
defer display(c)
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 1000*time.Millisecond, try.BodyContains("Host: example.com"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
client := &http.Client{
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
hosts []string
|
||||
path string
|
||||
}{
|
||||
{
|
||||
desc: "Stripped URL redirect",
|
||||
hosts: []string{"example.com", "foo.com", "bar.com"},
|
||||
path: "/api",
|
||||
},
|
||||
{
|
||||
desc: "Stripped URL with trailing slash redirect",
|
||||
hosts: []string{"example.com", "example2.com", "foo.com", "foo2.com", "bar.com", "bar2.com"},
|
||||
path: "/api/",
|
||||
},
|
||||
{
|
||||
desc: "Stripped URL with double trailing slash redirect",
|
||||
hosts: []string{"example.com", "example2.com", "foo.com", "foo2.com", "bar.com", "bar2.com"},
|
||||
path: "/api//",
|
||||
},
|
||||
{
|
||||
desc: "Stripped URL with path redirect",
|
||||
hosts: []string{"example.com", "example2.com", "foo.com", "foo2.com", "bar.com", "bar2.com"},
|
||||
path: "/api/bacon",
|
||||
},
|
||||
{
|
||||
desc: "Stripped URL with path and trailing slash redirect",
|
||||
hosts: []string{"example.com", "example2.com", "foo.com", "foo2.com", "bar.com", "bar2.com"},
|
||||
path: "/api/bacon/",
|
||||
},
|
||||
{
|
||||
desc: "Stripped URL with path and double trailing slash redirect",
|
||||
hosts: []string{"example.com", "example2.com", "foo.com", "foo2.com", "bar.com", "bar2.com"},
|
||||
path: "/api/bacon//",
|
||||
},
|
||||
{
|
||||
desc: "Root Path with redirect",
|
||||
hosts: []string{"test.com", "test2.com", "pow.com", "pow2.com"},
|
||||
path: "/",
|
||||
},
|
||||
{
|
||||
desc: "Root Path with double trailing slash redirect",
|
||||
hosts: []string{"test.com", "test2.com", "pow.com", "pow2.com"},
|
||||
path: "//",
|
||||
},
|
||||
{
|
||||
desc: "Path modify with redirect",
|
||||
hosts: []string{"test.com", "test2.com", "pow.com", "pow2.com"},
|
||||
path: "/wtf",
|
||||
},
|
||||
{
|
||||
desc: "Path modify with trailing slash redirect",
|
||||
hosts: []string{"test.com", "test2.com", "pow.com", "pow2.com"},
|
||||
path: "/wtf/",
|
||||
},
|
||||
{
|
||||
desc: "Path modify with matching path segment redirect",
|
||||
hosts: []string{"test.com", "test2.com", "pow.com", "pow2.com"},
|
||||
path: "/wtf/foo",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
sourceURL := fmt.Sprintf("http://127.0.0.1:8888%s", test.path)
|
||||
for _, host := range test.hosts {
|
||||
req, err := http.NewRequest("GET", sourceURL, nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Host = host
|
||||
|
||||
resp, err := client.Do(req)
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer resp.Body.Close()
|
||||
|
||||
location := resp.Header.Get("Location")
|
||||
expected := fmt.Sprintf("https://%s:8443%s", host, test.path)
|
||||
|
||||
c.Assert(location, checker.Equals, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -60,6 +60,7 @@ func init() {
|
||||
check.Suite(&RateLimitSuite{})
|
||||
check.Suite(&RetrySuite{})
|
||||
check.Suite(&SimpleSuite{})
|
||||
check.Suite(&TLSClientHeadersSuite{})
|
||||
check.Suite(&TimeoutSuite{})
|
||||
check.Suite(&TracingSuite{})
|
||||
check.Suite(&WebsocketSuite{})
|
||||
|
@@ -1,6 +1,6 @@
|
||||
pebble:
|
||||
image: ldez/pebble
|
||||
command: --dnsserver ${DOCKER_HOST_IP}:5053
|
||||
image: letsencrypt/pebble:2018-07-27
|
||||
command: pebble --dnsserver ${DOCKER_HOST_IP}:5053
|
||||
ports:
|
||||
- 14000:14000
|
||||
environment:
|
6
integration/resources/compose/tlsclientheaders.yml
Normal file
6
integration/resources/compose/tlsclientheaders.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
whoami:
|
||||
image: containous/whoami
|
||||
labels:
|
||||
- traefik.frontend.passTLSClientCert.pem=true
|
||||
- traefik.frontend.rule=PathPrefix:/
|
||||
|
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/containous/traefik/integration/try"
|
||||
"github.com/go-check/check"
|
||||
"github.com/gorilla/websocket"
|
||||
checker "github.com/vdemeester/shakers"
|
||||
)
|
||||
|
||||
@@ -38,3 +39,29 @@ func (s *RetrySuite) TestRetry(c *check.C) {
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
|
||||
}
|
||||
|
||||
func (s *RetrySuite) TestRetryWebsocket(c *check.C) {
|
||||
whoamiEndpoint := s.composeProject.Container(c, "whoami").NetworkSettings.IPAddress
|
||||
file := s.adaptFile(c, "fixtures/retry/simple.toml", struct {
|
||||
WhoamiEndpoint string
|
||||
}{whoamiEndpoint})
|
||||
defer os.Remove(file)
|
||||
|
||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||
defer display(c)
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 60*time.Second, try.BodyContains("PathPrefix:/"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// This simulates a DialTimeout when connecting to the backend server.
|
||||
_, response, err := websocket.DefaultDialer.Dial("ws://127.0.0.1:8000/echo", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(response.StatusCode, checker.Equals, http.StatusSwitchingProtocols)
|
||||
|
||||
_, response, err = websocket.DefaultDialer.Dial("ws://127.0.0.1:8000/echo", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(response.StatusCode, checker.Equals, http.StatusSwitchingProtocols)
|
||||
}
|
||||
|
71
integration/tls_client_headers_test.go
Normal file
71
integration/tls_client_headers_test.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/containous/traefik/integration/try"
|
||||
"github.com/go-check/check"
|
||||
checker "github.com/vdemeester/shakers"
|
||||
)
|
||||
|
||||
const (
|
||||
rootCertPath = "./fixtures/tlsclientheaders/root.pem"
|
||||
certPemPath = "./fixtures/tlsclientheaders/server.pem"
|
||||
certKeyPath = "./fixtures/tlsclientheaders/server.key"
|
||||
)
|
||||
|
||||
type TLSClientHeadersSuite struct{ BaseSuite }
|
||||
|
||||
func (s *TLSClientHeadersSuite) SetUpSuite(c *check.C) {
|
||||
s.createComposeProject(c, "tlsclientheaders")
|
||||
s.composeProject.Start(c)
|
||||
}
|
||||
|
||||
func (s *TLSClientHeadersSuite) TestTLSClientHeaders(c *check.C) {
|
||||
rootCertContent, err := ioutil.ReadFile(rootCertPath)
|
||||
c.Assert(err, check.IsNil)
|
||||
serverCertContent, err := ioutil.ReadFile(certPemPath)
|
||||
c.Assert(err, check.IsNil)
|
||||
ServerKeyContent, err := ioutil.ReadFile(certKeyPath)
|
||||
c.Assert(err, check.IsNil)
|
||||
|
||||
file := s.adaptFile(c, "fixtures/tlsclientheaders/simple.toml", struct {
|
||||
RootCertContent string
|
||||
ServerCertContent string
|
||||
ServerKeyContent string
|
||||
}{
|
||||
RootCertContent: string(rootCertContent),
|
||||
ServerCertContent: string(serverCertContent),
|
||||
ServerKeyContent: string(ServerKeyContent),
|
||||
})
|
||||
defer os.Remove(file)
|
||||
|
||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||
defer display(c)
|
||||
err = cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer cmd.Process.Kill()
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/providers", 2*time.Second, try.BodyContains("PathPrefix:/"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
request, err := http.NewRequest(http.MethodGet, "https://127.0.0.1:8443", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
certificate, err := tls.LoadX509KeyPair(certPemPath, certKeyPath)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
Certificates: []tls.Certificate{certificate},
|
||||
},
|
||||
}
|
||||
|
||||
err = try.RequestWithTransport(request, 2*time.Second, tr, try.BodyContains("Forwarded-Tls-Client-Cert: MIIDKjCCAhICCQDKAJTeuq3LHjANBgkqhkiG9w0BAQsFADBXMQswCQYDVQQGEwJGUjEPMA0GA1UECAwGRlJBTkNFMREwDwYDVQQHDAhUT1VMT1VTRTETMBEGA1UECgwKY29udGFpbm91czEPMA0GA1UEAwwGc2VydmVyMB4XDTE4MDMyMTEzNDM0MVoXDTIxMDEwODEzNDM0MVowVzELMAkGA1UEBhMCRlIxDzANBgNVBAgMBkZSQU5DRTERMA8GA1UEBwwIVE9VTE9VU0UxEzARBgNVBAoMCmNvbnRhaW5vdXMxDzANBgNVBAMMBnNlcnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKNHrqc7QcRHIc%2F%2FQW3oAcyl9%2BWFLdEtl86f5hTPoV0MpVgxwc98BA%2B0fPb97GOnj05P7QE%2BZerio5kP80ZUBX%2B0LVVilLWKvK47hZ%2FfxHgvtt95sZFT%2B0AHLk%2Bk%2FD86FIMrFuk8d889fFQ0TJz4cdX4wNYwKt%2FiFNNwaWxc%2BwpGAsZBv9cFh5rAdeix9mzMSa82qaYdp0g51JKAE7oEiXnPg8U7V9YXYwGiSvybCMIqAPy8sumIBNqF%2B7kWQaLtGwN8tEw5xaCFQFaiEmFn7M0xg5cC%2Fkg%2Fz%2FRmGtfRmZOIpnafIyw%2F%2FifXi7hxu%2Ba5ETrxOMW0j2xiBpGThGE5ox8CAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAPYDdGyNWp7R9j2oxZEbQS4lb%2B2Ol1r6PFo%2FzmpB6GK3CSNo65a0DtW%2FITeQi97MMgGS1D3wnaFPrwxtp0mEn7HjUuDcufHBqqBsjYC3NEtt%2ByyxNeYddLD%2FGdFXw4d6wNRdRaFCq5N1CPQzF4VTdoSLDxsOq%2FWAHHc2cyZyOprAqm2UXyWXxn4yWZqzDsZ41%2Fv2f3uMNxeqyIEtNZVzTKQBuwWw%2BjlQKGu0T8Ex1f0jaKI1OPtN5dzaIfO8acHcuNdmnE%2BhVsoqe17Dckxsj1ORf8ZcZ4qvULVouGINQBP4fcl5jv6TOm1U%2BZSk01FcHPmiDEMB6Utyy4ZLHPbmKYg%3D%3D"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
@@ -88,6 +88,31 @@ func HasBody() ResponseCondition {
|
||||
}
|
||||
}
|
||||
|
||||
// HasCn returns a retry condition function.
|
||||
// The condition returns an error if the cn is not correct.
|
||||
func HasCn(cn string) ResponseCondition {
|
||||
return func(res *http.Response) error {
|
||||
if res.TLS == nil {
|
||||
return errors.New("response doesn't have TLS")
|
||||
}
|
||||
|
||||
if len(res.TLS.PeerCertificates) == 0 {
|
||||
return errors.New("response TLS doesn't have peer certificates")
|
||||
}
|
||||
|
||||
if res.TLS.PeerCertificates[0] == nil {
|
||||
return errors.New("first peer certificate is nil")
|
||||
}
|
||||
|
||||
commonName := res.TLS.PeerCertificates[0].Subject.CommonName
|
||||
if cn != commonName {
|
||||
return fmt.Errorf("common name don't match: %s != %s", cn, commonName)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// StatusCodeIs returns a retry condition function.
|
||||
// The condition returns an error if the given response's status code is not the
|
||||
// given HTTP status code.
|
||||
|
@@ -31,7 +31,7 @@ func Sleep(d time.Duration) {
|
||||
// response body needs to be closed or not. Callers are expected to close on
|
||||
// their own if the function returns a nil error.
|
||||
func Response(req *http.Request, timeout time.Duration) (*http.Response, error) {
|
||||
return doTryRequest(req, timeout)
|
||||
return doTryRequest(req, timeout, nil)
|
||||
}
|
||||
|
||||
// ResponseUntilStatusCode is like Request, but returns the response for further
|
||||
@@ -40,7 +40,7 @@ func Response(req *http.Request, timeout time.Duration) (*http.Response, error)
|
||||
// response body needs to be closed or not. Callers are expected to close on
|
||||
// their own if the function returns a nil error.
|
||||
func ResponseUntilStatusCode(req *http.Request, timeout time.Duration, statusCode int) (*http.Response, error) {
|
||||
return doTryRequest(req, timeout, StatusCodeIs(statusCode))
|
||||
return doTryRequest(req, timeout, nil, StatusCodeIs(statusCode))
|
||||
}
|
||||
|
||||
// GetRequest is like Do, but runs a request against the given URL and applies
|
||||
@@ -48,7 +48,7 @@ func ResponseUntilStatusCode(req *http.Request, timeout time.Duration, statusCod
|
||||
// ResponseCondition may be nil, in which case only the request against the URL must
|
||||
// succeed.
|
||||
func GetRequest(url string, timeout time.Duration, conditions ...ResponseCondition) error {
|
||||
resp, err := doTryGet(url, timeout, conditions...)
|
||||
resp, err := doTryGet(url, timeout, nil, conditions...)
|
||||
|
||||
if resp != nil && resp.Body != nil {
|
||||
defer resp.Body.Close()
|
||||
@@ -62,7 +62,21 @@ func GetRequest(url string, timeout time.Duration, conditions ...ResponseConditi
|
||||
// ResponseCondition may be nil, in which case only the request against the URL must
|
||||
// succeed.
|
||||
func Request(req *http.Request, timeout time.Duration, conditions ...ResponseCondition) error {
|
||||
resp, err := doTryRequest(req, timeout, conditions...)
|
||||
resp, err := doTryRequest(req, timeout, nil, conditions...)
|
||||
|
||||
if resp != nil && resp.Body != nil {
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// RequestWithTransport is like Do, but runs a request against the given URL and applies
|
||||
// the condition on the response.
|
||||
// ResponseCondition may be nil, in which case only the request against the URL must
|
||||
// succeed.
|
||||
func RequestWithTransport(req *http.Request, timeout time.Duration, transport *http.Transport, conditions ...ResponseCondition) error {
|
||||
resp, err := doTryRequest(req, timeout, transport, conditions...)
|
||||
|
||||
if resp != nil && resp.Body != nil {
|
||||
defer resp.Body.Close()
|
||||
@@ -112,24 +126,27 @@ func Do(timeout time.Duration, operation DoCondition) error {
|
||||
}
|
||||
}
|
||||
|
||||
func doTryGet(url string, timeout time.Duration, conditions ...ResponseCondition) (*http.Response, error) {
|
||||
func doTryGet(url string, timeout time.Duration, transport *http.Transport, conditions ...ResponseCondition) (*http.Response, error) {
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return doTryRequest(req, timeout, conditions...)
|
||||
return doTryRequest(req, timeout, transport, conditions...)
|
||||
}
|
||||
|
||||
func doTryRequest(request *http.Request, timeout time.Duration, conditions ...ResponseCondition) (*http.Response, error) {
|
||||
return doRequest(Do, timeout, request, conditions...)
|
||||
func doTryRequest(request *http.Request, timeout time.Duration, transport *http.Transport, conditions ...ResponseCondition) (*http.Response, error) {
|
||||
return doRequest(Do, timeout, request, transport, conditions...)
|
||||
}
|
||||
|
||||
func doRequest(action timedAction, timeout time.Duration, request *http.Request, conditions ...ResponseCondition) (*http.Response, error) {
|
||||
func doRequest(action timedAction, timeout time.Duration, request *http.Request, transport *http.Transport, conditions ...ResponseCondition) (*http.Response, error) {
|
||||
var resp *http.Response
|
||||
return resp, action(timeout, func() error {
|
||||
var err error
|
||||
client := http.DefaultClient
|
||||
if transport != nil {
|
||||
client.Transport = transport
|
||||
}
|
||||
|
||||
resp, err = client.Do(request)
|
||||
if err != nil {
|
||||
|
@@ -10,8 +10,14 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Logger allows overriding the logrus logger behavior
|
||||
type Logger interface {
|
||||
logrus.FieldLogger
|
||||
WriterLevel(logrus.Level) *io.PipeWriter
|
||||
}
|
||||
|
||||
var (
|
||||
logger *logrus.Entry
|
||||
logger Logger
|
||||
logFilePath string
|
||||
logFile *os.File
|
||||
)
|
||||
@@ -41,6 +47,11 @@ func SetLevel(level logrus.Level) {
|
||||
logrus.SetLevel(level)
|
||||
}
|
||||
|
||||
// SetLogger sets the logger.
|
||||
func SetLogger(l Logger) {
|
||||
logger = l
|
||||
}
|
||||
|
||||
// GetLevel returns the standard logger level.
|
||||
func GetLevel() logrus.Level {
|
||||
return logrus.GetLevel()
|
||||
|
@@ -7,6 +7,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/containous/mux"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/safe"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/go-kit/kit/metrics"
|
||||
@@ -15,25 +16,31 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
metricNamePrefix = "traefik_"
|
||||
// MetricNamePrefix prefix of all metric names
|
||||
MetricNamePrefix = "traefik_"
|
||||
|
||||
// server meta information
|
||||
configReloadsTotalName = metricNamePrefix + "config_reloads_total"
|
||||
configReloadsFailuresTotalName = metricNamePrefix + "config_reloads_failure_total"
|
||||
configLastReloadSuccessName = metricNamePrefix + "config_last_reload_success"
|
||||
configLastReloadFailureName = metricNamePrefix + "config_last_reload_failure"
|
||||
metricConfigPrefix = MetricNamePrefix + "config_"
|
||||
configReloadsTotalName = metricConfigPrefix + "reloads_total"
|
||||
configReloadsFailuresTotalName = metricConfigPrefix + "reloads_failure_total"
|
||||
configLastReloadSuccessName = metricConfigPrefix + "last_reload_success"
|
||||
configLastReloadFailureName = metricConfigPrefix + "last_reload_failure"
|
||||
|
||||
// entrypoint
|
||||
entrypointReqsTotalName = metricNamePrefix + "entrypoint_requests_total"
|
||||
entrypointReqDurationName = metricNamePrefix + "entrypoint_request_duration_seconds"
|
||||
entrypointOpenConnsName = metricNamePrefix + "entrypoint_open_connections"
|
||||
metricEntryPointPrefix = MetricNamePrefix + "entrypoint_"
|
||||
entrypointReqsTotalName = metricEntryPointPrefix + "requests_total"
|
||||
entrypointReqDurationName = metricEntryPointPrefix + "request_duration_seconds"
|
||||
entrypointOpenConnsName = metricEntryPointPrefix + "open_connections"
|
||||
|
||||
// backend level
|
||||
backendReqsTotalName = metricNamePrefix + "backend_requests_total"
|
||||
backendReqDurationName = metricNamePrefix + "backend_request_duration_seconds"
|
||||
backendOpenConnsName = metricNamePrefix + "backend_open_connections"
|
||||
backendRetriesTotalName = metricNamePrefix + "backend_retries_total"
|
||||
backendServerUpName = metricNamePrefix + "backend_server_up"
|
||||
// backend level.
|
||||
|
||||
// MetricBackendPrefix prefix of all backend metric names
|
||||
MetricBackendPrefix = MetricNamePrefix + "backend_"
|
||||
backendReqsTotalName = MetricBackendPrefix + "requests_total"
|
||||
backendReqDurationName = MetricBackendPrefix + "request_duration_seconds"
|
||||
backendOpenConnsName = MetricBackendPrefix + "open_connections"
|
||||
backendRetriesTotalName = MetricBackendPrefix + "retries_total"
|
||||
backendServerUpName = MetricBackendPrefix + "server_up"
|
||||
)
|
||||
|
||||
// promState holds all metric state internally and acts as the only Collector we register for Prometheus.
|
||||
@@ -61,6 +68,16 @@ func (h PrometheusHandler) AddRoutes(router *mux.Router) {
|
||||
// RegisterPrometheus registers all Prometheus metrics.
|
||||
// It must be called only once and failing to register the metrics will lead to a panic.
|
||||
func RegisterPrometheus(config *types.Prometheus) Registry {
|
||||
standardRegistry := initStandardRegistry(config)
|
||||
|
||||
if !registerPromState() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return standardRegistry
|
||||
}
|
||||
|
||||
func initStandardRegistry(config *types.Prometheus) Registry {
|
||||
buckets := []float64{0.1, 0.3, 1.2, 5.0}
|
||||
if config.Buckets != nil {
|
||||
buckets = config.Buckets
|
||||
@@ -137,7 +154,6 @@ func RegisterPrometheus(config *types.Prometheus) Registry {
|
||||
backendRetries.cv.Describe,
|
||||
backendServerUp.gv.Describe,
|
||||
}
|
||||
stdprometheus.MustRegister(promState)
|
||||
|
||||
return &standardRegistry{
|
||||
enabled: true,
|
||||
@@ -156,6 +172,17 @@ func RegisterPrometheus(config *types.Prometheus) Registry {
|
||||
}
|
||||
}
|
||||
|
||||
func registerPromState() bool {
|
||||
if err := stdprometheus.Register(promState); err != nil {
|
||||
if _, ok := err.(stdprometheus.AlreadyRegisteredError); !ok {
|
||||
log.Errorf("Unable to register Traefik to Prometheus: %v", err)
|
||||
return false
|
||||
}
|
||||
log.Debug("Prometheus collector already registered.")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// OnConfigurationUpdate receives the current configuration from Traefik.
|
||||
// It then converts the configuration to the optimized package internal format
|
||||
// and sets it to the promState.
|
||||
|
@@ -11,8 +11,82 @@ import (
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestRegisterPromState(t *testing.T) {
|
||||
// Reset state of global promState.
|
||||
defer promState.reset()
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
prometheusSlice []*types.Prometheus
|
||||
initPromState bool
|
||||
unregisterPromState bool
|
||||
expectedNbRegistries int
|
||||
}{
|
||||
{
|
||||
desc: "Register once",
|
||||
prometheusSlice: []*types.Prometheus{{}},
|
||||
expectedNbRegistries: 1,
|
||||
initPromState: true,
|
||||
},
|
||||
{
|
||||
desc: "Register once with no promState init",
|
||||
prometheusSlice: []*types.Prometheus{{}},
|
||||
expectedNbRegistries: 0,
|
||||
},
|
||||
{
|
||||
desc: "Register twice",
|
||||
prometheusSlice: []*types.Prometheus{{}, {}},
|
||||
expectedNbRegistries: 2,
|
||||
initPromState: true,
|
||||
},
|
||||
{
|
||||
desc: "Register twice with no promstate init",
|
||||
prometheusSlice: []*types.Prometheus{{}, {}},
|
||||
expectedNbRegistries: 0,
|
||||
},
|
||||
{
|
||||
desc: "Register twice with unregister",
|
||||
prometheusSlice: []*types.Prometheus{{}, {}},
|
||||
unregisterPromState: true,
|
||||
expectedNbRegistries: 2,
|
||||
initPromState: true,
|
||||
},
|
||||
{
|
||||
desc: "Register twice with unregister but no promstate init",
|
||||
prometheusSlice: []*types.Prometheus{{}, {}},
|
||||
unregisterPromState: true,
|
||||
expectedNbRegistries: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
actualNbRegistries := 0
|
||||
for _, prom := range test.prometheusSlice {
|
||||
if test.initPromState {
|
||||
initStandardRegistry(prom)
|
||||
}
|
||||
|
||||
promReg := registerPromState()
|
||||
if promReg != false {
|
||||
actualNbRegistries++
|
||||
}
|
||||
|
||||
if test.unregisterPromState {
|
||||
prometheus.Unregister(promState)
|
||||
}
|
||||
|
||||
promState.reset()
|
||||
}
|
||||
|
||||
prometheus.Unregister(promState)
|
||||
|
||||
assert.Equal(t, test.expectedNbRegistries, actualNbRegistries)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrometheus(t *testing.T) {
|
||||
// Reset state of global promState.
|
||||
defer promState.reset()
|
||||
|
@@ -192,28 +192,28 @@ func TestLoggerJSON(t *testing.T) {
|
||||
Format: JSONFormat,
|
||||
},
|
||||
expected: map[string]func(t *testing.T, value interface{}){
|
||||
RequestHost: assertString(testHostname),
|
||||
RequestAddr: assertString(testHostname),
|
||||
RequestMethod: assertString(testMethod),
|
||||
RequestPath: assertString(testPath),
|
||||
RequestProtocol: assertString(testProto),
|
||||
RequestPort: assertString("-"),
|
||||
RequestLine: assertString(fmt.Sprintf("%s %s %s", testMethod, testPath, testProto)),
|
||||
DownstreamStatus: assertFloat64(float64(testStatus)),
|
||||
DownstreamStatusLine: assertString(fmt.Sprintf("%d ", testStatus)),
|
||||
DownstreamContentSize: assertFloat64(float64(len(testContent))),
|
||||
OriginContentSize: assertFloat64(float64(len(testContent))),
|
||||
OriginStatus: assertFloat64(float64(testStatus)),
|
||||
RequestRefererHeader: assertString(testReferer),
|
||||
RequestUserAgentHeader: assertString(testUserAgent),
|
||||
FrontendName: assertString(testFrontendName),
|
||||
BackendURL: assertString(testBackendName),
|
||||
ClientUsername: assertString(testUsername),
|
||||
ClientHost: assertString(testHostname),
|
||||
ClientPort: assertString(fmt.Sprintf("%d", testPort)),
|
||||
ClientAddr: assertString(fmt.Sprintf("%s:%d", testHostname, testPort)),
|
||||
"level": assertString("info"),
|
||||
"msg": assertString(""),
|
||||
RequestHost: assertString(testHostname),
|
||||
RequestAddr: assertString(testHostname),
|
||||
RequestMethod: assertString(testMethod),
|
||||
RequestPath: assertString(testPath),
|
||||
RequestProtocol: assertString(testProto),
|
||||
RequestPort: assertString("-"),
|
||||
RequestLine: assertString(fmt.Sprintf("%s %s %s", testMethod, testPath, testProto)),
|
||||
DownstreamStatus: assertFloat64(float64(testStatus)),
|
||||
DownstreamStatusLine: assertString(fmt.Sprintf("%d ", testStatus)),
|
||||
DownstreamContentSize: assertFloat64(float64(len(testContent))),
|
||||
OriginContentSize: assertFloat64(float64(len(testContent))),
|
||||
OriginStatus: assertFloat64(float64(testStatus)),
|
||||
RequestRefererHeader: assertString(testReferer),
|
||||
RequestUserAgentHeader: assertString(testUserAgent),
|
||||
FrontendName: assertString(testFrontendName),
|
||||
BackendURL: assertString(testBackendName),
|
||||
ClientUsername: assertString(testUsername),
|
||||
ClientHost: assertString(testHostname),
|
||||
ClientPort: assertString(fmt.Sprintf("%d", testPort)),
|
||||
ClientAddr: assertString(fmt.Sprintf("%s:%d", testHostname, testPort)),
|
||||
"level": assertString("info"),
|
||||
"msg": assertString(""),
|
||||
"downstream_Content-Type": assertString("text/plain; charset=utf-8"),
|
||||
RequestCount: assertFloat64NotZero(),
|
||||
Duration: assertFloat64NotZero(),
|
||||
@@ -234,9 +234,9 @@ func TestLoggerJSON(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: map[string]func(t *testing.T, value interface{}){
|
||||
"level": assertString("info"),
|
||||
"msg": assertString(""),
|
||||
"time": assertNotEqual(""),
|
||||
"level": assertString("info"),
|
||||
"msg": assertString(""),
|
||||
"time": assertNotEqual(""),
|
||||
"downstream_Content-Type": assertString("text/plain; charset=utf-8"),
|
||||
RequestRefererHeader: assertString(testReferer),
|
||||
RequestUserAgentHeader: assertString(testUserAgent),
|
||||
@@ -273,9 +273,9 @@ func TestLoggerJSON(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: map[string]func(t *testing.T, value interface{}){
|
||||
"level": assertString("info"),
|
||||
"msg": assertString(""),
|
||||
"time": assertNotEqual(""),
|
||||
"level": assertString("info"),
|
||||
"msg": assertString(""),
|
||||
"time": assertNotEqual(""),
|
||||
"downstream_Content-Type": assertString("REDACTED"),
|
||||
RequestRefererHeader: assertString("REDACTED"),
|
||||
RequestUserAgentHeader: assertString("REDACTED"),
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
@@ -10,12 +11,21 @@ type AddPrefix struct {
|
||||
Prefix string
|
||||
}
|
||||
|
||||
type key string
|
||||
|
||||
const (
|
||||
// AddPrefixKey is the key within the request context used to
|
||||
// store the added prefix
|
||||
AddPrefixKey key = "AddPrefix"
|
||||
)
|
||||
|
||||
func (s *AddPrefix) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
r.URL.Path = s.Prefix + r.URL.Path
|
||||
if r.URL.RawPath != "" {
|
||||
r.URL.RawPath = s.Prefix + r.URL.RawPath
|
||||
}
|
||||
r.RequestURI = r.URL.RequestURI()
|
||||
r = r.WithContext(context.WithValue(r.Context(), AddPrefixKey, s.Prefix))
|
||||
s.Handler.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
|
@@ -26,6 +26,10 @@ type tracingAuthenticator struct {
|
||||
clientSpanKind bool
|
||||
}
|
||||
|
||||
const (
|
||||
authorizationHeader = "Authorization"
|
||||
)
|
||||
|
||||
// NewAuthenticator builds a new Authenticator given a config
|
||||
func NewAuthenticator(authConfig *types.Auth, tracingMiddleware *tracing.Tracing) (*Authenticator, error) {
|
||||
if authConfig == nil {
|
||||
@@ -86,6 +90,10 @@ func createAuthDigestHandler(digestAuth *goauth.DigestAuth, authConfig *types.Au
|
||||
if authConfig.HeaderField != "" {
|
||||
r.Header[authConfig.HeaderField] = []string{username}
|
||||
}
|
||||
if authConfig.Digest.RemoveHeader {
|
||||
log.Debugf("Remove the Authorization header from the Digest auth")
|
||||
r.Header.Del(authorizationHeader)
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
}
|
||||
})
|
||||
@@ -101,6 +109,10 @@ func createAuthBasicHandler(basicAuth *goauth.BasicAuth, authConfig *types.Auth)
|
||||
if authConfig.HeaderField != "" {
|
||||
r.Header[authConfig.HeaderField] = []string{username}
|
||||
}
|
||||
if authConfig.Basic.RemoveHeader {
|
||||
log.Debugf("Remove the Authorization header from the Basic auth")
|
||||
r.Header.Del(authorizationHeader)
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
}
|
||||
})
|
||||
|
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/containous/traefik/testhelpers"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/urfave/negroni"
|
||||
)
|
||||
|
||||
@@ -51,13 +52,16 @@ func TestAuthUsersFromFile(t *testing.T) {
|
||||
t.Run(test.authType, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
usersFile, err := ioutil.TempFile("", "auth-users")
|
||||
assert.NoError(t, err, "there should be no error")
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(usersFile.Name())
|
||||
|
||||
_, err = usersFile.Write([]byte(test.usersStr))
|
||||
assert.NoError(t, err, "there should be no error")
|
||||
require.NoError(t, err)
|
||||
|
||||
users, err := test.parserFunc(usersFile.Name())
|
||||
assert.NoError(t, err, "there should be no error")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 2, len(users), "they should be equal")
|
||||
|
||||
_, ok := users[test.userKeys[0]]
|
||||
assert.True(t, ok, "user test should be found")
|
||||
_, ok = users[test.userKeys[1]]
|
||||
@@ -79,7 +83,7 @@ func TestBasicAuthFail(t *testing.T) {
|
||||
Users: []string{"test:test"},
|
||||
},
|
||||
}, &tracing.Tracing{})
|
||||
assert.NoError(t, err, "there should be no error")
|
||||
require.NoError(t, err)
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, "traefik")
|
||||
@@ -93,7 +97,7 @@ func TestBasicAuthFail(t *testing.T) {
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil)
|
||||
req.SetBasicAuth("test", "test")
|
||||
res, err := client.Do(req)
|
||||
assert.NoError(t, err, "there should be no error")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusUnauthorized, res.StatusCode, "they should be equal")
|
||||
}
|
||||
|
||||
@@ -103,7 +107,7 @@ func TestBasicAuthSuccess(t *testing.T) {
|
||||
Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"},
|
||||
},
|
||||
}, &tracing.Tracing{})
|
||||
assert.NoError(t, err, "there should be no error")
|
||||
require.NoError(t, err)
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, "traefik")
|
||||
@@ -117,11 +121,11 @@ func TestBasicAuthSuccess(t *testing.T) {
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil)
|
||||
req.SetBasicAuth("test", "test")
|
||||
res, err := client.Do(req)
|
||||
assert.NoError(t, err, "there should be no error")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, res.StatusCode, "they should be equal")
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
assert.NoError(t, err, "there should be no error")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "traefik\n", string(body), "they should be equal")
|
||||
}
|
||||
|
||||
@@ -138,7 +142,7 @@ func TestDigestAuthFail(t *testing.T) {
|
||||
Users: []string{"test:traefik:test"},
|
||||
},
|
||||
}, &tracing.Tracing{})
|
||||
assert.NoError(t, err, "there should be no error")
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, authMiddleware, "this should not be nil")
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -153,7 +157,7 @@ func TestDigestAuthFail(t *testing.T) {
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil)
|
||||
req.SetBasicAuth("test", "test")
|
||||
res, err := client.Do(req)
|
||||
assert.NoError(t, err, "there should be no error")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusUnauthorized, res.StatusCode, "they should be equal")
|
||||
}
|
||||
|
||||
@@ -164,7 +168,7 @@ func TestBasicAuthUserHeader(t *testing.T) {
|
||||
},
|
||||
HeaderField: "X-Webauth-User",
|
||||
}, &tracing.Tracing{})
|
||||
assert.NoError(t, err, "there should be no error")
|
||||
require.NoError(t, err)
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, "test", r.Header["X-Webauth-User"][0], "auth user should be set")
|
||||
@@ -179,11 +183,72 @@ func TestBasicAuthUserHeader(t *testing.T) {
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil)
|
||||
req.SetBasicAuth("test", "test")
|
||||
res, err := client.Do(req)
|
||||
assert.NoError(t, err, "there should be no error")
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, http.StatusOK, res.StatusCode, "they should be equal")
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
assert.NoError(t, err, "there should be no error")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "traefik\n", string(body), "they should be equal")
|
||||
}
|
||||
|
||||
func TestBasicAuthHeaderRemoved(t *testing.T) {
|
||||
middleware, err := NewAuthenticator(&types.Auth{
|
||||
Basic: &types.Basic{
|
||||
RemoveHeader: true,
|
||||
Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"},
|
||||
},
|
||||
}, &tracing.Tracing{})
|
||||
require.NoError(t, err)
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Empty(t, r.Header.Get(authorizationHeader))
|
||||
fmt.Fprintln(w, "traefik")
|
||||
})
|
||||
n := negroni.New(middleware)
|
||||
n.UseHandler(handler)
|
||||
ts := httptest.NewServer(n)
|
||||
defer ts.Close()
|
||||
|
||||
client := &http.Client{}
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil)
|
||||
req.SetBasicAuth("test", "test")
|
||||
res, err := client.Do(req)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, http.StatusOK, res.StatusCode, "they should be equal")
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "traefik\n", string(body), "they should be equal")
|
||||
}
|
||||
|
||||
func TestBasicAuthHeaderPresent(t *testing.T) {
|
||||
middleware, err := NewAuthenticator(&types.Auth{
|
||||
Basic: &types.Basic{
|
||||
Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"},
|
||||
},
|
||||
}, &tracing.Tracing{})
|
||||
require.NoError(t, err)
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.NotEmpty(t, r.Header.Get(authorizationHeader))
|
||||
fmt.Fprintln(w, "traefik")
|
||||
})
|
||||
n := negroni.New(middleware)
|
||||
n.UseHandler(handler)
|
||||
ts := httptest.NewServer(n)
|
||||
defer ts.Close()
|
||||
|
||||
client := &http.Client{}
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil)
|
||||
req.SetBasicAuth("test", "test")
|
||||
res, err := client.Do(req)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, http.StatusOK, res.StatusCode, "they should be equal")
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "traefik\n", string(body), "they should be equal")
|
||||
}
|
||||
|
@@ -73,6 +73,7 @@ func Forward(config *types.Forward, w http.ResponseWriter, r *http.Request, next
|
||||
log.Debugf("Remote error %s. StatusCode: %d", config.Address, forwardResponse.StatusCode)
|
||||
|
||||
utils.CopyHeaders(w.Header(), forwardResponse.Header)
|
||||
utils.RemoveHeaders(w.Header(), forward.HopHeaders...)
|
||||
|
||||
// Grab the location header, if any.
|
||||
redirectURL, err := forwardResponse.Location()
|
||||
@@ -104,6 +105,7 @@ func Forward(config *types.Forward, w http.ResponseWriter, r *http.Request, next
|
||||
|
||||
func writeHeader(req *http.Request, forwardReq *http.Request, trustForwardHeader bool) {
|
||||
utils.CopyHeaders(forwardReq.Header, req.Header)
|
||||
utils.RemoveHeaders(forwardReq.Header, forward.HopHeaders...)
|
||||
|
||||
if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
|
||||
if trustForwardHeader {
|
||||
|
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/urfave/negroni"
|
||||
"github.com/vulcand/oxy/forward"
|
||||
)
|
||||
|
||||
func TestForwardAuthFail(t *testing.T) {
|
||||
@@ -122,6 +123,59 @@ func TestForwardAuthRedirect(t *testing.T) {
|
||||
assert.NotEmpty(t, string(body), "there should be something in the body")
|
||||
}
|
||||
|
||||
func TestForwardAuthRemoveHopByHopHeaders(t *testing.T) {
|
||||
authTs := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
headers := w.Header()
|
||||
for _, header := range forward.HopHeaders {
|
||||
if header == forward.TransferEncoding {
|
||||
headers.Add(header, "identity")
|
||||
} else {
|
||||
headers.Add(header, "test")
|
||||
}
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "http://example.com/redirect-test", http.StatusFound)
|
||||
}))
|
||||
defer authTs.Close()
|
||||
|
||||
authMiddleware, err := NewAuthenticator(&types.Auth{
|
||||
Forward: &types.Forward{
|
||||
Address: authTs.URL,
|
||||
},
|
||||
}, &tracing.Tracing{})
|
||||
assert.NoError(t, err, "there should be no error")
|
||||
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, "traefik")
|
||||
})
|
||||
n := negroni.New(authMiddleware)
|
||||
n.UseHandler(handler)
|
||||
ts := httptest.NewServer(n)
|
||||
defer ts.Close()
|
||||
|
||||
client := &http.Client{
|
||||
CheckRedirect: func(r *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
}
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil)
|
||||
res, err := client.Do(req)
|
||||
assert.NoError(t, err, "there should be no error")
|
||||
assert.Equal(t, http.StatusFound, res.StatusCode, "they should be equal")
|
||||
|
||||
for _, header := range forward.HopHeaders {
|
||||
assert.Equal(t, "", res.Header.Get(header), "hop-by-hop header '%s' mustn't be set", header)
|
||||
}
|
||||
|
||||
location, err := res.Location()
|
||||
assert.NoError(t, err, "there should be no error")
|
||||
assert.Equal(t, "http://example.com/redirect-test", location.String(), "they should be equal")
|
||||
|
||||
body, err := ioutil.ReadAll(res.Body)
|
||||
assert.NoError(t, err, "there should be no error")
|
||||
assert.NotEmpty(t, string(body), "there should be something in the body")
|
||||
}
|
||||
|
||||
func TestForwardAuthFailResponseHeaders(t *testing.T) {
|
||||
authTs := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
cookie := &http.Cookie{Name: "example", Value: "testing", Path: "/"}
|
||||
@@ -177,11 +231,12 @@ func TestForwardAuthFailResponseHeaders(t *testing.T) {
|
||||
|
||||
func Test_writeHeader(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
headers map[string]string
|
||||
trustForwardHeader bool
|
||||
emptyHost bool
|
||||
expectedHeaders map[string]string
|
||||
name string
|
||||
headers map[string]string
|
||||
trustForwardHeader bool
|
||||
emptyHost bool
|
||||
expectedHeaders map[string]string
|
||||
checkForUnexpectedHeaders bool
|
||||
}{
|
||||
{
|
||||
name: "trust Forward Header",
|
||||
@@ -280,6 +335,29 @@ func Test_writeHeader(t *testing.T) {
|
||||
"X-Forwarded-Method": "GET",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "remove hop-by-hop headers",
|
||||
headers: map[string]string{
|
||||
forward.Connection: "Connection",
|
||||
forward.KeepAlive: "KeepAlive",
|
||||
forward.ProxyAuthenticate: "ProxyAuthenticate",
|
||||
forward.ProxyAuthorization: "ProxyAuthorization",
|
||||
forward.Te: "Te",
|
||||
forward.Trailers: "Trailers",
|
||||
forward.TransferEncoding: "TransferEncoding",
|
||||
forward.Upgrade: "Upgrade",
|
||||
"X-CustomHeader": "CustomHeader",
|
||||
},
|
||||
trustForwardHeader: false,
|
||||
expectedHeaders: map[string]string{
|
||||
"X-CustomHeader": "CustomHeader",
|
||||
"X-Forwarded-Proto": "http",
|
||||
"X-Forwarded-Host": "foo.bar",
|
||||
"X-Forwarded-Uri": "/path?q=1",
|
||||
"X-Forwarded-Method": "GET",
|
||||
},
|
||||
checkForUnexpectedHeaders: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
@@ -298,8 +376,16 @@ func Test_writeHeader(t *testing.T) {
|
||||
|
||||
writeHeader(req, forwardReq, test.trustForwardHeader)
|
||||
|
||||
for key, value := range test.expectedHeaders {
|
||||
assert.Equal(t, value, forwardReq.Header.Get(key))
|
||||
actualHeaders := forwardReq.Header
|
||||
expectedHeaders := test.expectedHeaders
|
||||
for key, value := range expectedHeaders {
|
||||
assert.Equal(t, value, actualHeaders.Get(key))
|
||||
actualHeaders.Del(key)
|
||||
}
|
||||
if test.checkForUnexpectedHeaders {
|
||||
for key := range actualHeaders {
|
||||
assert.Fail(t, "Unexpected header found", key)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@@ -45,7 +45,7 @@ func (wl *IPWhiteLister) handle(w http.ResponseWriter, r *http.Request, next htt
|
||||
return
|
||||
}
|
||||
|
||||
tracing.SetErrorAndDebugLog(r, "request %+v matched white list %s - passing", r, wl.whiteLister)
|
||||
tracing.SetErrorAndDebugLog(r, "request %+v matched white list %v - passing", r, wl.whiteLister)
|
||||
next.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
|
@@ -2,6 +2,7 @@ package middlewares
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"runtime"
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/urfave/negroni"
|
||||
@@ -10,7 +11,7 @@ import (
|
||||
// RecoverHandler recovers from a panic in http handlers
|
||||
func RecoverHandler(next http.Handler) http.Handler {
|
||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||
defer recoverFunc(w)
|
||||
defer recoverFunc(w, r)
|
||||
next.ServeHTTP(w, r)
|
||||
}
|
||||
return http.HandlerFunc(fn)
|
||||
@@ -19,15 +20,32 @@ func RecoverHandler(next http.Handler) http.Handler {
|
||||
// NegroniRecoverHandler recovers from a panic in negroni handlers
|
||||
func NegroniRecoverHandler() negroni.Handler {
|
||||
fn := func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||
defer recoverFunc(w)
|
||||
defer recoverFunc(w, r)
|
||||
next.ServeHTTP(w, r)
|
||||
}
|
||||
return negroni.HandlerFunc(fn)
|
||||
}
|
||||
|
||||
func recoverFunc(w http.ResponseWriter) {
|
||||
func recoverFunc(w http.ResponseWriter, r *http.Request) {
|
||||
if err := recover(); err != nil {
|
||||
log.Errorf("Recovered from panic in http handler: %+v", err)
|
||||
if !shouldLogPanic(err) {
|
||||
log.Debugf("Request has been aborted [%s - %s]: %v", r.RemoteAddr, r.URL, err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Errorf("Recovered from panic in HTTP handler [%s - %s]: %+v", r.RemoteAddr, r.URL, err)
|
||||
|
||||
const size = 64 << 10
|
||||
buf := make([]byte, size)
|
||||
buf = buf[:runtime.Stack(buf, false)]
|
||||
log.Errorf("Stack: %s", buf)
|
||||
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/golang/go/blob/a0d6420d8be2ae7164797051ec74fa2a2df466a1/src/net/http/server.go#L1761-L1775
|
||||
// https://github.com/golang/go/blob/c33153f7b416c03983324b3e8f869ce1116d84bc/src/net/http/httputil/reverseproxy.go#L284
|
||||
func shouldLogPanic(panicValue interface{}) bool {
|
||||
return panicValue != nil && panicValue != http.ErrAbortHandler
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@ import (
|
||||
"text/template"
|
||||
|
||||
"github.com/containous/traefik/configuration"
|
||||
"github.com/containous/traefik/middlewares"
|
||||
"github.com/urfave/negroni"
|
||||
"github.com/vulcand/oxy/utils"
|
||||
)
|
||||
@@ -85,6 +86,24 @@ func (h *handler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http
|
||||
return
|
||||
}
|
||||
|
||||
if stripPrefix, stripPrefixOk := req.Context().Value(middlewares.StripPrefixKey).(string); stripPrefixOk {
|
||||
if len(stripPrefix) > 0 {
|
||||
parsedURL.Path = stripPrefix
|
||||
}
|
||||
}
|
||||
|
||||
if addPrefix, addPrefixOk := req.Context().Value(middlewares.AddPrefixKey).(string); addPrefixOk {
|
||||
if len(addPrefix) > 0 {
|
||||
parsedURL.Path = strings.Replace(parsedURL.Path, addPrefix, "", 1)
|
||||
}
|
||||
}
|
||||
|
||||
if replacePath, replacePathOk := req.Context().Value(middlewares.ReplacePathKey).(string); replacePathOk {
|
||||
if len(replacePath) > 0 {
|
||||
parsedURL.Path = replacePath
|
||||
}
|
||||
}
|
||||
|
||||
if newURL != oldURL {
|
||||
handler := &moveHandler{location: parsedURL, permanent: h.permanent}
|
||||
handler.ServeHTTP(rw, req)
|
||||
|
@@ -1,11 +1,17 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ReplacedPathHeader is the default header to set the old path to
|
||||
const ReplacedPathHeader = "X-Replaced-Path"
|
||||
const (
|
||||
// ReplacePathKey is the key within the request context used to
|
||||
// store the replaced path
|
||||
ReplacePathKey key = "ReplacePath"
|
||||
// ReplacedPathHeader is the default header to set the old path to
|
||||
ReplacedPathHeader = "X-Replaced-Path"
|
||||
)
|
||||
|
||||
// ReplacePath is a middleware used to replace the path of a URL request
|
||||
type ReplacePath struct {
|
||||
@@ -14,6 +20,7 @@ type ReplacePath struct {
|
||||
}
|
||||
|
||||
func (s *ReplacePath) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
r = r.WithContext(context.WithValue(r.Context(), ReplacePathKey, r.URL.Path))
|
||||
r.Header.Add(ReplacedPathHeader, r.URL.Path)
|
||||
r.URL.Path = s.Path
|
||||
r.RequestURI = r.URL.RequestURI()
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -30,6 +31,7 @@ func NewReplacePathRegexHandler(regex string, replacement string, handler http.H
|
||||
|
||||
func (s *ReplacePathRegex) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if s.Regexp != nil && len(s.Replacement) > 0 && s.Regexp.MatchString(r.URL.Path) {
|
||||
r = r.WithContext(context.WithValue(r.Context(), ReplacePathKey, r.URL.Path))
|
||||
r.Header.Add(ReplacedPathHeader, r.URL.Path)
|
||||
r.URL.Path = s.Regexp.ReplaceAllString(r.URL.Path, s.Replacement)
|
||||
r.RequestURI = r.URL.RequestURI()
|
||||
|
@@ -2,6 +2,7 @@ package middlewares
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -41,11 +42,8 @@ func (retry *Retry) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
attempts := 1
|
||||
for {
|
||||
attemptsExhausted := attempts >= retry.attempts
|
||||
// Websocket requests can't be retried at this point in time.
|
||||
// This is due to the fact that gorilla/websocket doesn't use the request
|
||||
// context and so we don't get httptrace information.
|
||||
// Websocket clients should however retry on their own anyway.
|
||||
shouldRetry := !attemptsExhausted && !isWebsocketRequest(r)
|
||||
|
||||
shouldRetry := !attemptsExhausted
|
||||
retryResponseWriter := newRetryResponseWriter(rw, shouldRetry)
|
||||
|
||||
// Disable retries when the backend already received request data
|
||||
@@ -128,7 +126,7 @@ func (rr *retryResponseWriterWithoutCloseNotify) Header() http.Header {
|
||||
|
||||
func (rr *retryResponseWriterWithoutCloseNotify) Write(buf []byte) (int, error) {
|
||||
if rr.ShouldRetry() {
|
||||
return 0, nil
|
||||
return len(buf), nil
|
||||
}
|
||||
return rr.responseWriter.Write(buf)
|
||||
}
|
||||
@@ -137,7 +135,7 @@ func (rr *retryResponseWriterWithoutCloseNotify) WriteHeader(code int) {
|
||||
if rr.ShouldRetry() && code == http.StatusServiceUnavailable {
|
||||
// We get a 503 HTTP Status Code when there is no backend server in the pool
|
||||
// to which the request could be sent. Also, note that rr.ShouldRetry()
|
||||
// will never return true in case there was a connetion established to
|
||||
// will never return true in case there was a connection established to
|
||||
// the backend server and so we can be sure that the 503 was produced
|
||||
// inside Traefik already and we don't have to retry in this cases.
|
||||
rr.DisableRetries()
|
||||
@@ -150,7 +148,11 @@ func (rr *retryResponseWriterWithoutCloseNotify) WriteHeader(code int) {
|
||||
}
|
||||
|
||||
func (rr *retryResponseWriterWithoutCloseNotify) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
return rr.responseWriter.(http.Hijacker).Hijack()
|
||||
hijacker, ok := rr.responseWriter.(http.Hijacker)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("%T is not a http.Hijacker", rr.responseWriter)
|
||||
}
|
||||
return hijacker.Hijack()
|
||||
}
|
||||
|
||||
func (rr *retryResponseWriterWithoutCloseNotify) Flush() {
|
||||
|
@@ -3,21 +3,24 @@ package middlewares
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/containous/traefik/testhelpers"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/vulcand/oxy/forward"
|
||||
"github.com/vulcand/oxy/roundrobin"
|
||||
)
|
||||
|
||||
func TestRetry(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
maxRequestAttempts int
|
||||
wantRetryAttempts int
|
||||
wantResponseStatus int
|
||||
amountFaultyEndpoints int
|
||||
isWebsocketHandshakeRequest bool
|
||||
desc string
|
||||
maxRequestAttempts int
|
||||
wantRetryAttempts int
|
||||
wantResponseStatus int
|
||||
amountFaultyEndpoints int
|
||||
}{
|
||||
{
|
||||
desc: "no retry on success",
|
||||
@@ -54,14 +57,6 @@ func TestRetry(t *testing.T) {
|
||||
wantResponseStatus: http.StatusInternalServerError,
|
||||
amountFaultyEndpoints: 3,
|
||||
},
|
||||
{
|
||||
desc: "websocket request should not be retried",
|
||||
maxRequestAttempts: 3,
|
||||
wantRetryAttempts: 0,
|
||||
wantResponseStatus: http.StatusBadGateway,
|
||||
amountFaultyEndpoints: 1,
|
||||
isWebsocketHandshakeRequest: true,
|
||||
},
|
||||
}
|
||||
|
||||
backendServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
@@ -74,10 +69,10 @@ func TestRetry(t *testing.T) {
|
||||
t.Fatalf("Error creating forwarder: %s", err)
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
loadBalancer, err := roundrobin.New(forwarder)
|
||||
@@ -86,7 +81,7 @@ func TestRetry(t *testing.T) {
|
||||
}
|
||||
|
||||
basePort := 33444
|
||||
for i := 0; i < tc.amountFaultyEndpoints; i++ {
|
||||
for i := 0; i < test.amountFaultyEndpoints; i++ {
|
||||
// 192.0.2.0 is a non-routable IP for testing purposes.
|
||||
// See: https://stackoverflow.com/questions/528538/non-routable-ip-address/18436928#18436928
|
||||
// We only use the port specification here because the URL is used as identifier
|
||||
@@ -98,24 +93,91 @@ func TestRetry(t *testing.T) {
|
||||
loadBalancer.UpsertServer(testhelpers.MustParseURL(backendServer.URL))
|
||||
|
||||
retryListener := &countingRetryListener{}
|
||||
retry := NewRetry(tc.maxRequestAttempts, loadBalancer, retryListener)
|
||||
retry := NewRetry(test.maxRequestAttempts, loadBalancer, retryListener)
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodGet, "http://localhost:3000/ok", nil)
|
||||
|
||||
if tc.isWebsocketHandshakeRequest {
|
||||
req.Header.Add("Connection", "Upgrade")
|
||||
req.Header.Add("Upgrade", "websocket")
|
||||
}
|
||||
|
||||
retry.ServeHTTP(recorder, req)
|
||||
|
||||
if tc.wantResponseStatus != recorder.Code {
|
||||
t.Errorf("got status code %d, want %d", recorder.Code, tc.wantResponseStatus)
|
||||
assert.Equal(t, test.wantResponseStatus, recorder.Code)
|
||||
assert.Equal(t, test.wantRetryAttempts, retryListener.timesCalled)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetryWebsocket(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
maxRequestAttempts int
|
||||
expectedRetryAttempts int
|
||||
expectedResponseStatus int
|
||||
expectedError bool
|
||||
amountFaultyEndpoints int
|
||||
}{
|
||||
{
|
||||
desc: "Switching ok after 2 retries",
|
||||
maxRequestAttempts: 3,
|
||||
expectedRetryAttempts: 2,
|
||||
amountFaultyEndpoints: 2,
|
||||
expectedResponseStatus: http.StatusSwitchingProtocols,
|
||||
},
|
||||
{
|
||||
desc: "Switching failed",
|
||||
maxRequestAttempts: 2,
|
||||
expectedRetryAttempts: 1,
|
||||
amountFaultyEndpoints: 2,
|
||||
expectedResponseStatus: http.StatusBadGateway,
|
||||
expectedError: true,
|
||||
},
|
||||
}
|
||||
|
||||
forwarder, err := forward.New()
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating forwarder: %s", err)
|
||||
}
|
||||
|
||||
backendServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
upgrader := websocket.Upgrader{}
|
||||
upgrader.Upgrade(rw, req, nil)
|
||||
}))
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
loadBalancer, err := roundrobin.New(forwarder)
|
||||
if err != nil {
|
||||
t.Fatalf("Error creating load balancer: %s", err)
|
||||
}
|
||||
if tc.wantRetryAttempts != retryListener.timesCalled {
|
||||
t.Errorf("retry listener called %d time(s), want %d time(s)", retryListener.timesCalled, tc.wantRetryAttempts)
|
||||
|
||||
basePort := 33444
|
||||
for i := 0; i < test.amountFaultyEndpoints; i++ {
|
||||
// 192.0.2.0 is a non-routable IP for testing purposes.
|
||||
// See: https://stackoverflow.com/questions/528538/non-routable-ip-address/18436928#18436928
|
||||
// We only use the port specification here because the URL is used as identifier
|
||||
// in the load balancer and using the exact same URL would not add a new server.
|
||||
loadBalancer.UpsertServer(testhelpers.MustParseURL("http://192.0.2.0:" + string(basePort+i)))
|
||||
}
|
||||
|
||||
// add the functioning server to the end of the load balancer list
|
||||
loadBalancer.UpsertServer(testhelpers.MustParseURL(backendServer.URL))
|
||||
|
||||
retryListener := &countingRetryListener{}
|
||||
retry := NewRetry(test.maxRequestAttempts, loadBalancer, retryListener)
|
||||
|
||||
retryServer := httptest.NewServer(retry)
|
||||
|
||||
url := strings.Replace(retryServer.URL, "http", "ws", 1)
|
||||
_, response, err := websocket.DefaultDialer.Dial(url, nil)
|
||||
|
||||
if !test.expectedError {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
assert.Equal(t, test.expectedResponseStatus, response.StatusCode)
|
||||
assert.Equal(t, test.expectedRetryAttempts, retryListener.timesCalled)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -1,12 +1,18 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ForwardedPrefixHeader is the default header to set prefix
|
||||
const ForwardedPrefixHeader = "X-Forwarded-Prefix"
|
||||
const (
|
||||
// StripPrefixKey is the key within the request context used to
|
||||
// store the stripped prefix
|
||||
StripPrefixKey key = "StripPrefix"
|
||||
// ForwardedPrefixHeader is the default header to set prefix
|
||||
ForwardedPrefixHeader = "X-Forwarded-Prefix"
|
||||
)
|
||||
|
||||
// StripPrefix is a middleware used to strip prefix from an URL request
|
||||
type StripPrefix struct {
|
||||
@@ -17,18 +23,20 @@ type StripPrefix struct {
|
||||
func (s *StripPrefix) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
for _, prefix := range s.Prefixes {
|
||||
if strings.HasPrefix(r.URL.Path, prefix) {
|
||||
rawReqPath := r.URL.Path
|
||||
r.URL.Path = stripPrefix(r.URL.Path, prefix)
|
||||
if r.URL.RawPath != "" {
|
||||
r.URL.RawPath = stripPrefix(r.URL.RawPath, prefix)
|
||||
}
|
||||
s.serveRequest(w, r, strings.TrimSpace(prefix))
|
||||
s.serveRequest(w, r, strings.TrimSpace(prefix), rawReqPath)
|
||||
return
|
||||
}
|
||||
}
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
|
||||
func (s *StripPrefix) serveRequest(w http.ResponseWriter, r *http.Request, prefix string) {
|
||||
func (s *StripPrefix) serveRequest(w http.ResponseWriter, r *http.Request, prefix string, rawReqPath string) {
|
||||
r = r.WithContext(context.WithValue(r.Context(), StripPrefixKey, rawReqPath))
|
||||
r.Header.Add(ForwardedPrefixHeader, prefix)
|
||||
r.RequestURI = r.URL.RequestURI()
|
||||
s.Handler.ServeHTTP(w, r)
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/containous/mux"
|
||||
@@ -38,13 +39,14 @@ func (s *StripPrefixRegex) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
log.Error("Error in stripPrefix middleware", err)
|
||||
return
|
||||
}
|
||||
|
||||
rawReqPath := r.URL.Path
|
||||
r.URL.Path = r.URL.Path[len(prefix.Path):]
|
||||
if r.URL.RawPath != "" {
|
||||
r.URL.RawPath = r.URL.RawPath[len(prefix.Path):]
|
||||
}
|
||||
r = r.WithContext(context.WithValue(r.Context(), StripPrefixKey, rawReqPath))
|
||||
r.Header.Add(ForwardedPrefixHeader, prefix.Path)
|
||||
r.RequestURI = r.URL.RequestURI()
|
||||
r.RequestURI = ensureLeadingSlash(r.URL.RequestURI())
|
||||
s.Handler.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
251
middlewares/tlsClientHeaders.go
Normal file
251
middlewares/tlsClientHeaders.go
Normal file
@@ -0,0 +1,251 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/types"
|
||||
)
|
||||
|
||||
const xForwardedTLSClientCert = "X-Forwarded-Tls-Client-Cert"
|
||||
const xForwardedTLSClientCertInfos = "X-Forwarded-Tls-Client-Cert-Infos"
|
||||
|
||||
// TLSClientCertificateInfos is a struct for specifying the configuration for the tlsClientHeaders middleware.
|
||||
type TLSClientCertificateInfos struct {
|
||||
NotAfter bool
|
||||
NotBefore bool
|
||||
Subject *TLSCLientCertificateSubjectInfos
|
||||
Sans bool
|
||||
}
|
||||
|
||||
// TLSCLientCertificateSubjectInfos contains the configuration for the certificate subject infos.
|
||||
type TLSCLientCertificateSubjectInfos struct {
|
||||
Country bool
|
||||
Province bool
|
||||
Locality bool
|
||||
Organization bool
|
||||
CommonName bool
|
||||
SerialNumber bool
|
||||
}
|
||||
|
||||
// TLSClientHeaders is a middleware that helps setup a few tls infos features.
|
||||
type TLSClientHeaders struct {
|
||||
PEM bool // pass the sanitized pem to the backend in a specific header
|
||||
Infos *TLSClientCertificateInfos // pass selected informations from the client certificate
|
||||
}
|
||||
|
||||
func newTLSCLientCertificateSubjectInfos(infos *types.TLSCLientCertificateSubjectInfos) *TLSCLientCertificateSubjectInfos {
|
||||
if infos == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &TLSCLientCertificateSubjectInfos{
|
||||
SerialNumber: infos.SerialNumber,
|
||||
CommonName: infos.CommonName,
|
||||
Country: infos.Country,
|
||||
Locality: infos.Locality,
|
||||
Organization: infos.Organization,
|
||||
Province: infos.Province,
|
||||
}
|
||||
}
|
||||
|
||||
func newTLSClientInfos(infos *types.TLSClientCertificateInfos) *TLSClientCertificateInfos {
|
||||
if infos == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &TLSClientCertificateInfos{
|
||||
NotBefore: infos.NotBefore,
|
||||
NotAfter: infos.NotAfter,
|
||||
Sans: infos.Sans,
|
||||
Subject: newTLSCLientCertificateSubjectInfos(infos.Subject),
|
||||
}
|
||||
}
|
||||
|
||||
// NewTLSClientHeaders constructs a new TLSClientHeaders instance from supplied frontend header struct.
|
||||
func NewTLSClientHeaders(frontend *types.Frontend) *TLSClientHeaders {
|
||||
if frontend == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var pem bool
|
||||
var infos *TLSClientCertificateInfos
|
||||
|
||||
if frontend.PassTLSClientCert != nil {
|
||||
conf := frontend.PassTLSClientCert
|
||||
pem = conf.PEM
|
||||
infos = newTLSClientInfos(conf.Infos)
|
||||
}
|
||||
|
||||
return &TLSClientHeaders{
|
||||
PEM: pem,
|
||||
Infos: infos,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TLSClientHeaders) ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
|
||||
s.ModifyRequestHeaders(r)
|
||||
// If there is a next, call it.
|
||||
if next != nil {
|
||||
next(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
// sanitize As we pass the raw certificates, remove the useless data and make it http request compliant
|
||||
func sanitize(cert []byte) string {
|
||||
s := string(cert)
|
||||
r := strings.NewReplacer("-----BEGIN CERTIFICATE-----", "",
|
||||
"-----END CERTIFICATE-----", "",
|
||||
"\n", "")
|
||||
cleaned := r.Replace(s)
|
||||
|
||||
return url.QueryEscape(cleaned)
|
||||
}
|
||||
|
||||
// extractCertificate extract the certificate from the request
|
||||
func extractCertificate(cert *x509.Certificate) string {
|
||||
b := pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}
|
||||
certPEM := pem.EncodeToMemory(&b)
|
||||
if certPEM == nil {
|
||||
log.Error("Cannot extract the certificate content")
|
||||
return ""
|
||||
}
|
||||
return sanitize(certPEM)
|
||||
}
|
||||
|
||||
// getXForwardedTLSClientCert Build a string with the client certificates
|
||||
func getXForwardedTLSClientCert(certs []*x509.Certificate) string {
|
||||
var headerValues []string
|
||||
|
||||
for _, peerCert := range certs {
|
||||
headerValues = append(headerValues, extractCertificate(peerCert))
|
||||
}
|
||||
|
||||
return strings.Join(headerValues, ",")
|
||||
}
|
||||
|
||||
// getSANs get the Subject Alternate Name values
|
||||
func getSANs(cert *x509.Certificate) []string {
|
||||
var sans []string
|
||||
if cert == nil {
|
||||
return sans
|
||||
}
|
||||
|
||||
sans = append(cert.DNSNames, cert.EmailAddresses...)
|
||||
|
||||
var ips []string
|
||||
for _, ip := range cert.IPAddresses {
|
||||
ips = append(ips, ip.String())
|
||||
}
|
||||
sans = append(sans, ips...)
|
||||
|
||||
var uris []string
|
||||
for _, uri := range cert.URIs {
|
||||
uris = append(uris, uri.String())
|
||||
}
|
||||
|
||||
return append(sans, uris...)
|
||||
}
|
||||
|
||||
// getSubjectInfos extract the requested informations from the certificate subject
|
||||
func (s *TLSClientHeaders) getSubjectInfos(cs *pkix.Name) string {
|
||||
var subject string
|
||||
|
||||
if s.Infos != nil && s.Infos.Subject != nil {
|
||||
options := s.Infos.Subject
|
||||
|
||||
var content []string
|
||||
|
||||
if options.Country && len(cs.Country) > 0 {
|
||||
content = append(content, fmt.Sprintf("C=%s", cs.Country[0]))
|
||||
}
|
||||
|
||||
if options.Province && len(cs.Province) > 0 {
|
||||
content = append(content, fmt.Sprintf("ST=%s", cs.Province[0]))
|
||||
}
|
||||
|
||||
if options.Locality && len(cs.Locality) > 0 {
|
||||
content = append(content, fmt.Sprintf("L=%s", cs.Locality[0]))
|
||||
}
|
||||
|
||||
if options.Organization && len(cs.Organization) > 0 {
|
||||
content = append(content, fmt.Sprintf("O=%s", cs.Organization[0]))
|
||||
}
|
||||
|
||||
if options.CommonName && len(cs.CommonName) > 0 {
|
||||
content = append(content, fmt.Sprintf("CN=%s", cs.CommonName))
|
||||
}
|
||||
|
||||
if len(content) > 0 {
|
||||
subject = `Subject="` + strings.Join(content, ",") + `"`
|
||||
}
|
||||
}
|
||||
|
||||
return subject
|
||||
}
|
||||
|
||||
// getXForwardedTLSClientCertInfos Build a string with the wanted client certificates informations
|
||||
// like Subject="C=%s,ST=%s,L=%s,O=%s,CN=%s",NB=%d,NA=%d,SAN=%s;
|
||||
func (s *TLSClientHeaders) getXForwardedTLSClientCertInfos(certs []*x509.Certificate) string {
|
||||
var headerValues []string
|
||||
|
||||
for _, peerCert := range certs {
|
||||
var values []string
|
||||
var sans string
|
||||
var nb string
|
||||
var na string
|
||||
|
||||
subject := s.getSubjectInfos(&peerCert.Subject)
|
||||
if len(subject) > 0 {
|
||||
values = append(values, subject)
|
||||
}
|
||||
|
||||
ci := s.Infos
|
||||
if ci != nil {
|
||||
if ci.NotBefore {
|
||||
nb = fmt.Sprintf("NB=%d", uint64(peerCert.NotBefore.Unix()))
|
||||
values = append(values, nb)
|
||||
}
|
||||
if ci.NotAfter {
|
||||
na = fmt.Sprintf("NA=%d", uint64(peerCert.NotAfter.Unix()))
|
||||
values = append(values, na)
|
||||
}
|
||||
|
||||
if ci.Sans {
|
||||
sans = fmt.Sprintf("SAN=%s", strings.Join(getSANs(peerCert), ","))
|
||||
values = append(values, sans)
|
||||
}
|
||||
}
|
||||
|
||||
value := strings.Join(values, ",")
|
||||
headerValues = append(headerValues, value)
|
||||
}
|
||||
|
||||
return strings.Join(headerValues, ";")
|
||||
}
|
||||
|
||||
// ModifyRequestHeaders set the wanted headers with the certificates informations
|
||||
func (s *TLSClientHeaders) ModifyRequestHeaders(r *http.Request) {
|
||||
if s.PEM {
|
||||
if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 {
|
||||
r.Header.Set(xForwardedTLSClientCert, getXForwardedTLSClientCert(r.TLS.PeerCertificates))
|
||||
} else {
|
||||
log.Warn("Try to extract certificate on a request without TLS")
|
||||
}
|
||||
}
|
||||
|
||||
if s.Infos != nil {
|
||||
if r.TLS != nil && len(r.TLS.PeerCertificates) > 0 {
|
||||
headerContent := s.getXForwardedTLSClientCertInfos(r.TLS.PeerCertificates)
|
||||
r.Header.Set(xForwardedTLSClientCertInfos, url.QueryEscape(headerContent))
|
||||
} else {
|
||||
log.Warn("Try to extract certificate on a request without TLS")
|
||||
}
|
||||
}
|
||||
}
|
799
middlewares/tlsClientHeaders_test.go
Normal file
799
middlewares/tlsClientHeaders_test.go
Normal file
@@ -0,0 +1,799 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/containous/traefik/testhelpers"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
rootCrt = `-----BEGIN CERTIFICATE-----
|
||||
MIIDhjCCAm6gAwIBAgIJAIKZlW9a3VrYMA0GCSqGSIb3DQEBCwUAMFgxCzAJBgNV
|
||||
BAYTAkZSMRMwEQYDVQQIDApTb21lLVN0YXRlMREwDwYDVQQHDAhUb3Vsb3VzZTEh
|
||||
MB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMB4XDTE4MDcxNzIwMzQz
|
||||
OFoXDTE4MDgxNjIwMzQzOFowWDELMAkGA1UEBhMCRlIxEzARBgNVBAgMClNvbWUt
|
||||
U3RhdGUxETAPBgNVBAcMCFRvdWxvdXNlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn
|
||||
aXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1P8GJ
|
||||
H9LkIxIIqK9MyUpushnjmjwccpSMB3OecISKYLy62QDIcAw6NzGcSe8hMwciMJr+
|
||||
CdCjJlohybnaRI9hrJ3GPnI++UT/MMthf2IIcjmJxmD4k9L1fgs1V6zSTlo0+o0x
|
||||
0gkAGlWvRkgA+3nt555ee84XQZuneKKeRRIlSA1ygycewFobZ/pGYijIEko+gYkV
|
||||
sF3LnRGxNl673w+EQsvI7+z29T1nzjmM/xE7WlvnsrVd1/N61jAohLota0YTufwd
|
||||
ioJZNryzuPejHBCiQRGMbJ7uEEZLiSCN6QiZEfqhS3AulykjgFXQQHn4zoVljSBR
|
||||
UyLV0prIn5Scbks/AgMBAAGjUzBRMB0GA1UdDgQWBBTroRRnSgtkV+8dumtcftb/
|
||||
lwIkATAfBgNVHSMEGDAWgBTroRRnSgtkV+8dumtcftb/lwIkATAPBgNVHRMBAf8E
|
||||
BTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAJ67U5cLa0ZFa/7zQQT4ldkY6YOEgR
|
||||
0LNoTu51hc+ozaXSvF8YIBzkEpEnbGS3x4xodrwEBZjK2LFhNu/33gkCAuhmedgk
|
||||
KwZrQM6lqRFGHGVOlkVz+QrJ2EsKYaO4SCUIwVjijXRLA7A30G5C/CIh66PsMgBY
|
||||
6QHXVPEWm/v1d1Q/DfFfFzSOa1n1rIUw03qVJsxqSwfwYcegOF8YvS/eH4HUr2gF
|
||||
cEujh6CCnylf35ExHa45atr3+xxbOVdNjobISkYADtbhAAn4KjLS4v8W6445vxxj
|
||||
G5EIZLjOHyWg1sGaHaaAPkVpZQg8EKm21c4hrEEMfel60AMSSzad/a/V
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
minimalCert = `-----BEGIN CERTIFICATE-----
|
||||
MIIDGTCCAgECCQCqLd75YLi2kDANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJG
|
||||
UjETMBEGA1UECAwKU29tZS1TdGF0ZTERMA8GA1UEBwwIVG91bG91c2UxITAfBgNV
|
||||
BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0xODA3MTgwODI4MTZaFw0x
|
||||
ODA4MTcwODI4MTZaMEUxCzAJBgNVBAYTAkZSMRMwEQYDVQQIDApTb21lLVN0YXRl
|
||||
MSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQC/+frDMMTLQyXG34F68BPhQq0kzK4LIq9Y0/gl
|
||||
FjySZNn1C0QDWA1ubVCAcA6yY204I9cxcQDPNrhC7JlS5QA8Y5rhIBrqQlzZizAi
|
||||
Rj3NTrRjtGUtOScnHuJaWjLy03DWD+aMwb7q718xt5SEABmmUvLwQK+EjW2MeDwj
|
||||
y8/UEIpvrRDmdhGaqv7IFpIDkcIF7FowJ/hwDvx3PMc+z/JWK0ovzpvgbx69AVbw
|
||||
ZxCimeha65rOqVi+lEetD26le+WnOdYsdJ2IkmpPNTXGdfb15xuAc+gFXfMCh7Iw
|
||||
3Ynl6dZtZM/Ok2kiA7/OsmVnRKkWrtBfGYkI9HcNGb3zrk6nAgMBAAEwDQYJKoZI
|
||||
hvcNAQELBQADggEBAC/R+Yvhh1VUhcbK49olWsk/JKqfS3VIDQYZg1Eo+JCPbwgS
|
||||
I1BSYVfMcGzuJTX6ua3m/AHzGF3Tap4GhF4tX12jeIx4R4utnjj7/YKkTvuEM2f4
|
||||
xT56YqI7zalGScIB0iMeyNz1QcimRl+M/49au8ow9hNX8C2tcA2cwd/9OIj/6T8q
|
||||
SBRHc6ojvbqZSJCO0jziGDT1L3D+EDgTjED4nd77v/NRdP+egb0q3P0s4dnQ/5AV
|
||||
aQlQADUn61j3ScbGJ4NSeZFFvsl38jeRi/MEzp0bGgNBcPj6JHi7qbbauZcZfQ05
|
||||
jECvgAY7Nfd9mZ1KtyNaW31is+kag7NsvjxU/kM=
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
completeCert = `Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 3 (0x3)
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
Issuer: C=FR, ST=Some-State, L=Toulouse, O=Internet Widgits Pty Ltd
|
||||
Validity
|
||||
Not Before: Jul 18 08:00:16 2018 GMT
|
||||
Not After : Jul 18 08:00:16 2019 GMT
|
||||
Subject: C=FR, ST=SomeState, L=Toulouse, O=Cheese, CN=*.cheese.org
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (2048 bit)
|
||||
Modulus:
|
||||
00:a6:1f:96:7c:c1:cc:b8:1c:b5:91:5d:b8:bf:70:
|
||||
bc:f7:b8:04:4f:2a:42:de:ea:c5:c3:19:0b:03:04:
|
||||
ec:ef:a1:24:25:de:ad:05:e7:26:ea:89:6c:59:60:
|
||||
10:18:0c:73:f1:bf:d3:cc:7b:ed:6b:9c:ea:1d:88:
|
||||
e2:ee:14:81:d7:07:ee:87:95:3d:36:df:9c:38:b7:
|
||||
7b:1e:2b:51:9c:4a:1f:d0:cc:5b:af:5d:6c:5c:35:
|
||||
49:32:e4:01:5b:f9:8c:71:cf:62:48:5a:ea:b7:31:
|
||||
58:e2:c6:d0:5b:1c:50:b5:5c:6d:5a:6f:da:41:5e:
|
||||
d5:4c:6e:1a:21:f3:40:f9:9e:52:76:50:25:3e:03:
|
||||
9b:87:19:48:5b:47:87:d3:67:c6:25:69:77:29:8e:
|
||||
56:97:45:d9:6f:64:a8:4e:ad:35:75:2e:fc:6a:2e:
|
||||
47:87:76:fc:4e:3e:44:e9:16:b2:c7:f0:23:98:13:
|
||||
a2:df:15:23:cb:0c:3d:fd:48:5e:c7:2c:86:70:63:
|
||||
8b:c6:c8:89:17:52:d5:a7:8e:cb:4e:11:9d:69:8e:
|
||||
8e:59:cc:7e:a3:bd:a1:11:88:d7:cf:7b:8c:19:46:
|
||||
9c:1b:7a:c9:39:81:4c:58:08:1f:c7:ce:b0:0e:79:
|
||||
64:d3:11:72:65:e6:dd:bd:00:7f:22:30:46:9b:66:
|
||||
9c:b9
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Basic Constraints:
|
||||
CA:FALSE
|
||||
X509v3 Subject Alternative Name:
|
||||
DNS:*.cheese.org, DNS:*.cheese.net, DNS:cheese.in, IP Address:10.0.1.0, IP Address:10.0.1.2, email:test@cheese.org, email:test@cheese.net
|
||||
X509v3 Subject Key Identifier:
|
||||
AB:6B:89:25:11:FC:5E:7B:D4:B0:F7:D4:B6:D9:EB:D0:30:93:E5:58
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
ad:87:84:a0:88:a3:4c:d9:0a:c0:14:e4:2d:9a:1d:bb:57:b7:
|
||||
12:ef:3a:fb:8b:b2:ce:32:b8:04:e6:59:c8:4f:14:6a:b5:12:
|
||||
46:e9:c9:0a:11:64:ea:a1:86:20:96:0e:a7:40:e3:aa:e5:98:
|
||||
91:36:89:77:b6:b9:73:7e:1a:58:19:ae:d1:14:83:1e:c1:5f:
|
||||
a5:a0:32:bb:52:68:b4:8d:a3:1d:b3:08:d7:45:6e:3b:87:64:
|
||||
7e:ef:46:e6:6f:d5:79:d7:1d:57:68:67:d8:18:39:61:5b:8b:
|
||||
1a:7f:88:da:0a:51:9b:3d:6c:5d:b1:cf:b7:e9:1e:06:65:8e:
|
||||
96:d3:61:96:f8:a2:61:f9:40:5e:fa:bc:76:b9:64:0e:6f:90:
|
||||
37:de:ac:6d:7f:36:84:35:19:88:8c:26:af:3e:c3:6a:1a:03:
|
||||
ed:d7:90:89:ed:18:4c:9e:94:1f:d8:ae:6c:61:36:17:72:f9:
|
||||
bb:de:0a:56:9a:79:b4:7d:4a:9d:cb:4a:7d:71:9f:38:e7:8d:
|
||||
f0:87:24:21:0a:24:1f:82:9a:6b:67:ce:7d:af:cb:91:6b:8a:
|
||||
de:e6:d8:6f:a1:37:b9:2d:d0:cb:e8:4e:f4:43:af:ad:90:13:
|
||||
7d:61:7a:ce:86:48:fc:00:8c:37:fb:e0:31:6b:e2:18:ad:fd:
|
||||
1e:df:08:db
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDvTCCAqWgAwIBAgIBAzANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJGUjET
|
||||
MBEGA1UECAwKU29tZS1TdGF0ZTERMA8GA1UEBwwIVG91bG91c2UxITAfBgNVBAoM
|
||||
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0xODA3MTgwODAwMTZaFw0xOTA3
|
||||
MTgwODAwMTZaMFwxCzAJBgNVBAYTAkZSMRIwEAYDVQQIDAlTb21lU3RhdGUxETAP
|
||||
BgNVBAcMCFRvdWxvdXNlMQ8wDQYDVQQKDAZDaGVlc2UxFTATBgNVBAMMDCouY2hl
|
||||
ZXNlLm9yZzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKYflnzBzLgc
|
||||
tZFduL9wvPe4BE8qQt7qxcMZCwME7O+hJCXerQXnJuqJbFlgEBgMc/G/08x77Wuc
|
||||
6h2I4u4UgdcH7oeVPTbfnDi3ex4rUZxKH9DMW69dbFw1STLkAVv5jHHPYkha6rcx
|
||||
WOLG0FscULVcbVpv2kFe1UxuGiHzQPmeUnZQJT4Dm4cZSFtHh9NnxiVpdymOVpdF
|
||||
2W9kqE6tNXUu/GouR4d2/E4+ROkWssfwI5gTot8VI8sMPf1IXscshnBji8bIiRdS
|
||||
1aeOy04RnWmOjlnMfqO9oRGI1897jBlGnBt6yTmBTFgIH8fOsA55ZNMRcmXm3b0A
|
||||
fyIwRptmnLkCAwEAAaOBjTCBijAJBgNVHRMEAjAAMF4GA1UdEQRXMFWCDCouY2hl
|
||||
ZXNlLm9yZ4IMKi5jaGVlc2UubmV0ggljaGVlc2UuaW6HBAoAAQCHBAoAAQKBD3Rl
|
||||
c3RAY2hlZXNlLm9yZ4EPdGVzdEBjaGVlc2UubmV0MB0GA1UdDgQWBBSra4klEfxe
|
||||
e9Sw99S22evQMJPlWDANBgkqhkiG9w0BAQUFAAOCAQEArYeEoIijTNkKwBTkLZod
|
||||
u1e3Eu86+4uyzjK4BOZZyE8UarUSRunJChFk6qGGIJYOp0DjquWYkTaJd7a5c34a
|
||||
WBmu0RSDHsFfpaAyu1JotI2jHbMI10VuO4dkfu9G5m/VedcdV2hn2Bg5YVuLGn+I
|
||||
2gpRmz1sXbHPt+keBmWOltNhlviiYflAXvq8drlkDm+QN96sbX82hDUZiIwmrz7D
|
||||
ahoD7deQie0YTJ6UH9iubGE2F3L5u94KVpp5tH1KnctKfXGfOOeN8IckIQokH4Ka
|
||||
a2fOfa/LkWuK3ubYb6E3uS3Qy+hO9EOvrZATfWF6zoZI/ACMN/vgMWviGK39Ht8I
|
||||
2w==
|
||||
-----END CERTIFICATE-----
|
||||
`
|
||||
)
|
||||
|
||||
func getCleanCertContents(certContents []string) string {
|
||||
var re = regexp.MustCompile("-----BEGIN CERTIFICATE-----(?s)(.*)")
|
||||
|
||||
var cleanedCertContent []string
|
||||
for _, certContent := range certContents {
|
||||
cert := re.FindString(string(certContent))
|
||||
cleanedCertContent = append(cleanedCertContent, sanitize([]byte(cert)))
|
||||
}
|
||||
|
||||
return strings.Join(cleanedCertContent, ",")
|
||||
}
|
||||
|
||||
func getCertificate(certContent string) *x509.Certificate {
|
||||
roots := x509.NewCertPool()
|
||||
ok := roots.AppendCertsFromPEM([]byte(rootCrt))
|
||||
if !ok {
|
||||
panic("failed to parse root certificate")
|
||||
}
|
||||
|
||||
block, _ := pem.Decode([]byte(certContent))
|
||||
if block == nil {
|
||||
panic("failed to parse certificate PEM")
|
||||
}
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
panic("failed to parse certificate: " + err.Error())
|
||||
}
|
||||
|
||||
return cert
|
||||
}
|
||||
|
||||
func buildTLSWith(certContents []string) *tls.ConnectionState {
|
||||
var peerCertificates []*x509.Certificate
|
||||
|
||||
for _, certContent := range certContents {
|
||||
peerCertificates = append(peerCertificates, getCertificate(certContent))
|
||||
}
|
||||
|
||||
return &tls.ConnectionState{PeerCertificates: peerCertificates}
|
||||
}
|
||||
|
||||
var myPassTLSClientCustomHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("bar"))
|
||||
})
|
||||
|
||||
func getExpectedSanitized(s string) string {
|
||||
return url.QueryEscape(strings.Replace(s, "\n", "", -1))
|
||||
}
|
||||
|
||||
func TestSanitize(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
toSanitize []byte
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "Empty",
|
||||
},
|
||||
{
|
||||
desc: "With a minimal cert",
|
||||
toSanitize: []byte(minimalCert),
|
||||
expected: getExpectedSanitized(`MIIDGTCCAgECCQCqLd75YLi2kDANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJG
|
||||
UjETMBEGA1UECAwKU29tZS1TdGF0ZTERMA8GA1UEBwwIVG91bG91c2UxITAfBgNV
|
||||
BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0xODA3MTgwODI4MTZaFw0x
|
||||
ODA4MTcwODI4MTZaMEUxCzAJBgNVBAYTAkZSMRMwEQYDVQQIDApTb21lLVN0YXRl
|
||||
MSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQC/+frDMMTLQyXG34F68BPhQq0kzK4LIq9Y0/gl
|
||||
FjySZNn1C0QDWA1ubVCAcA6yY204I9cxcQDPNrhC7JlS5QA8Y5rhIBrqQlzZizAi
|
||||
Rj3NTrRjtGUtOScnHuJaWjLy03DWD+aMwb7q718xt5SEABmmUvLwQK+EjW2MeDwj
|
||||
y8/UEIpvrRDmdhGaqv7IFpIDkcIF7FowJ/hwDvx3PMc+z/JWK0ovzpvgbx69AVbw
|
||||
ZxCimeha65rOqVi+lEetD26le+WnOdYsdJ2IkmpPNTXGdfb15xuAc+gFXfMCh7Iw
|
||||
3Ynl6dZtZM/Ok2kiA7/OsmVnRKkWrtBfGYkI9HcNGb3zrk6nAgMBAAEwDQYJKoZI
|
||||
hvcNAQELBQADggEBAC/R+Yvhh1VUhcbK49olWsk/JKqfS3VIDQYZg1Eo+JCPbwgS
|
||||
I1BSYVfMcGzuJTX6ua3m/AHzGF3Tap4GhF4tX12jeIx4R4utnjj7/YKkTvuEM2f4
|
||||
xT56YqI7zalGScIB0iMeyNz1QcimRl+M/49au8ow9hNX8C2tcA2cwd/9OIj/6T8q
|
||||
SBRHc6ojvbqZSJCO0jziGDT1L3D+EDgTjED4nd77v/NRdP+egb0q3P0s4dnQ/5AV
|
||||
aQlQADUn61j3ScbGJ4NSeZFFvsl38jeRi/MEzp0bGgNBcPj6JHi7qbbauZcZfQ05
|
||||
jECvgAY7Nfd9mZ1KtyNaW31is+kag7NsvjxU/kM=`),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require.Equal(t, test.expected, sanitize(test.toSanitize), "The sanitized certificates should be equal")
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestTlsClientheadersWithPEM(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
certContents []string // set the request TLS attribute if defined
|
||||
tlsClientCertHeaders *types.TLSClientHeaders
|
||||
expectedHeader string
|
||||
}{
|
||||
{
|
||||
desc: "No TLS, no option",
|
||||
},
|
||||
{
|
||||
desc: "TLS, no option",
|
||||
certContents: []string{minimalCert},
|
||||
},
|
||||
{
|
||||
desc: "No TLS, with pem option true",
|
||||
tlsClientCertHeaders: &types.TLSClientHeaders{PEM: true},
|
||||
},
|
||||
{
|
||||
desc: "TLS with simple certificate, with pem option true",
|
||||
certContents: []string{minimalCert},
|
||||
tlsClientCertHeaders: &types.TLSClientHeaders{PEM: true},
|
||||
expectedHeader: getCleanCertContents([]string{minimalCert}),
|
||||
},
|
||||
{
|
||||
desc: "TLS with complete certificate, with pem option true",
|
||||
certContents: []string{completeCert},
|
||||
tlsClientCertHeaders: &types.TLSClientHeaders{PEM: true},
|
||||
expectedHeader: getCleanCertContents([]string{completeCert}),
|
||||
},
|
||||
{
|
||||
desc: "TLS with two certificate, with pem option true",
|
||||
certContents: []string{minimalCert, completeCert},
|
||||
tlsClientCertHeaders: &types.TLSClientHeaders{PEM: true},
|
||||
expectedHeader: getCleanCertContents([]string{minimalCert, completeCert}),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
tlsClientHeaders := NewTLSClientHeaders(&types.Frontend{PassTLSClientCert: test.tlsClientCertHeaders})
|
||||
|
||||
res := httptest.NewRecorder()
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, "http://example.com/foo", nil)
|
||||
|
||||
if test.certContents != nil && len(test.certContents) > 0 {
|
||||
req.TLS = buildTLSWith(test.certContents)
|
||||
}
|
||||
|
||||
tlsClientHeaders.ServeHTTP(res, req, myPassTLSClientCustomHandler)
|
||||
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require.Equal(t, http.StatusOK, res.Code, "Http Status should be OK")
|
||||
require.Equal(t, "bar", res.Body.String(), "Should be the expected body")
|
||||
|
||||
if test.expectedHeader != "" {
|
||||
require.Equal(t, getCleanCertContents(test.certContents), req.Header.Get(xForwardedTLSClientCert), "The request header should contain the cleaned certificate")
|
||||
} else {
|
||||
require.Empty(t, req.Header.Get(xForwardedTLSClientCert))
|
||||
}
|
||||
require.Empty(t, res.Header().Get(xForwardedTLSClientCert), "The response header should be always empty")
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestGetSans(t *testing.T) {
|
||||
urlFoo, err := url.Parse("my.foo.com")
|
||||
require.NoError(t, err)
|
||||
urlBar, err := url.Parse("my.bar.com")
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
cert *x509.Certificate // set the request TLS attribute if defined
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
desc: "With nil",
|
||||
},
|
||||
{
|
||||
desc: "Certificate without Sans",
|
||||
cert: &x509.Certificate{},
|
||||
},
|
||||
{
|
||||
desc: "Certificate with all Sans",
|
||||
cert: &x509.Certificate{
|
||||
DNSNames: []string{"foo", "bar"},
|
||||
EmailAddresses: []string{"test@test.com", "test2@test.com"},
|
||||
IPAddresses: []net.IP{net.IPv4(10, 0, 0, 1), net.IPv4(10, 0, 0, 2)},
|
||||
URIs: []*url.URL{urlFoo, urlBar},
|
||||
},
|
||||
expected: []string{"foo", "bar", "test@test.com", "test2@test.com", "10.0.0.1", "10.0.0.2", urlFoo.String(), urlBar.String()},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
sans := getSANs(test.cert)
|
||||
test := test
|
||||
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if len(test.expected) > 0 {
|
||||
for i, expected := range test.expected {
|
||||
require.Equal(t, expected, sans[i])
|
||||
}
|
||||
} else {
|
||||
require.Empty(t, sans)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestTlsClientheadersWithCertInfos(t *testing.T) {
|
||||
minimalCertAllInfos := `Subject="C=FR,ST=Some-State,O=Internet Widgits Pty Ltd",NB=1531902496,NA=1534494496,SAN=`
|
||||
completeCertAllInfos := `Subject="C=FR,ST=SomeState,L=Toulouse,O=Cheese,CN=*.cheese.org",NB=1531900816,NA=1563436816,SAN=*.cheese.org,*.cheese.net,cheese.in,test@cheese.org,test@cheese.net,10.0.1.0,10.0.1.2`
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
certContents []string // set the request TLS attribute if defined
|
||||
tlsClientCertHeaders *types.TLSClientHeaders
|
||||
expectedHeader string
|
||||
}{
|
||||
{
|
||||
desc: "No TLS, no option",
|
||||
},
|
||||
{
|
||||
desc: "TLS, no option",
|
||||
certContents: []string{minimalCert},
|
||||
},
|
||||
{
|
||||
desc: "No TLS, with pem option true",
|
||||
tlsClientCertHeaders: &types.TLSClientHeaders{
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
CommonName: true,
|
||||
Organization: true,
|
||||
Locality: true,
|
||||
Province: true,
|
||||
Country: true,
|
||||
SerialNumber: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "No TLS, with pem option true with no flag",
|
||||
tlsClientCertHeaders: &types.TLSClientHeaders{
|
||||
PEM: false,
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "TLS with simple certificate, with all infos",
|
||||
certContents: []string{minimalCert},
|
||||
tlsClientCertHeaders: &types.TLSClientHeaders{
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
NotAfter: true,
|
||||
NotBefore: true,
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
CommonName: true,
|
||||
Organization: true,
|
||||
Locality: true,
|
||||
Province: true,
|
||||
Country: true,
|
||||
SerialNumber: true,
|
||||
},
|
||||
Sans: true,
|
||||
},
|
||||
},
|
||||
expectedHeader: url.QueryEscape(minimalCertAllInfos),
|
||||
},
|
||||
{
|
||||
desc: "TLS with simple certificate, with some infos",
|
||||
certContents: []string{minimalCert},
|
||||
tlsClientCertHeaders: &types.TLSClientHeaders{
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
NotAfter: true,
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
Organization: true,
|
||||
},
|
||||
Sans: true,
|
||||
},
|
||||
},
|
||||
expectedHeader: url.QueryEscape(`Subject="O=Internet Widgits Pty Ltd",NA=1534494496,SAN=`),
|
||||
},
|
||||
{
|
||||
desc: "TLS with complete certificate, with all infos",
|
||||
certContents: []string{completeCert},
|
||||
tlsClientCertHeaders: &types.TLSClientHeaders{
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
NotAfter: true,
|
||||
NotBefore: true,
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
CommonName: true,
|
||||
Organization: true,
|
||||
Locality: true,
|
||||
Province: true,
|
||||
Country: true,
|
||||
SerialNumber: true,
|
||||
},
|
||||
Sans: true,
|
||||
},
|
||||
},
|
||||
expectedHeader: url.QueryEscape(completeCertAllInfos),
|
||||
},
|
||||
{
|
||||
desc: "TLS with 2 certificates, with all infos",
|
||||
certContents: []string{minimalCert, completeCert},
|
||||
tlsClientCertHeaders: &types.TLSClientHeaders{
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
NotAfter: true,
|
||||
NotBefore: true,
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
CommonName: true,
|
||||
Organization: true,
|
||||
Locality: true,
|
||||
Province: true,
|
||||
Country: true,
|
||||
SerialNumber: true,
|
||||
},
|
||||
Sans: true,
|
||||
},
|
||||
},
|
||||
expectedHeader: url.QueryEscape(strings.Join([]string{minimalCertAllInfos, completeCertAllInfos}, ";")),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
tlsClientHeaders := NewTLSClientHeaders(&types.Frontend{PassTLSClientCert: test.tlsClientCertHeaders})
|
||||
|
||||
res := httptest.NewRecorder()
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, "http://example.com/foo", nil)
|
||||
|
||||
if test.certContents != nil && len(test.certContents) > 0 {
|
||||
req.TLS = buildTLSWith(test.certContents)
|
||||
}
|
||||
|
||||
tlsClientHeaders.ServeHTTP(res, req, myPassTLSClientCustomHandler)
|
||||
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require.Equal(t, http.StatusOK, res.Code, "Http Status should be OK")
|
||||
require.Equal(t, "bar", res.Body.String(), "Should be the expected body")
|
||||
|
||||
if test.expectedHeader != "" {
|
||||
require.Equal(t, test.expectedHeader, req.Header.Get(xForwardedTLSClientCertInfos), "The request header should contain the cleaned certificate")
|
||||
} else {
|
||||
require.Empty(t, req.Header.Get(xForwardedTLSClientCertInfos))
|
||||
}
|
||||
require.Empty(t, res.Header().Get(xForwardedTLSClientCertInfos), "The response header should be always empty")
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestNewTLSClientHeadersFromStruct(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
frontend *types.Frontend
|
||||
expected *TLSClientHeaders
|
||||
}{
|
||||
{
|
||||
desc: "Without frontend",
|
||||
},
|
||||
{
|
||||
desc: "frontend without the option",
|
||||
frontend: &types.Frontend{},
|
||||
expected: &TLSClientHeaders{},
|
||||
},
|
||||
{
|
||||
desc: "frontend with the pem set false",
|
||||
frontend: &types.Frontend{
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
PEM: false,
|
||||
},
|
||||
},
|
||||
expected: &TLSClientHeaders{PEM: false},
|
||||
},
|
||||
{
|
||||
desc: "frontend with the pem set true",
|
||||
frontend: &types.Frontend{
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
PEM: true,
|
||||
},
|
||||
},
|
||||
expected: &TLSClientHeaders{PEM: true},
|
||||
},
|
||||
{
|
||||
desc: "frontend with the Infos with no flag",
|
||||
frontend: &types.Frontend{
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
NotAfter: false,
|
||||
NotBefore: false,
|
||||
Sans: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &TLSClientHeaders{
|
||||
PEM: false,
|
||||
Infos: &TLSClientCertificateInfos{},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "frontend with the Infos basic",
|
||||
frontend: &types.Frontend{
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
NotAfter: true,
|
||||
NotBefore: true,
|
||||
Sans: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &TLSClientHeaders{
|
||||
PEM: false,
|
||||
Infos: &TLSClientCertificateInfos{
|
||||
NotBefore: true,
|
||||
NotAfter: true,
|
||||
Sans: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "frontend with the Infos NotAfter",
|
||||
frontend: &types.Frontend{
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
NotAfter: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &TLSClientHeaders{
|
||||
PEM: false,
|
||||
Infos: &TLSClientCertificateInfos{
|
||||
NotAfter: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "frontend with the Infos NotBefore",
|
||||
frontend: &types.Frontend{
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
NotBefore: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &TLSClientHeaders{
|
||||
PEM: false,
|
||||
Infos: &TLSClientCertificateInfos{
|
||||
NotBefore: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "frontend with the Infos Sans",
|
||||
frontend: &types.Frontend{
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
Sans: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &TLSClientHeaders{
|
||||
PEM: false,
|
||||
Infos: &TLSClientCertificateInfos{
|
||||
Sans: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "frontend with the Infos Subject Organization",
|
||||
frontend: &types.Frontend{
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
Organization: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &TLSClientHeaders{
|
||||
PEM: false,
|
||||
Infos: &TLSClientCertificateInfos{
|
||||
Subject: &TLSCLientCertificateSubjectInfos{
|
||||
Organization: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "frontend with the Infos Subject Country",
|
||||
frontend: &types.Frontend{
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
Country: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &TLSClientHeaders{
|
||||
PEM: false,
|
||||
Infos: &TLSClientCertificateInfos{
|
||||
Subject: &TLSCLientCertificateSubjectInfos{
|
||||
Country: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "frontend with the Infos Subject SerialNumber",
|
||||
frontend: &types.Frontend{
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
SerialNumber: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &TLSClientHeaders{
|
||||
PEM: false,
|
||||
Infos: &TLSClientCertificateInfos{
|
||||
Subject: &TLSCLientCertificateSubjectInfos{
|
||||
SerialNumber: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "frontend with the Infos Subject Province",
|
||||
frontend: &types.Frontend{
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
Province: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &TLSClientHeaders{
|
||||
PEM: false,
|
||||
Infos: &TLSClientCertificateInfos{
|
||||
Subject: &TLSCLientCertificateSubjectInfos{
|
||||
Province: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "frontend with the Infos Subject Locality",
|
||||
frontend: &types.Frontend{
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
Locality: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &TLSClientHeaders{
|
||||
PEM: false,
|
||||
Infos: &TLSClientCertificateInfos{
|
||||
Subject: &TLSCLientCertificateSubjectInfos{
|
||||
Locality: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "frontend with the Infos Subject CommonName",
|
||||
frontend: &types.Frontend{
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
CommonName: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &TLSClientHeaders{
|
||||
PEM: false,
|
||||
Infos: &TLSClientCertificateInfos{
|
||||
Subject: &TLSCLientCertificateSubjectInfos{
|
||||
CommonName: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "frontend with the Infos NotBefore",
|
||||
frontend: &types.Frontend{
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
Sans: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &TLSClientHeaders{
|
||||
PEM: false,
|
||||
Infos: &TLSClientCertificateInfos{
|
||||
Sans: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "frontend with the Infos all",
|
||||
frontend: &types.Frontend{
|
||||
PassTLSClientCert: &types.TLSClientHeaders{
|
||||
Infos: &types.TLSClientCertificateInfos{
|
||||
NotAfter: true,
|
||||
NotBefore: true,
|
||||
Subject: &types.TLSCLientCertificateSubjectInfos{
|
||||
CommonName: true,
|
||||
Country: true,
|
||||
Locality: true,
|
||||
Organization: true,
|
||||
Province: true,
|
||||
SerialNumber: true,
|
||||
},
|
||||
Sans: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &TLSClientHeaders{
|
||||
PEM: false,
|
||||
Infos: &TLSClientCertificateInfos{
|
||||
NotBefore: true,
|
||||
NotAfter: true,
|
||||
Sans: true,
|
||||
Subject: &TLSCLientCertificateSubjectInfos{
|
||||
Province: true,
|
||||
Organization: true,
|
||||
Locality: true,
|
||||
Country: true,
|
||||
CommonName: true,
|
||||
SerialNumber: true,
|
||||
},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require.Equal(t, test.expected, NewTLSClientHeaders(test.frontend))
|
||||
})
|
||||
}
|
||||
|
||||
}
|
25
middlewares/tracing/carrier.go
Normal file
25
middlewares/tracing/carrier.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package tracing
|
||||
|
||||
import "net/http"
|
||||
|
||||
// HTTPHeadersCarrier custom implementation to fix duplicated headers
|
||||
// It has been fixed in https://github.com/opentracing/opentracing-go/pull/191
|
||||
type HTTPHeadersCarrier http.Header
|
||||
|
||||
// Set conforms to the TextMapWriter interface.
|
||||
func (c HTTPHeadersCarrier) Set(key, val string) {
|
||||
h := http.Header(c)
|
||||
h.Set(key, val)
|
||||
}
|
||||
|
||||
// ForeachKey conforms to the TextMapReader interface.
|
||||
func (c HTTPHeadersCarrier) ForeachKey(handler func(key, val string) error) error {
|
||||
for k, vals := range c {
|
||||
for _, v := range vals {
|
||||
if err := handler(k, v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user