Merge branch 'v2.0' into master
This commit is contained in:
commit
d66dd01438
46 changed files with 911 additions and 484 deletions
80
CHANGELOG.md
80
CHANGELOG.md
|
@ -1,3 +1,42 @@
|
||||||
|
## [v2.0.4](https://github.com/containous/traefik/tree/v2.0.4) (2019-10-28)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v2.0.3...v2.0.4)
|
||||||
|
|
||||||
|
Fixes releases system.
|
||||||
|
Same changelog as v2.0.3.
|
||||||
|
|
||||||
|
## [v2.0.3](https://github.com/containous/traefik/tree/v2.0.3) (2019-10-28)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v2.0.2...v2.0.3)
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[acme,logs]** Use debug for log about skipping addition of cert ([#5641](https://github.com/containous/traefik/pull/5641) by [sylr](https://github.com/sylr))
|
||||||
|
- **[file]** fix: add filename in the file provider logs. ([#5636](https://github.com/containous/traefik/pull/5636) by [ldez](https://github.com/ldez))
|
||||||
|
- **[k8s,k8s/crd,k8s/ingress]** Remove unnecessary reload of the configuration. ([#5707](https://github.com/containous/traefik/pull/5707) by [ldez](https://github.com/ldez))
|
||||||
|
- **[k8s,k8s/crd,k8s/ingress]** Fixing support for HTTPs backends with Kubernetes ExternalName services ([#5660](https://github.com/containous/traefik/pull/5660) by [kpeiruza](https://github.com/kpeiruza))
|
||||||
|
- **[k8s,k8s/ingress]** Normalize service and router names for ingress. ([#5623](https://github.com/containous/traefik/pull/5623) by [ldez](https://github.com/ldez))
|
||||||
|
- **[logs]** Set proxy protocol logger to DEBUG level ([#5712](https://github.com/containous/traefik/pull/5712) by [mmatur](https://github.com/mmatur))
|
||||||
|
- **[middleware]** fix: add stacktrace when recover. ([#5654](https://github.com/containous/traefik/pull/5654) by [ldez](https://github.com/ldez))
|
||||||
|
- **[tracing]** Let instana/go-sensor handle default agent host ([#5658](https://github.com/containous/traefik/pull/5658) by [sylr](https://github.com/sylr))
|
||||||
|
- **[tracing]** fix: default tracing backend. ([#5717](https://github.com/containous/traefik/pull/5717) by [ldez](https://github.com/ldez))
|
||||||
|
- fix: deep copy of passHostHeader on ServersLoadBalancer. ([#5720](https://github.com/containous/traefik/pull/5720) by [ldez](https://github.com/ldez))
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- **[acme]** Fix acme storage file docker mounting example ([#5633](https://github.com/containous/traefik/pull/5633) by [jansauer](https://github.com/jansauer))
|
||||||
|
- **[acme]** fix incorrect DNS reference ([#5666](https://github.com/containous/traefik/pull/5666) by [oskapt](https://github.com/oskapt))
|
||||||
|
- **[logs]** Clarify unit of duration field in access log ([#5664](https://github.com/containous/traefik/pull/5664) by [Sarke](https://github.com/Sarke))
|
||||||
|
- **[middleware]** Fix Security Headers Doc ([#5706](https://github.com/containous/traefik/pull/5706) by [FlorianPerrot](https://github.com/FlorianPerrot))
|
||||||
|
- **[middleware]** Migration guide: pathprefixstrip migration ([#5600](https://github.com/containous/traefik/pull/5600) by [dduportal](https://github.com/dduportal))
|
||||||
|
- **[middleware]** fix ForwardAuth tls.skipverify examples ([#5683](https://github.com/containous/traefik/pull/5683) by [remche](https://github.com/remche))
|
||||||
|
- **[rules]** Add documentation about backtick for rule definition. ([#5714](https://github.com/containous/traefik/pull/5714) by [ldez](https://github.com/ldez))
|
||||||
|
- **[webui]** Improve documentation of the router rules for API and dashboard ([#5625](https://github.com/containous/traefik/pull/5625) by [dduportal](https://github.com/dduportal))
|
||||||
|
- doc: @ is not authorized in names definition. ([#5734](https://github.com/containous/traefik/pull/5734) by [ldez](https://github.com/ldez))
|
||||||
|
- Remove obsolete v2 remark from README ([#5669](https://github.com/containous/traefik/pull/5669) by [dragetd](https://github.com/dragetd))
|
||||||
|
- Fix spelling mistake: "founded" -> "found" ([#5674](https://github.com/containous/traefik/pull/5674) by [ocanty](https://github.com/ocanty))
|
||||||
|
- fix typo for stripPrefix in tab File (YAML) ([#5694](https://github.com/containous/traefik/pull/5694) by [nalakawula](https://github.com/nalakawula))
|
||||||
|
- Add example for changing the port used by traefik to connect to a service ([#5224](https://github.com/containous/traefik/pull/5224) by [robertbaker](https://github.com/robertbaker))
|
||||||
|
|
||||||
|
**Misc:**
|
||||||
|
- **[logs,middleware]** Cherry pick v1.7 into v2.0 ([#5735](https://github.com/containous/traefik/pull/5735) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||||
|
|
||||||
## [v2.0.2](https://github.com/containous/traefik/tree/v2.0.2) (2019-10-09)
|
## [v2.0.2](https://github.com/containous/traefik/tree/v2.0.2) (2019-10-09)
|
||||||
[All Commits](https://github.com/containous/traefik/compare/v2.0.1...v2.0.2)
|
[All Commits](https://github.com/containous/traefik/compare/v2.0.1...v2.0.2)
|
||||||
|
|
||||||
|
@ -70,6 +109,24 @@
|
||||||
- Add the router priority documentation ([#5481](https://github.com/containous/traefik/pull/5481) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
- Add the router priority documentation ([#5481](https://github.com/containous/traefik/pull/5481) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||||
- Improve the Migration Guide ([#5391](https://github.com/containous/traefik/pull/5391) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
- Improve the Migration Guide ([#5391](https://github.com/containous/traefik/pull/5391) by [jbdoumenjou](https://github.com/jbdoumenjou))
|
||||||
|
|
||||||
|
## [v1.7.18](https://github.com/containous/traefik/tree/v1.7.18) (2019-09-23)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v1.7.17...v1.7.18)
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[go,security]** This version is compiled with [Go 1.12.10](https://groups.google.com/d/msg/golang-announce/cszieYyuL9Q/g4Z7pKaqAgAJ), which fixes a vulnerability in previous versions. See the [CVE](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-16276) about it for more details.
|
||||||
|
|
||||||
|
## [v1.7.17](https://github.com/containous/traefik/tree/v1.7.17) (2019-09-23)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v1.7.16...v1.7.17)
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[logs,middleware]** Avoid closing stdout when the accesslog handler is closed ([#5459](https://github.com/containous/traefik/pull/5459) by [nrwiersma](https://github.com/nrwiersma))
|
||||||
|
- **[middleware]** Actually send header and code during WriteHeader, if needed ([#5404](https://github.com/containous/traefik/pull/5404) by [mpl](https://github.com/mpl))
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- **[k8s]** Add note clarifying client certificate header ([#5362](https://github.com/containous/traefik/pull/5362) by [bradjones1](https://github.com/bradjones1))
|
||||||
|
- **[webui]** Update docs links. ([#5412](https://github.com/containous/traefik/pull/5412) by [ldez](https://github.com/ldez))
|
||||||
|
- Update Traefik image version. ([#5399](https://github.com/containous/traefik/pull/5399) by [ldez](https://github.com/ldez))
|
||||||
|
|
||||||
## [v2.0.0](https://github.com/containous/traefik/tree/v2.0.0) (2019-09-16)
|
## [v2.0.0](https://github.com/containous/traefik/tree/v2.0.0) (2019-09-16)
|
||||||
[All Commits](https://github.com/containous/traefik/compare/v2.0.0-alpha1...v2.0.0)
|
[All Commits](https://github.com/containous/traefik/compare/v2.0.0-alpha1...v2.0.0)
|
||||||
|
|
||||||
|
@ -414,6 +471,29 @@
|
||||||
- fix a service with one server .yaml example ([#5373](https://github.com/containous/traefik/pull/5373) by [zaverden](https://github.com/zaverden))
|
- fix a service with one server .yaml example ([#5373](https://github.com/containous/traefik/pull/5373) by [zaverden](https://github.com/zaverden))
|
||||||
- fix: services configuration documentation. ([#5359](https://github.com/containous/traefik/pull/5359) by [ldez](https://github.com/ldez))
|
- fix: services configuration documentation. ([#5359](https://github.com/containous/traefik/pull/5359) by [ldez](https://github.com/ldez))
|
||||||
|
|
||||||
|
## [v1.7.16](https://github.com/containous/traefik/tree/v1.7.16) (2019-09-13)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v1.7.15...v1.7.16)
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[middleware,websocket]** implement Flusher and Hijacker for codeCatcher ([#5376](https://github.com/containous/traefik/pull/5376) by [mpl](https://github.com/mpl))
|
||||||
|
|
||||||
|
## [v1.7.15](https://github.com/containous/traefik/tree/v1.7.15) (2019-09-12)
|
||||||
|
[All Commits](https://github.com/containous/traefik/compare/v1.7.14...v1.7.15)
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[authentication,k8s/ingress]** Kubernetes support for Auth.HeaderField ([#5235](https://github.com/containous/traefik/pull/5235) by [ErikWegner](https://github.com/ErikWegner))
|
||||||
|
- **[k8s,k8s/ingress]** Finish kubernetes throttling refactoring ([#5269](https://github.com/containous/traefik/pull/5269) by [mpl](https://github.com/mpl))
|
||||||
|
- **[k8s]** Throttle Kubernetes config refresh ([#4716](https://github.com/containous/traefik/pull/4716) by [benweissmann](https://github.com/benweissmann))
|
||||||
|
- **[k8s]** Fix wrong handling of insecure tls auth forward ingress annotation ([#5319](https://github.com/containous/traefik/pull/5319) by [majkrzak](https://github.com/majkrzak))
|
||||||
|
- **[middleware]** error pages: do not buffer response when it's not an error ([#5285](https://github.com/containous/traefik/pull/5285) by [mpl](https://github.com/mpl))
|
||||||
|
- **[tls]** Consider default cert domain in certificate store ([#5353](https://github.com/containous/traefik/pull/5353) by [nrwiersma](https://github.com/nrwiersma))
|
||||||
|
- **[tls]** Add TLS minversion constraint ([#5356](https://github.com/containous/traefik/pull/5356) by [dtomcej](https://github.com/dtomcej))
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- **[acme]** Update Acme doc - Vultr Wildcard & Root ([#5320](https://github.com/containous/traefik/pull/5320) by [ddymko](https://github.com/ddymko))
|
||||||
|
- **[consulcatalog]** Typo in basic auth usersFile label consul-catalog ([#5230](https://github.com/containous/traefik/pull/5230) by [pitan](https://github.com/pitan))
|
||||||
|
- **[logs]** Improve Access Logs Documentation page ([#5238](https://github.com/containous/traefik/pull/5238) by [dduportal](https://github.com/dduportal))
|
||||||
|
|
||||||
## [v2.0.0-rc3](https://github.com/containous/traefik/tree/v2.0.0-rc3) (2019-09-10)
|
## [v2.0.0-rc3](https://github.com/containous/traefik/tree/v2.0.0-rc3) (2019-09-10)
|
||||||
[All Commits](https://github.com/containous/traefik/compare/v2.0.0-rc2...v2.0.0-rc3)
|
[All Commits](https://github.com/containous/traefik/compare/v2.0.0-rc2...v2.0.0-rc3)
|
||||||
|
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -131,7 +131,7 @@ generate-crd:
|
||||||
## Create packages for the release
|
## Create packages for the release
|
||||||
release-packages: generate-webui build-dev-image
|
release-packages: generate-webui build-dev-image
|
||||||
rm -rf dist
|
rm -rf dist
|
||||||
$(DOCKER_RUN_TRAEFIK_NOTTY) goreleaser release --skip-publish
|
$(DOCKER_RUN_TRAEFIK_NOTTY) goreleaser release --skip-publish --timeout="60m"
|
||||||
$(DOCKER_RUN_TRAEFIK_NOTTY) tar cfz dist/traefik-${VERSION}.src.tar.gz \
|
$(DOCKER_RUN_TRAEFIK_NOTTY) tar cfz dist/traefik-${VERSION}.src.tar.gz \
|
||||||
--exclude-vcs \
|
--exclude-vcs \
|
||||||
--exclude .idea \
|
--exclude .idea \
|
||||||
|
|
|
@ -33,7 +33,7 @@ Pointing Traefik at your orchestrator should be the _only_ configuration step yo
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
:warning: Please be aware that the old configurations for Traefik v1.X are NOT compatible with the v2.X config as of now. If you're running v2, please ensure you are using a [v2 configuration](https://docs.traefik.io/).
|
:warning: Please be aware that the old configurations for Traefik v1.x are NOT compatible with the v2.x config as of now. If you're running v2, please ensure you are using a [v2 configuration](https://docs.traefik.io/).
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
|
@ -87,10 +87,11 @@ You can access the simple HTML frontend of Traefik.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
You can find the complete documentation at [https://docs.traefik.io](https://docs.traefik.io).
|
You can find the complete documentation of Traefik v2 at [https://docs.traefik.io](https://docs.traefik.io).
|
||||||
A collection of contributions around Traefik can be found at [https://awesome.traefik.io](https://awesome.traefik.io).
|
|
||||||
|
|
||||||
:warning: If you're testing out v2, please ensure you are using the [v2 documentation](https://docs.traefik.io/).
|
If you are using Traefik v1, you can find the complete documentation at [https://docs.traefik.io/v1.7/](https://docs.traefik.io/v1.7/)
|
||||||
|
|
||||||
|
A collection of contributions around Traefik can be found at [https://awesome.traefik.io](https://awesome.traefik.io).
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
|
|
|
@ -16,8 +16,8 @@ The _dynamic configuration_ contains everything that defines how the requests ar
|
||||||
This configuration can change and is seamlessly hot-reloaded, without any request interruption or connection loss.
|
This configuration can change and is seamlessly hot-reloaded, without any request interruption or connection loss.
|
||||||
|
|
||||||
!!! warning "Incompatible Configuration"
|
!!! warning "Incompatible Configuration"
|
||||||
Please be aware that the old configurations for Traefik v1.X are NOT compatible with the v2.X config as of now.
|
Please be aware that the old configurations for Traefik v1.x are NOT compatible with the v2.x config as of now.
|
||||||
If you're testing out v2, please ensure you are using a v2 configuration.
|
If you are running v2, please ensure you are using a v2 configuration.
|
||||||
|
|
||||||
## The Dynamic Configuration
|
## The Dynamic Configuration
|
||||||
|
|
||||||
|
|
|
@ -382,7 +382,7 @@ ACME certificates can be stored in a JSON file that needs to have a `600` file m
|
||||||
In Docker you can mount either the JSON file, or the folder containing it:
|
In Docker you can mount either the JSON file, or the folder containing it:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run -v "/my/host/acme.json:acme.json" traefik
|
docker run -v "/my/host/acme.json:/acme.json" traefik
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
|
@ -509,7 +509,8 @@ metadata:
|
||||||
spec:
|
spec:
|
||||||
forwardAuth:
|
forwardAuth:
|
||||||
address: https://authserver.com/auth
|
address: https://authserver.com/auth
|
||||||
insecureSkipVerify: true
|
tls:
|
||||||
|
insecureSkipVerify: true
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="Consul Catalog"
|
```yaml tab="Consul Catalog"
|
||||||
|
@ -531,7 +532,8 @@ labels:
|
||||||
[http.middlewares]
|
[http.middlewares]
|
||||||
[http.middlewares.test-auth.forwardAuth]
|
[http.middlewares.test-auth.forwardAuth]
|
||||||
address = "https://authserver.com/auth"
|
address = "https://authserver.com/auth"
|
||||||
insecureSkipVerify: true
|
[http.middlewares.test-auth.forwardAuth.tls]
|
||||||
|
insecureSkipVerify: true
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
|
@ -540,5 +542,6 @@ http:
|
||||||
test-auth:
|
test-auth:
|
||||||
forwardAuth:
|
forwardAuth:
|
||||||
address: "https://authserver.com/auth"
|
address: "https://authserver.com/auth"
|
||||||
insecureSkipVerify: true
|
tls:
|
||||||
|
insecureSkipVerify: true
|
||||||
```
|
```
|
||||||
|
|
|
@ -176,8 +176,8 @@ labels:
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
[http.middlewares]
|
[http.middlewares]
|
||||||
[http.middlewares.testHeader.headers]
|
[http.middlewares.testHeader.headers]
|
||||||
FrameDeny = true
|
frameDeny = true
|
||||||
SSLRedirect = true
|
sslRedirect = true
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
|
@ -185,8 +185,8 @@ http:
|
||||||
middlewares:
|
middlewares:
|
||||||
testHeader:
|
testHeader:
|
||||||
headers:
|
headers:
|
||||||
FrameDeny: true
|
frameDeny: true
|
||||||
SSLRedirect: true
|
sslRedirect: true
|
||||||
```
|
```
|
||||||
|
|
||||||
### CORS Headers
|
### CORS Headers
|
||||||
|
|
|
@ -245,7 +245,10 @@ PassTLSClientCert can add two headers to the request:
|
||||||
- `X-Forwarded-Tls-Client-Cert-Info` that contains all the selected certificate information in an escaped string.
|
- `X-Forwarded-Tls-Client-Cert-Info` that contains all the selected certificate information in an escaped string.
|
||||||
|
|
||||||
!!! info
|
!!! info
|
||||||
The headers are filled with escaped string so it can be safely placed inside a URL query.
|
|
||||||
|
* The headers are filled with escaped string so it can be safely placed inside a URL query.
|
||||||
|
* These options only work accordingly to the [MutualTLS configuration](../https/tls.md#client-authentication-mtls).
|
||||||
|
That is to say, only the certificates that match the `clientAuth.clientAuthType` policy are passed.
|
||||||
|
|
||||||
In the following example, you can see a complete certificate. We will use each part of it to explain the middleware options.
|
In the following example, you can see a complete certificate. We will use each part of it to explain the middleware options.
|
||||||
|
|
||||||
|
|
|
@ -3,13 +3,13 @@
|
||||||
How to Migrate from Traefik v1 to Traefik v2.
|
How to Migrate from Traefik v1 to Traefik v2.
|
||||||
{: .subtitle }
|
{: .subtitle }
|
||||||
|
|
||||||
The version 2 of Traefik introduces a number of breaking changes,
|
The version 2 of Traefik introduces a number of breaking changes,
|
||||||
which require one to update their configuration when they migrate from v1 to v2.
|
which require one to update their configuration when they migrate from v1 to v2.
|
||||||
The goal of this page is to recapitulate all of these changes, and in particular to give examples,
|
The goal of this page is to recapitulate all of these changes, and in particular to give examples,
|
||||||
feature by feature, of how the configuration looked like in v1, and how it now looks like in v2.
|
feature by feature, of how the configuration looked like in v1, and how it now looks like in v2.
|
||||||
|
|
||||||
!!! info "Migration Helper"
|
!!! info "Migration Helper"
|
||||||
|
|
||||||
We created a tool to help during the migration: [traefik-migration-tool](https://github.com/containous/traefik-migration-tool)
|
We created a tool to help during the migration: [traefik-migration-tool](https://github.com/containous/traefik-migration-tool)
|
||||||
|
|
||||||
This tool allows to:
|
This tool allows to:
|
||||||
|
@ -32,7 +32,7 @@ Then any router can refer to an instance of the wanted middleware.
|
||||||
!!! example "One frontend with basic auth and one backend, become one router, one service, and one basic auth middleware."
|
!!! example "One frontend with basic auth and one backend, become one router, one service, and one basic auth middleware."
|
||||||
|
|
||||||
!!! info "v1"
|
!!! info "v1"
|
||||||
|
|
||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
labels:
|
labels:
|
||||||
- "traefik.frontend.rule=Host:test.localhost;PathPrefix:/test"
|
- "traefik.frontend.rule=Host:test.localhost;PathPrefix:/test"
|
||||||
|
@ -92,9 +92,9 @@ Then any router can refer to an instance of the wanted middleware.
|
||||||
[backends.backend1.loadBalancer]
|
[backends.backend1.loadBalancer]
|
||||||
method = "wrr"
|
method = "wrr"
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! info "v2"
|
!!! info "v2"
|
||||||
|
|
||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
labels:
|
labels:
|
||||||
- "traefik.http.routers.router0.rule=Host(`bar.com`) && PathPrefix(`/test`)"
|
- "traefik.http.routers.router0.rule=Host(`bar.com`) && PathPrefix(`/test`)"
|
||||||
|
@ -103,7 +103,7 @@ Then any router can refer to an instance of the wanted middleware.
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="K8s IngressRoute"
|
```yaml tab="K8s IngressRoute"
|
||||||
# The definitions below require the definitions for the Middleware and IngressRoute kinds.
|
# The definitions below require the definitions for the Middleware and IngressRoute kinds.
|
||||||
# https://docs.traefik.io/v2.0/providers/kubernetes-crd/#traefik-ingressroute-definition
|
# https://docs.traefik.io/v2.0/providers/kubernetes-crd/#traefik-ingressroute-definition
|
||||||
apiVersion: traefik.containo.us/v1alpha1
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
kind: Middleware
|
kind: Middleware
|
||||||
|
@ -155,7 +155,7 @@ Then any router can refer to an instance of the wanted middleware.
|
||||||
[http.middlewares]
|
[http.middlewares]
|
||||||
[http.middlewares.auth.basicAuth]
|
[http.middlewares.auth.basicAuth]
|
||||||
users = [
|
users = [
|
||||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
@ -184,7 +184,7 @@ Then any router can refer to an instance of the wanted middleware.
|
||||||
- "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"
|
- "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"
|
||||||
```
|
```
|
||||||
|
|
||||||
## TLS configuration is now dynamic, per router.
|
## TLS Configuration Is Now Dynamic, per Router.
|
||||||
|
|
||||||
TLS parameters used to be specified in the static configuration, as an entryPoint field.
|
TLS parameters used to be specified in the static configuration, as an entryPoint field.
|
||||||
With Traefik v2, a new dynamic TLS section at the root contains all the desired TLS configurations.
|
With Traefik v2, a new dynamic TLS section at the root contains all the desired TLS configurations.
|
||||||
|
@ -214,13 +214,13 @@ Then, a [router's TLS field](../routing/routers/index.md#tls) can refer to one o
|
||||||
certFile = "path/to/my.cert"
|
certFile = "path/to/my.cert"
|
||||||
keyFile = "path/to/my.key"
|
keyFile = "path/to/my.key"
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--entryPoints='Name:web-secure Address::443 TLS:path/to/my.cert,path/to/my.key TLS.MinVersion:VersionTLS12 TLS.CipherSuites:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256'
|
--entryPoints='Name:web-secure Address::443 TLS:path/to/my.cert,path/to/my.key TLS.MinVersion:VersionTLS12 TLS.CipherSuites:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256'
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! info "v2"
|
!!! info "v2"
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
# dynamic configuration
|
# dynamic configuration
|
||||||
[http.routers]
|
[http.routers]
|
||||||
|
@ -250,7 +250,7 @@ Then, a [router's TLS field](../routing/routers/index.md#tls) can refer to one o
|
||||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
http:
|
http:
|
||||||
routers:
|
routers:
|
||||||
|
@ -275,9 +275,9 @@ Then, a [router's TLS field](../routing/routers/index.md#tls) can refer to one o
|
||||||
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
|
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
|
||||||
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="K8s IngressRoute"
|
```yaml tab="K8s IngressRoute"
|
||||||
# The definitions below require the definitions for the TLSOption and IngressRoute kinds.
|
# The definitions below require the definitions for the TLSOption and IngressRoute kinds.
|
||||||
# https://docs.traefik.io/v2.0/providers/kubernetes-crd/#traefik-ingressroute-definition
|
# https://docs.traefik.io/v2.0/providers/kubernetes-crd/#traefik-ingressroute-definition
|
||||||
apiVersion: traefik.containo.us/v1alpha1
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
kind: TLSOption
|
kind: TLSOption
|
||||||
|
@ -310,11 +310,11 @@ Then, a [router's TLS field](../routing/routers/index.md#tls) can refer to one o
|
||||||
- name: whoami
|
- name: whoami
|
||||||
port: 80
|
port: 80
|
||||||
tls:
|
tls:
|
||||||
options:
|
options:
|
||||||
name: mytlsoption
|
name: mytlsoption
|
||||||
namespace: default
|
namespace: default
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
labels:
|
labels:
|
||||||
# myTLSOptions must be defined by another provider, in this instance in the File Provider.
|
# myTLSOptions must be defined by another provider, in this instance in the File Provider.
|
||||||
|
@ -322,7 +322,7 @@ Then, a [router's TLS field](../routing/routers/index.md#tls) can refer to one o
|
||||||
- "traefik.http.routers.router0.tls.options=myTLSOptions@file"
|
- "traefik.http.routers.router0.tls.options=myTLSOptions@file"
|
||||||
```
|
```
|
||||||
|
|
||||||
## HTTP to HTTPS Redirection is now configured on Routers
|
## HTTP to HTTPS Redirection Is Now Configured on Routers
|
||||||
|
|
||||||
Previously on Traefik v1, the redirection was applied on an entry point or on a frontend.
|
Previously on Traefik v1, the redirection was applied on an entry point or on a frontend.
|
||||||
With Traefik v2 it is applied on a [Router](../routing/routers/index.md).
|
With Traefik v2 it is applied on a [Router](../routing/routers/index.md).
|
||||||
|
@ -350,14 +350,14 @@ To apply a redirection, one of the redirect middlewares, [RedirectRegex](../midd
|
||||||
certFile = "examples/traefik.crt"
|
certFile = "examples/traefik.crt"
|
||||||
keyFile = "examples/traefik.key"
|
keyFile = "examples/traefik.key"
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--entrypoints=Name:web Address::80 Redirect.EntryPoint:web-secure
|
--entrypoints=Name:web Address::80 Redirect.EntryPoint:web-secure
|
||||||
--entryPoints='Name:web-secure Address::443 TLS:path/to/my.cert,path/to/my.key'
|
--entryPoints='Name:web-secure Address::443 TLS:path/to/my.cert,path/to/my.key'
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! info "v2"
|
!!! info "v2"
|
||||||
|
|
||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
labels:
|
labels:
|
||||||
- traefik.http.routers.web.rule=Host(`foo.com`)
|
- traefik.http.routers.web.rule=Host(`foo.com`)
|
||||||
|
@ -454,9 +454,9 @@ To apply a redirection, one of the redirect middlewares, [RedirectRegex](../midd
|
||||||
|
|
||||||
[[tls.certificates]]
|
[[tls.certificates]]
|
||||||
certFile = "/path/to/domain.cert"
|
certFile = "/path/to/domain.cert"
|
||||||
keyFile = "/path/to/domain.key"
|
keyFile = "/path/to/domain.key"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
## static configuration
|
## static configuration
|
||||||
# traefik.yml
|
# traefik.yml
|
||||||
|
@ -506,7 +506,139 @@ To apply a redirection, one of the redirect middlewares, [RedirectRegex](../midd
|
||||||
certificates:
|
certificates:
|
||||||
- certFile: /app/certs/server/server.pem
|
- certFile: /app/certs/server/server.pem
|
||||||
keyFile: /app/certs/server/server.pem
|
keyFile: /app/certs/server/server.pem
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Strip and Rewrite Path Prefixes
|
||||||
|
|
||||||
|
With the new core notions of v2 (introduced earlier in the section
|
||||||
|
["Frontends and Backends Are Dead... Long Live Routers, Middlewares, and Services"](#frontends-and-backends-are-dead-long-live-routers-middlewares-and-services)),
|
||||||
|
transforming the URL path prefix of incoming requests is configured with [middlewares](../../middlewares/overview/),
|
||||||
|
after the routing step with [router rule `PathPrefix`](https://docs.traefik.io/v2.0/routing/routers/#rule).
|
||||||
|
|
||||||
|
Use Case: Incoming requests to `http://company.org/admin` are forwarded to the webapplication "admin",
|
||||||
|
with the path `/admin` stripped, e.g. to `http://<IP>:<port>/`. In this case, you must:
|
||||||
|
|
||||||
|
* First, configure a router named `admin` with a rule matching at least the path prefix with the `PathPrefix` keyword,
|
||||||
|
* Then, define a middlware of type [`stripprefix`](../../middlewares/stripprefix/), which remove the prefix `/admin`, associated to the router `admin`.
|
||||||
|
|
||||||
|
!!! example "Strip Path Prefix When Forwarding to Backend"
|
||||||
|
|
||||||
|
!!! info "v1"
|
||||||
|
|
||||||
|
```yaml tab="Docker"
|
||||||
|
labels:
|
||||||
|
- "traefik.frontend.rule=Host:company.org;PathPrefixStrip:/admin"
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Kubernetes Ingress"
|
||||||
|
apiVersion: networking.k8s.io/v1beta1
|
||||||
|
kind: Ingress
|
||||||
|
metadata:
|
||||||
|
name: traefik
|
||||||
|
annotations:
|
||||||
|
kubernetes.io/ingress.class: traefik
|
||||||
|
traefik.ingress.kubernetes.io/rule-type: PathPrefixStrip
|
||||||
|
spec:
|
||||||
|
rules:
|
||||||
|
- host: company.org
|
||||||
|
http:
|
||||||
|
paths:
|
||||||
|
- path: /admin
|
||||||
|
backend:
|
||||||
|
serviceName: admin-svc
|
||||||
|
servicePort: admin
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
[frontends.admin]
|
||||||
|
[frontends.admin.routes.admin_1]
|
||||||
|
rule = "Host:company.org;PathPrefixStrip:/admin"
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! info "v2"
|
||||||
|
|
||||||
|
```yaml tab="Docker"
|
||||||
|
labels:
|
||||||
|
- "traefik.http.routers.admin.rule=Host(`company.org`) && PathPrefix(`/admin`)"
|
||||||
|
- "traefik.http.middlewares.admin-stripprefix.stripprefix.prefixes=/admin"
|
||||||
|
- "traefik.http.routers.web.middlewares=admin-stripprefix@docker"
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Kubernetes IngressRoute"
|
||||||
|
---
|
||||||
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
|
kind: IngressRoute
|
||||||
|
metadata:
|
||||||
|
name: http-redirect-ingressRoute
|
||||||
|
namespace: admin-web
|
||||||
|
spec:
|
||||||
|
entryPoints:
|
||||||
|
- web
|
||||||
|
routes:
|
||||||
|
- match: Host(`company.org`) && PathPrefix(`/admin`)
|
||||||
|
kind: Rule
|
||||||
|
services:
|
||||||
|
- name: admin-svc
|
||||||
|
port: admin
|
||||||
|
middlewares:
|
||||||
|
- name: admin-stripprefix
|
||||||
|
---
|
||||||
|
kind: Middleware
|
||||||
|
metadata:
|
||||||
|
name: admin-stripprefix
|
||||||
|
spec:
|
||||||
|
stripPrefix:
|
||||||
|
prefixes:
|
||||||
|
- /admin
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
## Dynamic configuration
|
||||||
|
# dynamic-conf.toml
|
||||||
|
|
||||||
|
[http.routers.router1]
|
||||||
|
rule = "Host(`company.org`) && PathPrefix(`/admin`)"
|
||||||
|
service = "admin-svc"
|
||||||
|
entrypoints = ["web"]
|
||||||
|
middlewares = ["admin-stripprefix"]
|
||||||
|
|
||||||
|
[http.middlewares]
|
||||||
|
[http.middlewares.admin-stripprefix.stripPrefix]
|
||||||
|
prefixes = ["/admin"]
|
||||||
|
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
## Dynamic Configuration
|
||||||
|
# dynamic-conf.yml
|
||||||
|
|
||||||
|
# As YAML Configuration File
|
||||||
|
http:
|
||||||
|
routers:
|
||||||
|
admin:
|
||||||
|
service: admin-svc
|
||||||
|
middlewares:
|
||||||
|
- "admin-stripprefix"
|
||||||
|
rule: "Host(`company.org`) && PathPrefix(`/admin`)"
|
||||||
|
|
||||||
|
middlewares:
|
||||||
|
admin-stripprefix:
|
||||||
|
stripPrefix:
|
||||||
|
prefixes:
|
||||||
|
- "/admin"
|
||||||
|
|
||||||
|
# ...
|
||||||
|
```
|
||||||
|
|
||||||
|
??? question "What About Other Path Transformations?"
|
||||||
|
|
||||||
|
Instead of removing the path prefix with the [`stripprefix` middleware](../../middlewares/stripprefix/), you can also:
|
||||||
|
|
||||||
|
* Add a path prefix with the [`addprefix` middleware](../../middlewares/addprefix/)
|
||||||
|
* Replace the complete path of the request with the [`replacepath` middleware](../../middlewares/replacepath/)
|
||||||
|
* ReplaceRewrite path using Regexp with the [`replacepathregex` middleware](../../middlewares/replacepathregex/)
|
||||||
|
* And a lot more on the [`middlewares` page](../../middlewares/overview/)
|
||||||
|
|
||||||
## ACME (LetsEncrypt)
|
## ACME (LetsEncrypt)
|
||||||
|
|
||||||
|
@ -536,7 +668,7 @@ To apply a redirection, one of the redirect middlewares, [RedirectRegex](../midd
|
||||||
[acme.httpChallenge]
|
[acme.httpChallenge]
|
||||||
entryPoint = "web"
|
entryPoint = "web"
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--defaultentrypoints=web-secure,web
|
--defaultentrypoints=web-secure,web
|
||||||
--entryPoints=Name:web Address::80 Redirect.EntryPoint:web-secure
|
--entryPoints=Name:web Address::80 Redirect.EntryPoint:web-secure
|
||||||
|
@ -547,9 +679,9 @@ To apply a redirection, one of the redirect middlewares, [RedirectRegex](../midd
|
||||||
--acme.onHostRule=true
|
--acme.onHostRule=true
|
||||||
--acme.httpchallenge.entrypoint=http
|
--acme.httpchallenge.entrypoint=http
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! info "v2"
|
!!! info "v2"
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
# static configuration
|
# static configuration
|
||||||
[entryPoints]
|
[entryPoints]
|
||||||
|
@ -566,7 +698,7 @@ To apply a redirection, one of the redirect middlewares, [RedirectRegex](../midd
|
||||||
# used during the challenge
|
# used during the challenge
|
||||||
entryPoint = "web"
|
entryPoint = "web"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
entryPoints:
|
entryPoints:
|
||||||
web:
|
web:
|
||||||
|
@ -583,8 +715,8 @@ To apply a redirection, one of the redirect middlewares, [RedirectRegex](../midd
|
||||||
httpChallenge:
|
httpChallenge:
|
||||||
# used during the challenge
|
# used during the challenge
|
||||||
entryPoint: web
|
entryPoint: web
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--entryPoints.web.address=":80"
|
--entryPoints.web.address=":80"
|
||||||
--entryPoints.websecure.address=":443"
|
--entryPoints.websecure.address=":443"
|
||||||
|
@ -601,7 +733,7 @@ There is no more log configuration at the root level.
|
||||||
!!! example "Simple log configuration"
|
!!! example "Simple log configuration"
|
||||||
|
|
||||||
!!! info "v1"
|
!!! info "v1"
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
# static configuration
|
# static configuration
|
||||||
logLevel = "DEBUG"
|
logLevel = "DEBUG"
|
||||||
|
@ -610,15 +742,15 @@ There is no more log configuration at the root level.
|
||||||
filePath = "/path/to/traefik.log"
|
filePath = "/path/to/traefik.log"
|
||||||
format = "json"
|
format = "json"
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--logLevel="DEBUG"
|
--logLevel="DEBUG"
|
||||||
--traefikLog.filePath="/path/to/traefik.log"
|
--traefikLog.filePath="/path/to/traefik.log"
|
||||||
--traefikLog.format="json"
|
--traefikLog.format="json"
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! info "v2"
|
!!! info "v2"
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
# static configuration
|
# static configuration
|
||||||
[log]
|
[log]
|
||||||
|
@ -626,15 +758,15 @@ There is no more log configuration at the root level.
|
||||||
filePath = "/path/to/log-file.log"
|
filePath = "/path/to/log-file.log"
|
||||||
format = "json"
|
format = "json"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
# static configuration
|
# static configuration
|
||||||
log:
|
log:
|
||||||
level: DEBUG
|
level: DEBUG
|
||||||
filePath: /path/to/log-file.log
|
filePath: /path/to/log-file.log
|
||||||
format: json
|
format: json
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--log.level="DEBUG"
|
--log.level="DEBUG"
|
||||||
--log.filePath="/path/to/traefik.log"
|
--log.filePath="/path/to/traefik.log"
|
||||||
|
@ -644,11 +776,11 @@ There is no more log configuration at the root level.
|
||||||
## Tracing
|
## Tracing
|
||||||
|
|
||||||
Traefik v2 retains OpenTracing support. The `backend` root option from the v1 is gone, you just have to set your [tracing configuration](../observability/tracing/overview.md).
|
Traefik v2 retains OpenTracing support. The `backend` root option from the v1 is gone, you just have to set your [tracing configuration](../observability/tracing/overview.md).
|
||||||
|
|
||||||
!!! example "Simple Jaeger tracing configuration"
|
!!! example "Simple Jaeger tracing configuration"
|
||||||
|
|
||||||
!!! info "v1"
|
!!! info "v1"
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
# static configuration
|
# static configuration
|
||||||
[tracing]
|
[tracing]
|
||||||
|
@ -660,18 +792,18 @@ Traefik v2 retains OpenTracing support. The `backend` root option from the v1 is
|
||||||
samplingType = "const"
|
samplingType = "const"
|
||||||
localAgentHostPort = "12.0.0.1:6831"
|
localAgentHostPort = "12.0.0.1:6831"
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.backend="jaeger"
|
--tracing.backend="jaeger"
|
||||||
--tracing.servicename="tracing"
|
--tracing.servicename="tracing"
|
||||||
--tracing.jaeger.localagenthostport="12.0.0.1:6831"
|
--tracing.jaeger.localagenthostport="12.0.0.1:6831"
|
||||||
--tracing.jaeger.samplingparam="1.0"
|
--tracing.jaeger.samplingparam="1.0"
|
||||||
--tracing.jaeger.samplingserverurl="http://12.0.0.1:5778/sampling"
|
--tracing.jaeger.samplingserverurl="http://12.0.0.1:5778/sampling"
|
||||||
--tracing.jaeger.samplingtype="const"
|
--tracing.jaeger.samplingtype="const"
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! info "v2"
|
!!! info "v2"
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
# static configuration
|
# static configuration
|
||||||
[tracing]
|
[tracing]
|
||||||
|
@ -682,7 +814,7 @@ Traefik v2 retains OpenTracing support. The `backend` root option from the v1 is
|
||||||
samplingType = "const"
|
samplingType = "const"
|
||||||
localAgentHostPort = "12.0.0.1:6831"
|
localAgentHostPort = "12.0.0.1:6831"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
# static configuration
|
# static configuration
|
||||||
tracing:
|
tracing:
|
||||||
|
@ -692,8 +824,8 @@ Traefik v2 retains OpenTracing support. The `backend` root option from the v1 is
|
||||||
samplingServerURL: 'http://12.0.0.1:5778/sampling'
|
samplingServerURL: 'http://12.0.0.1:5778/sampling'
|
||||||
samplingType: const
|
samplingType: const
|
||||||
localAgentHostPort: '12.0.0.1:6831'
|
localAgentHostPort: '12.0.0.1:6831'
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--tracing.servicename="tracing"
|
--tracing.servicename="tracing"
|
||||||
--tracing.jaeger.localagenthostport="12.0.0.1:6831"
|
--tracing.jaeger.localagenthostport="12.0.0.1:6831"
|
||||||
|
@ -705,33 +837,33 @@ Traefik v2 retains OpenTracing support. The `backend` root option from the v1 is
|
||||||
## Metrics
|
## Metrics
|
||||||
|
|
||||||
The v2 retains metrics tools and allows metrics to be configured for the entrypoints and/or services.
|
The v2 retains metrics tools and allows metrics to be configured for the entrypoints and/or services.
|
||||||
For a basic configuration, the [metrics configuration](../observability/metrics/overview.md) remains the same.
|
For a basic configuration, the [metrics configuration](../observability/metrics/overview.md) remains the same.
|
||||||
|
|
||||||
!!! example "Simple Prometheus metrics configuration"
|
!!! example "Simple Prometheus metrics configuration"
|
||||||
|
|
||||||
!!! info "v1"
|
!!! info "v1"
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
# static configuration
|
# static configuration
|
||||||
[metrics.prometheus]
|
[metrics.prometheus]
|
||||||
buckets = [0.1,0.3,1.2,5.0]
|
buckets = [0.1,0.3,1.2,5.0]
|
||||||
entryPoint = "traefik"
|
entryPoint = "traefik"
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--metrics.prometheus.buckets=[0.1,0.3,1.2,5.0]
|
--metrics.prometheus.buckets=[0.1,0.3,1.2,5.0]
|
||||||
--metrics.prometheus.entrypoint="traefik"
|
--metrics.prometheus.entrypoint="traefik"
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! info "v2"
|
!!! info "v2"
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
# static configuration
|
# static configuration
|
||||||
[metrics.prometheus]
|
[metrics.prometheus]
|
||||||
buckets = [0.1,0.3,1.2,5.0]
|
buckets = [0.1,0.3,1.2,5.0]
|
||||||
entryPoint = "metrics"
|
entryPoint = "metrics"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
# static configuration
|
# static configuration
|
||||||
metrics:
|
metrics:
|
||||||
|
@ -742,22 +874,22 @@ For a basic configuration, the [metrics configuration](../observability/metrics/
|
||||||
- 1.2
|
- 1.2
|
||||||
- 5
|
- 5
|
||||||
entryPoint: metrics
|
entryPoint: metrics
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--metrics.prometheus.buckets=[0.1,0.3,1.2,5.0]
|
--metrics.prometheus.buckets=[0.1,0.3,1.2,5.0]
|
||||||
--metrics.prometheus.entrypoint="metrics"
|
--metrics.prometheus.entrypoint="metrics"
|
||||||
```
|
```
|
||||||
|
|
||||||
## No more root level key/values
|
## No More Root Level Key/Values
|
||||||
|
|
||||||
To avoid any source of confusion, there are no more configuration at the root level.
|
To avoid any source of confusion, there are no more configuration at the root level.
|
||||||
Each root item has been moved to a related section or removed.
|
Each root item has been moved to a related section or removed.
|
||||||
|
|
||||||
!!! example "From root to dedicated section"
|
!!! example "From root to dedicated section"
|
||||||
|
|
||||||
!!! info "v1"
|
!!! info "v1"
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
# static configuration
|
# static configuration
|
||||||
checkNewVersion = false
|
checkNewVersion = false
|
||||||
|
@ -772,7 +904,7 @@ Each root item has been moved to a related section or removed.
|
||||||
defaultEntryPoints = ["web", "web-secure"]
|
defaultEntryPoints = ["web", "web-secure"]
|
||||||
keepTrailingSlash = false
|
keepTrailingSlash = false
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--checknewversion=false
|
--checknewversion=false
|
||||||
--sendanonymoususage=true
|
--sendanonymoususage=true
|
||||||
|
@ -786,9 +918,9 @@ Each root item has been moved to a related section or removed.
|
||||||
--defaultentrypoints="web","web-secure"
|
--defaultentrypoints="web","web-secure"
|
||||||
--keeptrailingslash=true
|
--keeptrailingslash=true
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! info "v2"
|
!!! info "v2"
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
# static configuration
|
# static configuration
|
||||||
[global]
|
[global]
|
||||||
|
@ -804,9 +936,9 @@ Each root item has been moved to a related section or removed.
|
||||||
maxIdleConnsPerHost = 42
|
maxIdleConnsPerHost = 42
|
||||||
|
|
||||||
[providers]
|
[providers]
|
||||||
providersThrottleDuration = 42
|
providersThrottleDuration = 42
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
# static configuration
|
# static configuration
|
||||||
global:
|
global:
|
||||||
|
@ -824,8 +956,8 @@ Each root item has been moved to a related section or removed.
|
||||||
|
|
||||||
providers:
|
providers:
|
||||||
providersThrottleDuration: 42
|
providersThrottleDuration: 42
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--global.checknewversion=true
|
--global.checknewversion=true
|
||||||
--global.sendanonymoususage=true
|
--global.sendanonymoususage=true
|
||||||
|
@ -835,7 +967,7 @@ Each root item has been moved to a related section or removed.
|
||||||
--serverstransport.maxidleconnsperhost=42
|
--serverstransport.maxidleconnsperhost=42
|
||||||
--providers.providersthrottleduration=42
|
--providers.providersthrottleduration=42
|
||||||
```
|
```
|
||||||
|
|
||||||
## Dashboard
|
## Dashboard
|
||||||
|
|
||||||
You need to activate the API to access the [dashboard](../operations/dashboard.md).
|
You need to activate the API to access the [dashboard](../operations/dashboard.md).
|
||||||
|
@ -847,11 +979,11 @@ As the dashboard access is now secured by default you can either:
|
||||||
!!! info "Dashboard with k8s and dedicated router"
|
!!! info "Dashboard with k8s and dedicated router"
|
||||||
|
|
||||||
As `api@internal` is not a Kubernetes service, you have to use the file provider or the `insecure` API option.
|
As `api@internal` is not a Kubernetes service, you have to use the file provider or the `insecure` API option.
|
||||||
|
|
||||||
!!! example "Activate and access the dashboard"
|
!!! example "Activate and access the dashboard"
|
||||||
|
|
||||||
!!! info "v1"
|
!!! info "v1"
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
## static configuration
|
## static configuration
|
||||||
# traefik.toml
|
# traefik.toml
|
||||||
|
@ -868,14 +1000,14 @@ As the dashboard access is now secured by default you can either:
|
||||||
[api]
|
[api]
|
||||||
entryPoint = "web-secure"
|
entryPoint = "web-secure"
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--entryPoints='Name:web-secure Address::443 TLS Auth.Basic.Users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/'
|
--entryPoints='Name:web-secure Address::443 TLS Auth.Basic.Users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/'
|
||||||
--api
|
--api
|
||||||
```
|
```
|
||||||
|
|
||||||
!!! info "v2"
|
!!! info "v2"
|
||||||
|
|
||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
# dynamic configuration
|
# dynamic configuration
|
||||||
labels:
|
labels:
|
||||||
|
@ -916,7 +1048,7 @@ As the dashboard access is now secured by default you can either:
|
||||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
|
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
## static configuration
|
## static configuration
|
||||||
# traefik.yaml
|
# traefik.yaml
|
||||||
|
@ -952,8 +1084,8 @@ As the dashboard access is now secured by default you can either:
|
||||||
basicAuth:
|
basicAuth:
|
||||||
users:
|
users:
|
||||||
- 'test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/'
|
- 'test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Providers
|
## Providers
|
||||||
|
|
||||||
Supported [providers](../providers/overview.md), for now:
|
Supported [providers](../providers/overview.md), for now:
|
||||||
|
@ -976,7 +1108,7 @@ Supported [providers](../providers/overview.md), for now:
|
||||||
* [x] Rest
|
* [x] Rest
|
||||||
* [ ] Zookeeper
|
* [ ] Zookeeper
|
||||||
|
|
||||||
## Some Tips You Should Known
|
## Some Tips You Should Know
|
||||||
|
|
||||||
* Different sources of static configuration (file, CLI flags, ...) cannot be [mixed](../getting-started/configuration-overview.md#the-static-configuration).
|
* Different sources of static configuration (file, CLI flags, ...) cannot be [mixed](../getting-started/configuration-overview.md#the-static-configuration).
|
||||||
* Now, configuration elements can be referenced between different providers by using the provider namespace notation: `@<provider>`.
|
* Now, configuration elements can be referenced between different providers by using the provider namespace notation: `@<provider>`.
|
||||||
|
|
|
@ -180,7 +180,7 @@ accessLog:
|
||||||
|-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|-------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `StartUTC` | The time at which request processing started. |
|
| `StartUTC` | The time at which request processing started. |
|
||||||
| `StartLocal` | The local time at which request processing started. |
|
| `StartLocal` | The local time at which request processing started. |
|
||||||
| `Duration` | The total time taken by processing the response, including the origin server's time but not the log writing time. |
|
| `Duration` | The total time taken (in nanoseconds) by processing the response, including the origin server's time but not the log writing time. |
|
||||||
| `FrontendName` | The name of the Traefik frontend. |
|
| `FrontendName` | The name of the Traefik frontend. |
|
||||||
| `BackendName` | The name of the Traefik backend. |
|
| `BackendName` | The name of the Traefik backend. |
|
||||||
| `BackendURL` | The URL of the Traefik backend. |
|
| `BackendURL` | The URL of the Traefik backend. |
|
||||||
|
|
4
docs/content/operations/.markdownlint.json
Normal file
4
docs/content/operations/.markdownlint.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"extends": "../../.markdownlint.json",
|
||||||
|
"MD046": false
|
||||||
|
}
|
|
@ -14,7 +14,7 @@ In production, it should be at least secured by authentication and authorization
|
||||||
A good sane default (non exhaustive) set of recommendations
|
A good sane default (non exhaustive) set of recommendations
|
||||||
would be to apply the following protection mechanisms:
|
would be to apply the following protection mechanisms:
|
||||||
|
|
||||||
* At the transport level:
|
* At the transport level:
|
||||||
NOT publicly exposing the API's port,
|
NOT publicly exposing the API's port,
|
||||||
keeping it restricted to internal networks
|
keeping it restricted to internal networks
|
||||||
(as in the [principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege), applied to networks).
|
(as in the [principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege), applied to networks).
|
||||||
|
@ -23,13 +23,16 @@ would be to apply the following protection mechanisms:
|
||||||
|
|
||||||
If you enable the API, a new special `service` named `api@internal` is created and can then be referenced in a router.
|
If you enable the API, a new special `service` named `api@internal` is created and can then be referenced in a router.
|
||||||
|
|
||||||
To enable the API handler:
|
To enable the API handler, use the following option on the
|
||||||
|
[static configuration](../getting-started/configuration-overview.md#the-static-configuration):
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
|
# Static Configuration
|
||||||
[api]
|
[api]
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
|
# Static Configuration
|
||||||
api: {}
|
api: {}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -37,11 +40,13 @@ api: {}
|
||||||
--api=true
|
--api=true
|
||||||
```
|
```
|
||||||
|
|
||||||
And then you will be able to reference it like this:
|
And then define a routing configuration on Traefik itself with the
|
||||||
|
[dynamic configuration](../getting-started/configuration-overview.md#the-dynamic-configuration):
|
||||||
|
|
||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
|
# Dynamic Configuration
|
||||||
labels:
|
labels:
|
||||||
- "traefik.http.routers.api.rule=PathPrefix(`/api`) || PathPrefix(`/dashboard`)"
|
- "traefik.http.routers.api.rule=Host(`traefik.domain.com`)
|
||||||
- "traefik.http.routers.api.service=api@internal"
|
- "traefik.http.routers.api.service=api@internal"
|
||||||
- "traefik.http.routers.api.middlewares=auth"
|
- "traefik.http.routers.api.middlewares=auth"
|
||||||
- "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
|
- "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
|
||||||
|
@ -57,40 +62,42 @@ labels:
|
||||||
|
|
||||||
```json tab="Marathon"
|
```json tab="Marathon"
|
||||||
"labels": {
|
"labels": {
|
||||||
"traefik.http.routers.api.rule": "PathPrefix(`/api`) || PathPrefix(`/dashboard`)"
|
"traefik.http.routers.api.rule": "Host(`traefik.domain.com`)",
|
||||||
"traefik.http.routers.api.service": "api@internal"
|
"traefik.http.routers.api.service": "api@internal",
|
||||||
"traefik.http.routers.api.middlewares": "auth"
|
"traefik.http.routers.api.middlewares": "auth",
|
||||||
"traefik.http.middlewares.auth.basicauth.users": "test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
|
"traefik.http.middlewares.auth.basicauth.users": "test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="Rancher"
|
```yaml tab="Rancher"
|
||||||
# Declaring the user list
|
# Dynamic Configuration
|
||||||
labels:
|
labels:
|
||||||
- "traefik.http.routers.api.rule=PathPrefix(`/api`) || PathPrefix(`/dashboard`)"
|
- "traefik.http.routers.api.rule=Host(`traefik.domain.com`)
|
||||||
- "traefik.http.routers.api.service=api@internal"
|
- "traefik.http.routers.api.service=api@internal"
|
||||||
- "traefik.http.routers.api.middlewares=auth"
|
- "traefik.http.routers.api.middlewares=auth"
|
||||||
- "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
|
- "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
|
||||||
```
|
```
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
|
# Dynamic Configuration
|
||||||
[http.routers.my-api]
|
[http.routers.my-api]
|
||||||
rule="PathPrefix(`/api`) || PathPrefix(`/dashboard`)"
|
rule="Host(`traefik.domain.com`)
|
||||||
service="api@internal"
|
service="api@internal"
|
||||||
middlewares=["auth"]
|
middlewares=["auth"]
|
||||||
|
|
||||||
[http.middlewares.auth.basicAuth]
|
[http.middlewares.auth.basicAuth]
|
||||||
users = [
|
users = [
|
||||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
|
# Dynamic Configuration
|
||||||
http:
|
http:
|
||||||
routers:
|
routers:
|
||||||
api:
|
api:
|
||||||
rule: PathPrefix(`/api`) || PathPrefix(`/dashboard`)
|
rule: Host(`traefik.domain.com`)
|
||||||
service: api@internal
|
service: api@internal
|
||||||
middlewares:
|
middlewares:
|
||||||
- auth
|
- auth
|
||||||
|
@ -98,10 +105,32 @@ http:
|
||||||
auth:
|
auth:
|
||||||
basicAuth:
|
basicAuth:
|
||||||
users:
|
users:
|
||||||
- "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
|
- "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
|
||||||
- "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"
|
- "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
??? warning "The router's [rule](../../routing/routers#rule) must catch requests for the URI path `/api`"
|
||||||
|
Using an "Host" rule is recommended, by catching all the incoming traffic on this host domain to the API.
|
||||||
|
However, you can also use "path prefix" rule or any combination or rules.
|
||||||
|
|
||||||
|
```bash tab="Host Rule"
|
||||||
|
# Matches http://traefik.domain.com, http://traefik.domain.com/api
|
||||||
|
# or http://traefik.domain.com/hello
|
||||||
|
rule = "Host(`traefik.domain.com`)"
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash tab="Path Prefix Rule"
|
||||||
|
# Matches http://api.traefik.domain.com/api or http://domain.com/api
|
||||||
|
# but does not match http://api.traefik.domain.com/hello
|
||||||
|
rule = "PathPrefix(`/api`)"
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash tab="Combination of Rules"
|
||||||
|
# Matches http://traefik.domain.com/api or http://traefik.domain.com/dashboard
|
||||||
|
# but does not match http://traefik.domain.com/hello
|
||||||
|
rule = "Host(`traefik.domain.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"
|
||||||
|
```
|
||||||
|
|
||||||
### `insecure`
|
### `insecure`
|
||||||
|
|
||||||
Enable the API in `insecure` mode, which means that the API will be available directly on the entryPoint named `traefik`.
|
Enable the API in `insecure` mode, which means that the API will be available directly on the entryPoint named `traefik`.
|
||||||
|
@ -143,6 +172,9 @@ api:
|
||||||
--api.dashboard=true
|
--api.dashboard=true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
!!! warning "With Dashboard enabled, the router [rule](../../routing/routers#rule) must catch requests for both `/api` and `/dashboard`"
|
||||||
|
Please check the [Dashboard documentation](./dashboard.md#dashboard-router-rule) to learn more about this and to get examples.
|
||||||
|
|
||||||
### `debug`
|
### `debug`
|
||||||
|
|
||||||
_Optional, Default=false_
|
_Optional, Default=false_
|
||||||
|
|
|
@ -28,7 +28,8 @@ There are 2 ways to configure and access the dashboard:
|
||||||
|
|
||||||
This is the **recommended** method.
|
This is the **recommended** method.
|
||||||
|
|
||||||
Start by enabling the dashboard by using the following option from [Traefik's API](./api.md):
|
Start by enabling the dashboard by using the following option from [Traefik's API](./api.md)
|
||||||
|
on the [static configuration](../getting-started/configuration-overview.md#the-static-configuration):
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
[api]
|
[api]
|
||||||
|
@ -59,17 +60,103 @@ api:
|
||||||
--api.dashboard=true
|
--api.dashboard=true
|
||||||
```
|
```
|
||||||
|
|
||||||
Then specify a router associated to the service `api@internal` to allow:
|
Then define a routing configuration on Traefik itself,
|
||||||
|
with a router attached to the service `api@internal` in the
|
||||||
|
[dynamic configuration](../getting-started/configuration-overview.md#the-dynamic-configuration),
|
||||||
|
to allow defining:
|
||||||
|
|
||||||
- Defining one or more security features through [middlewares](../middlewares/overview.md)
|
- One or more security features through [middlewares](../middlewares/overview.md)
|
||||||
like authentication ([basicAuth](../middlewares/basicauth.md) , [digestAuth](../middlewares/digestauth.md),
|
like authentication ([basicAuth](../middlewares/basicauth.md) , [digestAuth](../middlewares/digestauth.md),
|
||||||
[forwardAuth](../middlewares/forwardauth.md)) or [whitelisting](../middlewares/ipwhitelist.md).
|
[forwardAuth](../middlewares/forwardauth.md)) or [whitelisting](../middlewares/ipwhitelist.md).
|
||||||
|
|
||||||
- Defining your own [HTTP routing rule](../../routing/routers/#rule) for accessing the dashboard,
|
- A [router rule](#dashboard-router-rule) for accessing the dashboard,
|
||||||
through Traefik itself (sometimes referred as "Traefik-ception").
|
through Traefik itself (sometimes referred as "Traefik-ception").
|
||||||
|
|
||||||
Please visit the ["Configuration" section of the API documentation](./api.md#configuration)
|
??? example "Dashboard Dynamic Configuration Examples"
|
||||||
to learn about configuring a router with the service `api@internal` and enabling the security features.
|
|
||||||
|
```yaml tab="Docker"
|
||||||
|
# Dynamic Configuration
|
||||||
|
labels:
|
||||||
|
- "traefik.http.routers.api.rule=Host(`traefik.domain.com`)
|
||||||
|
- "traefik.http.routers.api.service=api@internal"
|
||||||
|
- "traefik.http.routers.api.middlewares=auth"
|
||||||
|
- "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
|
||||||
|
```
|
||||||
|
|
||||||
|
```json tab="Marathon"
|
||||||
|
"labels": {
|
||||||
|
"traefik.http.routers.api.rule": "Host(`traefik.domain.com`)",
|
||||||
|
"traefik.http.routers.api.service": "api@internal",
|
||||||
|
"traefik.http.routers.api.middlewares": "auth",
|
||||||
|
"traefik.http.middlewares.auth.basicauth.users": "test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Rancher"
|
||||||
|
# Dynamic Configuration
|
||||||
|
labels:
|
||||||
|
- "traefik.http.routers.api.rule=Host(`traefik.domain.com`)
|
||||||
|
- "traefik.http.routers.api.service=api@internal"
|
||||||
|
- "traefik.http.routers.api.middlewares=auth"
|
||||||
|
- "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
# Dynamic Configuration
|
||||||
|
[http.routers.my-api]
|
||||||
|
rule="Host(`traefik.domain.com`)
|
||||||
|
service="api@internal"
|
||||||
|
middlewares=["auth"]
|
||||||
|
|
||||||
|
[http.middlewares.auth.basicAuth]
|
||||||
|
users = [
|
||||||
|
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||||
|
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
# Dynamic Configuration
|
||||||
|
http:
|
||||||
|
routers:
|
||||||
|
api:
|
||||||
|
rule: Host(`traefik.domain.com`)
|
||||||
|
service: api@internal
|
||||||
|
middlewares:
|
||||||
|
- auth
|
||||||
|
middlewares:
|
||||||
|
auth:
|
||||||
|
basicAuth:
|
||||||
|
users:
|
||||||
|
- "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
|
||||||
|
- "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Dashboard Router Rule
|
||||||
|
|
||||||
|
As underlined in the [documentation for the `api.dashboard` option](./api.md#dashboard),
|
||||||
|
the [router rule](../routing/routers/index.md#rule) defined for Traefik must match
|
||||||
|
the path prefixes `/api` and `/dashboard`.
|
||||||
|
|
||||||
|
We recommend to use a "Host Based rule" as ```Host(`traefik.domain.com`)``` to match everything on the host domain,
|
||||||
|
or to make sure that the defined rule captures both prefixes:
|
||||||
|
|
||||||
|
```bash tab="Host Rule"
|
||||||
|
# Matches http://traefik.domain.com/api or http://traefik.domain.com/dashboard
|
||||||
|
rule = "Host(`traefik.domain.com`)"
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash tab="Path Prefix Rule"
|
||||||
|
# Matches http://traefik.domain.com/api , http://domain.com/api or http://traefik.domain.com/dashboard
|
||||||
|
# but does not match http://traefik.domain.com/hello
|
||||||
|
rule = "PathPrefix(`/api`) || PathPrefix(`/dashboard`)"
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash tab="Combination of Rules"
|
||||||
|
# Matches http://traefik.domain.com/api or http://traefik.domain.com/dashboard
|
||||||
|
# but does not match http://traefik.domain.com/hello
|
||||||
|
rule = "Host(`traefik.domain.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"
|
||||||
|
```
|
||||||
|
|
||||||
## Insecure Mode
|
## Insecure Mode
|
||||||
|
|
||||||
|
|
|
@ -128,7 +128,7 @@ Traefik requires access to the docker socket to get its dynamic configuration.
|
||||||
??? info "Resources about Docker's Security"
|
??? info "Resources about Docker's Security"
|
||||||
|
|
||||||
- [KubeCon EU 2018 Keynote, Running with Scissors, from Liz Rice](https://www.youtube.com/watch?v=ltrV-Qmh3oY)
|
- [KubeCon EU 2018 Keynote, Running with Scissors, from Liz Rice](https://www.youtube.com/watch?v=ltrV-Qmh3oY)
|
||||||
- [Don't expose the Docker socket (not even to a container)](https://www.lvh.io/posts/dont-expose-the-docker-socket-not-even-to-a-container.html)
|
- [Don't expose the Docker socket (not even to a container)](https://www.lvh.io/posts/dont-expose-the-docker-socket-not-even-to-a-container/)
|
||||||
- [A thread on Stack Overflow about sharing the `/var/run/docker.sock` file](https://news.ycombinator.com/item?id=17983623)
|
- [A thread on Stack Overflow about sharing the `/var/run/docker.sock` file](https://news.ycombinator.com/item?id=17983623)
|
||||||
- [To DinD or not to DinD](https://blog.loof.fr/2018/01/to-dind-or-not-do-dind.html)
|
- [To DinD or not to DinD](https://blog.loof.fr/2018/01/to-dind-or-not-do-dind.html)
|
||||||
|
|
||||||
|
|
|
@ -580,7 +580,7 @@ Specifies the header name that will be used to store the trace ID.
|
||||||
Settings for Instana. (Default: ```false```)
|
Settings for Instana. (Default: ```false```)
|
||||||
|
|
||||||
`--tracing.instana.localagenthost`:
|
`--tracing.instana.localagenthost`:
|
||||||
Set instana-agent's host that the reporter will used. (Default: ```localhost```)
|
Set instana-agent's host that the reporter will used.
|
||||||
|
|
||||||
`--tracing.instana.localagentport`:
|
`--tracing.instana.localagentport`:
|
||||||
Set instana-agent's port that the reporter will used. (Default: ```42699```)
|
Set instana-agent's port that the reporter will used. (Default: ```42699```)
|
||||||
|
|
|
@ -580,7 +580,7 @@ Specifies the header name that will be used to store the trace ID.
|
||||||
Settings for Instana. (Default: ```false```)
|
Settings for Instana. (Default: ```false```)
|
||||||
|
|
||||||
`TRAEFIK_TRACING_INSTANA_LOCALAGENTHOST`:
|
`TRAEFIK_TRACING_INSTANA_LOCALAGENTHOST`:
|
||||||
Set instana-agent's host that the reporter will used. (Default: ```localhost```)
|
Set instana-agent's host that the reporter will used.
|
||||||
|
|
||||||
`TRAEFIK_TRACING_INSTANA_LOCALAGENTPORT`:
|
`TRAEFIK_TRACING_INSTANA_LOCALAGENTPORT`:
|
||||||
Set instana-agent's port that the reporter will used. (Default: ```42699```)
|
Set instana-agent's port that the reporter will used. (Default: ```42699```)
|
||||||
|
|
|
@ -12,16 +12,16 @@ Attach labels to your containers and let Traefik do the rest!
|
||||||
??? example "Configuring Docker & Deploying / Exposing Services"
|
??? example "Configuring Docker & Deploying / Exposing Services"
|
||||||
|
|
||||||
Enabling the docker provider
|
Enabling the docker provider
|
||||||
|
|
||||||
```toml tab="File (TOML)"
|
```toml tab="File (TOML)"
|
||||||
[providers.docker]
|
[providers.docker]
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
providers:
|
providers:
|
||||||
docker: {}
|
docker: {}
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.docker=true
|
--providers.docker=true
|
||||||
```
|
```
|
||||||
|
@ -37,6 +37,27 @@ Attach labels to your containers and let Traefik do the rest!
|
||||||
- traefik.http.routers.my-container.rule=Host(`mydomain.com`)
|
- traefik.http.routers.my-container.rule=Host(`mydomain.com`)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
??? example "Specify a Custom Port for the Container"
|
||||||
|
|
||||||
|
Forward requests for `http://mydomain.com` to `http://<private IP of container>:12345`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: "3"
|
||||||
|
services:
|
||||||
|
my-container:
|
||||||
|
# ...
|
||||||
|
labels:
|
||||||
|
- traefik.http.routers.my-container.rule=Host(`mydomain.com`)
|
||||||
|
# Tell Traefik to use the port 12345 to connect to `my-container`
|
||||||
|
- traefik.http.services.my-service.loadbalancer.server.port=12345
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! important "Traefik Connecting to the Wrong Port: `HTTP/502 Gateway Error`"
|
||||||
|
By default, Traefik uses the first exposed port of a container.
|
||||||
|
|
||||||
|
Setting the label `traefik.http.services.xxx.loadbalancer.server.port`
|
||||||
|
overrides that behavior.
|
||||||
|
|
||||||
??? example "Configuring Docker Swarm & Deploying / Exposing Services"
|
??? example "Configuring Docker Swarm & Deploying / Exposing Services"
|
||||||
|
|
||||||
Enabling the docker provider (Swarm Mode)
|
Enabling the docker provider (Swarm Mode)
|
||||||
|
@ -49,7 +70,7 @@ Attach labels to your containers and let Traefik do the rest!
|
||||||
endpoint = "tcp://127.0.0.1:2377"
|
endpoint = "tcp://127.0.0.1:2377"
|
||||||
swarmMode = true
|
swarmMode = true
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="File (YAML)"
|
```yaml tab="File (YAML)"
|
||||||
providers:
|
providers:
|
||||||
docker:
|
docker:
|
||||||
|
@ -59,7 +80,7 @@ Attach labels to your containers and let Traefik do the rest!
|
||||||
endpoint: "tcp://127.0.0.1:2375"
|
endpoint: "tcp://127.0.0.1:2375"
|
||||||
swarmMode: true
|
swarmMode: true
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash tab="CLI"
|
```bash tab="CLI"
|
||||||
--providers.docker.endpoint="tcp://127.0.0.1:2375"
|
--providers.docker.endpoint="tcp://127.0.0.1:2375"
|
||||||
--providers.docker.swarmMode=true
|
--providers.docker.swarmMode=true
|
||||||
|
@ -85,7 +106,7 @@ Attach labels to your containers and let Traefik do the rest!
|
||||||
## Routing Configuration
|
## Routing Configuration
|
||||||
|
|
||||||
!!! info "Labels"
|
!!! info "Labels"
|
||||||
|
|
||||||
- Labels are case insensitive.
|
- Labels are case insensitive.
|
||||||
- The complete list of labels can be found in [the reference page](../../reference/dynamic-configuration/docker.md).
|
- The complete list of labels can be found in [the reference page](../../reference/dynamic-configuration/docker.md).
|
||||||
|
|
||||||
|
@ -129,82 +150,84 @@ add labels starting with `traefik.http.routers.<name-of-your-choice>.` and follo
|
||||||
|
|
||||||
For example, to change the rule, you could add the label ```traefik.http.routers.my-container.rule=Host(`mydomain.com`)```.
|
For example, to change the rule, you could add the label ```traefik.http.routers.my-container.rule=Host(`mydomain.com`)```.
|
||||||
|
|
||||||
|
!!! warning "The character `@` is not authorized in the router name `<router_name>`."
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.rule`"
|
??? info "`traefik.http.routers.<router_name>.rule`"
|
||||||
|
|
||||||
See [rule](../routers/index.md#rule) for more information.
|
See [rule](../routers/index.md#rule) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.routers.myrouter.rule=Host(`mydomain.com`)"
|
- "traefik.http.routers.myrouter.rule=Host(`mydomain.com`)"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.entrypoints`"
|
??? info "`traefik.http.routers.<router_name>.entrypoints`"
|
||||||
|
|
||||||
See [entry points](../routers/index.md#entrypoints) for more information.
|
See [entry points](../routers/index.md#entrypoints) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.routers.myrouter.entrypoints=web,websecure"
|
- "traefik.http.routers.myrouter.entrypoints=web,websecure"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.middlewares`"
|
??? info "`traefik.http.routers.<router_name>.middlewares`"
|
||||||
|
|
||||||
See [middlewares](../routers/index.md#middlewares) and [middlewares overview](../../middlewares/overview.md) for more information.
|
See [middlewares](../routers/index.md#middlewares) and [middlewares overview](../../middlewares/overview.md) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.routers.myrouter.middlewares=auth,prefix,cb"
|
- "traefik.http.routers.myrouter.middlewares=auth,prefix,cb"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.service`"
|
??? info "`traefik.http.routers.<router_name>.service`"
|
||||||
|
|
||||||
See [rule](../routers/index.md#service) for more information.
|
See [rule](../routers/index.md#service) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.routers.myrouter.service=myservice"
|
- "traefik.http.routers.myrouter.service=myservice"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.tls`"
|
??? info "`traefik.http.routers.<router_name>.tls`"
|
||||||
|
|
||||||
See [tls](../routers/index.md#tls) for more information.
|
See [tls](../routers/index.md#tls) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.routers.myrouter.tls=true"
|
- "traefik.http.routers.myrouter.tls=true"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.tls.certresolver`"
|
??? info "`traefik.http.routers.<router_name>.tls.certresolver`"
|
||||||
|
|
||||||
See [certResolver](../routers/index.md#certresolver) for more information.
|
See [certResolver](../routers/index.md#certresolver) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.routers.myrouter.tls.certresolver=myresolver"
|
- "traefik.http.routers.myrouter.tls.certresolver=myresolver"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.tls.domains[n].main`"
|
??? info "`traefik.http.routers.<router_name>.tls.domains[n].main`"
|
||||||
|
|
||||||
See [domains](../routers/index.md#domains) for more information.
|
See [domains](../routers/index.md#domains) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.routers.myrouter.tls.domains[0].main=foobar.com"
|
- "traefik.http.routers.myrouter.tls.domains[0].main=foobar.com"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.tls.domains[n].sans`"
|
??? info "`traefik.http.routers.<router_name>.tls.domains[n].sans`"
|
||||||
|
|
||||||
See [domains](../routers/index.md#domains) for more information.
|
See [domains](../routers/index.md#domains) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.routers.myrouter.tls.domains[0].sans=test.foobar.com,dev.foobar.com"
|
- "traefik.http.routers.myrouter.tls.domains[0].sans=test.foobar.com,dev.foobar.com"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.tls.options`"
|
??? info "`traefik.http.routers.<router_name>.tls.options`"
|
||||||
|
|
||||||
See [options](../routers/index.md#options) for more information.
|
See [options](../routers/index.md#options) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.routers.myrouter.tls.options=foobar"
|
- "traefik.http.routers.myrouter.tls.options=foobar"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.priority`"
|
??? info "`traefik.http.routers.<router_name>.priority`"
|
||||||
|
|
||||||
See [options](../routers/index.md#priority) for more information.
|
See [priority](../routers/index.md#priority) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.routers.myrouter.priority=42"
|
- "traefik.http.routers.myrouter.priority=42"
|
||||||
```
|
```
|
||||||
|
@ -217,125 +240,127 @@ add labels starting with `traefik.http.services.<name-of-your-choice>.`, followe
|
||||||
For example, to change the `passHostHeader` behavior,
|
For example, to change the `passHostHeader` behavior,
|
||||||
you'd add the label `traefik.http.services.<name-of-your-choice>.loadbalancer.passhostheader=false`.
|
you'd add the label `traefik.http.services.<name-of-your-choice>.loadbalancer.passhostheader=false`.
|
||||||
|
|
||||||
|
!!! warning "The character `@` is not authorized in the service name `<service_name>`."
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.server.port`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.server.port`"
|
||||||
|
|
||||||
Registers a port.
|
Registers a port.
|
||||||
Useful when the container exposes multiples ports.
|
Useful when the container exposes multiples ports.
|
||||||
|
|
||||||
Mandatory for Docker Swarm.
|
Mandatory for Docker Swarm.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.server.port=8080"
|
- "traefik.http.services.myservice.loadbalancer.server.port=8080"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.server.scheme`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.server.scheme`"
|
||||||
|
|
||||||
Overrides the default scheme.
|
Overrides the default scheme.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.server.scheme=http"
|
- "traefik.http.services.myservice.loadbalancer.server.scheme=http"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.passhostheader`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.passhostheader`"
|
||||||
|
|
||||||
See [pass Host header](../services/index.md#pass-host-header) for more information.
|
See [pass Host header](../services/index.md#pass-host-header) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.passhostheader=true"
|
- "traefik.http.services.myservice.loadbalancer.passhostheader=true"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.headers.<header_name>`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.headers.<header_name>`"
|
||||||
|
|
||||||
See [health check](../services/index.md#health-check) for more information.
|
See [health check](../services/index.md#health-check) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.healthcheck.headers.X-Foo=foobar"
|
- "traefik.http.services.myservice.loadbalancer.healthcheck.headers.X-Foo=foobar"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.hostname`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.hostname`"
|
||||||
|
|
||||||
See [health check](../services/index.md#health-check) for more information.
|
See [health check](../services/index.md#health-check) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.healthcheck.hostname=foobar.com"
|
- "traefik.http.services.myservice.loadbalancer.healthcheck.hostname=foobar.com"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.interval`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.interval`"
|
||||||
|
|
||||||
See [health check](../services/index.md#health-check) for more information.
|
See [health check](../services/index.md#health-check) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.healthcheck.interval=10"
|
- "traefik.http.services.myservice.loadbalancer.healthcheck.interval=10"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.path`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.path`"
|
||||||
|
|
||||||
See [health check](../services/index.md#health-check) for more information.
|
See [health check](../services/index.md#health-check) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.healthcheck.path=/foo"
|
- "traefik.http.services.myservice.loadbalancer.healthcheck.path=/foo"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.port`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.port`"
|
||||||
|
|
||||||
See [health check](../services/index.md#health-check) for more information.
|
See [health check](../services/index.md#health-check) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.healthcheck.port=42"
|
- "traefik.http.services.myservice.loadbalancer.healthcheck.port=42"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.scheme`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.scheme`"
|
||||||
|
|
||||||
See [health check](../services/index.md#health-check) for more information.
|
See [health check](../services/index.md#health-check) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.healthcheck.scheme=http"
|
- "traefik.http.services.myservice.loadbalancer.healthcheck.scheme=http"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.timeout`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.healthcheck.timeout`"
|
||||||
|
|
||||||
See [health check](../services/index.md#health-check) for more information.
|
See [health check](../services/index.md#health-check) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.healthcheck.timeout=10"
|
- "traefik.http.services.myservice.loadbalancer.healthcheck.timeout=10"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky`"
|
||||||
|
|
||||||
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.sticky=true"
|
- "traefik.http.services.myservice.loadbalancer.sticky=true"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.httponly`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.httponly`"
|
||||||
|
|
||||||
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.sticky.cookie.httponly=true"
|
- "traefik.http.services.myservice.loadbalancer.sticky.cookie.httponly=true"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.name`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.name`"
|
||||||
|
|
||||||
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.sticky.cookie.name=foobar"
|
- "traefik.http.services.myservice.loadbalancer.sticky.cookie.name=foobar"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.secure`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.secure`"
|
||||||
|
|
||||||
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.sticky.cookie.secure=true"
|
- "traefik.http.services.myservice.loadbalancer.sticky.cookie.secure=true"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.responseforwarding.flushinterval`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.responseforwarding.flushinterval`"
|
||||||
|
|
||||||
See [response forwarding](../services/index.md#response-forwarding) for more information.
|
See [response forwarding](../services/index.md#response-forwarding) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.services.myservice.loadbalancer.responseforwarding.flushinterval=10"
|
- "traefik.http.services.myservice.loadbalancer.responseforwarding.flushinterval=10"
|
||||||
```
|
```
|
||||||
|
@ -350,6 +375,8 @@ you'd write `traefik.http.middlewares.my-redirect.redirectscheme.scheme=https`.
|
||||||
|
|
||||||
More information about available middlewares in the dedicated [middlewares section](../../middlewares/overview.md).
|
More information about available middlewares in the dedicated [middlewares section](../../middlewares/overview.md).
|
||||||
|
|
||||||
|
!!! warning "The character `@` is not authorized in the middleware name."
|
||||||
|
|
||||||
??? example "Declaring and Referencing a Middleware"
|
??? example "Declaring and Referencing a Middleware"
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
@ -391,73 +418,73 @@ You can declare TCP Routers and/or Services using labels.
|
||||||
#### TCP Routers
|
#### TCP Routers
|
||||||
|
|
||||||
??? info "`traefik.tcp.routers.<router_name>.entrypoints`"
|
??? info "`traefik.tcp.routers.<router_name>.entrypoints`"
|
||||||
|
|
||||||
See [entry points](../routers/index.md#entrypoints_1) for more information.
|
See [entry points](../routers/index.md#entrypoints_1) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.tcp.routers.mytcprouter.entrypoints=ep1,ep2"
|
- "traefik.tcp.routers.mytcprouter.entrypoints=ep1,ep2"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.tcp.routers.<router_name>.rule`"
|
??? info "`traefik.tcp.routers.<router_name>.rule`"
|
||||||
|
|
||||||
See [rule](../routers/index.md#rule_1) for more information.
|
See [rule](../routers/index.md#rule_1) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.tcp.routers.mytcprouter.rule=HostSNI(`myhost.com`)"
|
- "traefik.tcp.routers.mytcprouter.rule=HostSNI(`myhost.com`)"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.tcp.routers.<router_name>.service`"
|
??? info "`traefik.tcp.routers.<router_name>.service`"
|
||||||
|
|
||||||
See [service](../routers/index.md#services) for more information.
|
See [service](../routers/index.md#services) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.tcp.routers.mytcprouter.service=myservice"
|
- "traefik.tcp.routers.mytcprouter.service=myservice"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.tcp.routers.<router_name>.tls`"
|
??? info "`traefik.tcp.routers.<router_name>.tls`"
|
||||||
|
|
||||||
See [TLS](../routers/index.md#tls_1) for more information.
|
See [TLS](../routers/index.md#tls_1) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.tcp.routers.mytcprouter.tls=true"
|
- "traefik.tcp.routers.mytcprouter.tls=true"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.tcp.routers.<router_name>.tls.certresolver`"
|
??? info "`traefik.tcp.routers.<router_name>.tls.certresolver`"
|
||||||
|
|
||||||
See [certResolver](../routers/index.md#certresolver_1) for more information.
|
See [certResolver](../routers/index.md#certresolver_1) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.tcp.routers.mytcprouter.tls.certresolver=myresolver"
|
- "traefik.tcp.routers.mytcprouter.tls.certresolver=myresolver"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.tcp.routers.<router_name>.tls.domains[n].main`"
|
??? info "`traefik.tcp.routers.<router_name>.tls.domains[n].main`"
|
||||||
|
|
||||||
See [domains](../routers/index.md#domains_1) for more information.
|
See [domains](../routers/index.md#domains_1) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.tcp.routers.mytcprouter.tls.domains[0].main=foobar.com"
|
- "traefik.tcp.routers.mytcprouter.tls.domains[0].main=foobar.com"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.tcp.routers.<router_name>.tls.domains[n].sans`"
|
??? info "`traefik.tcp.routers.<router_name>.tls.domains[n].sans`"
|
||||||
|
|
||||||
See [domains](../routers/index.md#domains_1) for more information.
|
See [domains](../routers/index.md#domains_1) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.tcp.routers.mytcprouter.tls.domains[0].sans=test.foobar.com,dev.foobar.com"
|
- "traefik.tcp.routers.mytcprouter.tls.domains[0].sans=test.foobar.com,dev.foobar.com"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.tcp.routers.<router_name>.tls.options`"
|
??? info "`traefik.tcp.routers.<router_name>.tls.options`"
|
||||||
|
|
||||||
See [options](../routers/index.md#options_1) for more information.
|
See [options](../routers/index.md#options_1) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.tcp.routers.mytcprouter.tls.options=mysoptions"
|
- "traefik.tcp.routers.mytcprouter.tls.options=mysoptions"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.tcp.routers.<router_name>.tls.passthrough`"
|
??? info "`traefik.tcp.routers.<router_name>.tls.passthrough`"
|
||||||
|
|
||||||
See [TLS](../routers/index.md#tls_1) for more information.
|
See [TLS](../routers/index.md#tls_1) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.tcp.routers.mytcprouter.tls.passthrough=true"
|
- "traefik.tcp.routers.mytcprouter.tls.passthrough=true"
|
||||||
```
|
```
|
||||||
|
@ -465,17 +492,17 @@ You can declare TCP Routers and/or Services using labels.
|
||||||
#### TCP Services
|
#### TCP Services
|
||||||
|
|
||||||
??? info "`traefik.tcp.services.<service_name>.loadbalancer.server.port`"
|
??? info "`traefik.tcp.services.<service_name>.loadbalancer.server.port`"
|
||||||
|
|
||||||
Registers a port of the application.
|
Registers a port of the application.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.tcp.services.mytcpservice.loadbalancer.server.port=423"
|
- "traefik.tcp.services.mytcpservice.loadbalancer.server.port=423"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? info "`traefik.tcp.services.<service_name>.loadbalancer.terminationdelay`"
|
??? info "`traefik.tcp.services.<service_name>.loadbalancer.terminationdelay`"
|
||||||
|
|
||||||
See [termination delay](../services/index.md#termination-delay) for more information.
|
See [termination delay](../services/index.md#termination-delay) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.tcp.services.mytcpservice.loadbalancer.terminationdelay=100"
|
- "traefik.tcp.services.mytcpservice.loadbalancer.terminationdelay=100"
|
||||||
```
|
```
|
||||||
|
|
|
@ -52,6 +52,8 @@ add labels starting with `traefik.http.routers.{router-name-of-your-choice}.` an
|
||||||
|
|
||||||
For example, to change the routing rule, you could add the label ```"traefik.http.routers.routername.rule": "Host(`mydomain.com`)"```.
|
For example, to change the routing rule, you could add the label ```"traefik.http.routers.routername.rule": "Host(`mydomain.com`)"```.
|
||||||
|
|
||||||
|
!!! warning "The character `@` is not authorized in the router name `<router_name>`."
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.rule`"
|
??? info "`traefik.http.routers.<router_name>.rule`"
|
||||||
|
|
||||||
See [rule](../routers/index.md#rule) for more information.
|
See [rule](../routers/index.md#rule) for more information.
|
||||||
|
@ -126,7 +128,7 @@ For example, to change the routing rule, you could add the label ```"traefik.htt
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.priority`"
|
??? info "`traefik.http.routers.<router_name>.priority`"
|
||||||
|
|
||||||
See [options](../routers/index.md#priority) for more information.
|
See [priority](../routers/index.md#priority) for more information.
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"traefik.http.routers.myrouter.priority": "42"
|
"traefik.http.routers.myrouter.priority": "42"
|
||||||
|
@ -139,6 +141,8 @@ add labels starting with `traefik.http.services.{service-name-of-your-choice}.`,
|
||||||
|
|
||||||
For example, to change the passHostHeader behavior, you'd add the label `"traefik.http.services.servicename.loadbalancer.passhostheader": "false"`.
|
For example, to change the passHostHeader behavior, you'd add the label `"traefik.http.services.servicename.loadbalancer.passhostheader": "false"`.
|
||||||
|
|
||||||
|
!!! warning "The character `@` is not authorized in the service name `<service_name>`."
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.server.port`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.server.port`"
|
||||||
|
|
||||||
Registers a port.
|
Registers a port.
|
||||||
|
@ -268,6 +272,8 @@ For example, to declare a middleware [`redirectscheme`](../../middlewares/redire
|
||||||
|
|
||||||
More information about available middlewares in the dedicated [middlewares section](../../middlewares/overview.md).
|
More information about available middlewares in the dedicated [middlewares section](../../middlewares/overview.md).
|
||||||
|
|
||||||
|
!!! warning "The character `@` is not authorized in the middleware name."
|
||||||
|
|
||||||
??? example "Declaring and Referencing a Middleware"
|
??? example "Declaring and Referencing a Middleware"
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
|
|
@ -57,6 +57,8 @@ To update the configuration of the Router automatically attached to the containe
|
||||||
|
|
||||||
For example, to change the rule, you could add the label ```traefik.http.routers.my-container.rule=Host(`mydomain.com`)```.
|
For example, to change the rule, you could add the label ```traefik.http.routers.my-container.rule=Host(`mydomain.com`)```.
|
||||||
|
|
||||||
|
!!! warning "The character `@` is not authorized in the router name `<router_name>`."
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.rule`"
|
??? info "`traefik.http.routers.<router_name>.rule`"
|
||||||
|
|
||||||
See [rule](../routers/index.md#rule) for more information.
|
See [rule](../routers/index.md#rule) for more information.
|
||||||
|
@ -131,7 +133,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers
|
||||||
|
|
||||||
??? info "`traefik.http.routers.<router_name>.priority`"
|
??? info "`traefik.http.routers.<router_name>.priority`"
|
||||||
|
|
||||||
See [options](../routers/index.md#priority) for more information.
|
See [priority](../routers/index.md#priority) for more information.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- "traefik.http.routers.myrouter.priority=42"
|
- "traefik.http.routers.myrouter.priority=42"
|
||||||
|
@ -145,6 +147,8 @@ add labels starting with `traefik.http.services.{name-of-your-choice}.`, followe
|
||||||
For example, to change the `passHostHeader` behavior,
|
For example, to change the `passHostHeader` behavior,
|
||||||
you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.passhostheader=false`.
|
you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.passhostheader=false`.
|
||||||
|
|
||||||
|
!!! warning "The character `@` is not authorized in the service name `<service_name>`."
|
||||||
|
|
||||||
??? info "`traefik.http.services.<service_name>.loadbalancer.server.port`"
|
??? info "`traefik.http.services.<service_name>.loadbalancer.server.port`"
|
||||||
|
|
||||||
Registers a port.
|
Registers a port.
|
||||||
|
@ -274,6 +278,8 @@ For example, to declare a middleware [`redirectscheme`](../../middlewares/redire
|
||||||
|
|
||||||
More information about available middlewares in the dedicated [middlewares section](../../middlewares/overview.md).
|
More information about available middlewares in the dedicated [middlewares section](../../middlewares/overview.md).
|
||||||
|
|
||||||
|
!!! warning "The character `@` is not authorized in the middleware name."
|
||||||
|
|
||||||
??? example "Declaring and Referencing a Middleware"
|
??? example "Declaring and Referencing a Middleware"
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
|
|
|
@ -7,9 +7,8 @@ There are, however, exceptions when using label-based configurations:
|
||||||
and a label defines a service (e.g. implicitly through a loadbalancer server port value),
|
and a label defines a service (e.g. implicitly through a loadbalancer server port value),
|
||||||
but the router does not specify any service,
|
but the router does not specify any service,
|
||||||
then that service is automatically assigned to the router.
|
then that service is automatically assigned to the router.
|
||||||
1. If a label defines a router (e.g. through a router Rule)
|
1. If a label defines a router (e.g. through a router Rule) but no service is defined,
|
||||||
but no service is defined, then a service is automatically created
|
then a service is automatically created and assigned to the router.
|
||||||
and assigned to the router.
|
|
||||||
|
|
||||||
!!! info ""
|
!!! info ""
|
||||||
As one would expect, in either of these cases, if in addition a service is specified for the router,
|
As one would expect, in either of these cases, if in addition a service is specified for the router,
|
||||||
|
|
|
@ -84,6 +84,8 @@ In the process, routers may use pieces of [middleware](../../middlewares/overvie
|
||||||
|
|
||||||
## Configuring HTTP Routers
|
## Configuring HTTP Routers
|
||||||
|
|
||||||
|
!!! warning "The character `@` is not authorized in the router name"
|
||||||
|
|
||||||
### EntryPoints
|
### EntryPoints
|
||||||
|
|
||||||
If not specified, HTTP routers will accept requests from all defined entry points.
|
If not specified, HTTP routers will accept requests from all defined entry points.
|
||||||
|
@ -203,9 +205,14 @@ If you want to limit the router scope to a set of entry points, set the `entryPo
|
||||||
|
|
||||||
### Rule
|
### Rule
|
||||||
|
|
||||||
Rules are a set of matchers that determine if a particular request matches specific criteria.
|
Rules are a set of matchers configured with values, that determine if a particular request matches specific criteria.
|
||||||
If the rule is verified, the router becomes active, calls middlewares, and then forwards the request to the service.
|
If the rule is verified, the router becomes active, calls middlewares, and then forwards the request to the service.
|
||||||
|
|
||||||
|
??? tip "Backticks or Quotes?"
|
||||||
|
To set the value of a rule, use [backticks](https://en.wiktionary.org/wiki/backtick) ``` ` ``` or escaped double-quotes `\"`.
|
||||||
|
|
||||||
|
Single quotes `'` are not accepted as values are [Golang's String Literals](https://golang.org/ref/spec#String_literals).
|
||||||
|
|
||||||
!!! example "Host is traefik.io"
|
!!! example "Host is traefik.io"
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
|
@ -337,6 +344,8 @@ A value of `0` for the priority is ignored: `priority = 0` means that the defaul
|
||||||
You can attach a list of [middlewares](../../middlewares/overview.md) to each HTTP router.
|
You can attach a list of [middlewares](../../middlewares/overview.md) to each HTTP router.
|
||||||
The middlewares will take effect only if the rule matches, and before forwarding the request to the service.
|
The middlewares will take effect only if the rule matches, and before forwarding the request to the service.
|
||||||
|
|
||||||
|
!!! warning "The character `@` is not authorized in the middleware name."
|
||||||
|
|
||||||
!!! tip "Middlewares order"
|
!!! tip "Middlewares order"
|
||||||
|
|
||||||
Middlewares are applied in the same order as their declaration in **router**.
|
Middlewares are applied in the same order as their declaration in **router**.
|
||||||
|
@ -376,6 +385,8 @@ but there are exceptions for label-based providers.
|
||||||
See the specific [docker](../providers/docker.md#service-definition), [rancher](../providers/rancher.md#service-definition),
|
See the specific [docker](../providers/docker.md#service-definition), [rancher](../providers/rancher.md#service-definition),
|
||||||
or [marathon](../providers/marathon.md#service-definition) documentation.
|
or [marathon](../providers/marathon.md#service-definition) documentation.
|
||||||
|
|
||||||
|
!!! warning "The character `@` is not authorized in the middleware name."
|
||||||
|
|
||||||
!!! important "HTTP routers can only target HTTP services (not TCP services)."
|
!!! important "HTTP routers can only target HTTP services (not TCP services)."
|
||||||
|
|
||||||
### TLS
|
### TLS
|
||||||
|
@ -624,6 +635,8 @@ The [supported `provider` table](../../https/acme.md#providers) indicates if the
|
||||||
|
|
||||||
## Configuring TCP Routers
|
## Configuring TCP Routers
|
||||||
|
|
||||||
|
!!! warning "The character `@` is not authorized in the router name"
|
||||||
|
|
||||||
### General
|
### General
|
||||||
|
|
||||||
If both HTTP routers and TCP routers listen to the same entry points, the TCP routers will apply *before* the HTTP routers.
|
If both HTTP routers and TCP routers listen to the same entry points, the TCP routers will apply *before* the HTTP routers.
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../../.markdownlint.json",
|
"extends": "../../../.markdownlint.json",
|
||||||
"MD024": false
|
"MD024": false,
|
||||||
|
"MD046": false
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,9 @@ Configuring How to Reach the Services
|
||||||
|
|
||||||
![services](../../assets/img/services.png)
|
![services](../../assets/img/services.png)
|
||||||
|
|
||||||
The `Services` are responsible for configuring how to reach the actual services that will eventually handle the incoming requests.
|
The `Services` are responsible for configuring how to reach the actual services that will eventually handle the incoming requests.
|
||||||
|
|
||||||
## Configuration Example
|
## Configuration Examples
|
||||||
|
|
||||||
??? example "Declaring an HTTP Service with Two Servers -- Using the [File Provider](../../providers/file.md)"
|
??? example "Declaring an HTTP Service with Two Servers -- Using the [File Provider](../../providers/file.md)"
|
||||||
|
|
||||||
|
@ -17,11 +17,11 @@ The `Services` are responsible for configuring how to reach the actual services
|
||||||
[http.services.my-service.loadBalancer]
|
[http.services.my-service.loadBalancer]
|
||||||
|
|
||||||
[[http.services.my-service.loadBalancer.servers]]
|
[[http.services.my-service.loadBalancer.servers]]
|
||||||
url = "http://private-ip-server-1/"
|
url = "http://<private-ip-server-1>:<private-port-server-1>/"
|
||||||
[[http.services.my-service.loadBalancer.servers]]
|
[[http.services.my-service.loadBalancer.servers]]
|
||||||
url = "http://private-ip-server-2/"
|
url = "http://<private-ip-server-2>:<private-port-server-2>/"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="YAML"
|
```yaml tab="YAML"
|
||||||
## Dynamic configuration
|
## Dynamic configuration
|
||||||
http:
|
http:
|
||||||
|
@ -29,8 +29,8 @@ The `Services` are responsible for configuring how to reach the actual services
|
||||||
my-service:
|
my-service:
|
||||||
loadBalancer:
|
loadBalancer:
|
||||||
servers:
|
servers:
|
||||||
- url: "http://private-ip-server-1/"
|
- url: "http://<private-ip-server-1>:<private-port-server-1>/"
|
||||||
- url: "http://private-ip-server-2/"
|
- url: "http://<private-ip-server-2>:<private-port-server-2>/"
|
||||||
```
|
```
|
||||||
|
|
||||||
??? example "Declaring a TCP Service with Two Servers -- Using the [File Provider](../../providers/file.md)"
|
??? example "Declaring a TCP Service with Two Servers -- Using the [File Provider](../../providers/file.md)"
|
||||||
|
@ -40,26 +40,28 @@ The `Services` are responsible for configuring how to reach the actual services
|
||||||
[tcp.services]
|
[tcp.services]
|
||||||
[tcp.services.my-service.loadBalancer]
|
[tcp.services.my-service.loadBalancer]
|
||||||
[[tcp.services.my-service.loadBalancer.servers]]
|
[[tcp.services.my-service.loadBalancer.servers]]
|
||||||
address = "xx.xx.xx.xx:xx"
|
address = "<private-ip-server-1>:<private-port-server-1>"
|
||||||
[[tcp.services.my-service.loadBalancer.servers]]
|
[[tcp.services.my-service.loadBalancer.servers]]
|
||||||
address = "xx.xx.xx.xx:xx"
|
address = "<private-ip-server-2>:<private-port-server-2>"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="YAML"
|
```yaml tab="YAML"
|
||||||
tcp:
|
tcp:
|
||||||
services:
|
services:
|
||||||
my-service:
|
my-service:
|
||||||
loadBalancer:
|
loadBalancer:
|
||||||
servers:
|
servers:
|
||||||
- address: "xx.xx.xx.xx:xx"
|
- address: "<private-ip-server-1>:<private-port-server-1>"
|
||||||
- address: "xx.xx.xx.xx:xx"
|
- address: "<private-ip-server-2>:<private-port-server-2>"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuring HTTP Services
|
## Configuring HTTP Services
|
||||||
|
|
||||||
### Servers Load Balancer
|
### Servers Load Balancer
|
||||||
|
|
||||||
The load balancers are able to load balance the requests between multiple instances of your programs.
|
The load balancers are able to load balance the requests between multiple instances of your programs.
|
||||||
|
|
||||||
|
Each service has a load-balancer, even if there is only one server to forward traffic to.
|
||||||
|
|
||||||
??? example "Declaring a Service with Two Servers (with Load Balancing) -- Using the [File Provider](../../providers/file.md)"
|
??? example "Declaring a Service with Two Servers (with Load Balancing) -- Using the [File Provider](../../providers/file.md)"
|
||||||
|
|
||||||
|
@ -87,10 +89,10 @@ The load balancers are able to load balance the requests between multiple instan
|
||||||
#### Servers
|
#### Servers
|
||||||
|
|
||||||
Servers declare a single instance of your program.
|
Servers declare a single instance of your program.
|
||||||
The `url` option point to a specific instance.
|
The `url` option point to a specific instance.
|
||||||
|
|
||||||
!!! info ""
|
!!! info ""
|
||||||
Paths in the servers' `url` have no effect.
|
Paths in the servers' `url` have no effect.
|
||||||
If you want the requests to be sent to a specific path on your servers,
|
If you want the requests to be sent to a specific path on your servers,
|
||||||
configure your [`routers`](../routers/index.md) to use a corresponding [middleware](../../middlewares/overview.md) (e.g. the [AddPrefix](../../middlewares/addprefix.md) or [ReplacePath](../../middlewares/replacepath.md)) middlewares.
|
configure your [`routers`](../routers/index.md) to use a corresponding [middleware](../../middlewares/overview.md) (e.g. the [AddPrefix](../../middlewares/addprefix.md) or [ReplacePath](../../middlewares/replacepath.md)) middlewares.
|
||||||
|
|
||||||
|
@ -103,7 +105,7 @@ The `url` option point to a specific instance.
|
||||||
[[http.services.my-service.loadBalancer.servers]]
|
[[http.services.my-service.loadBalancer.servers]]
|
||||||
url = "http://private-ip-server-1/"
|
url = "http://private-ip-server-1/"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="YAML"
|
```yaml tab="YAML"
|
||||||
## Dynamic configuration
|
## Dynamic configuration
|
||||||
http:
|
http:
|
||||||
|
@ -142,21 +144,21 @@ For now, only round robin load balancing is supported:
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Sticky sessions
|
#### Sticky sessions
|
||||||
|
|
||||||
When sticky sessions are enabled, a cookie is set on the initial request to track which server handles the first response.
|
When sticky sessions are enabled, a cookie is set on the initial request to track which server handles the first response.
|
||||||
On subsequent requests, the client is forwarded to the same server.
|
On subsequent requests, the client is forwarded to the same server.
|
||||||
|
|
||||||
!!! info "Stickiness & Unhealthy Servers"
|
!!! info "Stickiness & Unhealthy Servers"
|
||||||
|
|
||||||
If the server specified in the cookie becomes unhealthy, the request will be forwarded to a new server (and the cookie will keep track of the new server).
|
If the server specified in the cookie becomes unhealthy, the request will be forwarded to a new server (and the cookie will keep track of the new server).
|
||||||
|
|
||||||
!!! info "Cookie Name"
|
!!! info "Cookie Name"
|
||||||
|
|
||||||
The default cookie name is an abbreviation of a sha1 (ex: `_1d52e`).
|
The default cookie name is an abbreviation of a sha1 (ex: `_1d52e`).
|
||||||
|
|
||||||
!!! info "Secure & HTTPOnly flags"
|
!!! info "Secure & HTTPOnly flags"
|
||||||
|
|
||||||
By default, the affinity cookie is created without those flags. One however can change that through configuration.
|
By default, the affinity cookie is created without those flags. One however can change that through configuration.
|
||||||
|
|
||||||
??? example "Adding Stickiness -- Using the [File Provider](../../providers/file.md)"
|
??? example "Adding Stickiness -- Using the [File Provider](../../providers/file.md)"
|
||||||
|
|
||||||
|
@ -166,7 +168,7 @@ On subsequent requests, the client is forwarded to the same server.
|
||||||
[http.services.my-service]
|
[http.services.my-service]
|
||||||
[http.services.my-service.loadBalancer.sticky.cookie]
|
[http.services.my-service.loadBalancer.sticky.cookie]
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="YAML"
|
```yaml tab="YAML"
|
||||||
## Dynamic configuration
|
## Dynamic configuration
|
||||||
http:
|
http:
|
||||||
|
@ -223,8 +225,8 @@ Below are the available options for the health check mechanism:
|
||||||
The interval must be greater than the timeout. If configuration doesn't reflect this, the interval will be set to timeout + 1 second.
|
The interval must be greater than the timeout. If configuration doesn't reflect this, the interval will be set to timeout + 1 second.
|
||||||
|
|
||||||
!!! info "Recovering Servers"
|
!!! info "Recovering Servers"
|
||||||
|
|
||||||
Traefik keeps monitoring the health of unhealthy servers.
|
Traefik keeps monitoring the health of unhealthy servers.
|
||||||
If a server has recovered (returning `2xx` -> `3xx` responses again), it will be added back to the load balacer rotation pool.
|
If a server has recovered (returning `2xx` -> `3xx` responses again), it will be added back to the load balacer rotation pool.
|
||||||
|
|
||||||
??? example "Custom Interval & Timeout -- Using the [File Provider](../../providers/file.md)"
|
??? example "Custom Interval & Timeout -- Using the [File Provider](../../providers/file.md)"
|
||||||
|
@ -261,7 +263,7 @@ Below are the available options for the health check mechanism:
|
||||||
path = "/health"
|
path = "/health"
|
||||||
port = 8080
|
port = 8080
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="YAML"
|
```yaml tab="YAML"
|
||||||
## Dynamic configuration
|
## Dynamic configuration
|
||||||
http:
|
http:
|
||||||
|
@ -283,7 +285,7 @@ Below are the available options for the health check mechanism:
|
||||||
path = "/health"
|
path = "/health"
|
||||||
scheme = "http"
|
scheme = "http"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="YAML"
|
```yaml tab="YAML"
|
||||||
## Dynamic configuration
|
## Dynamic configuration
|
||||||
http:
|
http:
|
||||||
|
@ -308,7 +310,7 @@ Below are the available options for the health check mechanism:
|
||||||
My-Custom-Header = "foo"
|
My-Custom-Header = "foo"
|
||||||
My-Header = "bar"
|
My-Header = "bar"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="YAML"
|
```yaml tab="YAML"
|
||||||
## Dynamic configuration
|
## Dynamic configuration
|
||||||
http:
|
http:
|
||||||
|
@ -337,7 +339,7 @@ By default, `passHostHeader` is true.
|
||||||
[http.services.Service01.loadBalancer]
|
[http.services.Service01.loadBalancer]
|
||||||
passHostHeader = false
|
passHostHeader = false
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="YAML"
|
```yaml tab="YAML"
|
||||||
## Dynamic configuration
|
## Dynamic configuration
|
||||||
http:
|
http:
|
||||||
|
@ -346,7 +348,7 @@ By default, `passHostHeader` is true.
|
||||||
loadBalancer:
|
loadBalancer:
|
||||||
passHostHeader: false
|
passHostHeader: false
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Response Forwarding
|
#### Response Forwarding
|
||||||
|
|
||||||
This section is about configuring how Traefik forwards the response from the backend server to the client.
|
This section is about configuring how Traefik forwards the response from the backend server to the client.
|
||||||
|
@ -358,7 +360,7 @@ Below are the available options for the Response Forwarding mechanism:
|
||||||
A negative value means to flush immediately after each write to the client.
|
A negative value means to flush immediately after each write to the client.
|
||||||
The FlushInterval is ignored when ReverseProxy recognizes a response as a streaming response;
|
The FlushInterval is ignored when ReverseProxy recognizes a response as a streaming response;
|
||||||
for such responses, writes are flushed to the client immediately.
|
for such responses, writes are flushed to the client immediately.
|
||||||
|
|
||||||
??? example "Using a custom FlushInterval -- Using the [File Provider](../../providers/file.md)"
|
??? example "Using a custom FlushInterval -- Using the [File Provider](../../providers/file.md)"
|
||||||
|
|
||||||
```toml tab="TOML"
|
```toml tab="TOML"
|
||||||
|
@ -368,7 +370,7 @@ Below are the available options for the Response Forwarding mechanism:
|
||||||
[http.services.Service-1.loadBalancer.responseForwarding]
|
[http.services.Service-1.loadBalancer.responseForwarding]
|
||||||
flushInterval = "1s"
|
flushInterval = "1s"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="YAML"
|
```yaml tab="YAML"
|
||||||
## Dynamic configuration
|
## Dynamic configuration
|
||||||
http:
|
http:
|
||||||
|
@ -378,7 +380,7 @@ Below are the available options for the Response Forwarding mechanism:
|
||||||
responseForwarding:
|
responseForwarding:
|
||||||
flushInterval: 1s
|
flushInterval: 1s
|
||||||
```
|
```
|
||||||
|
|
||||||
### Weighted Round Robin (service)
|
### Weighted Round Robin (service)
|
||||||
|
|
||||||
The WRR is able to load balance the requests between multiple services based on weights.
|
The WRR is able to load balance the requests between multiple services based on weights.
|
||||||
|
@ -539,7 +541,7 @@ The `address` option (IP:Port) point to a specific instance.
|
||||||
my-service:
|
my-service:
|
||||||
loadBalancer:
|
loadBalancer:
|
||||||
servers:
|
servers:
|
||||||
address: "xx.xx.xx.xx:xx"
|
- address: "xx.xx.xx.xx:xx"
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Termination Delay
|
#### Termination Delay
|
||||||
|
|
|
@ -101,4 +101,4 @@ curl [-k] https://your.domain.com/tls
|
||||||
curl [-k] http://your.domain.com:8000/notls
|
curl [-k] http://your.domain.com:8000/notls
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that you'll have to use `-k` as long as you're using the staging server of Let's Encrypt, since it is not in the root DNS servers.
|
Note that you'll have to use `-k` as long as you're using the staging server of Let's Encrypt, since it is not an authorized certificate authority on systems where it hasn't been manually added.
|
||||||
|
|
|
@ -21,7 +21,7 @@ find "${PATH_TO_SITE}" -type f -not -path "/app/site/theme/*" \
|
||||||
--check_external_hash \
|
--check_external_hash \
|
||||||
--alt_ignore="/traefik.logo.png/" \
|
--alt_ignore="/traefik.logo.png/" \
|
||||||
--http_status_ignore="0,500,501,503" \
|
--http_status_ignore="0,500,501,503" \
|
||||||
--url_ignore="/https://groups.google.com/a/traefik.io/forum/#!forum/security/,/localhost:/,/127.0.0.1:/,/fonts.gstatic.com/,/.minikube/,/github.com\/containous\/traefik\/*edit*/,/github.com\/containous\/traefik\/$/,/docs.traefik.io/,/github\.com\/golang\/oauth2\/blob\/36a7019397c4c86cf59eeab3bc0d188bac444277\/.+/" \
|
--url_ignore="/https://groups.google.com/a/traefik.io/forum/#!forum/security/,/localhost:/,/127.0.0.1:/,/fonts.gstatic.com/,/.minikube/,/github.com\/containous\/traefik\/*edit*/,/github.com\/containous\/traefik\/$/,/docs.traefik.io/,/github\.com\/golang\/oauth2\/blob\/36a7019397c4c86cf59eeab3bc0d188bac444277\/.+/,/www.akamai.com/" \
|
||||||
'{}' 1>/dev/null
|
'{}' 1>/dev/null
|
||||||
## HTML-proofer options at https://github.com/gjtorikian/html-proofer#configuration
|
## HTML-proofer options at https://github.com/gjtorikian/html-proofer#configuration
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# WEBUI
|
# WEBUI
|
||||||
FROM node:8.15.0 as webui
|
FROM node:12.11 as webui
|
||||||
|
|
||||||
ENV WEBUI_DIR /src/webui
|
ENV WEBUI_DIR /src/webui
|
||||||
RUN mkdir -p $WEBUI_DIR
|
RUN mkdir -p $WEBUI_DIR
|
||||||
|
@ -7,7 +7,7 @@ RUN mkdir -p $WEBUI_DIR
|
||||||
COPY ./webui/ $WEBUI_DIR/
|
COPY ./webui/ $WEBUI_DIR/
|
||||||
|
|
||||||
WORKDIR $WEBUI_DIR
|
WORKDIR $WEBUI_DIR
|
||||||
RUN yarn install
|
RUN npm install
|
||||||
|
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
|
|
|
@ -973,6 +973,11 @@ func (in *ServersLoadBalancer) DeepCopyInto(out *ServersLoadBalancer) {
|
||||||
*out = new(HealthCheck)
|
*out = new(HealthCheck)
|
||||||
(*in).DeepCopyInto(*out)
|
(*in).DeepCopyInto(*out)
|
||||||
}
|
}
|
||||||
|
if in.PassHostHeader != nil {
|
||||||
|
in, out := &in.PassHostHeader, &out.PassHostHeader
|
||||||
|
*out = new(bool)
|
||||||
|
**out = **in
|
||||||
|
}
|
||||||
if in.ResponseForwarding != nil {
|
if in.ResponseForwarding != nil {
|
||||||
in, out := &in.ResponseForwarding, &out.ResponseForwarding
|
in, out := &in.ResponseForwarding, &out.ResponseForwarding
|
||||||
*out = new(ResponseForwarding)
|
*out = new(ResponseForwarding)
|
||||||
|
|
|
@ -1,123 +0,0 @@
|
||||||
package hostresolver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/containous/traefik/v2/pkg/log"
|
|
||||||
"github.com/miekg/dns"
|
|
||||||
"github.com/patrickmn/go-cache"
|
|
||||||
)
|
|
||||||
|
|
||||||
type cnameResolv struct {
|
|
||||||
TTL time.Duration
|
|
||||||
Record string
|
|
||||||
}
|
|
||||||
|
|
||||||
type byTTL []*cnameResolv
|
|
||||||
|
|
||||||
func (a byTTL) Len() int { return len(a) }
|
|
||||||
func (a byTTL) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
||||||
func (a byTTL) Less(i, j int) bool { return a[i].TTL > a[j].TTL }
|
|
||||||
|
|
||||||
// Resolver used for host resolver
|
|
||||||
type Resolver struct {
|
|
||||||
CnameFlattening bool
|
|
||||||
ResolvConfig string
|
|
||||||
ResolvDepth int
|
|
||||||
cache *cache.Cache
|
|
||||||
}
|
|
||||||
|
|
||||||
// CNAMEFlatten check if CNAME record exists, flatten if possible
|
|
||||||
func (hr *Resolver) CNAMEFlatten(host string) (string, string) {
|
|
||||||
if hr.cache == nil {
|
|
||||||
hr.cache = cache.New(30*time.Minute, 5*time.Minute)
|
|
||||||
}
|
|
||||||
|
|
||||||
result := []string{host}
|
|
||||||
request := host
|
|
||||||
|
|
||||||
value, found := hr.cache.Get(host)
|
|
||||||
if found {
|
|
||||||
result = strings.Split(value.(string), ",")
|
|
||||||
} else {
|
|
||||||
var cacheDuration = 0 * time.Second
|
|
||||||
|
|
||||||
for depth := 0; depth < hr.ResolvDepth; depth++ {
|
|
||||||
resolv, err := cnameResolve(request, hr.ResolvConfig)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if resolv == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
result = append(result, resolv.Record)
|
|
||||||
if depth == 0 {
|
|
||||||
cacheDuration = resolv.TTL
|
|
||||||
}
|
|
||||||
request = resolv.Record
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := hr.cache.Add(host, strings.Join(result, ","), cacheDuration); err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result[0], result[len(result)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
// cnameResolve resolves CNAME if exists, and return with the highest TTL
|
|
||||||
func cnameResolve(host string, resolvPath string) (*cnameResolv, error) {
|
|
||||||
config, err := dns.ClientConfigFromFile(resolvPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid resolver configuration file: %s", resolvPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
client := &dns.Client{Timeout: 30 * time.Second}
|
|
||||||
|
|
||||||
m := &dns.Msg{}
|
|
||||||
m.SetQuestion(dns.Fqdn(host), dns.TypeCNAME)
|
|
||||||
|
|
||||||
var result []*cnameResolv
|
|
||||||
for _, server := range config.Servers {
|
|
||||||
tempRecord, err := getRecord(client, m, server, config.Port)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Failed to resolve host %s: %v", host, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
result = append(result, tempRecord)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(result) == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Sort(byTTL(result))
|
|
||||||
return result[0], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getRecord(client *dns.Client, msg *dns.Msg, server string, port string) (*cnameResolv, error) {
|
|
||||||
resp, _, err := client.Exchange(msg, net.JoinHostPort(server, port))
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("exchange error for server %s: %v", server, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp == nil || len(resp.Answer) == 0 {
|
|
||||||
return nil, fmt.Errorf("empty answer for server %s", server)
|
|
||||||
}
|
|
||||||
|
|
||||||
rr, ok := resp.Answer[0].(*dns.CNAME)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("invalid response type for server %s", server)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &cnameResolv{
|
|
||||||
TTL: time.Duration(rr.Hdr.Ttl) * time.Second,
|
|
||||||
Record: strings.TrimSuffix(rr.Target, "."),
|
|
||||||
}, nil
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
package hostresolver
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCNAMEFlatten(t *testing.T) {
|
|
||||||
testCase := []struct {
|
|
||||||
desc string
|
|
||||||
resolvFile string
|
|
||||||
domain string
|
|
||||||
expectedDomain string
|
|
||||||
isCNAME bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "host request is CNAME record",
|
|
||||||
resolvFile: "/etc/resolv.conf",
|
|
||||||
domain: "www.github.com",
|
|
||||||
expectedDomain: "github.com",
|
|
||||||
isCNAME: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "resolve file not found",
|
|
||||||
resolvFile: "/etc/resolv.oops",
|
|
||||||
domain: "www.github.com",
|
|
||||||
expectedDomain: "www.github.com",
|
|
||||||
isCNAME: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "host request is not CNAME record",
|
|
||||||
resolvFile: "/etc/resolv.conf",
|
|
||||||
domain: "github.com",
|
|
||||||
expectedDomain: "github.com",
|
|
||||||
isCNAME: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range testCase {
|
|
||||||
test := test
|
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
hostResolver := &Resolver{
|
|
||||||
ResolvConfig: test.resolvFile,
|
|
||||||
ResolvDepth: 5,
|
|
||||||
}
|
|
||||||
|
|
||||||
reqH, flatH := hostResolver.CNAMEFlatten(test.domain)
|
|
||||||
assert.Equal(t, test.domain, reqH)
|
|
||||||
assert.Equal(t, test.expectedDomain, flatH)
|
|
||||||
|
|
||||||
if test.isCNAME {
|
|
||||||
assert.NotEqual(t, test.expectedDomain, reqH)
|
|
||||||
} else {
|
|
||||||
assert.Equal(t, test.expectedDomain, reqH)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,6 +3,7 @@ package accesslog
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -31,6 +32,19 @@ const (
|
||||||
JSONFormat string = "json"
|
JSONFormat string = "json"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type noopCloser struct {
|
||||||
|
*os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n noopCloser) Write(p []byte) (int, error) {
|
||||||
|
return n.File.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n noopCloser) Close() error {
|
||||||
|
// noop
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type handlerParams struct {
|
type handlerParams struct {
|
||||||
logDataTable *LogData
|
logDataTable *LogData
|
||||||
crr *captureRequestReader
|
crr *captureRequestReader
|
||||||
|
@ -41,7 +55,7 @@ type handlerParams struct {
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
config *types.AccessLog
|
config *types.AccessLog
|
||||||
logger *logrus.Logger
|
logger *logrus.Logger
|
||||||
file *os.File
|
file io.WriteCloser
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
httpCodeRanges types.HTTPCodeRanges
|
httpCodeRanges types.HTTPCodeRanges
|
||||||
logHandlerChan chan handlerParams
|
logHandlerChan chan handlerParams
|
||||||
|
@ -59,7 +73,7 @@ func WrapHandler(handler *Handler) alice.Constructor {
|
||||||
|
|
||||||
// NewHandler creates a new Handler.
|
// NewHandler creates a new Handler.
|
||||||
func NewHandler(config *types.AccessLog) (*Handler, error) {
|
func NewHandler(config *types.AccessLog) (*Handler, error) {
|
||||||
file := os.Stdout
|
var file io.WriteCloser = noopCloser{os.Stdout}
|
||||||
if len(config.FilePath) > 0 {
|
if len(config.FilePath) > 0 {
|
||||||
f, err := openAccessLogFile(config.FilePath)
|
f, err := openAccessLogFile(config.FilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -213,14 +227,15 @@ func (h *Handler) Close() error {
|
||||||
|
|
||||||
// Rotate closes and reopens the log file to allow for rotation by an external source.
|
// Rotate closes and reopens the log file to allow for rotation by an external source.
|
||||||
func (h *Handler) Rotate() error {
|
func (h *Handler) Rotate() error {
|
||||||
var err error
|
if h.config.FilePath == "" {
|
||||||
|
return nil
|
||||||
if h.file != nil {
|
|
||||||
defer func(f *os.File) {
|
|
||||||
f.Close()
|
|
||||||
}(h.file)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if h.file != nil {
|
||||||
|
defer func(f io.Closer) { _ = f.Close() }(h.file)
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
h.file, err = os.OpenFile(h.config.FilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0664)
|
h.file, err = os.OpenFile(h.config.FilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0664)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -22,7 +22,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Compile time validation that the response recorder implements http interfaces correctly.
|
// Compile time validation that the response recorder implements http interfaces correctly.
|
||||||
var _ middlewares.Stateful = &responseRecorderWithCloseNotify{}
|
var (
|
||||||
|
_ middlewares.Stateful = &responseRecorderWithCloseNotify{}
|
||||||
|
_ middlewares.Stateful = &codeCatcherWithCloseNotify{}
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
typeName = "customError"
|
typeName = "customError"
|
||||||
|
@ -80,25 +83,29 @@ func (c *customErrors) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
recorder := newResponseRecorder(ctx, rw)
|
catcher := newCodeCatcher(rw, c.httpCodeRanges)
|
||||||
c.next.ServeHTTP(recorder, req)
|
c.next.ServeHTTP(catcher, req)
|
||||||
|
if !catcher.isFilteredCode() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// check the recorder code against the configured http status code ranges
|
// check the recorder code against the configured http status code ranges
|
||||||
|
code := catcher.getCode()
|
||||||
for _, block := range c.httpCodeRanges {
|
for _, block := range c.httpCodeRanges {
|
||||||
if recorder.GetCode() >= block[0] && recorder.GetCode() <= block[1] {
|
if code >= block[0] && code <= block[1] {
|
||||||
logger.Errorf("Caught HTTP Status Code %d, returning error page", recorder.GetCode())
|
logger.Errorf("Caught HTTP Status Code %d, returning error page", code)
|
||||||
|
|
||||||
var query string
|
var query string
|
||||||
if len(c.backendQuery) > 0 {
|
if len(c.backendQuery) > 0 {
|
||||||
query = "/" + strings.TrimPrefix(c.backendQuery, "/")
|
query = "/" + strings.TrimPrefix(c.backendQuery, "/")
|
||||||
query = strings.Replace(query, "{status}", strconv.Itoa(recorder.GetCode()), -1)
|
query = strings.Replace(query, "{status}", strconv.Itoa(code), -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
pageReq, err := newRequest(backendURL + query)
|
pageReq, err := newRequest(backendURL + query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
rw.WriteHeader(recorder.GetCode())
|
rw.WriteHeader(code)
|
||||||
_, err = fmt.Fprint(rw, http.StatusText(recorder.GetCode()))
|
_, err = fmt.Fprint(rw, http.StatusText(code))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||||
}
|
}
|
||||||
|
@ -111,7 +118,7 @@ func (c *customErrors) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
c.backendHandler.ServeHTTP(recorderErrorPage, pageReq.WithContext(req.Context()))
|
c.backendHandler.ServeHTTP(recorderErrorPage, pageReq.WithContext(req.Context()))
|
||||||
|
|
||||||
utils.CopyHeaders(rw.Header(), recorderErrorPage.Header())
|
utils.CopyHeaders(rw.Header(), recorderErrorPage.Header())
|
||||||
rw.WriteHeader(recorder.GetCode())
|
rw.WriteHeader(code)
|
||||||
|
|
||||||
if _, err = rw.Write(recorderErrorPage.GetBody().Bytes()); err != nil {
|
if _, err = rw.Write(recorderErrorPage.GetBody().Bytes()); err != nil {
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
|
@ -119,14 +126,6 @@ func (c *customErrors) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// did not catch a configured status code so proceed with the request
|
|
||||||
utils.CopyHeaders(rw.Header(), recorder.Header())
|
|
||||||
rw.WriteHeader(recorder.GetCode())
|
|
||||||
_, err := rw.Write(recorder.GetBody().Bytes())
|
|
||||||
if err != nil {
|
|
||||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRequest(baseURL string) (*http.Request, error) {
|
func newRequest(baseURL string) (*http.Request, error) {
|
||||||
|
@ -144,6 +143,132 @@ func newRequest(baseURL string) (*http.Request, error) {
|
||||||
return req, nil
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type responseInterceptor interface {
|
||||||
|
http.ResponseWriter
|
||||||
|
http.Flusher
|
||||||
|
getCode() int
|
||||||
|
isFilteredCode() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// codeCatcher is a response writer that detects as soon as possible whether the
|
||||||
|
// response is a code within the ranges of codes it watches for. If it is, it
|
||||||
|
// simply drops the data from the response. Otherwise, it forwards it directly to
|
||||||
|
// the original client (its responseWriter) without any buffering.
|
||||||
|
type codeCatcher struct {
|
||||||
|
headerMap http.Header
|
||||||
|
code int
|
||||||
|
httpCodeRanges types.HTTPCodeRanges
|
||||||
|
firstWrite bool
|
||||||
|
caughtFilteredCode bool
|
||||||
|
responseWriter http.ResponseWriter
|
||||||
|
headersSent bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type codeCatcherWithCloseNotify struct {
|
||||||
|
*codeCatcher
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseNotify returns a channel that receives at most a
|
||||||
|
// single value (true) when the client connection has gone away.
|
||||||
|
func (cc *codeCatcherWithCloseNotify) CloseNotify() <-chan bool {
|
||||||
|
return cc.responseWriter.(http.CloseNotifier).CloseNotify()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCodeCatcher(rw http.ResponseWriter, httpCodeRanges types.HTTPCodeRanges) responseInterceptor {
|
||||||
|
catcher := &codeCatcher{
|
||||||
|
headerMap: make(http.Header),
|
||||||
|
code: http.StatusOK, // If backend does not call WriteHeader on us, we consider it's a 200.
|
||||||
|
responseWriter: rw,
|
||||||
|
httpCodeRanges: httpCodeRanges,
|
||||||
|
firstWrite: true,
|
||||||
|
}
|
||||||
|
if _, ok := rw.(http.CloseNotifier); ok {
|
||||||
|
return &codeCatcherWithCloseNotify{catcher}
|
||||||
|
}
|
||||||
|
return catcher
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc *codeCatcher) Header() http.Header {
|
||||||
|
if cc.headerMap == nil {
|
||||||
|
cc.headerMap = make(http.Header)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cc.headerMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc *codeCatcher) getCode() int {
|
||||||
|
return cc.code
|
||||||
|
}
|
||||||
|
|
||||||
|
// isFilteredCode returns whether the codeCatcher received a response code among the ones it is watching,
|
||||||
|
// and for which the response should be deferred to the error handler.
|
||||||
|
func (cc *codeCatcher) isFilteredCode() bool {
|
||||||
|
return cc.caughtFilteredCode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc *codeCatcher) Write(buf []byte) (int, error) {
|
||||||
|
if !cc.firstWrite {
|
||||||
|
if cc.caughtFilteredCode {
|
||||||
|
// We don't care about the contents of the response,
|
||||||
|
// since we want to serve the ones from the error page,
|
||||||
|
// so we just drop them.
|
||||||
|
return len(buf), nil
|
||||||
|
}
|
||||||
|
return cc.responseWriter.Write(buf)
|
||||||
|
}
|
||||||
|
cc.firstWrite = false
|
||||||
|
|
||||||
|
// If WriteHeader was already called from the caller, this is a NOOP.
|
||||||
|
// Otherwise, cc.code is actually a 200 here.
|
||||||
|
cc.WriteHeader(cc.code)
|
||||||
|
|
||||||
|
if cc.caughtFilteredCode {
|
||||||
|
return len(buf), nil
|
||||||
|
}
|
||||||
|
return cc.responseWriter.Write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cc *codeCatcher) WriteHeader(code int) {
|
||||||
|
if cc.headersSent || cc.caughtFilteredCode {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cc.code = code
|
||||||
|
for _, block := range cc.httpCodeRanges {
|
||||||
|
if cc.code >= block[0] && cc.code <= block[1] {
|
||||||
|
cc.caughtFilteredCode = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// it will be up to the other response recorder to send the headers,
|
||||||
|
// so it is out of our hands now.
|
||||||
|
if cc.caughtFilteredCode {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
utils.CopyHeaders(cc.responseWriter.Header(), cc.Header())
|
||||||
|
cc.responseWriter.WriteHeader(cc.code)
|
||||||
|
cc.headersSent = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hijack hijacks the connection
|
||||||
|
func (cc *codeCatcher) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||||
|
if hj, ok := cc.responseWriter.(http.Hijacker); ok {
|
||||||
|
return hj.Hijack()
|
||||||
|
}
|
||||||
|
return nil, nil, fmt.Errorf("%T is not a http.Hijacker", cc.responseWriter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush sends any buffered data to the client.
|
||||||
|
func (cc *codeCatcher) Flush() {
|
||||||
|
// If WriteHeader was already called from the caller, this is a NOOP.
|
||||||
|
// Otherwise, cc.code is actually a 200 here.
|
||||||
|
cc.WriteHeader(cc.code)
|
||||||
|
|
||||||
|
if flusher, ok := cc.responseWriter.(http.Flusher); ok {
|
||||||
|
flusher.Flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type responseRecorder interface {
|
type responseRecorder interface {
|
||||||
http.ResponseWriter
|
http.ResponseWriter
|
||||||
http.Flusher
|
http.Flusher
|
||||||
|
|
|
@ -33,6 +33,30 @@ func TestHandler(t *testing.T) {
|
||||||
assert.Contains(t, recorder.Body.String(), http.StatusText(http.StatusOK))
|
assert.Contains(t, recorder.Body.String(), http.StatusText(http.StatusOK))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "no error, but not a 200",
|
||||||
|
errorPage: &dynamic.ErrorPage{Service: "error", Query: "/test", Status: []string{"500-501", "503-599"}},
|
||||||
|
backendCode: http.StatusPartialContent,
|
||||||
|
backendErrorHandler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprintln(w, "My error page.")
|
||||||
|
}),
|
||||||
|
validate: func(t *testing.T, recorder *httptest.ResponseRecorder) {
|
||||||
|
assert.Equal(t, http.StatusPartialContent, recorder.Code, "HTTP status")
|
||||||
|
assert.Contains(t, recorder.Body.String(), http.StatusText(http.StatusPartialContent))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "a 304, so no Write called",
|
||||||
|
errorPage: &dynamic.ErrorPage{Service: "error", Query: "/test", Status: []string{"500-501", "503-599"}},
|
||||||
|
backendCode: http.StatusNotModified,
|
||||||
|
backendErrorHandler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fmt.Fprintln(w, "whatever, should not be called")
|
||||||
|
}),
|
||||||
|
validate: func(t *testing.T, recorder *httptest.ResponseRecorder) {
|
||||||
|
assert.Equal(t, http.StatusNotModified, recorder.Code, "HTTP status")
|
||||||
|
assert.Contains(t, recorder.Body.String(), "")
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
desc: "in the range",
|
desc: "in the range",
|
||||||
errorPage: &dynamic.ErrorPage{Service: "error", Query: "/test", Status: []string{"500-501", "503-599"}},
|
errorPage: &dynamic.ErrorPage{Service: "error", Query: "/test", Status: []string{"500-501", "503-599"}},
|
||||||
|
@ -104,6 +128,9 @@ func TestHandler(t *testing.T) {
|
||||||
|
|
||||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(test.backendCode)
|
w.WriteHeader(test.backendCode)
|
||||||
|
if test.backendCode == http.StatusNotModified {
|
||||||
|
return
|
||||||
|
}
|
||||||
fmt.Fprintln(w, http.StatusText(test.backendCode))
|
fmt.Fprintln(w, http.StatusText(test.backendCode))
|
||||||
})
|
})
|
||||||
errorPageHandler, err := New(context.Background(), handler, *test.errorPage, serviceBuilderMock, "test")
|
errorPageHandler, err := New(context.Background(), handler, *test.errorPage, serviceBuilderMock, "test")
|
||||||
|
|
|
@ -3,6 +3,7 @@ package recovery
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
"github.com/containous/traefik/v2/pkg/log"
|
"github.com/containous/traefik/v2/pkg/log"
|
||||||
"github.com/containous/traefik/v2/pkg/middlewares"
|
"github.com/containous/traefik/v2/pkg/middlewares"
|
||||||
|
@ -28,13 +29,30 @@ func New(ctx context.Context, next http.Handler, name string) (http.Handler, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (re *recovery) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
func (re *recovery) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
defer recoverFunc(middlewares.GetLoggerCtx(req.Context(), re.name, typeName), rw)
|
defer recoverFunc(middlewares.GetLoggerCtx(req.Context(), re.name, typeName), rw, req)
|
||||||
re.next.ServeHTTP(rw, req)
|
re.next.ServeHTTP(rw, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
func recoverFunc(ctx context.Context, rw http.ResponseWriter) {
|
func recoverFunc(ctx context.Context, rw http.ResponseWriter, r *http.Request) {
|
||||||
if err := recover(); err != nil {
|
if err := recover(); err != nil {
|
||||||
log.FromContext(ctx).Errorf("Recovered from panic in http handler: %+v", err)
|
if !shouldLogPanic(err) {
|
||||||
|
log.FromContext(ctx).Debugf("Request has been aborted [%s - %s]: %v", r.RemoteAddr, r.URL, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.FromContext(ctx).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.FromContext(ctx).Errorf("Stack: %s", buf)
|
||||||
|
|
||||||
http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
http.Error(rw, 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
|
||||||
|
}
|
||||||
|
|
|
@ -323,7 +323,7 @@ func (p *Provider) resolveDomains(ctx context.Context, domains []string, tlsStor
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.FromContext(ctx).Debugf("Try to challenge certificate for domain %v founded in HostSNI rule", domains)
|
log.FromContext(ctx).Debugf("Try to challenge certificate for domain %v found in HostSNI rule", domains)
|
||||||
|
|
||||||
var domain types.Domain
|
var domain types.Domain
|
||||||
if len(domains) > 0 {
|
if len(domains) > 0 {
|
||||||
|
|
|
@ -199,8 +199,6 @@ func flattenCertificates(ctx context.Context, tlsConfig *dynamic.TLSConfiguratio
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory string, configuration *dynamic.Configuration) (*dynamic.Configuration, error) {
|
func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory string, configuration *dynamic.Configuration) (*dynamic.Configuration, error) {
|
||||||
logger := log.FromContext(ctx)
|
|
||||||
|
|
||||||
fileList, err := ioutil.ReadDir(directory)
|
fileList, err := ioutil.ReadDir(directory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return configuration, fmt.Errorf("unable to read directory %s: %v", directory, err)
|
return configuration, fmt.Errorf("unable to read directory %s: %v", directory, err)
|
||||||
|
@ -227,6 +225,8 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st
|
||||||
configTLSMaps := make(map[*tls.CertAndStores]struct{})
|
configTLSMaps := make(map[*tls.CertAndStores]struct{})
|
||||||
|
|
||||||
for _, item := range fileList {
|
for _, item := range fileList {
|
||||||
|
logger := log.FromContext(log.With(ctx, log.Str("filename", item.Name())))
|
||||||
|
|
||||||
if item.IsDir() {
|
if item.IsDir() {
|
||||||
configuration, err = p.loadFileConfigFromDirectory(ctx, filepath.Join(directory, item.Name()), configuration)
|
configuration, err = p.loadFileConfigFromDirectory(ctx, filepath.Join(directory, item.Name()), configuration)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -245,7 +245,7 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st
|
||||||
var c *dynamic.Configuration
|
var c *dynamic.Configuration
|
||||||
c, err = p.loadFileConfig(ctx, filepath.Join(directory, item.Name()), true)
|
c, err = p.loadFileConfig(ctx, filepath.Join(directory, item.Name()), true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return configuration, err
|
return configuration, fmt.Errorf("%s: %v", filepath.Join(directory, item.Name()), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, conf := range c.HTTP.Routers {
|
for name, conf := range c.HTTP.Routers {
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -21,6 +20,7 @@ import (
|
||||||
"github.com/containous/traefik/v2/pkg/safe"
|
"github.com/containous/traefik/v2/pkg/safe"
|
||||||
"github.com/containous/traefik/v2/pkg/tls"
|
"github.com/containous/traefik/v2/pkg/tls"
|
||||||
"github.com/containous/traefik/v2/pkg/types"
|
"github.com/containous/traefik/v2/pkg/types"
|
||||||
|
"github.com/mitchellh/hashstructure"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
)
|
)
|
||||||
|
@ -127,10 +127,14 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
|
||||||
// track more information about the dropped events.
|
// track more information about the dropped events.
|
||||||
conf := p.loadConfigurationFromCRD(ctxLog, k8sClient)
|
conf := p.loadConfigurationFromCRD(ctxLog, k8sClient)
|
||||||
|
|
||||||
if reflect.DeepEqual(p.lastConfiguration.Get(), conf) {
|
confHash, err := hashstructure.Hash(conf, nil)
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
logger.Error("Unable to hash the configuration")
|
||||||
|
case p.lastConfiguration.Get() == confHash:
|
||||||
logger.Debugf("Skipping Kubernetes event kind %T", event)
|
logger.Debugf("Skipping Kubernetes event kind %T", event)
|
||||||
} else {
|
default:
|
||||||
p.lastConfiguration.Set(conf)
|
p.lastConfiguration.Set(confHash)
|
||||||
configurationChan <- dynamic.Message{
|
configurationChan <- dynamic.Message{
|
||||||
ProviderName: "kubernetescrd",
|
ProviderName: "kubernetescrd",
|
||||||
Configuration: conf,
|
Configuration: conf,
|
||||||
|
|
|
@ -209,8 +209,13 @@ func loadServers(client Client, namespace string, svc v1alpha1.Service) ([]dynam
|
||||||
|
|
||||||
var servers []dynamic.Server
|
var servers []dynamic.Server
|
||||||
if service.Spec.Type == corev1.ServiceTypeExternalName {
|
if service.Spec.Type == corev1.ServiceTypeExternalName {
|
||||||
|
protocol := "http"
|
||||||
|
if portSpec.Port == 443 || strings.HasPrefix(portSpec.Name, "https") {
|
||||||
|
protocol = "https"
|
||||||
|
}
|
||||||
|
|
||||||
servers = append(servers, dynamic.Server{
|
servers = append(servers, dynamic.Server{
|
||||||
URL: fmt.Sprintf("http://%s:%d", service.Spec.ExternalName, portSpec.Port),
|
URL: fmt.Sprintf("%s://%s:%d", protocol, service.Spec.ExternalName, portSpec.Port),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
endpoints, endpointsExists, endpointsErr := client.GetEndpoints(namespace, svc.Name)
|
endpoints, endpointsExists, endpointsErr := client.GetEndpoints(namespace, svc.Name)
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -16,9 +15,11 @@ import (
|
||||||
"github.com/containous/traefik/v2/pkg/config/dynamic"
|
"github.com/containous/traefik/v2/pkg/config/dynamic"
|
||||||
"github.com/containous/traefik/v2/pkg/job"
|
"github.com/containous/traefik/v2/pkg/job"
|
||||||
"github.com/containous/traefik/v2/pkg/log"
|
"github.com/containous/traefik/v2/pkg/log"
|
||||||
|
"github.com/containous/traefik/v2/pkg/provider"
|
||||||
"github.com/containous/traefik/v2/pkg/safe"
|
"github.com/containous/traefik/v2/pkg/safe"
|
||||||
"github.com/containous/traefik/v2/pkg/tls"
|
"github.com/containous/traefik/v2/pkg/tls"
|
||||||
"github.com/containous/traefik/v2/pkg/types"
|
"github.com/containous/traefik/v2/pkg/types"
|
||||||
|
"github.com/mitchellh/hashstructure"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/api/extensions/v1beta1"
|
"k8s.io/api/extensions/v1beta1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
|
@ -138,10 +139,14 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
|
||||||
// track more information about the dropped events.
|
// track more information about the dropped events.
|
||||||
conf := p.loadConfigurationFromIngresses(ctxLog, k8sClient)
|
conf := p.loadConfigurationFromIngresses(ctxLog, k8sClient)
|
||||||
|
|
||||||
if reflect.DeepEqual(p.lastConfiguration.Get(), conf) {
|
confHash, err := hashstructure.Hash(conf, nil)
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
logger.Error("Unable to hash the configuration")
|
||||||
|
case p.lastConfiguration.Get() == confHash:
|
||||||
logger.Debugf("Skipping Kubernetes event kind %T", event)
|
logger.Debugf("Skipping Kubernetes event kind %T", event)
|
||||||
} else {
|
default:
|
||||||
p.lastConfiguration.Set(conf)
|
p.lastConfiguration.Set(confHash)
|
||||||
configurationChan <- dynamic.Message{
|
configurationChan <- dynamic.Message{
|
||||||
ProviderName: "kubernetes",
|
ProviderName: "kubernetes",
|
||||||
Configuration: conf,
|
Configuration: conf,
|
||||||
|
@ -202,8 +207,13 @@ func loadService(client Client, namespace string, backend v1beta1.IngressBackend
|
||||||
}
|
}
|
||||||
|
|
||||||
if service.Spec.Type == corev1.ServiceTypeExternalName {
|
if service.Spec.Type == corev1.ServiceTypeExternalName {
|
||||||
|
protocol := "http"
|
||||||
|
if portSpec.Port == 443 || strings.HasPrefix(portSpec.Name, "https") {
|
||||||
|
protocol = "https"
|
||||||
|
}
|
||||||
|
|
||||||
servers = append(servers, dynamic.Server{
|
servers = append(servers, dynamic.Server{
|
||||||
URL: fmt.Sprintf("http://%s:%d", service.Spec.ExternalName, portSpec.Port),
|
URL: fmt.Sprintf("%s://%s:%d", protocol, service.Spec.ExternalName, portSpec.Port),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
endpoints, endpointsExists, endpointsErr := client.GetEndpoints(namespace, backend.ServiceName)
|
endpoints, endpointsExists, endpointsErr := client.GetEndpoints(namespace, backend.ServiceName)
|
||||||
|
@ -324,8 +334,7 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceName := ingress.Namespace + "-" + p.Backend.ServiceName + "-" + p.Backend.ServicePort.String()
|
serviceName := provider.Normalize(ingress.Namespace + "-" + p.Backend.ServiceName + "-" + p.Backend.ServicePort.String())
|
||||||
serviceName = strings.ReplaceAll(serviceName, ".", "-")
|
|
||||||
var rules []string
|
var rules []string
|
||||||
if len(rule.Host) > 0 {
|
if len(rule.Host) > 0 {
|
||||||
rules = []string{"Host(`" + rule.Host + "`)"}
|
rules = []string{"Host(`" + rule.Host + "`)"}
|
||||||
|
@ -335,10 +344,7 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
|
||||||
rules = append(rules, "PathPrefix(`"+p.Path+"`)")
|
rules = append(rules, "PathPrefix(`"+p.Path+"`)")
|
||||||
}
|
}
|
||||||
|
|
||||||
routerKey := strings.Replace(rule.Host, ".", "-", -1) + strings.Replace(p.Path, "/", "-", 1)
|
routerKey := strings.TrimPrefix(provider.Normalize(rule.Host+p.Path), "-")
|
||||||
if strings.HasPrefix(routerKey, "-") {
|
|
||||||
routerKey = strings.Replace(routerKey, "-", "", 1)
|
|
||||||
}
|
|
||||||
conf.HTTP.Routers[routerKey] = &dynamic.Router{
|
conf.HTTP.Routers[routerKey] = &dynamic.Router{
|
||||||
Rule: strings.Join(rules, " && "),
|
Rule: strings.Join(rules, " && "),
|
||||||
Service: serviceName,
|
Service: serviceName,
|
||||||
|
|
|
@ -94,8 +94,9 @@ func setupTracing(conf *static.Tracing) tracing.Backend {
|
||||||
|
|
||||||
if backend == nil {
|
if backend == nil {
|
||||||
log.WithoutContext().Debug("Could not initialize tracing, use Jaeger by default")
|
log.WithoutContext().Debug("Could not initialize tracing, use Jaeger by default")
|
||||||
backend := &jaeger.Config{}
|
bcd := &jaeger.Config{}
|
||||||
backend.SetDefaults()
|
bcd.SetDefaults()
|
||||||
|
backend = bcd
|
||||||
}
|
}
|
||||||
|
|
||||||
return backend
|
return backend
|
||||||
|
|
|
@ -254,6 +254,15 @@ func (ln tcpKeepAliveListener) Accept() (net.Conn, error) {
|
||||||
return tc, nil
|
return tc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type proxyProtocolLogger struct {
|
||||||
|
log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// Printf force log level to debug.
|
||||||
|
func (p proxyProtocolLogger) Printf(format string, v ...interface{}) {
|
||||||
|
p.Debugf(format, v...)
|
||||||
|
}
|
||||||
|
|
||||||
func buildProxyProtocolListener(ctx context.Context, entryPoint *static.EntryPoint, listener net.Listener) (net.Listener, error) {
|
func buildProxyProtocolListener(ctx context.Context, entryPoint *static.EntryPoint, listener net.Listener) (net.Listener, error) {
|
||||||
var sourceCheck func(addr net.Addr) (bool, error)
|
var sourceCheck func(addr net.Addr) (bool, error)
|
||||||
if entryPoint.ProxyProtocol.Insecure {
|
if entryPoint.ProxyProtocol.Insecure {
|
||||||
|
@ -280,7 +289,7 @@ func buildProxyProtocolListener(ctx context.Context, entryPoint *static.EntryPoi
|
||||||
|
|
||||||
return proxyprotocol.NewDefaultListener(listener).
|
return proxyprotocol.NewDefaultListener(listener).
|
||||||
WithSourceChecker(sourceCheck).
|
WithSourceChecker(sourceCheck).
|
||||||
WithLogger(log.FromContext(ctx)), nil
|
WithLogger(proxyProtocolLogger{Logger: log.FromContext(ctx)}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildListener(ctx context.Context, entryPoint *static.EntryPoint) (net.Listener, error) {
|
func buildListener(ctx context.Context, entryPoint *static.EntryPoint) (net.Listener, error) {
|
||||||
|
|
|
@ -189,7 +189,7 @@ func (c *Certificate) AppendCertificate(certs map[string]map[string]*tls.Certifi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if certExists {
|
if certExists {
|
||||||
log.Warnf("Skipping addition of certificate for domain(s) %q, to EntryPoint %s, as it already exists for this Entrypoint.", certKey, ep)
|
log.Debugf("Skipping addition of certificate for domain(s) %q, to EntryPoint %s, as it already exists for this Entrypoint.", certKey, ep)
|
||||||
} else {
|
} else {
|
||||||
log.Debugf("Adding certificate for domain(s) %s", certKey)
|
log.Debugf("Adding certificate for domain(s) %s", certKey)
|
||||||
certs[ep][certKey] = &tlsCert
|
certs[ep][certKey] = &tlsCert
|
||||||
|
|
|
@ -86,6 +86,7 @@ func derCert(privKey *rsa.PrivateKey, expiration time.Time, domain string) ([]by
|
||||||
NotAfter: expiration,
|
NotAfter: expiration,
|
||||||
|
|
||||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageKeyAgreement | x509.KeyUsageDataEncipherment,
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageKeyAgreement | x509.KeyUsageDataEncipherment,
|
||||||
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||||
BasicConstraintsValid: true,
|
BasicConstraintsValid: true,
|
||||||
DNSNames: []string{domain},
|
DNSNames: []string{domain},
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ type Config struct {
|
||||||
|
|
||||||
// SetDefaults sets the default values.
|
// SetDefaults sets the default values.
|
||||||
func (c *Config) SetDefaults() {
|
func (c *Config) SetDefaults() {
|
||||||
c.LocalAgentHost = "localhost"
|
|
||||||
c.LocalAgentPort = 42699
|
c.LocalAgentPort = 42699
|
||||||
c.LogLevel = "info"
|
c.LogLevel = "info"
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ fi
|
||||||
# Build binaries
|
# Build binaries
|
||||||
# shellcheck disable=SC2086
|
# shellcheck disable=SC2086
|
||||||
CGO_ENABLED=0 GOGC=off go build ${FLAGS[*]} -ldflags "-s -w \
|
CGO_ENABLED=0 GOGC=off go build ${FLAGS[*]} -ldflags "-s -w \
|
||||||
-X github.com/containous/traefik/pkg/v2/version.Version=$VERSION \
|
-X github.com/containous/traefik/v2/pkg/version.Version=$VERSION \
|
||||||
-X github.com/containous/traefik/pkg/v2/version.Codename=$CODENAME \
|
-X github.com/containous/traefik/v2/pkg/version.Codename=$CODENAME \
|
||||||
-X github.com/containous/traefik/pkg/v2/version.BuildDate=$DATE" \
|
-X github.com/containous/traefik/v2/pkg/version.BuildDate=$DATE" \
|
||||||
-a -installsuffix nocgo -o dist/traefik ./cmd/traefik
|
-a -installsuffix nocgo -o dist/traefik ./cmd/traefik
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM node:8.15.0
|
FROM node:12.11
|
||||||
|
|
||||||
ENV WEBUI_DIR /src/webui
|
ENV WEBUI_DIR /src/webui
|
||||||
RUN mkdir -p $WEBUI_DIR
|
RUN mkdir -p $WEBUI_DIR
|
||||||
|
|
|
@ -20,7 +20,7 @@ make generate-webui # Generate static contents in `traefik/static/` folder.
|
||||||
|
|
||||||
## How to build (only for frontend developer)
|
## How to build (only for frontend developer)
|
||||||
|
|
||||||
- prerequisite: [Node 6+](https://nodejs.org) [Npm](https://www.npmjs.com/)
|
- prerequisite: [Node 12.11+](https://nodejs.org) [Npm](https://www.npmjs.com/)
|
||||||
|
|
||||||
- Go to the directory `webui`
|
- Go to the directory `webui`
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue