Compare commits

...

20 Commits

Author SHA1 Message Date
Ludovic Fernandez
ecd51a1428 Prepare release v2.0.6 2019-12-02 18:14:05 +01:00
José Carlos Chávez
78097b96c9 Fix extraction for zipkin tracing 2019-12-02 14:18:07 +01:00
mpl
2af8589afd Do not give responsewriter or its headers to asynchronous logging goroutine
Co-authored-by: Julien Salleyron <julien.salleyron@gmail.com>
2019-12-02 03:14:04 +01:00
Jean-Baptiste Doumenjou
efcc9d51d4 Healthcheck managed for all related services
Co-authored-by: Mathieu Lonjaret <mathieu.lonjaret@gmail.com>
2019-11-29 12:40:05 +01:00
Daniel Tomcej
a87c104172 Remove Request Headers CORS Preflight Requirement 2019-11-28 15:24:06 +01:00
Руслан Корнев
b2c59be8de Update router entrypoint example 2019-11-27 20:08:03 +01:00
Damien Duportal
2685e06528 Add Swarm section to the Docker Provider Documentation 2019-11-27 17:12:04 +01:00
Damien Duportal
ba49012447 Mention the experimental Helm Chart in the installation section of documentation 2019-11-27 16:02:05 +01:00
Matthieu Hostache
407eda0ba0 Web UI: Avoid unnecessary duplicated api calls 2019-11-27 12:04:05 +01:00
Manuel Zapf
5b1dc0bfbd Change service name in rancher provider to make webui service details view work 2019-11-27 11:12:07 +01:00
Matthieu Hostache
00db3a0922 Web UI: Avoid some router properties to overflow their container 2019-11-23 23:18:04 +01:00
Jean-Baptiste Doumenjou
abdb3b9475 Uses, if it exists, the ping entry point provided in the static configuration
Co-authored-by: Ludovic Fernandez <ldez@users.noreply.github.com>
2019-11-20 18:34:05 +01:00
Matthieu Hostache
9761161163 Web UI: Fix displayed tcp service details 2019-11-20 18:26:10 +01:00
Ludovic Fernandez
e5104021b1 doc: remove double quotes on CLI flags. 2019-11-19 10:18:05 +01:00
Ludovic Fernandez
42a8d84a1f X-Forwarded-Proto must not skip the redirection. 2019-11-15 12:36:04 +01:00
kolaente
3fd330c2fb Update go-acme/lego to 3.2.0 2019-11-15 12:06:05 +01:00
Pascal Andy
8f340afca1 Add back the security section from v1 2019-11-15 10:48:05 +01:00
Ludovic Fernandez
e28d9426b9 doc: fix wrong acme information 2019-11-15 10:08:05 +01:00
Ludovic Fernandez
b3078b75cd fix: location header rewrite.
Co-authored-by: Daniel Tomcej <daniel.tomcej@gmail.com>
2019-11-15 07:50:04 +01:00
Blake Buthod
424b97994e Fixed spelling error 2019-11-15 00:42:04 +01:00
57 changed files with 752 additions and 370 deletions

View File

@@ -1,3 +1,29 @@
## [v2.0.6](https://github.com/containous/traefik/tree/v2.0.6) (2019-12-02)
[All Commits](https://github.com/containous/traefik/compare/v2.0.5...v2.0.6)
**Bug fixes:**
- **[acme]** Update go-acme/lego to 3.2.0 ([#5839](https://github.com/containous/traefik/pull/5839) by [kolaente](https://github.com/kolaente))
- **[cli,healthcheck]** Uses, if it exists, the ping entry point provided in the static configuration ([#5867](https://github.com/containous/traefik/pull/5867) by [jbdoumenjou](https://github.com/jbdoumenjou))
- **[healthcheck]** Healthcheck managed for all related services ([#5860](https://github.com/containous/traefik/pull/5860) by [jbdoumenjou](https://github.com/jbdoumenjou))
- **[logs,middleware]** Do not give responsewriter or its headers to asynchronous logging goroutine ([#5840](https://github.com/containous/traefik/pull/5840) by [mpl](https://github.com/mpl))
- **[middleware]** X-Forwarded-Proto must not skip the redirection. ([#5836](https://github.com/containous/traefik/pull/5836) by [ldez](https://github.com/ldez))
- **[middleware]** fix: location header rewrite. ([#5835](https://github.com/containous/traefik/pull/5835) by [ldez](https://github.com/ldez))
- **[middleware]** Remove Request Headers CORS Preflight Requirement ([#5903](https://github.com/containous/traefik/pull/5903) by [dtomcej](https://github.com/dtomcej))
- **[rancher]** Change service name in rancher provider to make webui service details view work ([#5895](https://github.com/containous/traefik/pull/5895) by [SantoDE](https://github.com/SantoDE))
- **[tracing]** Fix extraction for zipkin tracing ([#5920](https://github.com/containous/traefik/pull/5920) by [jcchavezs](https://github.com/jcchavezs))
- **[webui]** Web UI: Avoid unnecessary duplicated api calls ([#5884](https://github.com/containous/traefik/pull/5884) by [matthieuh](https://github.com/matthieuh))
- **[webui]** Web UI: Avoid some router properties to overflow their container ([#5872](https://github.com/containous/traefik/pull/5872) by [matthieuh](https://github.com/matthieuh))
- **[webui]** Web UI: Fix displayed tcp service details ([#5868](https://github.com/containous/traefik/pull/5868) by [matthieuh](https://github.com/matthieuh))
**Documentation:**
- **[acme]** doc: fix wrong acme information ([#5837](https://github.com/containous/traefik/pull/5837) by [ldez](https://github.com/ldez))
- **[docker,docker/swarm]** Add Swarm section to the Docker Provider Documentation ([#5874](https://github.com/containous/traefik/pull/5874) by [dduportal](https://github.com/dduportal))
- **[docker]** Update router entrypoint example ([#5766](https://github.com/containous/traefik/pull/5766) by [woto](https://github.com/woto))
- **[k8s/helm]** Mention the experimental Helm Chart in the installation section of documentation ([#5879](https://github.com/containous/traefik/pull/5879) by [dduportal](https://github.com/dduportal))
- doc: remove double quotes on CLI flags. ([#5862](https://github.com/containous/traefik/pull/5862) by [ldez](https://github.com/ldez))
- Fixed spelling error ([#5834](https://github.com/containous/traefik/pull/5834) by [blakebuthod](https://github.com/blakebuthod))
- Add back the security section from v1 ([#5832](https://github.com/containous/traefik/pull/5832) by [pascalandy](https://github.com/pascalandy))
## [v2.0.5](https://github.com/containous/traefik/tree/v2.0.5) (2019-11-14)
[All Commits](https://github.com/containous/traefik/compare/v2.0.4...v2.0.5)

View File

@@ -51,9 +51,14 @@ func Do(staticConfiguration static.Configuration) (*http.Response, error) {
return nil, errors.New("please enable `ping` to use health check")
}
pingEntryPoint, ok := staticConfiguration.EntryPoints["traefik"]
ep := staticConfiguration.Ping.EntryPoint
if ep == "" {
ep = "traefik"
}
pingEntryPoint, ok := staticConfiguration.EntryPoints[ep]
if !ok {
return nil, errors.New("missing `ping` entrypoint")
return nil, fmt.Errorf("ping: missing %s entry point", ep)
}
client := &http.Client{Timeout: 5 * time.Second}

View File

@@ -0,0 +1,16 @@
# Security
## Security Advisories
We strongly advise you to join our mailing list to be aware of the latest announcements from our security team.
You can subscribe sending a mail to security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
## CVE
Reported vulnerabilities can be found on
[cve.mitre.org](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=traefik).
## Report a Vulnerability
We want to keep Traefik safe for everyone.
If you've discovered a security vulnerability in Traefik, we appreciate your help in disclosing it to us in a responsible manner, using [this form](https://security.traefik.io).

View File

@@ -3,6 +3,7 @@
You can install Traefik with the following flavors:
* [Use the official Docker image](./#use-the-official-docker-image)
* [(Experimental) Use the Helm Chart](./#use-the-helm-chart)
* [Use the binary distribution](./#use-the-binary-distribution)
* [Compile your binary from the sources](./#compile-your-binary-from-the-sources)
@@ -24,6 +25,70 @@ For more details, go to the [Docker provider documentation](../providers/docker.
* Docker images are based from the [Alpine Linux Official image](https://hub.docker.com/_/alpine).
* All the orchestrator using docker images could fetch the official Traefik docker image.
## Use the Helm Chart
!!! warning "Experimental Helm Chart"
Please note that the Helm Chart for Traefik v2 is still experimental.
The Traefik Stable Chart from
[Helm's default charts repository](https://github.com/helm/charts/tree/master/stable/traefik) is still using [Traefik v1.7](https://docs.traefik.io/v1.7).
Traefik can be installed in Kubernetes using the v2.0 Helm chart from <https://github.com/containous/traefik-helm-chart>.
Ensure that the following requirements are met:
* Kubernetes 1.14+
* Helm version 2.x is [installed](https://v2.helm.sh/docs/using_helm/) and initialized with Tiller
Retrieve the latest chart version from the repository:
```bash
# Retrieve Chart from the repository
git clone https://github.com/containous/traefik-helm-chart
```
And install it with the `helm` command line:
```bash
helm install ./traefik-helm-chart
```
!!! tip "Helm Features"
All [Helm features](https://v2.helm.sh/docs/using_helm/#using-helm) are supported.
For instance, installing the chart in a dedicated namespace:
```bash tab="Install in a Dedicated Namespace"
# Install in the namespace "traefik-v2"
helm install --namespace=traefik-v2 \
./traefik-helm-chart
```
??? example "Installing with Custom Values"
You can customize the installation by specifying custom values,
as with [any helm chart](https://v2.helm.sh/docs/using_helm/#customizing-the-chart-before-installing).
{: #helm-custom-values }
The values are not (yet) documented, but are self-explanatory:
you can look at the [default `values.yaml`](https://github.com/containous/traefik-helm-chart/blob/master/values.yaml) file to explore possibilities.
Example of installation with logging set to `DEBUG`:
```bash tab="Using Helm CLI"
helm install --namespace=traefik-v2 \
--set="logs.loglevel=DEBUG" \
./traefik-helm-chart
```
```yml tab="With a custom values file"
# File custom-values.yml
## Install with "helm install --values=./custom-values.yml ./traefik-helm-chart
logs:
loglevel: DEBUG
```
## Use the Binary Distribution
Grab the latest binary from the [releases](https://github.com/containous/traefik/releases) page.

View File

@@ -47,11 +47,11 @@ You can configure Traefik to use an ACME provider (like Let's Encrypt) for autom
```
```bash tab="CLI"
--entryPoints.web.address=":80"
--entryPoints.websecure.address=":443"
--entryPoints.web.address=:80
--entryPoints.websecure.address=:443
# ...
--certificatesResolvers.sample.acme.email="your-email@your-domain.org"
--certificatesResolvers.sample.acme.storage="acme.json"
--certificatesResolvers.sample.acme.email=your-email@your-domain.org
--certificatesResolvers.sample.acme.storage=acme.json
# used during the challenge
--certificatesResolvers.sample.acme.httpChallenge.entryPoint=web
```
@@ -156,8 +156,8 @@ when using the `HTTP-01` challenge, `certificatesResolvers.sample.acme.httpChall
```
```bash tab="CLI"
--entryPoints.web.address=":80"
--entryPoints.websecure.address=":443"
--entryPoints.web.address=:80
--entryPoints.websecure.address=:443
# ...
--certificatesResolvers.sample.acme.httpChallenge.entryPoint=web
```
@@ -215,6 +215,7 @@ For example, `CF_API_EMAIL_FILE=/run/secrets/traefik_cf-api-email` could be used
| [ACME DNS](https://github.com/joohoi/acme-dns) | `acme-dns` | `ACME_DNS_API_BASE`, `ACME_DNS_STORAGE_PATH` | [Additional configuration](https://go-acme.github.io/lego/dns/acme-dns) |
| [Alibaba Cloud](https://www.alibabacloud.com) | `alidns` | `ALICLOUD_ACCESS_KEY`, `ALICLOUD_SECRET_KEY`, `ALICLOUD_REGION_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/alidns) |
| [Auroradns](https://www.pcextreme.com/aurora/dns) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` | [Additional configuration](https://go-acme.github.io/lego/dns/auroradns) |
| [Autodns](https://www.internetx.com/domains/autodns/) | `autodns` | `AUTODNS_API_USER`, `AUTODNS_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/autodns) |
| [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP`, `[AZURE_METADATA_ENDPOINT]` | [Additional configuration](https://go-acme.github.io/lego/dns/azure) |
| [Bindman](https://github.com/labbsr0x/bindman-dns-webhook) | `bindman` | `BINDMAN_MANAGER_ADDRESS` | [Additional configuration](https://go-acme.github.io/lego/dns/bindman) |
| [Blue Cat](https://www.bluecatnetworks.com/) | `bluecat` | `BLUECAT_SERVER_URL`, `BLUECAT_USER_NAME`, `BLUECAT_PASSWORD`, `BLUECAT_CONFIG_NAME`, `BLUECAT_DNS_VIEW` | [Additional configuration](https://go-acme.github.io/lego/dns/bluecat) |
@@ -311,7 +312,7 @@ certificatesResolvers:
```bash tab="CLI"
# ...
--certificatesResolvers.sample.acme.dnsChallenge.resolvers:="1.1.1.1:53,8.8.8.8:53"
--certificatesResolvers.sample.acme.dnsChallenge.resolvers:=1.1.1.1:53,8.8.8.8:53
```
#### Wildcard Domains
@@ -341,7 +342,7 @@ As described in [Let's Encrypt's post](https://community.letsencrypt.org/t/stagi
```bash tab="CLI"
# ...
--certificatesResolvers.sample.acme.caServer="https://acme-staging-v02.api.letsencrypt.org/directory"
--certificatesResolvers.sample.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory
# ...
```

View File

@@ -4,13 +4,13 @@
#
# Required
#
--certificatesResolvers.sample.acme.email="test@traefik.io"
--certificatesResolvers.sample.acme.email=test@traefik.io
# File or key used for certificates storage.
#
# Required
#
--certificatesResolvers.sample.acme.storage="acme.json"
--certificatesResolvers.sample.acme.storage=acme.json
# CA server to use.
# Uncomment the line to use Let's Encrypt's staging server,
@@ -19,7 +19,7 @@
# Optional
# Default: "https://acme-v02.api.letsencrypt.org/directory"
#
--certificatesResolvers.sample.acme.caServer="https://acme-staging-v02.api.letsencrypt.org/directory"
--certificatesResolvers.sample.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory
# KeyType to use.
#
@@ -75,7 +75,7 @@
# Optional
# Default: empty
#
--certificatesResolvers.sample.acme.dnsChallenge.resolvers="1.1.1.1:53,8.8.8.8:53"
--certificatesResolvers.sample.acme.dnsChallenge.resolvers=1.1.1.1:53,8.8.8.8:53
# Disable the DNS propagation checks before notifying ACME that the DNS challenge is ready.
#

View File

@@ -718,11 +718,11 @@ with the path `/admin` stripped, e.g. to `http://<IP>:<port>/`. In this case, yo
```
```bash tab="CLI"
--entryPoints.web.address=":80"
--entryPoints.websecure.address=":443"
--certificatesResolvers.sample.acme.email: your-email@your-domain.org
--certificatesResolvers.sample.acme.storage: acme.json
--certificatesResolvers.sample.acme.httpChallenge.entryPoint: web
--entryPoints.web.address=:80
--entryPoints.websecure.address=:443
--certificatesResolvers.sample.acme.email=your-email@your-domain.org
--certificatesResolvers.sample.acme.storage=acme.json
--certificatesResolvers.sample.acme.httpChallenge.entryPoint=web
```
## Traefik Logs
@@ -744,9 +744,9 @@ There is no more log configuration at the root level.
```
```bash tab="CLI"
--logLevel="DEBUG"
--traefikLog.filePath="/path/to/traefik.log"
--traefikLog.format="json"
--logLevel=DEBUG
--traefikLog.filePath=/path/to/traefik.log
--traefikLog.format=json
```
!!! info "v2"
@@ -768,9 +768,9 @@ There is no more log configuration at the root level.
```
```bash tab="CLI"
--log.level="DEBUG"
--log.filePath="/path/to/traefik.log"
--log.format="json"
--log.level=DEBUG
--log.filePath=/path/to/traefik.log
--log.format=json
```
## Tracing
@@ -794,12 +794,12 @@ Traefik v2 retains OpenTracing support. The `backend` root option from the v1 is
```
```bash tab="CLI"
--tracing.backend="jaeger"
--tracing.servicename="tracing"
--tracing.jaeger.localagenthostport="12.0.0.1:6831"
--tracing.jaeger.samplingparam="1.0"
--tracing.jaeger.samplingserverurl="http://12.0.0.1:5778/sampling"
--tracing.jaeger.samplingtype="const"
--tracing.backend=jaeger
--tracing.servicename=tracing
--tracing.jaeger.localagenthostport=12.0.0.1:6831
--tracing.jaeger.samplingparam=1.0
--tracing.jaeger.samplingserverurl=http://12.0.0.1:5778/sampling
--tracing.jaeger.samplingtype=const
```
!!! info "v2"
@@ -827,11 +827,11 @@ Traefik v2 retains OpenTracing support. The `backend` root option from the v1 is
```
```bash tab="CLI"
--tracing.servicename="tracing"
--tracing.jaeger.localagenthostport="12.0.0.1:6831"
--tracing.jaeger.samplingparam="1.0"
--tracing.jaeger.samplingserverurl="http://12.0.0.1:5778/sampling"
--tracing.jaeger.samplingtype="const"
--tracing.servicename=tracing
--tracing.jaeger.localagenthostport=12.0.0.1:6831
--tracing.jaeger.samplingparam=1.0
--tracing.jaeger.samplingserverurl=http://12.0.0.1:5778/sampling
--tracing.jaeger.samplingtype=const
```
## Metrics
@@ -852,7 +852,7 @@ For a basic configuration, the [metrics configuration](../observability/metrics/
```bash tab="CLI"
--metrics.prometheus.buckets=[0.1,0.3,1.2,5.0]
--metrics.prometheus.entrypoint="traefik"
--metrics.prometheus.entrypoint=traefik
```
!!! info "v2"
@@ -878,7 +878,7 @@ For a basic configuration, the [metrics configuration](../observability/metrics/
```bash tab="CLI"
--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
@@ -908,14 +908,14 @@ Each root item has been moved to a related section or removed.
```bash tab="CLI"
--checknewversion=false
--sendanonymoususage=true
--loglevel="DEBUG"
--loglevel=DEBUG
--insecureskipverify=true
--rootcas="/mycert.cert"
--rootcas=/mycert.cert
--maxidleconnsperhost=200
--providersthrottleduration="2s"
--providersthrottleduration=2s
--allowminweightzero=true
--debug=true
--defaultentrypoints="web","web-secure"
--defaultentrypoints=web,web-secure
--keeptrailingslash=true
```
@@ -961,9 +961,9 @@ Each root item has been moved to a related section or removed.
```bash tab="CLI"
--global.checknewversion=true
--global.sendanonymoususage=true
--log.level="DEBUG"
--log.level=DEBUG
--serverstransport.insecureskipverify=true
--serverstransport.rootcas="/mycert.cert"
--serverstransport.rootcas=/mycert.cert
--serverstransport.maxidleconnsperhost=42
--providers.providersthrottleduration=42
```
@@ -1029,7 +1029,7 @@ As the dashboard access is now secured by default you can either:
[api]
[providers.file]
filename = "/dynamic-conf.toml"
filename = "/dynamic-conf.toml"
##---------------------##

View File

@@ -61,7 +61,7 @@ accessLog:
```bash tab="CLI"
# Configuring a buffer of 100 lines
--accesslog=true
--accesslog.filepath="/path/to/access.log"
--accesslog.filepath=/path/to/access.log
--accesslog.bufferingsize=100
```
@@ -104,11 +104,11 @@ accessLog:
```bash tab="CLI"
# Configuring Multiple Filters
--accesslog=true
--accesslog.filepath="/path/to/access.log"
--accesslog.format="json"
--accesslog.filters.statuscodes="200, 300-302"
--accesslog.filepath=/path/to/access.log
--accesslog.format=json
--accesslog.filters.statuscodes=200,300-302
--accesslog.filters.retryattempts
--accesslog.filters.minduration="10ms"
--accesslog.filters.minduration=10ms
```
### Limiting the Fields
@@ -164,14 +164,14 @@ accessLog:
```bash tab="CLI"
# Limiting the Logs to Specific Fields
--accesslog=true
--accesslog.filepath="/path/to/access.log"
--accesslog.format="json"
--accesslog.fields.defaultmode="keep"
--accesslog.fields.names.ClientUsername="drop"
--accesslog.fields.headers.defaultmode="keep"
--accesslog.fields.headers.names.User-Agent="redact"
--accesslog.fields.headers.names.Authorization="drop"
--accesslog.fields.headers.names.Content-Type="keep"
--accesslog.filepath=/path/to/access.log
--accesslog.format=json
--accesslog.fields.defaultmode=keep
--accesslog.fields.names.ClientUsername=drop
--accesslog.fields.headers.defaultmode=keep
--accesslog.fields.headers.names.User-Agent=redact
--accesslog.fields.headers.names.Authorization=drop
--accesslog.fields.headers.names.Content-Type=keep
```
??? info "Available Fields"

View File

@@ -30,7 +30,7 @@ log:
```bash tab="CLI"
# Writing Logs to a File
--log.filePath="/path/to/traefik.log"
--log.filePath=/path/to/traefik.log
```
#### `format`
@@ -53,8 +53,8 @@ log:
```bash tab="CLI"
# Writing Logs to a File, in JSON
--log.filePath="/path/to/traefik.log"
--log.format="json"
--log.filePath=/path/to/traefik.log
--log.format=json
```
#### `level`
@@ -72,7 +72,7 @@ log:
```
```bash tab="CLI"
--log.level="DEBUG"
--log.level=DEBUG
```
## Log Rotation

View File

@@ -35,7 +35,7 @@ metrics:
```
```bash tab="CLI"
--metrics.datadog.address="127.0.0.1:8125"
--metrics.datadog.address=127.0.0.1:8125
```
#### `addEntryPointsLabels`

View File

@@ -35,7 +35,7 @@ metrics:
```
```bash tab="CLI"
--metrics.influxdb.address="localhost:8089"
--metrics.influxdb.address=localhost:8089
```
#### `protocol`
@@ -57,7 +57,7 @@ metrics:
```
```bash tab="CLI"
--metrics.influxdb.protocol="udp"
--metrics.influxdb.protocol=udp
```
#### `database`
@@ -69,17 +69,17 @@ InfluxDB database used when protocol is http.
```toml tab="File (TOML)"
[metrics]
[metrics.influxDB]
database = ""
database = "db"
```
```yaml tab="File (YAML)"
metrics:
influxDB:
database: ""
database: "db"
```
```bash tab="CLI"
--metrics.influxdb.database=""
--metrics.influxdb.database=db
```
#### `retentionPolicy`
@@ -91,17 +91,17 @@ InfluxDB retention policy used when protocol is http.
```toml tab="File (TOML)"
[metrics]
[metrics.influxDB]
retentionPolicy = ""
retentionPolicy = "two_hours"
```
```yaml tab="File (YAML)"
metrics:
influxDB:
retentionPolicy: ""
retentionPolicy: "two_hours"
```
```bash tab="CLI"
--metrics.influxdb.retentionPolicy=""
--metrics.influxdb.retentionPolicy=two_hours
```
#### `username`
@@ -113,17 +113,17 @@ InfluxDB username (only with http).
```toml tab="File (TOML)"
[metrics]
[metrics.influxDB]
username = ""
username = "john"
```
```yaml tab="File (YAML)"
metrics:
influxDB:
username: ""
username: "john"
```
```bash tab="CLI"
--metrics.influxdb.username=""
--metrics.influxdb.username=john
```
#### `password`
@@ -135,17 +135,17 @@ InfluxDB password (only with http).
```toml tab="File (TOML)"
[metrics]
[metrics.influxDB]
password = ""
password = "secret"
```
```yaml tab="File (YAML)"
metrics:
influxDB:
password: ""
password: "secret"
```
```bash tab="CLI"
--metrics.influxdb.password=""
--metrics.influxdb.password=secret
```
#### `addEntryPointsLabels`

View File

@@ -113,6 +113,6 @@ metrics:
```
```bash tab="CLI"
--entryPoints.metrics.address=":8082"
--metrics.prometheus.entryPoint="metrics"
--entryPoints.metrics.address=:8082
--metrics.prometheus.entryPoint=metrics
```

View File

@@ -35,7 +35,7 @@ metrics:
```
```bash tab="CLI"
--metrics.statsd.address="localhost:8125"
--metrics.statsd.address=localhost:8125
```
#### `addEntryPointsLabels`

View File

@@ -35,7 +35,7 @@ tracing:
```
```bash tab="CLI"
--tracing.datadog.localAgentHostPort="127.0.0.1:8126"
--tracing.datadog.localAgentHostPort=127.0.0.1:8126
```
#### `debug`
@@ -79,7 +79,7 @@ tracing:
```
```bash tab="CLI"
--tracing.datadog.globalTag="sample"
--tracing.datadog.globalTag=sample
```
#### `prioritySampling`

View File

@@ -35,7 +35,7 @@ tracing:
```
```bash tab="CLI"
--tracing.haystack.localAgentHost="127.0.0.1"
--tracing.haystack.localAgentHost=127.0.0.1
```
#### `localAgentPort`
@@ -79,7 +79,7 @@ tracing:
```
```bash tab="CLI"
--tracing.haystack.globalTag="sample:test"
--tracing.haystack.globalTag=sample:test
```
#### `traceIDHeaderName`
@@ -101,7 +101,7 @@ tracing:
```
```bash tab="CLI"
--tracing.haystack.traceIDHeaderName="sample"
--tracing.haystack.traceIDHeaderName=sample
```
#### `parentIDHeaderName`
@@ -123,7 +123,7 @@ tracing:
```
```bash tab="CLI"
--tracing.haystack.parentIDHeaderName="sample"
--tracing.haystack.parentIDHeaderName=sample
```
#### `spanIDHeaderName`
@@ -168,5 +168,5 @@ tracing:
```bash tab="CLI"
--tracing.haystack.baggagePrefixHeaderName="sample"
--tracing.haystack.baggagePrefixHeaderName=sample
```

View File

@@ -35,7 +35,7 @@ tracing:
```
```bash tab="CLI"
--tracing.instana.localAgentHost="127.0.0.1"
--tracing.instana.localAgentHost=127.0.0.1
```
#### `localAgentPort`
@@ -86,5 +86,5 @@ tracing:
```
```bash tab="CLI"
--tracing.instana.logLevel="info"
--tracing.instana.logLevel=info
```

View File

@@ -39,7 +39,7 @@ tracing:
```
```bash tab="CLI"
--tracing.jaeger.samplingServerURL="http://localhost:5778/sampling"
--tracing.jaeger.samplingServerURL=http://localhost:5778/sampling
```
#### `samplingType`
@@ -61,7 +61,7 @@ tracing:
```
```bash tab="CLI"
--tracing.jaeger.samplingType="const"
--tracing.jaeger.samplingType=const
```
#### `samplingParam`
@@ -89,7 +89,7 @@ tracing:
```
```bash tab="CLI"
--tracing.jaeger.samplingParam="1.0"
--tracing.jaeger.samplingParam=1.0
```
#### `localAgentHostPort`
@@ -111,7 +111,7 @@ tracing:
```
```bash tab="CLI"
--tracing.jaeger.localAgentHostPort="127.0.0.1:6831"
--tracing.jaeger.localAgentHostPort=127.0.0.1:6831
```
#### `gen128Bit`
@@ -159,7 +159,7 @@ tracing:
```
```bash tab="CLI"
--tracing.jaeger.propagation="jaeger"
--tracing.jaeger.propagation=jaeger
```
#### `traceContextHeaderName`
@@ -182,7 +182,7 @@ tracing:
```
```bash tab="CLI"
--tracing.jaeger.traceContextHeaderName="uber-trace-id"
--tracing.jaeger.traceContextHeaderName=uber-trace-id
```
### `collector`
@@ -206,7 +206,7 @@ tracing:
```
```bash tab="CLI"
--tracing.jaeger.collector.endpoint="http://127.0.0.1:14268/api/traces?format=jaeger.thrift"
--tracing.jaeger.collector.endpoint=http://127.0.0.1:14268/api/traces?format=jaeger.thrift
```
#### `user`
@@ -229,7 +229,7 @@ tracing:
```
```bash tab="CLI"
--tracing.jaeger.collector.user="my-user"
--tracing.jaeger.collector.user=my-user
```
#### `password`
@@ -252,5 +252,5 @@ tracing:
```
```bash tab="CLI"
--tracing.jaeger.collector.password="my-password"
--tracing.jaeger.collector.password=my-password
```

View File

@@ -52,7 +52,7 @@ tracing:
```
```bash tab="CLI"
--tracing.serviceName="traefik"
--tracing.serviceName=traefik
```
#### `spanNameLimit`

View File

@@ -35,7 +35,7 @@ tracing:
```
```bash tab="CLI"
--tracing.zipkin.httpEndpoint="http://localhost:9411/api/v2/spans"
--tracing.zipkin.httpEndpoint=http://localhost:9411/api/v2/spans
```
#### `sameSpan`
@@ -101,5 +101,5 @@ tracing:
```
```bash tab="CLI"
--tracing.zipkin.sampleRate="0.2"
--tracing.zipkin.sampleRate=0.2
```

View File

@@ -55,6 +55,6 @@ ping:
```
```bash tab="CLI"
--entryPoints.ping.address=":8082"
--ping.entryPoint="ping"
--entryPoints.ping.address=:8082
--ping.entryPoint=ping
```

View File

@@ -7,6 +7,9 @@ A Story of Labels & Containers
Attach labels to your containers and let Traefik do the rest!
Traefik works with both [Docker (standalone) Engine](https://docs.docker.com/engine/)
and [Docker Swarm Mode](https://docs.docker.com/engine/swarm/).
!!! tip "The Quick Start Uses Docker"
If you haven't already, maybe you'd like to go through the [quick start](../getting-started/quick-start.md) that uses the docker provider!
@@ -64,7 +67,7 @@ Attach labels to your containers and let Traefik do the rest!
```
```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
```
@@ -80,15 +83,136 @@ Attach labels to your containers and let Traefik do the rest!
- traefik.http.services.my-container-service.loadbalancer.server.port=8080
```
!!! important "Labels in Docker Swarm Mode"
While in Swarm Mode, Traefik uses labels found on services, not on individual containers.
Therefore, if you use a compose file with Swarm Mode, labels should be defined in the `deploy` part of your service.
This behavior is only enabled for docker-compose version 3+ ([Compose file reference](https://docs.docker.com/compose/compose-file/#labels-1)).
## Routing Configuration
See the dedicated section in [routing](../routing/providers/docker.md).
When using Docker as a [provider](https://docs.traefik.io/providers/overview/),
Trafik uses [container labels](https://docs.docker.com/engine/reference/commandline/run/#set-metadata-on-container--l---label---label-file) to retrieve its routing configuration.
See the list of labels in the dedicated [routing](../routing/providers/docker.md) section.
### Routing Configuration with Labels
By default, Traefik watches for [container level labels](https://docs.docker.com/config/labels-custom-metadata/) on a standalone Docker Engine.
When using Docker Compose, labels are specified by the directive
[`labels`](https://docs.docker.com/compose/compose-file/#labels) from the
["services" objects](https://docs.docker.com/compose/compose-file/#service-configuration-reference).
!!! tip "Not Only Docker"
Please note that any tool like Nomad, Terraform, Ansible, etc.
that is able to define a Docker container with labels can work
with Traefik & the Docker provider.
### Port Detection
Traefik retrieves the private IP and port of containers from the Docker API.
Ports detection works as follows:
- If a container [exposes](https://docs.docker.com/engine/reference/builder/#expose) only one port,
then Traefik uses this port for private communication.
- If a container [exposes](https://docs.docker.com/engine/reference/builder/#expose) multiple ports,
or does not expose any port, then you must manually specify which port Traefik should use for communication
by using the label `traefik.http.services.<service_name>.loadbalancer.server.port`
(Read more on this label in the dedicated section in [routing](../routing/providers/docker.md#port)).
### Docker API Access
Traefik requires access to the docker socket to get its dynamic configuration.
You can specify which Docker API Endpoint to use with the directive [`endpoint`](#endpoint).
!!! warning "Security Note"
Accessing the Docker API without any restriction is a security concern:
If Traefik is attacked, then the attacker might get access to the underlying host.
{: #security-note }
As explained in the Docker documentation: ([Docker Daemon Attack Surface page](https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface)):
!!! quote
[...] only **trusted** users should be allowed to control your Docker daemon [...]
??? success "Solutions"
Expose the Docker socket over TCP, instead of the default Unix socket file.
It allows different implementation levels of the [AAA (Authentication, Authorization, Accounting) concepts](https://en.wikipedia.org/wiki/AAA_(computer_security)), depending on your security assessment:
- Authentication with Client Certificates as described in ["Protect the Docker daemon socket."](https://docs.docker.com/engine/security/https/)
- Authorize and filter requests to restrict possible actions with [the TecnativaDocker Socket Proxy](https://github.com/Tecnativa/docker-socket-proxy).
- Authorization with the [Docker Authorization Plugin Mechanism](https://docs.docker.com/engine/extend/plugins_authorization/)
- Accounting at networking level, by exposing the socket only inside a Docker private network, only available for Traefik.
- Accounting at container level, by exposing the socket on a another container than Traefik's.
With Swarm mode, it allows scheduling of Traefik on worker nodes, with only the "socket exposer" container on the manager nodes.
- Accounting at kernel level, by enforcing kernel calls with mechanisms like [SELinux](https://en.wikipedia.org/wiki/Security-Enhanced_Linux), to only allows an identified set of actions for Traefik's process (or the "socket exposer" process).
??? info "More Resources and Examples"
- ["Paranoid about mounting /var/run/docker.sock?"](https://medium.com/@containeroo/traefik-2-0-paranoid-about-mounting-var-run-docker-sock-22da9cb3e78c)
- [Traefik and Docker: A Discussion with Docker Captain, Bret Fisher](https://blog.containo.us/traefik-and-docker-a-discussion-with-docker-captain-bret-fisher-7f0b9a54ff88)
- [KubeCon EU 2018 Keynote, Running with Scissors, from Liz Rice](https://www.youtube.com/watch?v=ltrV-Qmh3oY)
- [Don't expose the Docker socket (not even to a container)](https://www.lvh.io/posts/dont-expose-the-docker-socket-not-even-to-a-container/)
- [A thread on Stack Overflow about sharing the `/var/run/docker.sock` file](https://news.ycombinator.com/item?id=17983623)
- [To DinD or not to DinD](https://blog.loof.fr/2018/01/to-dind-or-not-do-dind.html)
- [Traefik issue GH-4174 about security with Docker socket](https://github.com/containous/traefik/issues/4174)
- [Inspecting Docker Activity with Socat](https://developers.redhat.com/blog/2015/02/25/inspecting-docker-activity-with-socat/)
- [Letting Traefik run on Worker Nodes](https://blog.mikesir87.io/2018/07/letting-traefik-run-on-worker-nodes/)
- [Docker Socket Proxy from Tecnativa](https://github.com/Tecnativa/docker-socket-proxy)
## Docker Swarm Mode
To enable Docker Swarm (instead of standalone Docker) as a configuration provider,
set the [`swarmMode`](#swarmmode) directive to `true`.
### Routing Configuration with Labels
While in Swarm Mode, Traefik uses labels found on services, not on individual containers.
Therefore, if you use a compose file with Swarm Mode, labels should be defined in the
[`deploy`](https://docs.docker.com/compose/compose-file/#labels-1) part of your service.
This behavior is only enabled for docker-compose version 3+ ([Compose file reference](https://docs.docker.com/compose/compose-file)).
### Port Detection
Docker Swarm does not provide any [port detection](#port-detection) information to Traefik.
Therefore you **must** specify the port to use for communication by using the label `traefik.http.services.<service_name>.loadbalancer.server.port`
(Check the reference for this label in the [routing section for Docker](../routing/providers/docker.md#port)).
### Docker API Access
Docker Swarm Mode follows the same rules as Docker [API Access](#docker-api-access).
As the Swarm API is only exposed on the [manager nodes](https://docs.docker.com/engine/swarm/how-swarm-mode-works/nodes/#manager-nodes), you should schedule Traefik on the Swarm manager nodes by default,
by deploying Traefik with a [constraint](https://success.docker.com/article/using-contraints-and-labels-to-control-the-placement-of-containers) on the node's "role":
```shell tab="With Docker CLI"
docker service create \
--constraint=node.role==manager \
#... \
```
```yml tab="With Docker Compose"
version: '3'
services:
traefik:
# ...
deploy:
placement:
constraints:
- node.role == manager
```
!!! tip "Scheduling Traefik on Worker Nodes"
Following the guidelines given in the previous section ["Docker API Access"](#docker-api-access),
if you expose the Docker API through TCP, then Traefik can be scheduled on any node if the TCP
socket is reachable.
Please consider the security implications by reading the [Security Note](#security-note).
A good example can be found on [Bret Fisher's repository](https://github.com/BretFisher/dogvscat/blob/master/stack-proxy-global.yml#L124).
## Provider Configuration
@@ -108,51 +232,10 @@ providers:
```
```bash tab="CLI"
--providers.docker.endpoint="unix:///var/run/docker.sock"
--providers.docker.endpoint=unix:///var/run/docker.sock
```
Traefik requires access to the docker socket to get its dynamic configuration.
??? warning "Security Notes"
Depending on your context, accessing the Docker API without any restriction can be a security concern: If Traefik is attacked, then the attacker might get access to the Docker (or Swarm Mode) backend.
As explained in the Docker documentation: ([Docker Daemon Attack Surface page](https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface)):
`[...] only **trusted** users should be allowed to control your Docker daemon [...]`
!!! tip "Improved Security"
[TraefikEE](https://containo.us/traefikee) solves this problem by separating the control plane (connected to Docker) and the data plane (handling the requests).
??? info "Resources about Docker's Security"
- [KubeCon EU 2018 Keynote, Running with Scissors, from Liz Rice](https://www.youtube.com/watch?v=ltrV-Qmh3oY)
- [Don't expose the Docker socket (not even to a container)](https://www.lvh.io/posts/dont-expose-the-docker-socket-not-even-to-a-container/)
- [A thread on Stack Overflow about sharing the `/var/run/docker.sock` file](https://news.ycombinator.com/item?id=17983623)
- [To DinD or not to DinD](https://blog.loof.fr/2018/01/to-dind-or-not-do-dind.html)
??? tip "Security Compensation"
Expose the Docker socket over TCP, instead of the default Unix socket file.
It allows different implementation levels of the [AAA (Authentication, Authorization, Accounting) concepts](https://en.wikipedia.org/wiki/AAA_(computer_security)), depending on your security assessment:
- Authentication with Client Certificates as described in ["Protect the Docker daemon socket."](https://docs.docker.com/engine/security/https/)
- Authorization with the [Docker Authorization Plugin Mechanism](https://docs.docker.com/engine/extend/plugins_authorization/)
- Accounting at networking level, by exposing the socket only inside a Docker private network, only available for Traefik.
- Accounting at container level, by exposing the socket on a another container than Traefik's.
With Swarm mode, it allows scheduling of Traefik on worker nodes, with only the "socket exposer" container on the manager nodes.
- Accounting at kernel level, by enforcing kernel calls with mechanisms like [SELinux](https://en.wikipedia.org/wiki/Security-Enhanced_Linux), to only allows an identified set of actions for Traefik's process (or the "socket exposer" process).
??? info "Additional Resources"
- [Traefik issue GH-4174 about security with Docker socket](https://github.com/containous/traefik/issues/4174)
- [Inspecting Docker Activity with Socat](https://developers.redhat.com/blog/2015/02/25/inspecting-docker-activity-with-socat/)
- [Letting Traefik run on Worker Nodes](https://blog.mikesir87.io/2018/07/letting-traefik-run-on-worker-nodes/)
- [Docker Socket Proxy from Tecnativa](https://github.com/Tecnativa/docker-socket-proxy)
!!! info "Traefik & Swarm Mode"
To let Traefik access the Docker Socket of the Swarm manager, it is mandatory to schedule Traefik on the Swarm manager nodes.
See the sections [Docker API Access](#docker-api-access) and [Docker Swarm API Access](#docker-api-access_1) for more information.
??? example "Using the docker.sock"
@@ -186,7 +269,7 @@ Traefik requires access to the docker socket to get its dynamic configuration.
```
```bash tab="CLI"
--providers.docker.endpoint="unix:///var/run/docker.sock"
--providers.docker.endpoint=unix:///var/run/docker.sock
# ...
```
@@ -311,7 +394,7 @@ providers:
```
```bash tab="CLI"
--providers.docker.defaultRule="Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
--providers.docker.defaultRule=Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)
# ...
```
@@ -343,7 +426,7 @@ providers:
# ...
```
Activates the Swarm Mode.
Activates the Swarm Mode (instead of standalone Docker).
### `swarmModeRefreshSeconds`
@@ -375,19 +458,19 @@ _Optional, Default=""_
```toml tab="File (TOML)"
[providers.docker]
constraints = "Label(`a.label.name`, `foo`)"
constraints = "Label(`a.label.name`,`foo`)"
# ...
```
```yaml tab="File (YAML)"
providers:
docker:
constraints: "Label(`a.label.name`, `foo`)"
constraints: "Label(`a.label.name`,`foo`)"
# ...
```
```bash tab="CLI"
--providers.docker.constraints="Label(`a.label.name`, `foo`)"
--providers.docker.constraints=Label(`a.label.name`,`foo`)
# ...
```

View File

@@ -32,7 +32,7 @@ providers:
```
```bash tab="CLI"
--providers.kubernetescrd.endpoint="http://localhost:8080"
--providers.kubernetescrd.endpoint=http://localhost:8080
```
The Kubernetes server endpoint as URL.
@@ -66,7 +66,7 @@ providers:
```
```bash tab="CLI"
--providers.kubernetescrd.token="mytoken"
--providers.kubernetescrd.token=mytoken
```
Bearer token used for the Kubernetes client configuration.
@@ -89,7 +89,7 @@ providers:
```
```bash tab="CLI"
--providers.kubernetescrd.certauthfilepath="/my/ca.crt"
--providers.kubernetescrd.certauthfilepath=/my/ca.crt
```
Path to the certificate authority file.
@@ -115,7 +115,7 @@ providers:
```
```bash tab="CLI"
--providers.kubernetescrd.namespaces="default,production"
--providers.kubernetescrd.namespaces=default,production
```
Array of namespaces to watch.
@@ -164,7 +164,7 @@ providers:
```
```bash tab="CLI"
--providers.kubernetescrd.ingressclass="traefik-internal"
--providers.kubernetescrd.ingressclass=traefik-internal
```
Value of `kubernetes.io/ingress.class` annotation that identifies Ingress objects to be processed.
@@ -190,7 +190,7 @@ providers:
```
```bash tab="CLI"
--providers.kubernetescrd.throttleDuration="10s"
--providers.kubernetescrd.throttleDuration=10s
```
## Further

View File

@@ -67,7 +67,7 @@ providers:
```
```bash tab="CLI"
--providers.kubernetesingress.endpoint="http://localhost:8080"
--providers.kubernetesingress.endpoint=http://localhost:8080
```
The Kubernetes server endpoint as URL, which is only used when the behavior based on environment variables described below does not apply.
@@ -99,7 +99,7 @@ providers:
```
```bash tab="CLI"
--providers.kubernetesingress.token="mytoken"
--providers.kubernetesingress.token=mytoken
```
Bearer token used for the Kubernetes client configuration.
@@ -122,7 +122,7 @@ providers:
```
```bash tab="CLI"
--providers.kubernetesingress.certauthfilepath="/my/ca.crt"
--providers.kubernetesingress.certauthfilepath=/my/ca.crt
```
Path to the certificate authority file.
@@ -171,7 +171,7 @@ providers:
```
```bash tab="CLI"
--providers.kubernetesingress.namespaces="default,production"
--providers.kubernetesingress.namespaces=default,production
```
Array of namespaces to watch.
@@ -220,7 +220,7 @@ providers:
```
```bash tab="CLI"
--providers.kubernetesingress.ingressclass="traefik-internal"
--providers.kubernetesingress.ingressclass=traefik-internal
```
Value of `kubernetes.io/ingress.class` annotation that identifies Ingress objects to be processed.
@@ -249,7 +249,7 @@ providers:
```
```bash tab="CLI"
--providers.kubernetesingress.ingressendpoint.hostname="foo.com"
--providers.kubernetesingress.ingressendpoint.hostname=foo.com
```
Hostname used for Kubernetes Ingress endpoints.
@@ -273,7 +273,7 @@ providers:
```
```bash tab="CLI"
--providers.kubernetesingress.ingressendpoint.ip="1.2.3.4"
--providers.kubernetesingress.ingressendpoint.ip=1.2.3.4
```
IP used for Kubernetes Ingress endpoints.
@@ -297,7 +297,7 @@ providers:
```
```bash tab="CLI"
--providers.kubernetesingress.ingressendpoint.publishedservice="foo-service"
--providers.kubernetesingress.ingressendpoint.publishedservice=foo-service
```
Published Kubernetes Service to copy status from.
@@ -320,7 +320,7 @@ providers:
```
```bash tab="CLI"
--providers.kubernetesingress.throttleDuration="10s"
--providers.kubernetesingress.throttleDuration=10s
```
## Further

View File

@@ -74,8 +74,8 @@ providers:
```
```bash tab="CLI"
--providers.marathon.basic.httpbasicauthuser="foo"
--providers.marathon.basic.httpbasicpassword="bar"
--providers.marathon.basic.httpbasicauthuser=foo
--providers.marathon.basic.httpbasicpassword=bar
```
Enables Marathon basic authentication.
@@ -98,7 +98,7 @@ providers:
```
```bash tab="CLI"
--providers.marathon.dcosToken="xxxxxx"
--providers.marathon.dcosToken=xxxxxx
```
DCOSToken for DCOS environment.
@@ -123,7 +123,7 @@ providers:
```
```bash tab="CLI"
--providers.marathon.defaultRule="Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
--providers.marathon.defaultRule=Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)
# ...
```
@@ -182,7 +182,7 @@ providers:
```
```bash tab="CLI"
--providers.marathon.endpoint="http://10.241.1.71:8080,10.241.1.72:8080,10.241.1.73:8080"
--providers.marathon.endpoint=http://10.241.1.71:8080,10.241.1.72:8080,10.241.1.73:8080
```
Marathon server endpoint.
@@ -223,19 +223,19 @@ _Optional, Default=""_
```toml tab="File (TOML)"
[providers.marathon]
constraints = "Label(`a.label.name`, `foo`)"
constraints = "Label(`a.label.name`,`foo`)"
# ...
```
```yaml tab="File (YAML)"
providers:
marathon:
constraints: "Label(`a.label.name`, `foo`)"
constraints: "Label(`a.label.name`,`foo`)"
# ...
```
```bash tab="CLI"
--providers.marathon.constraints="Label(`a.label.name`, `foo`)"
--providers.marathon.constraints=Label(`a.label.name`,`foo`)
# ...
```
@@ -389,7 +389,7 @@ providers:
```
```bash tab="CLI"
--providers.marathon.responseHeaderTimeout="66s"
--providers.marathon.responseHeaderTimeout=66s
# ...
```
@@ -532,7 +532,7 @@ providers:
```
```bash tab="CLI"
--providers.marathon.responseHeaderTimeout="10s"
--providers.marathon.responseHeaderTimeout=10s
# ...
```

View File

@@ -104,7 +104,7 @@ providers:
```
```bash tab="CLI"
--providers.rancher.defaultRule="Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
--providers.rancher.defaultRule=Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)
# ...
```
@@ -209,7 +209,7 @@ providers:
```
```bash tab="CLI"
--providers.rancher.prefix="/test"
--providers.rancher.prefix=/test
# ...
```
@@ -221,19 +221,19 @@ _Optional, Default=""_
```toml tab="File (TOML)"
[providers.rancher]
constraints = "Label(`a.label.name`, `foo`)"
constraints = "Label(`a.label.name`,`foo`)"
# ...
```
```yaml tab="File (YAML)"
providers:
rancher:
constraints: "Label(`a.label.name`, `foo`)"
constraints: "Label(`a.label.name`,`foo`)"
# ...
```
```bash tab="CLI"
--providers.rancher.constraints="Label(`a.label.name`, `foo`)"
--providers.rancher.constraints=Label(`a.label.name`,`foo`)
# ...
```

View File

@@ -17,4 +17,4 @@
--providers.rancher.intervalPoll=false
# Prefix used for accessing the Rancher metadata service
--providers.rancher.prefix="/latest"
--providers.rancher.prefix=/latest

View File

@@ -18,4 +18,4 @@ providers:
intervalPoll: false
# Prefix used for accessing the Rancher metadata service
prefix: "/latest"
prefix: /latest

View File

@@ -128,9 +128,9 @@ You can define them using a toml file, CLI arguments, or a key-value store.
--entryPoints.name.transport.respondingTimeouts.writeTimeout=42
--entryPoints.name.transport.respondingTimeouts.idleTimeout=42
--entryPoints.name.proxyProtocol.insecure=true
--entryPoints.name.proxyProtocol.trustedIPs="127.0.0.1,192.168.0.1"
--entryPoints.name.proxyProtocol.trustedIPs=127.0.0.1,192.168.0.1
--entryPoints.name.forwardedHeaders.insecure=true
--entryPoints.name.forwardedHeaders.trustedIPs="127.0.0.1,192.168.0.1"
--entryPoints.name.forwardedHeaders.trustedIPs=127.0.0.1,192.168.0.1
```
### Forwarded Header

View File

@@ -151,7 +151,7 @@ http:
```bash tab="CLI"
# Listen on port 8081 for incoming requests
--entryPoints.web.address=":8081"
--entryPoints.web.address=:8081
# Enable the file provider to define routers / middlewares / services in a file
--providers.file.filename=dynamic_conf.toml

View File

@@ -82,7 +82,7 @@ Attach labels to your containers and let Traefik do the rest!
```
```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
```
@@ -165,7 +165,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers
See [entry points](../routers/index.md#entrypoints) for more information.
```yaml
- "traefik.http.routers.myrouter.entrypoints=web,websecure"
- "traefik.http.routers.myrouter.entrypoints=ep1,ep2"
```
??? info "`traefik.http.routers.<router_name>.middlewares`"
@@ -243,11 +243,12 @@ you'd add the label `traefik.http.services.<name-of-your-choice>.loadbalancer.pa
!!! warning "The character `@` is not authorized in the service name `<service_name>`."
??? info "`traefik.http.services.<service_name>.loadbalancer.server.port`"
Registers a port.
Useful when the container exposes multiples ports.
Mandatory for Docker Swarm.
Mandatory for Docker Swarm (see the section ["Port Detection with Docker Swarm"](../../providers/docker.md#port-detection_1)).
{: #port }
```yaml
- "traefik.http.services.myservice.loadbalancer.server.port=8080"

View File

@@ -67,7 +67,7 @@ For example, to change the routing rule, you could add the label ```"traefik.htt
See [entry points](../routers/index.md#entrypoints) for more information.
```json
"traefik.http.routers.myrouter.entrypoints": "web,websecure"
"traefik.http.routers.myrouter.entrypoints": "ep1,ep2"
```
??? info "`traefik.http.routers.<router_name>.middlewares`"

View File

@@ -72,7 +72,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers
See [entry points](../routers/index.md#entrypoints) for more information.
```yaml
- "traefik.http.routers.myrouter.entrypoints=web,websecure"
- "traefik.http.routers.myrouter.entrypoints=ep1,ep2"
```
??? info "`traefik.http.routers.<router_name>.middlewares`"

View File

@@ -78,8 +78,8 @@ In the process, routers may use pieces of [middleware](../../middlewares/overvie
```bash tab="CLI"
## Static configuration
--entryPoints.web.address=":80"
--entryPoints.mysql.address=":3306"
--entryPoints.web.address=:80
--entryPoints.mysql.address=:3306
```
## Configuring HTTP Routers
@@ -140,9 +140,9 @@ If you want to limit the router scope to a set of entry points, set the `entryPo
```bash tab="CLI"
## Static configuration
--entrypoints.web.address=":80"
--entrypoints.websecure.address=":443"
--entrypoints.other.address=":9090"
--entrypoints.web.address=:80
--entrypoints.websecure.address=:443
--entrypoints.other.address=:9090
```
??? example "Listens to Specific EntryPoints"
@@ -198,9 +198,9 @@ If you want to limit the router scope to a set of entry points, set the `entryPo
```bash tab="CLI"
## Static configuration
--entrypoints.web.address=":80"
--entrypoints.websecure.address=":443"
--entrypoints.other.address=":9090"
--entrypoints.web.address=:80
--entrypoints.websecure.address=:443
--entrypoints.other.address=:9090
```
### Rule
@@ -300,7 +300,7 @@ A value of `0` for the priority is ignored: `priority = 0` means that the defaul
The previous table shows that `Router-1` has a higher priority than `Router-2`.
To solve this issue, the priority must be setted.
To solve this issue, the priority must be set.
??? example "Set priorities -- using the [File Provider](../../providers/file.md)"
@@ -419,10 +419,6 @@ Traefik will terminate the SSL connections (meaning that it will send decrypted
tls: {}
```
!!! info "HTTPS & ACME"
In the current version, with [ACME](../../https/acme.md) enabled, automatic certificate generation will apply to every router declaring a TLS section.
!!! important "Routers for HTTP & HTTPS"
If you need to define the same route for both HTTP and HTTPS requests, you will need to define two different routers:
@@ -704,9 +700,9 @@ If you want to limit the router scope to a set of entry points, set the entry po
```bash tab="CLI"
## Static configuration
--entrypoints.web.address=":80"
--entrypoints.websecure.address=":443"
--entrypoints.other.address=":9090"
--entrypoints.web.address=:80
--entrypoints.websecure.address=:443
--entrypoints.other.address=:9090
```
??? example "Listens to Specific Entry Points"
@@ -768,9 +764,9 @@ If you want to limit the router scope to a set of entry points, set the entry po
```bash tab="CLI"
## Static configuration
--entrypoints.web.address=":80"
--entrypoints.websecure.address=":443"
--entrypoints.other.address=":9090"
--entrypoints.web.address=:80
--entrypoints.websecure.address=:443
--entrypoints.other.address=:9090
```
### Rule
@@ -846,10 +842,6 @@ Services are the target for the router.
passthrough: true
```
!!! info "TLS & ACME"
In the current version, with [ACME](../../https/acme.md) enabled, automatic certificate generation will apply to every router declaring a TLS section.
#### `options`
The `options` field enables fine-grained control of the TLS parameters.

View File

@@ -32,7 +32,7 @@ api: {}
```
```yaml tab="CLI"
--entryPoints.web.address=":80"
--entryPoints.web.address=:80
--providers.file.filename=dynamic_conf.toml
--api.insecure=true
```
@@ -153,7 +153,7 @@ api: {}
```
```yaml tab="CLI"
--entryPoints.websecure.address=":4443"
--entryPoints.websecure.address=:4443
# For secure connection on backend.local
--serversTransport.rootCAs=./backend.cert
--providers.file.filename=dynamic_conf.toml

View File

@@ -157,6 +157,7 @@ nav:
- 'Thank You!': 'contributing/thank-you.md'
- 'Submitting Issues': 'contributing/submitting-issues.md'
- 'Submitting PRs': 'contributing/submitting-pull-requests.md'
- 'Security': 'contributing/submitting-security-issues.md'
- 'Building and Testing': 'contributing/building-testing.md'
- 'Documentation': 'contributing/documentation.md'
- 'Data Collection': 'contributing/data-collection.md'

6
go.mod
View File

@@ -39,7 +39,7 @@ require (
github.com/felixge/httpsnoop v1.0.0 // indirect
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect
github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2
github.com/go-acme/lego/v3 v3.1.0
github.com/go-acme/lego/v3 v3.2.0
github.com/go-check/check v0.0.0-00010101000000-000000000000
github.com/go-kit/kit v0.9.0
github.com/golang/protobuf v1.3.2
@@ -66,7 +66,7 @@ require (
github.com/opencontainers/runc v1.0.0-rc8 // indirect
github.com/opentracing/basictracer-go v1.0.0 // indirect
github.com/opentracing/opentracing-go v1.1.0
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.4
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5
github.com/openzipkin/zipkin-go v0.2.1
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/philhofer/fwd v1.0.0 // indirect
@@ -82,7 +82,7 @@ require (
github.com/uber/jaeger-client-go v2.16.0+incompatible
github.com/uber/jaeger-lib v2.0.0+incompatible
github.com/unrolled/render v1.0.1
github.com/unrolled/secure v1.0.4
github.com/unrolled/secure v1.0.5
github.com/vdemeester/shakers v0.1.0
github.com/vulcand/oxy v1.0.0
github.com/vulcand/predicate v1.1.0

12
go.sum
View File

@@ -181,8 +181,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2 h1:df6OFl8WNXk82xxP3R9ZPZ5seOA8XZkwLdbEzZF1/xI=
github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2/go.mod h1:GLyXJD41gBO/NPKVPGQbhyyC06eugGy15QEZyUkE2/s=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-acme/lego/v3 v3.1.0 h1:yanYFoYW8azFkCvJfIk7edWWfjkYkhDxe45ZsxoW4Xk=
github.com/go-acme/lego/v3 v3.1.0/go.mod h1:074uqt+JS6plx+c9Xaiz6+L+GBb+7itGtzfcDM2AhEE=
github.com/go-acme/lego/v3 v3.2.0 h1:z0zvNlL1niv/1qA06V5X1BRC5PeLoGKAlVaWthXQz9c=
github.com/go-acme/lego/v3 v3.2.0/go.mod h1:074uqt+JS6plx+c9Xaiz6+L+GBb+7itGtzfcDM2AhEE=
github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s=
github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
@@ -396,8 +396,8 @@ github.com/opentracing/basictracer-go v1.0.0 h1:YyUAhaEfjoWXclZVJ9sGoNct7j4TVk7l
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.4 h1:bzTJRoOZEN7uI1gq594S5HhMYNSud4FKUEwd4aFbsEI=
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.4/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5 h1:ZCnq+JUrvXcDVhX/xRolRBZifmabN1HcS1wrPSvxhrU=
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openzipkin/zipkin-go v0.2.1 h1:noL5/5Uf1HpVl3wNsfkZhIKbSWCVi5jgqkONNx8PXcA=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
@@ -488,8 +488,8 @@ github.com/uber/jaeger-lib v2.0.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6
github.com/ugorji/go v0.0.0-20171019201919-bdcc60b419d1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
github.com/unrolled/render v1.0.1 h1:VDDnQQVfBMsOsp3VaCJszSO0nkBIVEYoPWeRThk9spY=
github.com/unrolled/render v1.0.1/go.mod h1:gN9T0NhL4Bfbwu8ann7Ry/TGHYfosul+J0obPf6NBdM=
github.com/unrolled/secure v1.0.4 h1:DksfKsRTyXP2R8quDdOOuRpRO45VprFL0X9t9+JX1PU=
github.com/unrolled/secure v1.0.4/go.mod h1:R6rugAuzh4TQpbFAq69oqZggyBQxFRFQIewtz5z7Jsc=
github.com/unrolled/secure v1.0.5 h1:KRGJ8DQC3jKpERjBKF3H6b3HcAsM/SRTVwfNJnWs25E=
github.com/unrolled/secure v1.0.5/go.mod h1:R6rugAuzh4TQpbFAq69oqZggyBQxFRFQIewtz5z7Jsc=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vdemeester/shakers v0.1.0 h1:K+n9sSyUCg2ywmZkv+3c7vsYZfivcfKhMh8kRxCrONM=
github.com/vdemeester/shakers v0.1.0/go.mod h1:IZ1HHynUOQt32iQ3rvAeVddXLd19h/6LWiKsh9RZtAQ=

View File

@@ -0,0 +1,40 @@
[global]
checkNewVersion = false
sendAnonymousUsage = false
[log]
level = "DEBUG"
[entryPoints]
[entryPoints.web1]
address = ":8000"
[entryPoints.web2]
address = ":9000"
[api]
insecure = true
[providers.file]
filename = "{{ .SelfFilename }}"
## dynamic configuration ##
[http.routers]
[http.routers.router1]
entryPoints = ["web1"]
service = "service1"
rule = "Host(`test.localhost`)"
[http.routers.router2]
entryPoints = ["web2"]
service = "service1"
rule = "Host(`test.localhost`)"
[http.services]
[http.services.service1.loadBalancer]
[http.services.service1.loadBalancer.healthcheck]
path = "/health"
interval = "1s"
timeout = "0.9s"
[[http.services.service1.loadBalancer.servers]]
url = "http://{{.Server1}}:80"

View File

@@ -205,3 +205,69 @@ func (s *HealthCheckSuite) TestPortOverload(c *check.C) {
err = try.Request(frontendHealthReq, 3*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
c.Assert(err, checker.IsNil)
}
// Checks if all the loadbalancers created will correctly update the server status
func (s *HealthCheckSuite) TestMultipleRoutersOnSameService(c *check.C) {
file := s.adaptFile(c, "fixtures/healthcheck/multiple-routers-one-same-service.toml", struct {
Server1 string
}{s.whoami1IP})
defer os.Remove(file)
cmd, display := s.traefikCmd(withConfigFile(file))
defer display(c)
err := cmd.Start()
c.Assert(err, checker.IsNil)
defer cmd.Process.Kill()
// wait for traefik
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`test.localhost`)"))
c.Assert(err, checker.IsNil)
// Set whoami health to 200 to be sure to start with the wanted status
client := &http.Client{}
statusOkReq, err := http.NewRequest(http.MethodPost, "http://"+s.whoami1IP+"/health", bytes.NewBuffer([]byte("200")))
c.Assert(err, checker.IsNil)
_, err = client.Do(statusOkReq)
c.Assert(err, checker.IsNil)
// check healthcheck on web1 entrypoint
healthReqWeb1, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/health", nil)
c.Assert(err, checker.IsNil)
healthReqWeb1.Host = "test.localhost"
err = try.Request(healthReqWeb1, 1*time.Second, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
// check healthcheck on web2 entrypoint
healthReqWeb2, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:9000/health", nil)
c.Assert(err, checker.IsNil)
healthReqWeb2.Host = "test.localhost"
err = try.Request(healthReqWeb2, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
// Set whoami health to 500
statusInternalServerErrorReq, err := http.NewRequest(http.MethodPost, "http://"+s.whoami1IP+"/health", bytes.NewBuffer([]byte("500")))
c.Assert(err, checker.IsNil)
_, err = client.Do(statusInternalServerErrorReq)
c.Assert(err, checker.IsNil)
// Verify no backend service is available due to failing health checks
err = try.Request(healthReqWeb1, 3*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
c.Assert(err, checker.IsNil)
err = try.Request(healthReqWeb2, 3*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
c.Assert(err, checker.IsNil)
// Change one whoami health to 200
statusOKReq1, err := http.NewRequest(http.MethodPost, "http://"+s.whoami1IP+"/health", bytes.NewBuffer([]byte("200")))
c.Assert(err, checker.IsNil)
_, err = client.Do(statusOKReq1)
c.Assert(err, checker.IsNil)
// Verify health check
err = try.Request(healthReqWeb1, 3*time.Second, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
err = try.Request(healthReqWeb2, 3*time.Second, try.StatusCodeIs(http.StatusOK))
c.Assert(err, checker.IsNil)
}

View File

@@ -25,14 +25,20 @@ const (
var singleton *HealthCheck
var once sync.Once
// BalancerHandler includes functionality for load-balancing management.
type BalancerHandler interface {
ServeHTTP(w http.ResponseWriter, req *http.Request)
// Balancer is the set of operations required to manage the list of servers in a
// load-balancer.
type Balancer interface {
Servers() []*url.URL
RemoveServer(u *url.URL) error
UpsertServer(u *url.URL, options ...roundrobin.ServerOption) error
}
// BalancerHandler includes functionality for load-balancing management.
type BalancerHandler interface {
ServeHTTP(w http.ResponseWriter, req *http.Request)
Balancer
}
// metricsRegistry is a local interface in the health check package, exposing only the required metrics
// necessary for the health check package. This makes it easier for the tests.
type metricsRegistry interface {
@@ -49,7 +55,7 @@ type Options struct {
Transport http.RoundTripper
Interval time.Duration
Timeout time.Duration
LB BalancerHandler
LB Balancer
}
func (opt Options) String() string {
@@ -146,18 +152,18 @@ func (hc *HealthCheck) checkBackend(ctx context.Context, backend *BackendConfig)
enabledURLs := backend.LB.Servers()
var newDisabledURLs []backendURL
// FIXME re enable metrics
for _, disableURL := range backend.disabledURLs {
for _, disabledURL := range backend.disabledURLs {
// FIXME serverUpMetricValue := float64(0)
if err := checkHealth(disableURL.url, backend); err == nil {
if err := checkHealth(disabledURL.url, backend); err == nil {
logger.Warnf("Health check up: Returning to server list. Backend: %q URL: %q Weight: %d",
backend.name, disableURL.url.String(), disableURL.weight)
if err = backend.LB.UpsertServer(disableURL.url, roundrobin.Weight(disableURL.weight)); err != nil {
backend.name, disabledURL.url.String(), disabledURL.weight)
if err = backend.LB.UpsertServer(disabledURL.url, roundrobin.Weight(disabledURL.weight)); err != nil {
logger.Error(err)
}
// FIXME serverUpMetricValue = 1
} else {
logger.Warnf("Health check still failing. Backend: %q URL: %q Reason: %s", backend.name, disableURL.url.String(), err)
newDisabledURLs = append(newDisabledURLs, disableURL)
logger.Warnf("Health check still failing. Backend: %q URL: %q Reason: %s", backend.name, disabledURL.url.String(), err)
newDisabledURLs = append(newDisabledURLs, disabledURL)
}
// FIXME labelValues := []string{"backend", backend.name, "url", backendurl.url.String()}
// FIXME hc.metrics.BackendServerUpGauge().With(labelValues...).Set(serverUpMetricValue)
@@ -177,7 +183,7 @@ func (hc *HealthCheck) checkBackend(ctx context.Context, backend *BackendConfig)
weight = 1
}
}
logger.Warnf("Health check failed: Remove from server list. Backend: %q URL: %q Weight: %d Reason: %s", backend.name, enableURL.String(), weight, err)
logger.Warnf("Health check failed, removing from server list. Backend: %q URL: %q Weight: %d Reason: %s", backend.name, enableURL.String(), weight, err)
if err := backend.LB.RemoveServer(enableURL); err != nil {
logger.Error(err)
}
@@ -281,3 +287,38 @@ func (lb *LbStatusUpdater) UpsertServer(u *url.URL, options ...roundrobin.Server
}
return err
}
// Balancers is a list of Balancers(s) that implements the Balancer interface.
type Balancers []Balancer
// Servers returns the servers url from all the BalancerHandler
func (b Balancers) Servers() []*url.URL {
var servers []*url.URL
for _, lb := range b {
servers = append(servers, lb.Servers()...)
}
return servers
}
// RemoveServer removes the given server from all the BalancerHandler,
// and updates the status of the server to "DOWN".
func (b Balancers) RemoveServer(u *url.URL) error {
for _, lb := range b {
if err := lb.RemoveServer(u); err != nil {
return err
}
}
return nil
}
// UpsertServer adds the given server to all the BalancerHandler,
// and updates the status of the server to "UP".
func (b Balancers) UpsertServer(u *url.URL, options ...roundrobin.ServerOption) error {
for _, lb := range b {
if err := lb.UpsertServer(u, options...); err != nil {
return err
}
}
return nil
}

View File

@@ -116,7 +116,18 @@ type CoreLogData map[string]interface{}
// LogData is the data captured by the middleware so that it can be logged.
type LogData struct {
Core CoreLogData
Request http.Header
Request request
OriginResponse http.Header
DownstreamResponse http.Header
DownstreamResponse downstreamResponse
}
type downstreamResponse struct {
headers http.Header
status int
size int64
}
type request struct {
headers http.Header
count int64
}

View File

@@ -47,8 +47,6 @@ func (n noopCloser) Close() error {
type handlerParams struct {
logDataTable *LogData
crr *captureRequestReader
crw *captureResponseWriter
}
// Handler will write each request and its response to the access log.
@@ -122,7 +120,7 @@ func NewHandler(config *types.AccessLog) (*Handler, error) {
go func() {
defer logHandler.wg.Done()
for handlerParams := range logHandler.logHandlerChan {
logHandler.logTheRoundTrip(handlerParams.logDataTable, handlerParams.crr, handlerParams.crw)
logHandler.logTheRoundTrip(handlerParams.logDataTable)
}
}()
}
@@ -162,7 +160,12 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http
StartLocal: now.Local(),
}
logDataTable := &LogData{Core: core, Request: req.Header}
logDataTable := &LogData{
Core: core,
Request: request{
headers: req.Header,
},
}
reqWithDataTable := req.WithContext(context.WithValue(req.Context(), DataTableKey, logDataTable))
@@ -205,16 +208,21 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http
core[ClientUsername] = usernameIfPresent(reqWithDataTable.URL)
}
logDataTable.DownstreamResponse = crw.Header()
logDataTable.DownstreamResponse = downstreamResponse{
headers: crw.Header().Clone(),
status: crw.Status(),
size: crw.Size(),
}
if crr != nil {
logDataTable.Request.count = crr.count
}
if h.config.BufferingSize > 0 {
h.logHandlerChan <- handlerParams{
logDataTable: logDataTable,
crr: crr,
crw: crw,
}
} else {
h.logTheRoundTrip(logDataTable, crr, crw)
h.logTheRoundTrip(logDataTable)
}
}
@@ -264,7 +272,7 @@ func usernameIfPresent(theURL *url.URL) string {
}
// Logging handler to log frontend name, backend name, and elapsed time.
func (h *Handler) logTheRoundTrip(logDataTable *LogData, crr *captureRequestReader, crw *captureResponseWriter) {
func (h *Handler) logTheRoundTrip(logDataTable *LogData) {
core := logDataTable.Core
retryAttempts, ok := core[RetryAttempts].(int)
@@ -272,23 +280,22 @@ func (h *Handler) logTheRoundTrip(logDataTable *LogData, crr *captureRequestRead
retryAttempts = 0
}
core[RetryAttempts] = retryAttempts
core[RequestContentSize] = logDataTable.Request.count
if crr != nil {
core[RequestContentSize] = crr.count
}
core[DownstreamStatus] = crw.Status()
status := logDataTable.DownstreamResponse.status
core[DownstreamStatus] = status
// n.b. take care to perform time arithmetic using UTC to avoid errors at DST boundaries.
totalDuration := time.Now().UTC().Sub(core[StartUTC].(time.Time))
core[Duration] = totalDuration
if h.keepAccessLog(crw.Status(), retryAttempts, totalDuration) {
core[DownstreamContentSize] = crw.Size()
if h.keepAccessLog(status, retryAttempts, totalDuration) {
size := logDataTable.DownstreamResponse.size
core[DownstreamContentSize] = size
if original, ok := core[OriginContentSize]; ok {
o64 := original.(int64)
if crw.Size() != o64 && crw.Size() != 0 {
core[GzipRatio] = float64(o64) / float64(crw.Size())
if size != o64 && size != 0 {
core[GzipRatio] = float64(o64) / float64(size)
}
}
@@ -305,9 +312,9 @@ func (h *Handler) logTheRoundTrip(logDataTable *LogData, crr *captureRequestRead
}
}
h.redactHeaders(logDataTable.Request, fields, "request_")
h.redactHeaders(logDataTable.Request.headers, fields, "request_")
h.redactHeaders(logDataTable.OriginResponse, fields, "origin_")
h.redactHeaders(logDataTable.DownstreamResponse, fields, "downstream_")
h.redactHeaders(logDataTable.DownstreamResponse.headers, fields, "downstream_")
h.mu.Lock()
defer h.mu.Unlock()

View File

@@ -192,6 +192,7 @@ func TestLoggerJSON(t *testing.T) {
Format: JSONFormat,
},
expected: map[string]func(t *testing.T, value interface{}){
RequestContentSize: assertFloat64(0),
RequestHost: assertString(testHostname),
RequestAddr: assertString(testHostname),
RequestMethod: assertString(testMethod),

View File

@@ -221,13 +221,11 @@ func (s *Header) processCorsHeaders(rw http.ResponseWriter, req *http.Request) b
}
reqAcMethod := req.Header.Get("Access-Control-Request-Method")
reqAcHeaders := req.Header.Get("Access-Control-Request-Headers")
originHeader := req.Header.Get("Origin")
if reqAcMethod != "" && reqAcHeaders != "" && originHeader != "" && req.Method == http.MethodOptions {
if reqAcMethod != "" && originHeader != "" && req.Method == http.MethodOptions {
// If the request is an OPTIONS request with an Access-Control-Request-Method header,
// and Access-Control-Request-Headers headers, and Origin headers,
// then it is a CORS preflight request,
// and Origin headers, then it is a CORS preflight request,
// and we need to build a custom response: https://www.w3.org/TR/cors/#preflight-request
if s.headers.AccessControlAllowCredentials {
rw.Header().Set("Access-Control-Allow-Credentials", "true")

View File

@@ -275,6 +275,25 @@ func TestCORSPreflights(t *testing.T) {
"Access-Control-Allow-Headers": {"origin,X-Forwarded-For"},
},
},
{
desc: "No Request Headers Preflight",
header: NewHeader(emptyHandler, dynamic.Headers{
AccessControlAllowMethods: []string{"GET", "OPTIONS", "PUT"},
AccessControlAllowOrigin: "*",
AccessControlAllowHeaders: []string{"origin", "X-Forwarded-For"},
AccessControlMaxAge: 600,
}),
requestHeaders: map[string][]string{
"Access-Control-Request-Method": {"GET", "OPTIONS"},
"Origin": {"https://foo.bar.org"},
},
expected: map[string][]string{
"Access-Control-Allow-Origin": {"*"},
"Access-Control-Max-Age": {"600"},
"Access-Control-Allow-Methods": {"GET,OPTIONS,PUT"},
"Access-Control-Allow-Headers": {"origin,X-Forwarded-For"},
},
},
}
for _, test := range testCases {

View File

@@ -132,19 +132,13 @@ func rawURL(req *http.Request) string {
uri = match[4]
}
if req.TLS != nil || isXForwardedHTTPS(req) {
if req.TLS != nil {
scheme = "https"
}
return strings.Join([]string{scheme, "://", host, port, uri}, "")
}
func isXForwardedHTTPS(request *http.Request) bool {
xForwardedProto := request.Header.Get("X-Forwarded-Proto")
return len(xForwardedProto) > 0 && xForwardedProto == "https"
}
func applyString(in string, out io.Writer, req *http.Request) error {
t, err := template.New("t").Parse(in)
if err != nil {

View File

@@ -19,6 +19,7 @@ func TestRedirectRegexHandler(t *testing.T) {
config dynamic.RedirectRegex
method string
url string
headers map[string]string
secured bool
expectedURL string
expectedStatus int
@@ -104,6 +105,19 @@ func TestRedirectRegexHandler(t *testing.T) {
expectedURL: "https://foo:443",
expectedStatus: http.StatusFound,
},
{
desc: "HTTP to HTTPS, with X-Forwarded-Proto",
config: dynamic.RedirectRegex{
Regex: `http://foo:80`,
Replacement: "https://foo:443",
},
url: "http://foo:80",
headers: map[string]string{
"X-Forwarded-Proto": "https",
},
expectedURL: "https://foo:443",
expectedStatus: http.StatusFound,
},
{
desc: "HTTPS to HTTP",
config: dynamic.RedirectRegex{
@@ -171,12 +185,18 @@ func TestRedirectRegexHandler(t *testing.T) {
if test.method != "" {
method = test.method
}
r := testhelpers.MustNewRequest(method, test.url, nil)
req := testhelpers.MustNewRequest(method, test.url, nil)
if test.secured {
r.TLS = &tls.ConnectionState{}
req.TLS = &tls.ConnectionState{}
}
r.Header.Set("X-Foo", "bar")
handler.ServeHTTP(recorder, r)
for k, v := range test.headers {
req.Header.Set(k, v)
}
req.Header.Set("X-Foo", "bar")
handler.ServeHTTP(recorder, req)
assert.Equal(t, test.expectedStatus, recorder.Code)
switch test.expectedStatus {

View File

@@ -19,6 +19,7 @@ func TestRedirectSchemeHandler(t *testing.T) {
config dynamic.RedirectScheme
method string
url string
headers map[string]string
secured bool
expectedURL string
expectedStatus int
@@ -39,6 +40,18 @@ func TestRedirectSchemeHandler(t *testing.T) {
expectedURL: "https://foo",
expectedStatus: http.StatusFound,
},
{
desc: "HTTP to HTTPS, with X-Forwarded-Proto",
config: dynamic.RedirectScheme{
Scheme: "https",
},
url: "http://foo",
headers: map[string]string{
"X-Forwarded-Proto": "https",
},
expectedURL: "https://foo",
expectedStatus: http.StatusFound,
},
{
desc: "HTTP with port to HTTPS without port",
config: dynamic.RedirectScheme{
@@ -197,13 +210,17 @@ func TestRedirectSchemeHandler(t *testing.T) {
if test.method != "" {
method = test.method
}
r := httptest.NewRequest(method, test.url, nil)
req := httptest.NewRequest(method, test.url, nil)
for k, v := range test.headers {
req.Header.Set(k, v)
}
if test.secured {
r.TLS = &tls.ConnectionState{}
req.TLS = &tls.ConnectionState{}
}
r.Header.Set("X-Foo", "bar")
handler.ServeHTTP(recorder, r)
req.Header.Set("X-Foo", "bar")
handler.ServeHTTP(recorder, req)
assert.Equal(t, test.expectedStatus, recorder.Code)
@@ -223,9 +240,9 @@ func TestRedirectSchemeHandler(t *testing.T) {
if re.Match([]byte(test.url)) {
match := re.FindStringSubmatch(test.url)
r.RequestURI = match[4]
req.RequestURI = match[4]
handler.ServeHTTP(recorder, r)
handler.ServeHTTP(recorder, req)
assert.Equal(t, test.expectedStatus, recorder.Code)
if test.expectedStatus == http.StatusMovedPermanently ||

View File

@@ -34,7 +34,11 @@ type entryPointMiddleware struct {
}
func (e *entryPointMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
spanCtx, _ := e.Extract(opentracing.HTTPHeaders, tracing.HTTPHeadersCarrier(req.Header))
spanCtx, err := e.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
if err != nil {
log.FromContext(middlewares.GetLoggerCtx(req.Context(), "tracing", entryPointTypeName)).
Debugf("Failed to extract the context: %v", err)
}
span, req, finish := e.StartSpanf(req, ext.SpanKindRPCServerEnum, "EntryPoint", []string{e.entryPoint, req.Host}, " ", ext.RPCServerOption(spanCtx))
defer finish()

View File

@@ -193,7 +193,7 @@ func (p *Provider) parseMetadataSourcedRancherData(ctx context.Context, stacks [
}
service := rancherData{
Name: service.Name + "/" + stack.Name,
Name: service.Name + "_" + stack.Name,
State: service.State,
Labels: service.Labels,
Port: servicePort,

View File

@@ -40,7 +40,7 @@ func NewManager(configs map[string]*runtime.ServiceInfo, defaultRoundTripper htt
metricsRegistry: metricsRegistry,
bufferPool: newBufferPool(),
defaultRoundTripper: defaultRoundTripper,
balancers: make(map[string][]healthcheck.BalancerHandler),
balancers: make(map[string]healthcheck.Balancers),
configs: configs,
api: api,
rest: rest,
@@ -53,10 +53,14 @@ type Manager struct {
metricsRegistry metrics.Registry
bufferPool httputil.BufferPool
defaultRoundTripper http.RoundTripper
balancers map[string][]healthcheck.BalancerHandler
configs map[string]*runtime.ServiceInfo
api http.Handler
rest http.Handler
// balancers is the map of all Balancers, keyed by service name.
// There is one Balancer per service handler, and there is one service handler per reference to a service
// (e.g. if 2 routers refer to the same service name, 2 service handlers are created),
// which is why there is not just one Balancer per service name.
balancers map[string]healthcheck.Balancers
configs map[string]*runtime.ServiceInfo
api http.Handler
rest http.Handler
}
// BuildHTTP Creates a http.Handler for a service configuration.
@@ -110,14 +114,14 @@ func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string, respons
}
case conf.Weighted != nil:
var err error
lb, err = m.getLoadBalancerWRRServiceHandler(ctx, serviceName, conf.Weighted, responseModifier)
lb, err = m.getWRRServiceHandler(ctx, serviceName, conf.Weighted, responseModifier)
if err != nil {
conf.AddError(err, true)
return nil, err
}
case conf.Mirroring != nil:
var err error
lb, err = m.getLoadBalancerMirrorServiceHandler(ctx, serviceName, conf.Mirroring, responseModifier)
lb, err = m.getMirrorServiceHandler(ctx, serviceName, conf.Mirroring, responseModifier)
if err != nil {
conf.AddError(err, true)
return nil, err
@@ -131,7 +135,7 @@ func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string, respons
return lb, nil
}
func (m *Manager) getLoadBalancerMirrorServiceHandler(ctx context.Context, serviceName string, config *dynamic.Mirroring, responseModifier func(*http.Response) error) (http.Handler, error) {
func (m *Manager) getMirrorServiceHandler(ctx context.Context, serviceName string, config *dynamic.Mirroring, responseModifier func(*http.Response) error) (http.Handler, error) {
serviceHandler, err := m.BuildHTTP(ctx, config.Service, responseModifier)
if err != nil {
return nil, err
@@ -152,7 +156,7 @@ func (m *Manager) getLoadBalancerMirrorServiceHandler(ctx context.Context, servi
return handler, nil
}
func (m *Manager) getLoadBalancerWRRServiceHandler(ctx context.Context, serviceName string, config *dynamic.WeightedRoundRobin, responseModifier func(*http.Response) error) (http.Handler, error) {
func (m *Manager) getWRRServiceHandler(ctx context.Context, serviceName string, config *dynamic.WeightedRoundRobin, responseModifier func(*http.Response) error) (http.Handler, error) {
// TODO Handle accesslog and metrics with multiple service name
if config.Sticky != nil && config.Sticky.Cookie != nil {
config.Sticky.Cookie.Name = cookie.GetName(config.Sticky.Cookie.Name, serviceName)
@@ -218,15 +222,12 @@ func (m *Manager) LaunchHealthCheck() {
for serviceName, balancers := range m.balancers {
ctx := log.With(context.Background(), log.Str(log.ServiceName, serviceName))
// TODO aggregate
balancer := balancers[0]
// TODO Should all the services handle healthcheck? Handle different types
service := m.configs[serviceName].LoadBalancer
// Health Check
var backendHealthCheck *healthcheck.BackendConfig
if hcOpts := buildHealthCheckOptions(ctx, balancer, serviceName, service.HealthCheck); hcOpts != nil {
if hcOpts := buildHealthCheckOptions(ctx, balancers, serviceName, service.HealthCheck); hcOpts != nil {
log.FromContext(ctx).Debugf("Setting up healthcheck for service %s with %s", serviceName, *hcOpts)
hcOpts.Transport = m.defaultRoundTripper
@@ -242,7 +243,7 @@ func (m *Manager) LaunchHealthCheck() {
healthcheck.GetHealthCheck().SetBackendsConfiguration(context.Background(), backendConfigs)
}
func buildHealthCheckOptions(ctx context.Context, lb healthcheck.BalancerHandler, backend string, hc *dynamic.HealthCheck) *healthcheck.Options {
func buildHealthCheckOptions(ctx context.Context, lb healthcheck.Balancer, backend string, hc *dynamic.HealthCheck) *healthcheck.Options {
if hc == nil || hc.Path == "" {
return nil
}

View File

@@ -1,25 +0,0 @@
package tracing
import "net/http"
// HTTPHeadersCarrier custom implementation to fix duplicated headers
// It has been fixed in https://github.com/opentracing/opentracing-go/pull/191
type HTTPHeadersCarrier http.Header
// Set conforms to the TextMapWriter interface.
func (c HTTPHeadersCarrier) Set(key, val string) {
h := http.Header(c)
h.Set(key, val)
}
// ForeachKey conforms to the TextMapReader interface.
func (c HTTPHeadersCarrier) ForeachKey(handler func(key, val string) error) error {
for k, vals := range c {
for _, v := range vals {
if err := handler(k, v); err != nil {
return err
}
}
}
return nil
}

View File

@@ -134,7 +134,7 @@ func InjectRequestHeaders(r *http.Request) {
err := opentracing.GlobalTracer().Inject(
span.Context(),
opentracing.HTTPHeaders,
HTTPHeadersCarrier(r.Header))
opentracing.HTTPHeadersCarrier(r.Header))
if err != nil {
log.FromContext(r.Context()).Error(err)
}

View File

@@ -3,15 +3,12 @@ import { APP } from '../_helpers/APP'
const apiBase = '/http'
function getAllRouters (params) {
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}`)
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
.then(body => {
const total = body.data ? body.data.length : 0
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
.then(body => {
console.log('Success -> HttpService -> getAllRouters', body.data)
// TODO - suggestion: add the total-pages in api response to optimize the query
return { data: body.data || [], total }
})
console.log('Success -> HttpService -> getAllRouters', body.data)
// TODO - suggestion: add the total-pages in api response to optimize the query
return { data: body.data || [], total }
})
}
@@ -24,15 +21,12 @@ function getRouterByName (name) {
}
function getAllServices (params) {
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}`)
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
.then(body => {
const total = body.data ? body.data.length : 0
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
.then(body => {
console.log('Success -> HttpService -> getAllServices', body.data)
// TODO - suggestion: add the total-pages in api response to optimize the query
return { data: body.data || [], total }
})
console.log('Success -> HttpService -> getAllServices', body.data)
// TODO - suggestion: add the total-pages in api response to optimize the query
return { data: body.data || [], total }
})
}
@@ -45,15 +39,12 @@ function getServiceByName (name) {
}
function getAllMiddlewares (params) {
return APP.api.get(`${apiBase}/middlewares?search=${params.query}&status=${params.status}`)
return APP.api.get(`${apiBase}/middlewares?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
.then(body => {
const total = body.data ? body.data.length : 0
return APP.api.get(`${apiBase}/middlewares?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
.then(body => {
console.log('Success -> HttpService -> getAllMiddlewares', body.data)
// TODO - suggestion: add the total-pages in api response to optimize the query
return { data: body.data || [], total }
})
console.log('Success -> HttpService -> getAllMiddlewares', body.data)
// TODO - suggestion: add the total-pages in api response to optimize the query
return { data: body.data || [], total }
})
}

View File

@@ -3,15 +3,12 @@ import { APP } from '../_helpers/APP'
const apiBase = '/tcp'
function getAllRouters (params) {
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}`)
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
.then(body => {
const total = body.data ? body.data.length : 0
return APP.api.get(`${apiBase}/routers?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
.then(body => {
console.log('Success -> HttpService -> getAllRouters', body.data)
// TODO - suggestion: add the total-pages in api response to optimize the query
return { data: body.data || [], total }
})
console.log('Success -> HttpService -> getAllRouters', body.data)
// TODO - suggestion: add the total-pages in api response to optimize the query
return { data: body.data || [], total }
})
}
@@ -24,15 +21,12 @@ function getRouterByName (name) {
}
function getAllServices (params) {
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}`)
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
.then(body => {
const total = body.data ? body.data.length : 0
return APP.api.get(`${apiBase}/services?search=${params.query}&status=${params.status}&per_page=${params.limit}&page=${params.page}`)
.then(body => {
console.log('Success -> HttpService -> getAllServices', body.data)
// TODO - suggestion: add the total-pages in api response to optimize the query
return { data: body.data || [], total }
})
console.log('Success -> HttpService -> getAllServices', body.data)
// TODO - suggestion: add the total-pages in api response to optimize the query
return { data: body.data || [], total }
})
}

View File

@@ -27,7 +27,7 @@
<div class="text-subtitle2">RULE</div>
<q-chip
dense
class="app-chip app-chip-rule">
class="app-chip app-chip-wrap app-chip-rule">
{{ data.rule }}
</q-chip>
</div>
@@ -39,7 +39,7 @@
<div class="text-subtitle2">NAME</div>
<q-chip
dense
class="app-chip app-chip-name">
class="app-chip app-chip-wrap app-chip-name">
{{ data.name }}
</q-chip>
</div>
@@ -66,7 +66,7 @@
dense
clickable
@click.native="$router.push({ path: `/${protocol}/services/${getServiceId()}`})"
class="app-chip app-chip-service">
class="app-chip app-chip-wrap app-chip-service">
{{ data.service }}
</q-chip>
</div>

View File

@@ -45,7 +45,7 @@
</div>
</div>
</q-card-section>
<q-card-section v-if="data.loadBalancer">
<q-card-section v-if="data.loadBalancer && $route.meta.protocol !== 'tcp'">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">Pass Host Header</div>
@@ -54,6 +54,19 @@
</div>
</q-card-section>
<q-card-section v-if="data.loadBalancer.terminationDelay">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">Termination Delay</div>
<q-chip
dense
class="app-chip app-chip-name">
{{ data.loadBalancer.terminationDelay }} ms
</q-chip>
</div>
</div>
</q-card-section>
<q-separator v-if="sticky" />
<StickyServiceDetails v-if="sticky" :sticky="sticky" :dense="dense"/>
</q-scroll-area>