Compare commits

..

3 Commits
v1.7.6 ... v1.4

Author SHA1 Message Date
Fernandez Ludovic
145a485255 fix: mkdocs.yml 2023-01-23 11:04:07 +01:00
mmatur
d3839eae6a feat: add dockerfile for documentation 2021-10-05 10:23:25 +02:00
Fernandez Ludovic
3c96dd3014 doc: fix version in requirements.txt 2018-09-28 23:16:28 +02:00
4775 changed files with 532479 additions and 809754 deletions

1
.gitattributes vendored
View File

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

View File

@@ -2,10 +2,10 @@
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
The issue tracker is for reporting bugs and feature requests only.
For end-user related support questions, please refer to one of the following:
For end-user related support questions, refer to one of the following:
- Stack Overflow (using the "traefik" tag): https://stackoverflow.com/questions/tagged/traefik
- the Traefik community Slack channel: https://slack.traefik.io
- the Traefik community Slack channel: https://traefik.herokuapp.com
-->
@@ -22,10 +22,10 @@ If you intend to ask a support question: DO NOT FILE AN ISSUE.
HOW TO WRITE A GOOD ISSUE?
- Respect the issue template as much as possible.
- If possible, use the command `traefik bug`. See https://www.youtube.com/watch?v=Lyz62L8m93I.
- The title should be short and descriptive.
- Explain the conditions which led you to report this issue: the context.
- Respect the issue template as more as possible.
- If it's possible use the command `traefik bug`. See https://www.youtube.com/watch?v=Lyz62L8m93I.
- The title must be short and descriptive.
- Explain the conditions which led you to write this issue: the context.
- The context should lead to something, an idea or a problem that youre facing.
- Remain clear and concise.
- Format your messages to help the reader focus on what matters and understand the structure of your message, use Markdown syntax https://help.github.com/articles/github-flavored-markdown
@@ -46,10 +46,6 @@ HOW TO WRITE A GOOD ISSUE?
For the Traefik Docker image:
docker run [IMAGE] version
ex: docker run traefik version
For the alpine Traefik Docker image:
docker run [IMAGE] traefik version
ex: docker run traefik traefik version
-->
```
@@ -66,7 +62,7 @@ Add more configuration information here.
-->
### If applicable, please paste the log output at DEBUG level (`--logLevel=DEBUG` switch)
### If applicable, please paste the log output in debug mode (`--debug` switch)
```
(paste your output here)

View File

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

View File

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

View File

@@ -12,7 +12,7 @@ HOW TO WRITE A GOOD PULL REQUEST?
- Write useful descriptions and titles.
- Address review comments in terms of additional commits.
- Do not amend/squash existing ones unless the PR is trivial.
- Read the contributing guide: https://github.com/containous/traefik/blob/master/CONTRIBUTING.md.
- Read the contributing guide: https://github.com/containous/traefik/blob/master/.github/CONTRIBUTING.md.
-->

View File

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

View File

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

9
.gitignore vendored
View File

@@ -1,15 +1,14 @@
/dist
/autogen/genstatic/gen.go
.idea/
.intellij/
/autogen/gen.go
.idea
.intellij
*.iml
/traefik
/traefik.toml
/static/
/webui/.tmp/
.vscode/
/site/
*.log
*.exe
.DS_Store
/examples/acme/acme.json
/example/acme/acme.json

View File

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

View File

@@ -1,4 +0,0 @@
#!/usr/bin/env bash
set -e
sudo rm -rf static

View File

@@ -1,6 +0,0 @@
#!/usr/bin/env bash
set -e
if [ -n "$SHOULD_TEST" ]; then ci_retry make pull-images; fi
if [ -n "$SHOULD_TEST" ]; then ci_retry make test-integration; fi

View File

@@ -1,8 +0,0 @@
#!/usr/bin/env bash
set -e
ci_retry make validate
if [ -n "$SHOULD_TEST" ]; then ci_retry make test-unit; fi
if [ -n "$SHOULD_TEST" ]; then make -j${N_MAKE_JOBS} crossbinary-default-parallel; fi

View File

@@ -1,16 +1,11 @@
#!/usr/bin/env bash
set -e
export DOCKER_VERSION=17.03.1
sudo -E apt-get -yq update
sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install docker-ce=${DOCKER_VERSION}*
docker version
source .semaphoreci/vars
pip install --user -r requirements.txt
if [ -z "${PULL_REQUEST_NUMBER}" ]; then SHOULD_TEST="-*-"; else TEMP_STORAGE=$(curl --silent https://patch-diff.githubusercontent.com/raw/containous/traefik/pull/${PULL_REQUEST_NUMBER}.diff | patch --dry-run -p1 -R); fi
if [ -n "$TEMP_STORAGE" ]; then SHOULD_TEST=$(echo "$TEMP_STORAGE" | grep -Ev '(.md|.yaml|.yml)' || :); fi
if [ -n "$SHOULD_TEST" ]; then sudo -E apt-get -yq update; fi
if [ -n "$SHOULD_TEST" ]; then sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install docker-ce=${DOCKER_VERSION}*; fi
if [ -n "$SHOULD_TEST" ]; then docker version; fi
make pull-images
ci_retry make validate

6
.semaphoreci/tests.sh Executable file
View File

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

View File

@@ -10,7 +10,7 @@ else
export VERSION=''
fi
export CODENAME=maroilles
export CODENAME=roquefort
export N_MAKE_JOBS=2

View File

@@ -1,9 +1,6 @@
sudo: required
dist: trusty
git:
depth: false
services:
- docker
@@ -11,12 +8,11 @@ env:
global:
- REPO: $TRAVIS_REPO_SLUG
- VERSION: $TRAVIS_TAG
- CODENAME: maroilles
- CODENAME: roquefort
- N_MAKE_JOBS: 2
script:
- echo "Skipping tests... (Tests are executed on SemaphoreCI)"
- if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then make docs-verify; fi
before_deploy:
- >
@@ -25,16 +21,22 @@ before_deploy:
sudo -E apt-get -yq update;
sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install docker-ce=${DOCKER_VERSION}*;
docker version;
make image;
if [ "$TRAVIS_TAG" ]; then
make -j${N_MAKE_JOBS} crossbinary-parallel;
tar cfz dist/traefik-${VERSION}.src.tar.gz --exclude-vcs --exclude dist .;
fi;
curl -sI https://github.com/containous/structor/releases/latest | grep -Fi Location | tr -d '\r' | sed "s/tag/download/g" | awk -F " " '{ print $2 "/structor_linux-amd64"}' | wget --output-document=$GOPATH/bin/structor -i -;
chmod +x $GOPATH/bin/structor;
structor -o containous -r traefik --dockerfile-url="https://raw.githubusercontent.com/containous/traefik/master/docs.Dockerfile" --menu.js-url="https://raw.githubusercontent.com/containous/structor/master/traefik-menu.js.gotmpl" --rqts-url="https://raw.githubusercontent.com/containous/structor/master/requirements-override.txt" --exp-branch=master --debug;
pip install --user -r requirements.txt;
make -j${N_MAKE_JOBS} crossbinary-parallel;
make image-dirty;
mkdocs build --clean;
tar cfz dist/traefik-${VERSION}.src.tar.gz --exclude-vcs --exclude dist .;
fi
deploy:
- provider: pages
edge: true
github_token: ${GITHUB_TOKEN}
local_dir: site
skip_cleanup: true
on:
repo: containous/traefik
tags: true
condition: ${TRAVIS_TAG} =~ ^v[0-9]+\.[0-9]+\.[0-9]+$
- provider: releases
api_key: ${GITHUB_TOKEN}
file: dist/traefik*
@@ -54,11 +56,3 @@ deploy:
skip_cleanup: true
on:
repo: containous/traefik
- provider: pages
edge: false
github_token: ${GITHUB_TOKEN}
local_dir: site
skip_cleanup: true
on:
repo: containous/traefik
all_branches: true

File diff suppressed because it is too large Load Diff

View File

@@ -2,8 +2,7 @@
## Building
You need either [Docker](https://github.com/docker/docker) and `make` (Method 1), or `go` (Method 2) in order to build Traefik.
For changes to its dependencies, the `dep` dependency management tool is required.
You need either [Docker](https://github.com/docker/docker) and `make` (Method 1), or `go` (Method 2) in order to build Traefik. For changes to its dependencies, the `glide` dependency management tool and `glide-vc` plugin are required.
### Method 1: Using `Docker` and `Makefile`
@@ -13,11 +12,11 @@ You need to run the `binary` target. This will create binaries for Linux platfor
$ make binary
docker build -t "traefik-dev:no-more-godep-ever" -f build.Dockerfile .
Sending build context to Docker daemon 295.3 MB
Step 0 : FROM golang:1.11-alpine
Step 0 : FROM golang:1.9-alpine
---> 8c6473912976
Step 1 : RUN go get github.com/golang/dep/cmd/dep
Step 1 : RUN go get github.com/Masterminds/glide
[...]
docker run --rm -v "/var/run/docker.sock:/var/run/docker.sock" -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -v "/home/user/go/src/github.com/containous/traefik/"dist":/go/src/github.com/containous/traefik/"dist"" "traefik-dev:no-more-godep-ever" ./script/make.sh generate binary
docker run --rm -v "/var/run/docker.sock:/var/run/docker.sock" -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -v "/home/emile/dev/go/src/github.com/containous/traefik/"dist":/go/src/github.com/containous/traefik/"dist"" "traefik-dev:no-more-godep-ever" ./script/make.sh generate binary
---> Making bundle: generate (in .)
removed 'gen.go'
@@ -32,7 +31,7 @@ traefik*
##### Setting up your `go` environment
- You need `go` v1.9+
- It is recommended you clone Traefik into a directory like `~/go/src/github.com/containous/traefik` (This is the official golang workspace hierarchy, and will allow dependencies to resolve properly)
- It is recommended you clone Træfik into a directory like `~/go/src/github.com/containous/traefik` (This is the official golang workspace hierarchy, and will allow dependencies to resolve properly)
- Set your `GOPATH` and `PATH` variable to be set to `~/go` via:
```bash
@@ -56,20 +55,17 @@ GORACE=""
## more go env's will be listed
```
##### Build Traefik
##### Build Træfik
Once your environment is set up and the Traefik repository cloned you can build Traefik. You need get `go-bindata` once to be able to use `go generate` command as part of the build. The steps to build are:
Once your environment is set up and the Træfik repository cloned you can build Træfik. You need get `go-bindata` once to be able to use `go generate` command as part of the build. The steps to build are:
```bash
cd ~/go/src/github.com/containous/traefik
# Get go-bindata. Please note, the ellipses are required
go get github.com/containous/go-bindata/...
go get github.com/jteeuwen/go-bindata/...
# Start build
# generate
# (required to merge non-code components into the final binary, such as the web dashboard and provider's Go templates)
go generate
# Standard go build
@@ -77,28 +73,23 @@ go build ./cmd/traefik
# run other commands like tests
```
You will find the Traefik executable in the `~/go/src/github.com/containous/traefik` folder as `traefik`.
You will find the Træfik executable in the `~/go/src/github.com/containous/traefik` folder as `traefik`.
### Updating the templates
### Setting up `glide` and `glide-vc` for dependency management
If you happen to update the provider templates (in `/templates`), you need to run `go generate` to update the `autogen` package.
- Glide is not required for building; however, it is necessary to modify dependencies (i.e., add, update, or remove third-party packages)
- Glide can be installed either via homebrew: `$ brew install glide` or via the official glide script: `$ curl https://glide.sh/get | sh`
- The glide plugin `glide-vc` must be installed from source: `go get github.com/sgotti/glide-vc`
### Setting up dependency management
If you want to add a dependency, use `$ glide get` to have glide put it into the vendor folder and update the glide manifest/lock files (`glide.yaml` and `glide.lock`, respectively). A following `glide-vc` run should be triggered to trim down the size of the vendor folder. The final result must be committed into VCS.
[dep](https://github.com/golang/dep) is not required for building; however, it is necessary to modify dependencies (i.e., add, update, or remove third-party packages)
Care must be taken to choose the right arguments to `glide` when dealing with dependencies, or otherwise risk ending up with a broken build. For that reason, the helper script `script/glide.sh` encapsulates the gory details and conveniently calls `glide-vc` as well. Call it without parameters for basic usage instructions.
You need to use [dep](https://github.com/golang/dep) >= O.4.1.
If you want to add a dependency, use `dep ensure -add` to have [dep](https://github.com/golang/dep) put it into the vendor folder and update the dep manifest/lock files (`Gopkg.toml` and `Gopkg.lock`, respectively).
A following `make dep-prune` run should be triggered to trim down the size of the vendor folder.
The final result must be committed into VCS.
Here's a full example using dep to add a new dependency:
Here's a full example using glide to add a new dependency:
```bash
# install the new main dependency github.com/foo/bar and minimize vendor size
$ dep ensure -add github.com/foo/bar
$ ./script/glide.sh get github.com/foo/bar
# generate (Only required to integrate other components such as web dashboard)
$ go generate
# Standard go build
@@ -117,7 +108,7 @@ integration test using the `test-integration` target.
$ make test-unit
docker build -t "traefik-dev:your-feature-branch" -f build.Dockerfile .
# […]
docker run --rm -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -v "/home/user/go/src/github/containous/traefik/dist:/go/src/github.com/containous/traefik/dist" "traefik-dev:your-feature-branch" ./script/make.sh generate test-unit
docker run --rm -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -v "/home/vincent/src/github/vdemeester/traefik/dist:/go/src/github.com/containous/traefik/dist" "traefik-dev:your-feature-branch" ./script/make.sh generate test-unit
---> Making bundle: generate (in .)
removed 'gen.go'
@@ -129,7 +120,6 @@ Test success
```
For development purposes, you can specify which tests to run by using:
```bash
# Run every tests in the MyTest suite
TESTFLAGS="-check.f MyTestSuite" make test-integration
@@ -148,49 +138,18 @@ More: https://labix.org/gocheck
#### Method 2: `go`
Unit tests can be run from the cloned directory by `$ go test ./...` which should return `ok` similar to:
- Tests can be run from the cloned directory, by `$ go test ./...` which should return `ok` similar to:
```
ok _/home/user/go/src/github/containous/traefik 0.004s
ok _/home/vincent/src/github/vdemeester/traefik 0.004s
```
Integration tests must be run from the `integration/` directory and require the `-integration` switch to be passed like this: `$ cd integration && go test -integration ./...`.
## Documentation
The [documentation site](http://docs.traefik.io/) is built with [mkdocs](http://mkdocs.org/)
### Building Documentation
#### Method 1: `Docker` and `make`
You can build the documentation and serve it locally with livereloading, using the `docs` target:
```bash
$ make docs
docker build -t traefik-docs -f docs.Dockerfile .
# […]
docker run --rm -v /home/user/go/github/containous/traefik:/mkdocs -p 8000:8000 traefik-docs mkdocs serve
# […]
[I 170828 20:47:48 server:283] Serving on http://0.0.0.0:8000
[I 170828 20:47:48 handlers:60] Start watching changes
[I 170828 20:47:48 handlers:62] Start detecting changes
```
And go to [http://127.0.0.1:8000](http://127.0.0.1:8000).
If you only want to build the documentation without serving it locally, you can use the following command:
```bash
$ make docs-build
...
```
#### Method 2: `mkdocs`
First make sure you have python and pip installed
```bash
```shell
$ python --version
Python 2.7.2
$ pip --version
@@ -199,49 +158,29 @@ pip 1.5.2
Then install mkdocs with pip
```bash
pip install --user -r requirements.txt
```shell
$ pip install mkdocs
```
To build documentation locally and serve it locally,
run `mkdocs serve` in the root directory,
this should start a server locally to preview your changes.
To test documentation locally run `mkdocs serve` in the root directory, this should start a server locally to preview your changes.
```bash
```shell
$ mkdocs serve
INFO - Building documentation...
WARNING - Config value: 'theme'. Warning: The theme 'united' will be removed in an upcoming MkDocs release. See http://www.mkdocs.org/about/release-notes/ for more details
INFO - Cleaning site directory
[I 160505 22:31:24 server:281] Serving on http://127.0.0.1:8000
[I 160505 22:31:24 handlers:59] Start watching changes
[I 160505 22:31:24 handlers:61] Start detecting changes
```
### Verify Documentation
You can verify that the documentation meets some expectations, as checking for dead links, html markup validity.
```bash
$ make docs-verify
docker build -t traefik-docs-verify ./script/docs-verify-docker-image ## Build Validator image
...
docker run --rm -v /home/travis/build/containous/traefik:/app traefik-docs-verify ## Check for dead links and w3c compliance
=== Checking HTML content...
Running ["HtmlCheck", "ImageCheck", "ScriptCheck", "LinkCheck"] on /app/site/basics/index.html on *.html...
```
If you recently changed the documentation, do not forget to clean it to have it rebuilt:
```bash
$ make docs-clean docs-verify
...
```
## How to Write a Good Issue
Please keep in mind that the GitHub issue tracker is not intended as a general support forum, but for reporting bugs and feature requests.
For end-user related support questions, refer to one of the following:
- the Traefik community Slack channel: [![Join the chat at https://slack.traefik.io](https://img.shields.io/badge/style-register-green.svg?style=social&label=Slack)](https://slack.traefik.io)
- the Traefik community Slack channel: [![Join the chat at https://traefik.herokuapp.com](https://img.shields.io/badge/style-register-green.svg?style=social&label=Slack)](https://traefik.herokuapp.com)
- [Stack Overflow](https://stackoverflow.com/questions/tagged/traefik) (using the `traefik` tag)
### Title

View File

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

1863
Gopkg.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,266 +0,0 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
[[constraint]]
branch = "master"
name = "github.com/ArthurHlt/go-eureka-client"
[[constraint]]
branch = "master"
name = "github.com/BurntSushi/toml"
[[constraint]]
branch = "master"
name = "github.com/BurntSushi/ty"
[[constraint]]
branch = "master"
name = "github.com/NYTimes/gziphandler"
[[constraint]]
branch = "containous-fork"
name = "github.com/abbot/go-http-auth"
source = "github.com/containous/go-http-auth"
[[constraint]]
branch = "master"
name = "github.com/armon/go-proxyproto"
[[constraint]]
name = "github.com/aws/aws-sdk-go"
version = "1.13.11"
[[constraint]]
branch = "master"
name = "github.com/cenk/backoff"
[[constraint]]
name = "github.com/containous/flaeg"
version = "1.4.1"
[[constraint]]
branch = "master"
name = "github.com/containous/mux"
[[constraint]]
name = "github.com/containous/staert"
version = "3.1.1"
[[constraint]]
name = "github.com/containous/traefik-extra-service-fabric"
version = "1.3.0"
[[constraint]]
name = "github.com/coreos/go-systemd"
version = "14.0.0"
[[constraint]]
branch = "master"
name = "github.com/docker/leadership"
source = "github.com/containous/leadership"
[[constraint]]
name = "github.com/eapache/channels"
version = "1.1.0"
[[constraint]]
branch = "master"
name = "github.com/elazarl/go-bindata-assetfs"
[[constraint]]
branch = "fork-containous"
name = "github.com/go-check/check"
source = "github.com/containous/check"
[[override]]
branch = "fork-containous"
name = "github.com/go-check/check"
source = "github.com/containous/check"
[[constraint]]
name = "github.com/go-kit/kit"
version = "0.7.0"
[[constraint]]
branch = "master"
name = "github.com/gorilla/websocket"
[[constraint]]
name = "github.com/hashicorp/consul"
version = "1.0.6"
[[constraint]]
name = "github.com/influxdata/influxdb"
version = "1.3.7"
[[constraint]]
branch = "master"
name = "github.com/jjcollinge/servicefabric"
[[constraint]]
branch = "master"
name = "github.com/abronan/valkeyrie"
[[constraint]]
name = "github.com/mesosphere/mesos-dns"
source = "https://github.com/containous/mesos-dns.git"
[[constraint]]
branch = "master"
name = "github.com/mitchellh/copystructure"
[[constraint]]
branch = "master"
name = "github.com/mitchellh/hashstructure"
[[constraint]]
branch = "master"
name = "github.com/mitchellh/mapstructure"
[[constraint]]
name = "github.com/opentracing/opentracing-go"
version = "1.0.2"
[[constraint]]
branch = "containous-fork"
name = "github.com/rancher/go-rancher-metadata"
source = "github.com/containous/go-rancher-metadata"
[[constraint]]
branch = "master"
name = "github.com/ryanuber/go-glob"
[[constraint]]
name = "github.com/satori/go.uuid"
version = "1.1.0"
[[constraint]]
branch = "master"
name = "github.com/stvp/go-udp-testing"
[[constraint]]
name = "github.com/stretchr/testify"
version = "1.2.1"
[[constraint]]
name = "github.com/uber/jaeger-client-go"
version = "2.9.0"
[[constraint]]
name = "github.com/uber/jaeger-lib"
version = "1.1.0"
[[constraint]]
branch = "v1"
name = "github.com/unrolled/secure"
[[constraint]]
name = "github.com/vdemeester/shakers"
version = "0.1.0"
[[constraint]]
branch = "master"
name = "github.com/vulcand/oxy"
[[constraint]]
branch = "master"
name = "github.com/xenolf/lego"
# version = "1.0.0"
[[constraint]]
name = "google.golang.org/grpc"
version = "1.5.2"
[[constraint]]
name = "gopkg.in/fsnotify.v1"
source = "github.com/fsnotify/fsnotify"
version = "1.4.2"
[[constraint]]
name = "k8s.io/client-go"
version = "6.0.0"
[[constraint]]
name = "k8s.io/api"
version = "kubernetes-1.9.0"
[[constraint]]
name = "k8s.io/apimachinery"
version = "kubernetes-1.9.0"
[[constraint]]
branch = "master"
name = "github.com/libkermit/docker"
[[constraint]]
branch = "master"
name = "github.com/libkermit/docker-check"
[[constraint]]
branch = "master"
name = "github.com/libkermit/compose"
[[constraint]]
name = "github.com/docker/docker"
revision = "7848b8beb9d38a98a78b75f78e05f8d2255f9dfe"
[[override]]
name = "github.com/docker/docker"
revision = "7848b8beb9d38a98a78b75f78e05f8d2255f9dfe"
[[override]]
name = "github.com/docker/cli"
revision = "6b63d7b96a41055baddc3fa71f381c7f60bd5d8e"
[[override]]
name = "github.com/docker/distribution"
revision = "edc3ab29cdff8694dd6feb85cfeb4b5f1b38ed9c"
[[override]]
branch = "master"
name = "github.com/docker/libcompose"
[[override]]
name = "github.com/Nvveen/Gotty"
revision = "a8b993ba6abdb0e0c12b0125c603323a71c7790c"
source = "github.com/ijc25/Gotty"
[[override]]
# ALWAYS keep this override
name = "github.com/mailgun/timetools"
revision = "7e6055773c5137efbeb3bd2410d705fe10ab6bfd"
[[override]]
branch = "master"
name = "github.com/miekg/dns"
[prune]
non-go = true
go-tests = true
unused-packages = true
[[constraint]]
name = "github.com/patrickmn/go-cache"
version = "2.1.0"
[[constraint]]
name = "gopkg.in/DataDog/dd-trace-go.v1"
version = "1.5.0"

View File

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

View File

@@ -12,14 +12,13 @@
* Julien Salleyron [@juliens](https://github.com/juliens)
* Nicolas Mengin [@nmengin](https://github.com/nmengin)
* Marco Jantke [@marco-jantke](https://github.com/marco-jantke)
* Michaël Matur [@mmatur](https://github.com/mmatur)
## PR review process:
* The status `needs-design-review` is only used in complex/heavy/tricky PRs.
* From `1` to `2`: 1 comment that says “design LGTM” (by a senior maintainer).
* From `2` to `3`: 3 LGTM approvals by any maintainer.
* From `1` to `2`: 1 design LGTM in comment, by a senior maintainer, if needed.
* From `2` to `3`: 3 LGTM by any maintainer.
* If needed, a specific maintainer familiar with a particular domain can be requested for the review.
We use [PRM](https://github.com/ldez/prm) to manage locally pull requests.
@@ -34,21 +33,12 @@ We use [PRM](https://github.com/ldez/prm) to manage locally pull requests.
The maintainer giving the final LGTM must add the `status/3-needs-merge` label to trigger the merge bot.
By default, a squash-rebase merge will be carried out.
To preserve commits, add `bot/merge-method-rebase` before `status/3-needs-merge`.
If you want to preserve commits you must add `bot/merge-method-rebase` before `status/3-needs-merge`.
The status `status/4-merge-in-progress` is only used by the bot.
The status `status/4-merge-in-progress` is only for the bot.
If the bot is not able to perform the merge, the label `bot/need-human-merge` is added.
In such a situation, solve the conflicts/CI/... and then remove the label `bot/need-human-merge`.
To prevent the bot from automatically merging a PR, add the label `bot/no-merge`.
The label `bot/light-review` decreases the number of required LGTM from 3 to 1.
This label is used when:
- Updating the vendors from previously reviewed PRs
- Merging branches into the master
- Preparing the release
In this case you must solve conflicts/CI/... and after you only need to remove `bot/need-human-merge`.
### [Myrmica Bibikoffi](https://github.com/containous/bibikoffi/)
@@ -62,38 +52,37 @@ This label is used when:
**Manage GitHub labels**
* Add labels on new PR [GitHub WebHook]
* Add milestone to a new PR based on a branch version (1.4, 1.3, ...) [GitHub WebHook]
* Add and remove `contributor/waiting-for-corrections` label when a review request changes [GitHub WebHook]
* Weekly report of PR status on Slack (CaptainPR) [cron]
## Labels
A maintainer that looks at an issue/PR must define its `kind/*`, `area/*`, and `status/*`.
If we open/look an issue/PR, we must add a `kind/*` and an `area/*`.
### Contributor
* `contributor/need-more-information`: we need more information from the contributor in order to analyze a problem.
* `contributor/waiting-for-feedback`: we need the contributor to give us feedback.
* `contributor/waiting-for-corrections`: we need the contributor to take actions in order to move forward with a PR. **(only for PR)** _[bot, humans]_
* `contributor/waiting-for-corrections`: we need the contributor to take actions in order to move forward with a PR. **(only for PR)**
* `contributor/needs-resolve-conflicts`: use it only when there is some conflicts (and an automatic rebase is not possible). **(only for PR)** _[bot, humans]_
### Kind
* `kind/enhancement`: a new or improved feature.
* `kind/question`: a question. **(only for issue)**
* `kind/proposal`: a proposal that needs to be discussed.
* _Proposal issues_ are design proposals
* `kind/question`: It's a question. **(only for issue)**
* `kind/proposal`: proposal PR/issues need a public debate.
* _Proposal issues_ are design proposal that need to be refined with multiple contributors.
* _Proposal PRs_ are technical prototypes that need to be refined with multiple contributors.
* `kind/bug/possible`: a possible bug that needs analysis before it is confirmed or fixed. **(only for issues)**
* `kind/bug/confirmed`: a confirmed bug (reproducible). **(only for issues)**
* `kind/bug/fix`: a bug fix. **(only for PR)**
* `kind/bug/possible`: if we need to analyze to understand if it's a bug or not. **(only for issues)** _[bot only]_
* `kind/bug/confirmed`: we are sure, it's a bug. **(only for issues)**
* `kind/bug/fix`: it's a bug fix. **(only for PR)**
### Resolution
* `resolution/duplicate`: a duplicate issue/PR.
* `resolution/declined`: declined (Rule #1 of open-source: no is temporary, yes is forever).
* `resolution/duplicate`: it's a duplicate issue/PR.
* `resolution/declined`: Rule #1 of open-source: no is temporary, yes is forever.
* `WIP`: Work In Progress. **(only for PR)**
### Platform
@@ -106,10 +95,10 @@ A maintainer that looks at an issue/PR must define its `kind/*`, `area/*`, and `
* `area/api`: Traefik API related.
* `area/authentication`: Authentication related.
* `area/cluster`: Traefik clustering related.
* `area/documentation`: Documentation related.
* `area/infrastructure`: CI or Traefik building scripts related.
* `area/documentation`: regards improving/adding documentation.
* `area/infrastructure`: related to CI or Traefik building scripts.
* `area/healthcheck`: Health-check related.
* `area/logs`: Logs related.
* `area/logs`: Traefik logs related.
* `area/middleware`: Middleware related.
* `area/middleware/metrics`: Metrics related. (Prometheus, StatsD, ...)
* `area/oxy`: Oxy related.
@@ -133,23 +122,23 @@ A maintainer that looks at an issue/PR must define its `kind/*`, `area/*`, and `
### Priority
* `priority/P0`: needs a hot fix. **(only for issue)**
* `priority/P1`: needs to be fixed the next release. **(only for issue)**
* `priority/P2`: needs to be fixed in the future. **(only for issue)**
* `priority/P0`: needs hot fix. **(only for issue)**
* `priority/P1`: need to be fixed in next release. **(only for issue)**
* `priority/P2`: need to be fixed in the future. **(only for issue)**
* `priority/P3`: maybe. **(only for issue)**
### PR size
* `size/S`: small PR. **(only for PR)** _[bot only]_
* `size/M`: medium PR. **(only for PR)** _[bot only]_
* `size/L`: large PR. **(only for PR)** _[bot only]_
* `size/L`: Large PR. **(only for PR)** _[bot only]_
### Status - Workflow
The `status/*` labels represent the desired state in the workflow.
* `status/0-needs-triage`: all the new issues and PRs have this status. _[bot only]_
* `status/1-needs-design-review`: needs a design review. **(only for PR)**
* `status/2-needs-review`: needs a code/documentation review. **(only for PR)**
* `status/0-needs-triage`: all new issue or PR have this status. _[bot only]_
* `status/1-needs-design-review`: need a design review. **(only for PR)**
* `status/2-needs-review`: need a code/documentation review. **(only for PR)**
* `status/3-needs-merge`: ready to merge. **(only for PR)**
* `status/4-merge-in-progress`: merge is in progress. _[bot only]_
* `status/4-merge-in-progress`: merge in progress. _[bot only]_

View File

@@ -1,4 +1,4 @@
.PHONY: all docs-verify docs docs-clean docs-build
.PHONY: all
TRAEFIK_ENVS := \
-e OS_ARCH_ARG \
@@ -20,17 +20,12 @@ GIT_BRANCH := $(subst heads/,,$(shell git rev-parse --abbrev-ref HEAD 2>/dev/nul
TRAEFIK_DEV_IMAGE := traefik-dev$(if $(GIT_BRANCH),:$(subst /,-,$(GIT_BRANCH)))
REPONAME := $(shell echo $(REPO) | tr '[:upper:]' '[:lower:]')
TRAEFIK_IMAGE := $(if $(REPONAME),$(REPONAME),"containous/traefik")
INTEGRATION_OPTS := $(if $(MAKE_DOCKER_HOST),-e "DOCKER_HOST=$(MAKE_DOCKER_HOST)", -e "TEST_CONTAINER=1" -v "/var/run/docker.sock:/var/run/docker.sock")
TRAEFIK_DOC_IMAGE := traefik-docs
TRAEFIK_DOC_VERIFY_IMAGE := $(TRAEFIK_DOC_IMAGE)-verify
INTEGRATION_OPTS := $(if $(MAKE_DOCKER_HOST),-e "DOCKER_HOST=$(MAKE_DOCKER_HOST)", -v "/var/run/docker.sock:/var/run/docker.sock")
DOCKER_BUILD_ARGS := $(if $(DOCKER_VERSION), "--build-arg=DOCKER_VERSION=$(DOCKER_VERSION)",)
DOCKER_RUN_OPTS := $(TRAEFIK_ENVS) $(TRAEFIK_MOUNT) "$(TRAEFIK_DEV_IMAGE)"
DOCKER_RUN_TRAEFIK := docker run $(INTEGRATION_OPTS) -it $(DOCKER_RUN_OPTS)
DOCKER_RUN_TRAEFIK_NOTTY := docker run $(INTEGRATION_OPTS) -i $(DOCKER_RUN_OPTS)
DOCKER_RUN_DOC_PORT := 8000
DOCKER_RUN_DOC_MOUNT := -v $(CURDIR):/mkdocs
DOCKER_RUN_DOC_OPTS := --rm $(DOCKER_RUN_DOC_MOUNT) -p $(DOCKER_RUN_DOC_PORT):8000
print-%: ; @echo $*=$($*)
@@ -72,10 +67,9 @@ test-unit: build ## run the unit tests
test-integration: build ## run the integration tests
$(DOCKER_RUN_TRAEFIK) ./script/make.sh generate binary test-integration
TEST_HOST=1 ./script/make.sh test-integration
validate: build ## validate code, vendor and autogen
$(DOCKER_RUN_TRAEFIK) ./script/make.sh validate-gofmt validate-govet validate-golint validate-misspell validate-vendor validate-autogen
validate: build ## validate gofmt, golint and go vet
$(DOCKER_RUN_TRAEFIK) ./script/make.sh validate-glide validate-gofmt validate-govet validate-golint validate-misspell validate-vendor
build: dist
docker build $(DOCKER_BUILD_ARGS) -t "$(TRAEFIK_DEV_IMAGE)" -f build.Dockerfile .
@@ -95,24 +89,6 @@ image-dirty: binary ## build a docker traefik image
image: clear-static binary ## clean up static directory and build a docker traefik image
docker build -t $(TRAEFIK_IMAGE) .
docs-image:
docker build -t $(TRAEFIK_DOC_IMAGE) -f docs.Dockerfile .
docs: docs-image
docker run $(DOCKER_RUN_DOC_OPTS) $(TRAEFIK_DOC_IMAGE) mkdocs serve
docs-build: site
docs-verify: site
docker build -t $(TRAEFIK_DOC_VERIFY_IMAGE) ./script/docs-verify-docker-image ## Build Validator image
docker run --rm -v $(CURDIR):/app $(TRAEFIK_DOC_VERIFY_IMAGE) ## Check for dead links and w3c compliance
site: docs-image
docker run $(DOCKER_RUN_DOC_OPTS) $(TRAEFIK_DOC_IMAGE) mkdocs build
docs-clean:
rm -rf $(CURDIR)/site
clear-static:
rm -rf static
@@ -140,12 +116,5 @@ fmt:
pull-images:
grep --no-filename -E '^\s+image:' ./integration/resources/compose/*.yml | awk '{print $$2}' | sort | uniq | xargs -P 6 -n 1 docker pull
dep-ensure:
dep ensure -v
./script/prune-dep.sh
dep-prune:
./script/prune-dep.sh
help: ## this help
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {sub("\\\\n",sprintf("\n%22c"," "), $$2);printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)

187
README.md
View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -6,127 +6,79 @@ import (
"net/http"
"net/http/httptest"
"reflect"
"sort"
"sync"
"testing"
"time"
acmeprovider "github.com/containous/traefik/provider/acme"
"github.com/containous/traefik/tls/generate"
"github.com/containous/traefik/types"
"github.com/stretchr/testify/assert"
"github.com/xenolf/lego/acme"
)
func TestDomainsSet(t *testing.T) {
testCases := []struct {
input string
expected types.Domains
}{
{
input: "",
expected: types.Domains{},
},
{
input: "foo1.com",
expected: types.Domains{
types.Domain{Main: "foo1.com"},
},
},
{
input: "foo2.com,bar.net",
expected: types.Domains{
types.Domain{
Main: "foo2.com",
SANs: []string{"bar.net"},
},
},
},
{
input: "foo3.com,bar1.net,bar2.net,bar3.net",
expected: types.Domains{
types.Domain{
Main: "foo3.com",
SANs: []string{"bar1.net", "bar2.net", "bar3.net"},
},
},
},
checkMap := map[string]Domains{
"": {},
"foo.com": {Domain{Main: "foo.com", SANs: []string{}}},
"foo.com,bar.net": {Domain{Main: "foo.com", SANs: []string{"bar.net"}}},
"foo.com,bar1.net,bar2.net,bar3.net": {Domain{Main: "foo.com", SANs: []string{"bar1.net", "bar2.net", "bar3.net"}}},
}
for _, test := range testCases {
test := test
t.Run(test.input, func(t *testing.T) {
t.Parallel()
domains := types.Domains{}
domains.Set(test.input)
assert.Exactly(t, test.expected, domains)
})
for in, check := range checkMap {
ds := Domains{}
ds.Set(in)
if !reflect.DeepEqual(check, ds) {
t.Errorf("Expected %+v\nGot %+v", check, ds)
}
}
}
func TestDomainsSetAppend(t *testing.T) {
testCases := []struct {
input string
expected types.Domains
}{
{
input: "",
expected: types.Domains{},
},
{
input: "foo1.com",
expected: types.Domains{
types.Domain{Main: "foo1.com"},
},
},
{
input: "foo2.com,bar.net",
expected: types.Domains{
types.Domain{Main: "foo1.com"},
types.Domain{
Main: "foo2.com",
SANs: []string{"bar.net"},
},
},
},
{
input: "foo3.com,bar1.net,bar2.net,bar3.net",
expected: types.Domains{
types.Domain{Main: "foo1.com"},
types.Domain{
Main: "foo2.com",
SANs: []string{"bar.net"},
},
types.Domain{
Main: "foo3.com",
SANs: []string{"bar1.net", "bar2.net", "bar3.net"},
},
},
},
inSlice := []string{
"",
"foo1.com",
"foo2.com,bar.net",
"foo3.com,bar1.net,bar2.net,bar3.net",
}
// append to
domains := types.Domains{}
for _, test := range testCases {
t.Run(test.input, func(t *testing.T) {
domains.Set(test.input)
assert.Exactly(t, test.expected, domains)
})
checkSlice := []Domains{
{},
{
Domain{
Main: "foo1.com",
SANs: []string{}}},
{
Domain{
Main: "foo1.com",
SANs: []string{}},
Domain{
Main: "foo2.com",
SANs: []string{"bar.net"}}},
{
Domain{
Main: "foo1.com",
SANs: []string{}},
Domain{
Main: "foo2.com",
SANs: []string{"bar.net"}},
Domain{Main: "foo3.com",
SANs: []string{"bar1.net", "bar2.net", "bar3.net"}}},
}
ds := Domains{}
for i, in := range inSlice {
ds.Set(in)
if !reflect.DeepEqual(checkSlice[i], ds) {
t.Errorf("Expected %s %+v\nGot %+v", in, checkSlice[i], ds)
}
}
}
func TestCertificatesRenew(t *testing.T) {
foo1Cert, foo1Key, _ := generate.KeyPair("foo1.com", time.Now())
foo2Cert, foo2Key, _ := generate.KeyPair("foo2.com", time.Now())
foo1Cert, foo1Key, _ := generateKeyPair("foo1.com", time.Now())
foo2Cert, foo2Key, _ := generateKeyPair("foo2.com", time.Now())
domainsCertificates := DomainsCertificates{
lock: sync.RWMutex{},
Certs: []*DomainsCertificate{
{
Domains: types.Domain{
Main: "foo1.com"},
Domains: Domain{
Main: "foo1.com",
SANs: []string{}},
Certificate: &Certificate{
Domain: "foo1.com",
CertURL: "url",
@@ -136,8 +88,9 @@ func TestCertificatesRenew(t *testing.T) {
},
},
{
Domains: types.Domain{
Main: "foo2.com"},
Domains: Domain{
Main: "foo2.com",
SANs: []string{}},
Certificate: &Certificate{
Domain: "foo2.com",
CertURL: "url",
@@ -148,8 +101,7 @@ func TestCertificatesRenew(t *testing.T) {
},
},
}
foo1Cert, foo1Key, _ = generate.KeyPair("foo1.com", time.Now())
foo1Cert, foo1Key, _ = generateKeyPair("foo1.com", time.Now())
newCertificate := &Certificate{
Domain: "foo1.com",
CertURL: "url",
@@ -158,15 +110,17 @@ func TestCertificatesRenew(t *testing.T) {
Certificate: foo1Cert,
}
err := domainsCertificates.renewCertificates(newCertificate, types.Domain{Main: "foo1.com"})
err := domainsCertificates.renewCertificates(
newCertificate,
Domain{
Main: "foo1.com",
SANs: []string{}})
if err != nil {
t.Errorf("Error in renewCertificates :%v", err)
}
if len(domainsCertificates.Certs) != 2 {
t.Errorf("Expected domainsCertificates length %d %+v\nGot %+v", 2, domainsCertificates.Certs, len(domainsCertificates.Certs))
}
if !reflect.DeepEqual(domainsCertificates.Certs[0].Certificate, newCertificate) {
t.Errorf("Expected new certificate %+v \nGot %+v", newCertificate, domainsCertificates.Certs[0].Certificate)
}
@@ -174,16 +128,17 @@ func TestCertificatesRenew(t *testing.T) {
func TestRemoveDuplicates(t *testing.T) {
now := time.Now()
fooCert, fooKey, _ := generate.KeyPair("foo.com", now)
foo24Cert, foo24Key, _ := generate.KeyPair("foo.com", now.Add(24*time.Hour))
foo48Cert, foo48Key, _ := generate.KeyPair("foo.com", now.Add(48*time.Hour))
barCert, barKey, _ := generate.KeyPair("bar.com", now)
fooCert, fooKey, _ := generateKeyPair("foo.com", now)
foo24Cert, foo24Key, _ := generateKeyPair("foo.com", now.Add(24*time.Hour))
foo48Cert, foo48Key, _ := generateKeyPair("foo.com", now.Add(48*time.Hour))
barCert, barKey, _ := generateKeyPair("bar.com", now)
domainsCertificates := DomainsCertificates{
lock: sync.RWMutex{},
Certs: []*DomainsCertificate{
{
Domains: types.Domain{
Main: "foo.com"},
Domains: Domain{
Main: "foo.com",
SANs: []string{}},
Certificate: &Certificate{
Domain: "foo.com",
CertURL: "url",
@@ -193,8 +148,9 @@ func TestRemoveDuplicates(t *testing.T) {
},
},
{
Domains: types.Domain{
Main: "foo.com"},
Domains: Domain{
Main: "foo.com",
SANs: []string{}},
Certificate: &Certificate{
Domain: "foo.com",
CertURL: "url",
@@ -204,8 +160,9 @@ func TestRemoveDuplicates(t *testing.T) {
},
},
{
Domains: types.Domain{
Main: "foo.com"},
Domains: Domain{
Main: "foo.com",
SANs: []string{}},
Certificate: &Certificate{
Domain: "foo.com",
CertURL: "url",
@@ -215,8 +172,9 @@ func TestRemoveDuplicates(t *testing.T) {
},
},
{
Domains: types.Domain{
Main: "bar.com"},
Domains: Domain{
Main: "bar.com",
SANs: []string{}},
Certificate: &Certificate{
Domain: "bar.com",
CertURL: "url",
@@ -226,8 +184,9 @@ func TestRemoveDuplicates(t *testing.T) {
},
},
{
Domains: types.Domain{
Main: "foo.com"},
Domains: Domain{
Main: "foo.com",
SANs: []string{}},
Certificate: &Certificate{
Domain: "foo.com",
CertURL: "url",
@@ -300,19 +259,14 @@ llJh9MC0svjevGtNlxJoE3lmEQIhAKXy1wfZ32/XtcrnENPvi6lzxI0T94X7s5pP3aCoPPoJAiEAl
cijFkALeQp/qyeXdFld2v9gUN3eCgljgcl0QweRoIc=---`)
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`{
"GPHhmRVEDas": "https://community.letsencrypt.org/t/adding-random-entries-to-the-directory/33417",
"keyChange": "https://foo/acme/key-change",
"meta": {
"termsOfService": "https://boulder:4431/terms/v7"
},
"newAccount": "https://foo/acme/new-acct",
"newNonce": "https://foo/acme/new-nonce",
"newOrder": "https://foo/acme/new-order",
"revokeCert": "https://foo/acme/revoke-cert"
"new-authz": "https://foo/acme/new-authz",
"new-cert": "https://foo/acme/new-cert",
"new-reg": "https://foo/acme/new-reg",
"revoke-cert": "https://foo/acme/revoke-cert"
}`))
}))
defer ts.Close()
a := ACME{DNSChallenge: &acmeprovider.DNSChallenge{Provider: "manual", DelayBeforeCheck: 10}, CAServer: ts.URL}
a := ACME{DNSProvider: "manual", DelayDontCheckDNS: 10, CAServer: ts.URL}
client, err := a.buildACMEClient(account)
if err != nil {
@@ -326,39 +280,6 @@ cijFkALeQp/qyeXdFld2v9gUN3eCgljgcl0QweRoIc=---`)
}
}
func TestAcme_getUncheckedCertificates(t *testing.T) {
mm := make(map[string]*tls.Certificate)
mm["*.containo.us"] = &tls.Certificate{}
mm["traefik.acme.io"] = &tls.Certificate{}
dm := make(map[string]struct{})
dm["*.traefik.wtf"] = struct{}{}
a := ACME{TLSConfig: &tls.Config{NameToCertificate: mm}, resolvingDomains: dm}
domains := []string{"traefik.containo.us", "trae.containo.us", "foo.traefik.wtf"}
uncheckedDomains := a.getUncheckedDomains(domains, nil)
assert.Empty(t, uncheckedDomains)
domains = []string{"traefik.acme.io", "trae.acme.io"}
uncheckedDomains = a.getUncheckedDomains(domains, nil)
assert.Len(t, uncheckedDomains, 1)
domainsCertificates := DomainsCertificates{Certs: []*DomainsCertificate{
{
tlsCert: &tls.Certificate{},
Domains: types.Domain{
Main: "*.acme.wtf",
SANs: []string{"trae.acme.io"},
},
},
}}
account := Account{DomainsCertificate: domainsCertificates}
uncheckedDomains = a.getUncheckedDomains(domains, &account)
assert.Empty(t, uncheckedDomains)
domains = []string{"traefik.containo.us", "trae.containo.us", "traefik.wtf"}
uncheckedDomains = a.getUncheckedDomains(domains, nil)
assert.Len(t, uncheckedDomains, 1)
}
func TestAcme_getProvidedCertificate(t *testing.T) {
mm := make(map[string]*tls.Certificate)
mm["*.containo.us"] = &tls.Certificate{}
@@ -366,459 +287,10 @@ func TestAcme_getProvidedCertificate(t *testing.T) {
a := ACME{TLSConfig: &tls.Config{NameToCertificate: mm}}
domain := "traefik.containo.us"
certificate := a.getProvidedCertificate(domain)
domains := []string{"traefik.containo.us", "trae.containo.us"}
certificate := a.getProvidedCertificate(domains)
assert.NotNil(t, certificate)
domain = "trae.acme.io"
certificate = a.getProvidedCertificate(domain)
domains = []string{"traefik.acme.io", "trae.acme.io"}
certificate = a.getProvidedCertificate(domains)
assert.Nil(t, certificate)
}
func TestAcme_getValidDomain(t *testing.T) {
testCases := []struct {
desc string
domains []string
wildcardAllowed bool
dnsChallenge *acmeprovider.DNSChallenge
expectedErr string
expectedDomains []string
}{
{
desc: "valid wildcard",
domains: []string{"*.traefik.wtf"},
dnsChallenge: &acmeprovider.DNSChallenge{},
wildcardAllowed: true,
expectedErr: "",
expectedDomains: []string{"*.traefik.wtf"},
},
{
desc: "no wildcard",
domains: []string{"traefik.wtf", "foo.traefik.wtf"},
dnsChallenge: &acmeprovider.DNSChallenge{},
expectedErr: "",
wildcardAllowed: true,
expectedDomains: []string{"traefik.wtf", "foo.traefik.wtf"},
},
{
desc: "unauthorized wildcard",
domains: []string{"*.traefik.wtf"},
dnsChallenge: &acmeprovider.DNSChallenge{},
wildcardAllowed: false,
expectedErr: "unable to generate a wildcard certificate for domain \"*.traefik.wtf\" from a 'Host' rule",
expectedDomains: nil,
},
{
desc: "no domain",
domains: []string{},
dnsChallenge: nil,
wildcardAllowed: true,
expectedErr: "unable to generate a certificate when no domain is given",
expectedDomains: nil,
},
{
desc: "no DNSChallenge",
domains: []string{"*.traefik.wtf", "foo.traefik.wtf"},
dnsChallenge: nil,
wildcardAllowed: true,
expectedErr: "unable to generate a wildcard certificate for domain \"*.traefik.wtf,foo.traefik.wtf\" : ACME needs a DNSChallenge",
expectedDomains: nil,
},
{
desc: "unauthorized wildcard with SAN",
domains: []string{"*.*.traefik.wtf", "foo.traefik.wtf"},
dnsChallenge: &acmeprovider.DNSChallenge{},
wildcardAllowed: true,
expectedErr: "unable to generate a wildcard certificate for domain \"*.*.traefik.wtf,foo.traefik.wtf\" : ACME does not allow '*.*' wildcard domain",
expectedDomains: nil,
},
{
desc: "wildcard with SANs",
domains: []string{"*.traefik.wtf", "traefik.wtf"},
dnsChallenge: &acmeprovider.DNSChallenge{},
wildcardAllowed: true,
expectedErr: "",
expectedDomains: []string{"*.traefik.wtf", "traefik.wtf"},
},
{
desc: "unexpected SANs",
domains: []string{"*.traefik.wtf", "*.acme.wtf"},
dnsChallenge: &acmeprovider.DNSChallenge{},
wildcardAllowed: true,
expectedErr: "unable to generate a certificate for domains \"*.traefik.wtf,*.acme.wtf\": SANs can not be a wildcard domain",
expectedDomains: nil,
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
a := ACME{}
if test.dnsChallenge != nil {
a.DNSChallenge = test.dnsChallenge
}
domains, err := a.getValidDomains(test.domains, test.wildcardAllowed)
if len(test.expectedErr) > 0 {
assert.EqualError(t, err, test.expectedErr, "Unexpected error.")
} else {
assert.Equal(t, len(test.expectedDomains), len(domains), "Unexpected domains.")
}
})
}
}
func TestAcme_getCertificateForDomain(t *testing.T) {
testCases := []struct {
desc string
domain string
dc *DomainsCertificates
expected *DomainsCertificate
expectedFound bool
}{
{
desc: "non-wildcard exact match",
domain: "foo.traefik.wtf",
dc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Domains: types.Domain{
Main: "foo.traefik.wtf",
},
},
},
},
expected: &DomainsCertificate{
Domains: types.Domain{
Main: "foo.traefik.wtf",
},
},
expectedFound: true,
},
{
desc: "non-wildcard no match",
domain: "bar.traefik.wtf",
dc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Domains: types.Domain{
Main: "foo.traefik.wtf",
},
},
},
},
expected: nil,
expectedFound: false,
},
{
desc: "wildcard match",
domain: "foo.traefik.wtf",
dc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Domains: types.Domain{
Main: "*.traefik.wtf",
},
},
},
},
expected: &DomainsCertificate{
Domains: types.Domain{
Main: "*.traefik.wtf",
},
},
expectedFound: true,
},
{
desc: "wildcard no match",
domain: "foo.traefik.wtf",
dc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Domains: types.Domain{
Main: "*.bar.traefik.wtf",
},
},
},
},
expected: nil,
expectedFound: false,
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
got, found := test.dc.getCertificateForDomain(test.domain)
assert.Equal(t, test.expectedFound, found)
assert.Equal(t, test.expected, got)
})
}
}
func TestRemoveEmptyCertificates(t *testing.T) {
now := time.Now()
fooCert, fooKey, _ := generate.KeyPair("foo.com", now)
acmeCert, acmeKey, _ := generate.KeyPair("acme.wtf", now.Add(24*time.Hour))
barCert, barKey, _ := generate.KeyPair("bar.com", now)
testCases := []struct {
desc string
dc *DomainsCertificates
expectedDc *DomainsCertificates
}{
{
desc: "No empty certificate",
dc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Certificate: &Certificate{
Certificate: fooCert,
PrivateKey: fooKey,
},
Domains: types.Domain{
Main: "foo.com",
},
},
{
Certificate: &Certificate{
Certificate: acmeCert,
PrivateKey: acmeKey,
},
Domains: types.Domain{
Main: "acme.wtf",
},
},
{
Certificate: &Certificate{
Certificate: barCert,
PrivateKey: barKey,
},
Domains: types.Domain{
Main: "bar.com",
},
},
},
},
expectedDc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Certificate: &Certificate{
Certificate: fooCert,
PrivateKey: fooKey,
},
Domains: types.Domain{
Main: "foo.com",
},
},
{
Certificate: &Certificate{
Certificate: acmeCert,
PrivateKey: acmeKey,
},
Domains: types.Domain{
Main: "acme.wtf",
},
},
{
Certificate: &Certificate{
Certificate: barCert,
PrivateKey: barKey,
},
Domains: types.Domain{
Main: "bar.com",
},
},
},
},
},
{
desc: "First certificate is nil",
dc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Domains: types.Domain{
Main: "foo.com",
},
},
{
Certificate: &Certificate{
Certificate: acmeCert,
PrivateKey: acmeKey,
},
Domains: types.Domain{
Main: "acme.wtf",
},
},
{
Certificate: &Certificate{
Certificate: barCert,
PrivateKey: barKey,
},
Domains: types.Domain{
Main: "bar.com",
},
},
},
},
expectedDc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Certificate: &Certificate{
Certificate: acmeCert,
PrivateKey: acmeKey,
},
Domains: types.Domain{
Main: "acme.wtf",
},
},
{
Certificate: &Certificate{
Certificate: nil,
PrivateKey: barKey,
},
Domains: types.Domain{
Main: "bar.com",
},
},
},
},
},
{
desc: "Last certificate is empty",
dc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Certificate: &Certificate{
Certificate: fooCert,
PrivateKey: fooKey,
},
Domains: types.Domain{
Main: "foo.com",
},
},
{
Certificate: &Certificate{
Certificate: acmeCert,
PrivateKey: acmeKey,
},
Domains: types.Domain{
Main: "acme.wtf",
},
},
{
Certificate: &Certificate{},
Domains: types.Domain{
Main: "bar.com",
},
},
},
},
expectedDc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Certificate: &Certificate{
Certificate: fooCert,
PrivateKey: fooKey,
},
Domains: types.Domain{
Main: "foo.com",
},
},
{
Certificate: &Certificate{
Certificate: acmeCert,
PrivateKey: acmeKey,
},
Domains: types.Domain{
Main: "acme.wtf",
},
},
},
},
},
{
desc: "First and last certificates are nil or empty",
dc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Domains: types.Domain{
Main: "foo.com",
},
},
{
Certificate: &Certificate{
Certificate: acmeCert,
PrivateKey: acmeKey,
},
Domains: types.Domain{
Main: "acme.wtf",
},
},
{
Certificate: &Certificate{},
Domains: types.Domain{
Main: "bar.com",
},
},
},
},
expectedDc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Certificate: &Certificate{
Certificate: acmeCert,
PrivateKey: acmeKey,
},
Domains: types.Domain{
Main: "acme.wtf",
},
},
},
},
},
{
desc: "All certificates are nil or empty",
dc: &DomainsCertificates{
Certs: []*DomainsCertificate{
{
Domains: types.Domain{
Main: "foo.com",
},
},
{
Domains: types.Domain{
Main: "foo24.com",
},
},
{
Certificate: &Certificate{},
Domains: types.Domain{
Main: "bar.com",
},
},
},
},
expectedDc: &DomainsCertificates{
Certs: []*DomainsCertificate{},
},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
a := &Account{DomainsCertificate: *test.dc}
a.Init()
assert.Equal(t, len(test.expectedDc.Certs), len(a.DomainsCertificate.Certs))
sort.Sort(&a.DomainsCertificate)
sort.Sort(test.expectedDc)
for key, value := range test.expectedDc.Certs {
assert.Equal(t, value.Domains.Main, a.DomainsCertificate.Certs[key].Domains.Main)
}
})
}
}

97
acme/challengeProvider.go Normal file
View File

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

View File

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

View File

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

133
acme/crypto.go Normal file
View File

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

View File

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

View File

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

25
acme/localStore_unix.go Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

0
autogen/.placeholder Normal file
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +1,26 @@
FROM golang:1.11-alpine
FROM golang:1.9-alpine
RUN apk --update upgrade \
&& apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar \
&& rm -rf /var/cache/apk/*
RUN go get golang.org/x/lint/golint \
RUN go get github.com/jteeuwen/go-bindata/... \
&& go get github.com/golang/lint/golint \
&& go get github.com/kisielk/errcheck \
&& go get github.com/client9/misspell/cmd/misspell
&& go get github.com/client9/misspell/cmd/misspell \
&& go get github.com/mattfarina/glide-hash \
&& go get github.com/sgotti/glide-vc
# Which docker version to test on
ARG DOCKER_VERSION=17.03.2
ARG DEP_VERSION=0.4.1
# Download go-bindata binary to bin folder in $GOPATH
RUN mkdir -p /usr/local/bin \
&& curl -fsSL -o /usr/local/bin/go-bindata https://github.com/containous/go-bindata/releases/download/v1.0.0/go-bindata \
&& chmod +x /usr/local/bin/go-bindata
# Which glide version to test on
ARG GLIDE_VERSION=v0.12.3
# Download dep binary to bin folder in $GOPATH
# Download glide
RUN mkdir -p /usr/local/bin \
&& curl -fsSL -o /usr/local/bin/dep https://github.com/golang/dep/releases/download/v${DEP_VERSION}/dep-linux-amd64 \
&& chmod +x /usr/local/bin/dep
&& curl -fL https://github.com/Masterminds/glide/releases/download/${GLIDE_VERSION}/glide-${GLIDE_VERSION}-linux-amd64.tar.gz \
| tar -xzC /usr/local/bin --transform 's#^.+/##x'
# Download docker
RUN mkdir -p /usr/local/bin \

View File

@@ -7,12 +7,12 @@ import (
"sync"
"time"
"github.com/abronan/valkeyrie/store"
"github.com/cenk/backoff"
"github.com/containous/staert"
"github.com/containous/traefik/job"
"github.com/containous/traefik/log"
"github.com/containous/traefik/safe"
"github.com/docker/libkv/store"
"github.com/satori/go.uuid"
)
@@ -76,9 +76,9 @@ func NewDataStore(ctx context.Context, kvSource staert.KvSource, object Object,
func (d *Datastore) watchChanges() error {
stopCh := make(chan struct{})
kvCh, err := d.kv.Watch(d.lockKey, stopCh, nil)
kvCh, err := d.kv.Watch(d.lockKey, stopCh)
if err != nil {
return fmt.Errorf("error while watching key %s: %v", d.lockKey, err)
return err
}
safe.Go(func() {
ctx, cancel := context.WithCancel(d.ctx)
@@ -152,7 +152,7 @@ func (d *Datastore) Begin() (Transaction, Object, error) {
operation := func() error {
meta := d.get()
if meta.Lock != id {
return fmt.Errorf("object lock value: expected %s, got %s", id, meta.Lock)
return fmt.Errorf("Object lock value: expected %s, got %s", id, meta.Lock)
}
return nil
}
@@ -167,7 +167,7 @@ func (d *Datastore) Begin() (Transaction, Object, error) {
ebo.MaxElapsedTime = 60 * time.Second
err = backoff.RetryNotify(safe.OperationWithRecover(operation), ebo, notify)
if err != nil {
return nil, nil, fmt.Errorf("datastore cannot sync: %v", err)
return nil, nil, fmt.Errorf("Datastore cannot sync: %v", err)
}
// we synced with KV store, we can now return Setter
@@ -224,12 +224,12 @@ func (s *datastoreTransaction) Commit(object Object) error {
s.localLock.Lock()
defer s.localLock.Unlock()
if s.dirty {
return fmt.Errorf("transaction already used, please begin a new one")
return fmt.Errorf("Transaction already used, please begin a new one")
}
s.Datastore.meta.object = object
err := s.Datastore.meta.Marshall()
if err != nil {
return fmt.Errorf("marshall error: %s", err)
return fmt.Errorf("Marshall error: %s", err)
}
err = s.kv.StoreConfig(s.Datastore.meta)
if err != nil {
@@ -238,7 +238,7 @@ func (s *datastoreTransaction) Commit(object Object) error {
err = s.remoteLock.Unlock()
if err != nil {
return fmt.Errorf("unlock error: %s", err)
return fmt.Errorf("Unlock error: %s", err)
}
s.dirty = true

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,20 +2,16 @@ package anonymize
import (
"crypto/tls"
"os"
"testing"
"time"
"github.com/containous/flaeg"
"github.com/containous/traefik/acme"
"github.com/containous/traefik/api"
"github.com/containous/traefik/configuration"
"github.com/containous/traefik/middlewares"
"github.com/containous/traefik/provider"
acmeprovider "github.com/containous/traefik/provider/acme"
"github.com/containous/traefik/provider/boltdb"
"github.com/containous/traefik/provider/consul"
"github.com/containous/traefik/provider/consulcatalog"
"github.com/containous/traefik/provider/docker"
"github.com/containous/traefik/provider/dynamodb"
"github.com/containous/traefik/provider/ecs"
@@ -27,12 +23,11 @@ import (
"github.com/containous/traefik/provider/marathon"
"github.com/containous/traefik/provider/mesos"
"github.com/containous/traefik/provider/rancher"
"github.com/containous/traefik/provider/web"
"github.com/containous/traefik/provider/zk"
"github.com/containous/traefik/safe"
traefiktls "github.com/containous/traefik/tls"
"github.com/containous/traefik/types"
"github.com/elazarl/go-bindata-assetfs"
"github.com/thoas/stats"
thoas_stats "github.com/thoas/stats"
)
func TestDo_globalConfiguration(t *testing.T) {
@@ -51,20 +46,18 @@ func TestDo_globalConfiguration(t *testing.T) {
config.LogLevel = "LogLevel"
config.EntryPoints = configuration.EntryPoints{
"foo": {
Network: "foo Network",
Address: "foo Address",
TLS: &traefiktls.TLS{
TLS: &configuration.TLS{
MinVersion: "foo MinVersion",
CipherSuites: []string{"foo CipherSuites 1", "foo CipherSuites 2", "foo CipherSuites 3"},
Certificates: traefiktls.Certificates{
Certificates: configuration.Certificates{
{CertFile: "CertFile 1", KeyFile: "KeyFile 1"},
{CertFile: "CertFile 2", KeyFile: "KeyFile 2"},
},
ClientCA: traefiktls.ClientCA{
Files: traefiktls.FilesOrContents{"foo ClientCAFiles 1", "foo ClientCAFiles 2", "foo ClientCAFiles 3"},
Optional: false,
},
ClientCAFiles: []string{"foo ClientCAFiles 1", "foo ClientCAFiles 2", "foo ClientCAFiles 3"},
},
Redirect: &types.Redirect{
Redirect: &configuration.Redirect{
Replacement: "foo Replacement",
Regex: "foo Regex",
EntryPoint: "foo EntryPoint",
@@ -96,20 +89,18 @@ func TestDo_globalConfiguration(t *testing.T) {
},
},
"fii": {
Network: "fii Network",
Address: "fii Address",
TLS: &traefiktls.TLS{
TLS: &configuration.TLS{
MinVersion: "fii MinVersion",
CipherSuites: []string{"fii CipherSuites 1", "fii CipherSuites 2", "fii CipherSuites 3"},
Certificates: traefiktls.Certificates{
Certificates: configuration.Certificates{
{CertFile: "CertFile 1", KeyFile: "KeyFile 1"},
{CertFile: "CertFile 2", KeyFile: "KeyFile 2"},
},
ClientCA: traefiktls.ClientCA{
Files: traefiktls.FilesOrContents{"fii ClientCAFiles 1", "fii ClientCAFiles 2", "fii ClientCAFiles 3"},
Optional: false,
},
ClientCAFiles: []string{"fii ClientCAFiles 1", "fii ClientCAFiles 2", "fii ClientCAFiles 3"},
},
Redirect: &types.Redirect{
Redirect: &configuration.Redirect{
Replacement: "fii Replacement",
Regex: "fii Regex",
EntryPoint: "fii EntryPoint",
@@ -162,7 +153,7 @@ func TestDo_globalConfiguration(t *testing.T) {
}
config.ACME = &acme.ACME{
Email: "acme Email",
Domains: []types.Domain{
Domains: []acme.Domain{
{
Main: "Domains Main",
SANs: []string{"Domains acme SANs 1", "Domains acme SANs 2", "Domains acme SANs 3"},
@@ -174,7 +165,7 @@ func TestDo_globalConfiguration(t *testing.T) {
OnHostRule: true,
CAServer: "CAServer",
EntryPoint: "EntryPoint",
DNSChallenge: &acmeprovider.DNSChallenge{Provider: "DNSProvider"},
DNSProvider: "DNSProvider",
DelayDontCheckDNS: 666,
ACMELogging: true,
TLSConfig: &tls.Config{
@@ -187,42 +178,13 @@ func TestDo_globalConfiguration(t *testing.T) {
config.MaxIdleConnsPerHost = 666
config.IdleTimeout = flaeg.Duration(666 * time.Second)
config.InsecureSkipVerify = true
config.RootCAs = traefiktls.FilesOrContents{"RootCAs 1", "RootCAs 2", "RootCAs 3"}
config.RootCAs = configuration.RootCAs{"RootCAs 1", "RootCAs 2", "RootCAs 3"}
config.Retry = &configuration.Retry{
Attempts: 666,
}
config.HealthCheck = &configuration.HealthCheckConfig{
Interval: flaeg.Duration(666 * time.Second),
}
config.API = &api.Handler{
EntryPoint: "traefik",
Dashboard: true,
Debug: true,
CurrentConfigurations: &safe.Safe{},
Statistics: &types.Statistics{
RecentErrors: 666,
},
Stats: &stats.Stats{
Uptime: time.Now(),
Pid: 666,
ResponseCounts: map[string]int{"foo": 1},
TotalResponseCounts: map[string]int{"bar": 1},
TotalResponseTime: time.Now(),
},
StatsRecorder: &middlewares.StatsRecorder{},
DashboardAssets: &assetfs.AssetFS{
Asset: func(path string) ([]byte, error) {
return nil, nil
},
AssetDir: func(path string) ([]string, error) {
return nil, nil
},
AssetInfo: func(path string) (os.FileInfo, error) {
return nil, nil
},
Prefix: "fii",
},
}
config.RespondingTimeouts = &configuration.RespondingTimeouts{
ReadTimeout: flaeg.Duration(666 * time.Second),
WriteTimeout: flaeg.Duration(666 * time.Second),
@@ -248,7 +210,7 @@ func TestDo_globalConfiguration(t *testing.T) {
MustMatch: true,
},
},
Trace: true,
Trace: true,
DebugLogGeneratedTemplate: true,
},
Endpoint: "docker Endpoint",
@@ -279,12 +241,12 @@ func TestDo_globalConfiguration(t *testing.T) {
MustMatch: true,
},
},
Trace: true,
Trace: true,
DebugLogGeneratedTemplate: true,
},
Directory: "file Directory",
}
config.Web = &configuration.WebCompatibility{
config.Web = &web.Provider{
Address: "web Address",
CertFile: "web CertFile",
KeyFile: "web KeyFile",
@@ -327,6 +289,15 @@ func TestDo_globalConfiguration(t *testing.T) {
},
},
Debug: true,
CurrentConfigurations: &safe.Safe{},
Stats: &thoas_stats.Stats{
Uptime: time.Now(),
Pid: 666,
ResponseCounts: map[string]int{"foo": 1, "fii": 2, "fuu": 3},
TotalResponseCounts: map[string]int{"foo": 1, "fii": 2, "fuu": 3},
TotalResponseTime: time.Now(),
},
StatsRecorder: &middlewares.StatsRecorder{},
}
config.Marathon = &marathon.Provider{
BaseProvider: provider.BaseProvider{
@@ -344,7 +315,7 @@ func TestDo_globalConfiguration(t *testing.T) {
MustMatch: true,
},
},
Trace: true,
Trace: true,
DebugLogGeneratedTemplate: true,
},
Endpoint: "",
@@ -368,7 +339,7 @@ func TestDo_globalConfiguration(t *testing.T) {
},
RespectReadinessChecks: true,
}
config.ConsulCatalog = &consulcatalog.Provider{
config.ConsulCatalog = &consul.CatalogProvider{
BaseProvider: provider.BaseProvider{
Watch: true,
Filename: "ConsulCatalog Filename",
@@ -384,7 +355,7 @@ func TestDo_globalConfiguration(t *testing.T) {
MustMatch: true,
},
},
Trace: true,
Trace: true,
DebugLogGeneratedTemplate: true,
},
Endpoint: "ConsulCatalog Endpoint",
@@ -409,7 +380,7 @@ func TestDo_globalConfiguration(t *testing.T) {
MustMatch: true,
},
},
Trace: true,
Trace: true,
DebugLogGeneratedTemplate: true,
},
Endpoint: "k8s Endpoint",
@@ -435,7 +406,7 @@ func TestDo_globalConfiguration(t *testing.T) {
MustMatch: true,
},
},
Trace: true,
Trace: true,
DebugLogGeneratedTemplate: true,
},
Endpoint: "mesos Endpoint",
@@ -464,12 +435,11 @@ func TestDo_globalConfiguration(t *testing.T) {
MustMatch: true,
},
},
Trace: true,
Trace: true,
DebugLogGeneratedTemplate: true,
},
Endpoint: "eureka Endpoint",
Delay: flaeg.Duration(30 * time.Second),
RefreshSeconds: flaeg.Duration(30 * time.Second),
Endpoint: "eureka Endpoint",
Delay: "eureka Delay",
}
config.ECS = &ecs.Provider{
BaseProvider: provider.BaseProvider{
@@ -487,7 +457,7 @@ func TestDo_globalConfiguration(t *testing.T) {
MustMatch: true,
},
},
Trace: true,
Trace: true,
DebugLogGeneratedTemplate: true,
},
Domain: "ecs Domain",
@@ -516,7 +486,7 @@ func TestDo_globalConfiguration(t *testing.T) {
MustMatch: true,
},
},
Trace: true,
Trace: true,
DebugLogGeneratedTemplate: true,
},
APIConfiguration: rancher.APIConfiguration{
@@ -554,7 +524,7 @@ func TestDo_globalConfiguration(t *testing.T) {
MustMatch: true,
},
},
Trace: true,
Trace: true,
DebugLogGeneratedTemplate: true,
},
AccessKeyID: "dynamodb AccessKeyID",
@@ -581,7 +551,7 @@ func TestDo_globalConfiguration(t *testing.T) {
MustMatch: true,
},
},
Trace: true,
Trace: true,
DebugLogGeneratedTemplate: true,
},
Endpoint: "etcd Endpoint",
@@ -613,7 +583,7 @@ func TestDo_globalConfiguration(t *testing.T) {
MustMatch: true,
},
},
Trace: true,
Trace: true,
DebugLogGeneratedTemplate: true,
},
Endpoint: "zk Endpoint",
@@ -645,7 +615,7 @@ func TestDo_globalConfiguration(t *testing.T) {
MustMatch: true,
},
},
Trace: true,
Trace: true,
DebugLogGeneratedTemplate: true,
},
Endpoint: "boltdb Endpoint",
@@ -677,7 +647,7 @@ func TestDo_globalConfiguration(t *testing.T) {
MustMatch: true,
},
},
Trace: true,
Trace: true,
DebugLogGeneratedTemplate: true,
},
Endpoint: "consul Endpoint",

View File

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

View File

@@ -1,4 +1,4 @@
package bug
package main
import (
"bytes"
@@ -9,9 +9,7 @@ import (
"text/template"
"github.com/containous/flaeg"
"github.com/containous/traefik/anonymize"
"github.com/containous/traefik/cmd"
"github.com/containous/traefik/cmd/version"
"github.com/containous/traefik/cmd/traefik/anonymize"
)
const (
@@ -23,7 +21,7 @@ The issue tracker is for reporting bugs and feature requests only.
For end-user related support questions, refer to one of the following:
- Stack Overflow (using the "traefik" tag): https://stackoverflow.com/questions/tagged/traefik
- the Traefik community Slack channel: https://slack.traefik.io
- the Traefik community Slack channel: https://traefik.herokuapp.com
-->
@@ -31,7 +29,7 @@ For end-user related support questions, refer to one of the following:
(If you intend to ask a support question: **DO NOT FILE AN ISSUE**.
Use [Stack Overflow](https://stackoverflow.com/questions/tagged/traefik)
or [Slack](https://slack.traefik.io) instead.)
or [Slack](https://traefik.herokuapp.com) instead.)
@@ -76,7 +74,7 @@ HOW TO WRITE A GOOD ISSUE?
Add more configuration information here.
-->
### If applicable, please paste the log output at DEBUG level (` + "`" + `--logLevel=DEBUG` + "`" + ` switch)
### If applicable, please paste the log output in debug mode (` + "`" + `--debug` + "`" + ` switch)
` + "```" + `
(paste your output here)
@@ -85,39 +83,39 @@ Add more configuration information here.
`
)
// NewCmd builds a new Bug command
func NewCmd(traefikConfiguration *cmd.TraefikConfiguration, traefikPointersConfiguration *cmd.TraefikConfiguration) *flaeg.Command {
// newBugCmd builds a new Bug command
func newBugCmd(traefikConfiguration *TraefikConfiguration, traefikPointersConfiguration *TraefikConfiguration) *flaeg.Command {
// version Command init
//version Command init
return &flaeg.Command{
Name: "bug",
Description: `Report an issue on Traefik bugtracker`,
Config: traefikConfiguration,
DefaultPointersConfig: traefikPointersConfiguration,
Run: runCmd(traefikConfiguration),
Run: runBugCmd(traefikConfiguration),
Metadata: map[string]string{
"parseAllSources": "true",
},
}
}
func runCmd(traefikConfiguration *cmd.TraefikConfiguration) func() error {
func runBugCmd(traefikConfiguration *TraefikConfiguration) func() error {
return func() error {
body, err := createReport(traefikConfiguration)
body, err := createBugReport(traefikConfiguration)
if err != nil {
return err
}
sendReport(body)
sendBugReport(body)
return nil
}
}
func createReport(traefikConfiguration *cmd.TraefikConfiguration) (string, error) {
var versionPrint bytes.Buffer
if err := version.GetPrint(&versionPrint); err != nil {
func createBugReport(traefikConfiguration *TraefikConfiguration) (string, error) {
var version bytes.Buffer
if err := getVersionPrint(&version); err != nil {
return "", err
}
@@ -135,7 +133,7 @@ func createReport(traefikConfiguration *cmd.TraefikConfiguration) (string, error
Version string
Configuration string
}{
Version: versionPrint.String(),
Version: version.String(),
Configuration: config,
}
@@ -147,7 +145,7 @@ func createReport(traefikConfiguration *cmd.TraefikConfiguration) (string, error
return bug.String(), nil
}
func sendReport(body string) {
func sendBugReport(body string) {
URL := bugTracker + "?body=" + url.QueryEscape(body)
if err := openBrowser(URL); err != nil {
fmt.Printf("Please file a new issue at %s using this template:\n\n", bugTracker)

View File

@@ -1,19 +1,17 @@
package bug
package main
import (
"testing"
"github.com/containous/traefik/anonymize"
"github.com/containous/traefik/cmd"
"github.com/containous/traefik/cmd/traefik/anonymize"
"github.com/containous/traefik/configuration"
"github.com/containous/traefik/provider/file"
"github.com/containous/traefik/tls"
"github.com/containous/traefik/types"
"github.com/stretchr/testify/assert"
)
func Test_createReport(t *testing.T) {
traefikConfiguration := &cmd.TraefikConfiguration{
func Test_createBugReport(t *testing.T) {
traefikConfiguration := &TraefikConfiguration{
ConfigFile: "FOO",
GlobalConfiguration: configuration.GlobalConfiguration{
EntryPoints: configuration.EntryPoints{
@@ -34,11 +32,11 @@ func Test_createReport(t *testing.T) {
File: &file.Provider{
Directory: "BAR",
},
RootCAs: tls.FilesOrContents{"fllf"},
RootCAs: configuration.RootCAs{"fllf"},
},
}
report, err := createReport(traefikConfiguration)
report, err := createBugReport(traefikConfiguration)
assert.NoError(t, err, report)
// exported anonymous configuration
@@ -48,7 +46,7 @@ func Test_createReport(t *testing.T) {
}
func Test_anonymize_traefikConfiguration(t *testing.T) {
traefikConfiguration := &cmd.TraefikConfiguration{
traefikConfiguration := &TraefikConfiguration{
ConfigFile: "FOO",
GlobalConfiguration: configuration.GlobalConfiguration{
EntryPoints: configuration.EntryPoints{

View File

@@ -0,0 +1,226 @@
package main
import (
"time"
"github.com/containous/flaeg"
"github.com/containous/traefik/configuration"
"github.com/containous/traefik/middlewares/accesslog"
"github.com/containous/traefik/provider/boltdb"
"github.com/containous/traefik/provider/consul"
"github.com/containous/traefik/provider/docker"
"github.com/containous/traefik/provider/dynamodb"
"github.com/containous/traefik/provider/ecs"
"github.com/containous/traefik/provider/etcd"
"github.com/containous/traefik/provider/eureka"
"github.com/containous/traefik/provider/file"
"github.com/containous/traefik/provider/kubernetes"
"github.com/containous/traefik/provider/marathon"
"github.com/containous/traefik/provider/mesos"
"github.com/containous/traefik/provider/rancher"
"github.com/containous/traefik/provider/web"
"github.com/containous/traefik/provider/zk"
"github.com/containous/traefik/types"
)
// TraefikConfiguration holds GlobalConfiguration and other stuff
type TraefikConfiguration struct {
configuration.GlobalConfiguration `mapstructure:",squash" export:"true"`
ConfigFile string `short:"c" description:"Configuration file to use (TOML)." export:"true"`
}
// NewTraefikDefaultPointersConfiguration creates a TraefikConfiguration with pointers default values
func NewTraefikDefaultPointersConfiguration() *TraefikConfiguration {
//default Docker
var defaultDocker docker.Provider
defaultDocker.Watch = true
defaultDocker.ExposedByDefault = true
defaultDocker.Endpoint = "unix:///var/run/docker.sock"
defaultDocker.SwarmMode = false
// default File
var defaultFile file.Provider
defaultFile.Watch = true
defaultFile.Filename = "" //needs equivalent to viper.ConfigFileUsed()
// default Web
var defaultWeb web.Provider
defaultWeb.Address = ":8080"
defaultWeb.Statistics = &types.Statistics{
RecentErrors: 10,
}
// default Metrics
defaultWeb.Metrics = &types.Metrics{
Prometheus: &types.Prometheus{
Buckets: types.Buckets{0.1, 0.3, 1.2, 5},
},
Datadog: &types.Datadog{
Address: "localhost:8125",
PushInterval: "10s",
},
StatsD: &types.Statsd{
Address: "localhost:8125",
PushInterval: "10s",
},
}
// default Marathon
var defaultMarathon marathon.Provider
defaultMarathon.Watch = true
defaultMarathon.Endpoint = "http://127.0.0.1:8080"
defaultMarathon.ExposedByDefault = true
defaultMarathon.Constraints = types.Constraints{}
defaultMarathon.DialerTimeout = flaeg.Duration(60 * time.Second)
defaultMarathon.KeepAlive = flaeg.Duration(10 * time.Second)
// default Consul
var defaultConsul consul.Provider
defaultConsul.Watch = true
defaultConsul.Endpoint = "127.0.0.1:8500"
defaultConsul.Prefix = "traefik"
defaultConsul.Constraints = types.Constraints{}
// default CatalogProvider
var defaultConsulCatalog consul.CatalogProvider
defaultConsulCatalog.Endpoint = "127.0.0.1:8500"
defaultConsulCatalog.ExposedByDefault = true
defaultConsulCatalog.Constraints = types.Constraints{}
defaultConsulCatalog.Prefix = "traefik"
defaultConsulCatalog.FrontEndRule = "Host:{{.ServiceName}}.{{.Domain}}"
// default Etcd
var defaultEtcd etcd.Provider
defaultEtcd.Watch = true
defaultEtcd.Endpoint = "127.0.0.1:2379"
defaultEtcd.Prefix = "/traefik"
defaultEtcd.Constraints = types.Constraints{}
//default Zookeeper
var defaultZookeeper zk.Provider
defaultZookeeper.Watch = true
defaultZookeeper.Endpoint = "127.0.0.1:2181"
defaultZookeeper.Prefix = "/traefik"
defaultZookeeper.Constraints = types.Constraints{}
//default Boltdb
var defaultBoltDb boltdb.Provider
defaultBoltDb.Watch = true
defaultBoltDb.Endpoint = "127.0.0.1:4001"
defaultBoltDb.Prefix = "/traefik"
defaultBoltDb.Constraints = types.Constraints{}
//default Kubernetes
var defaultKubernetes kubernetes.Provider
defaultKubernetes.Watch = true
defaultKubernetes.Endpoint = ""
defaultKubernetes.LabelSelector = ""
defaultKubernetes.Constraints = types.Constraints{}
// default Mesos
var defaultMesos mesos.Provider
defaultMesos.Watch = true
defaultMesos.Endpoint = "http://127.0.0.1:5050"
defaultMesos.ExposedByDefault = true
defaultMesos.Constraints = types.Constraints{}
defaultMesos.RefreshSeconds = 30
defaultMesos.ZkDetectionTimeout = 30
defaultMesos.StateTimeoutSecond = 30
//default ECS
var defaultECS ecs.Provider
defaultECS.Watch = true
defaultECS.ExposedByDefault = true
defaultECS.AutoDiscoverClusters = false
defaultECS.Clusters = ecs.Clusters{"default"}
defaultECS.RefreshSeconds = 15
defaultECS.Constraints = types.Constraints{}
//default Rancher
var defaultRancher rancher.Provider
defaultRancher.Watch = true
defaultRancher.ExposedByDefault = true
defaultRancher.RefreshSeconds = 15
// default DynamoDB
var defaultDynamoDB dynamodb.Provider
defaultDynamoDB.Constraints = types.Constraints{}
defaultDynamoDB.RefreshSeconds = 15
defaultDynamoDB.TableName = "traefik"
defaultDynamoDB.Watch = true
// default Eureka
var defaultEureka eureka.Provider
defaultEureka.Delay = "30s"
// default AccessLog
defaultAccessLog := types.AccessLog{
Format: accesslog.CommonFormat,
FilePath: "",
}
// default HealthCheckConfig
healthCheck := configuration.HealthCheckConfig{
Interval: flaeg.Duration(configuration.DefaultHealthCheckInterval),
}
// default RespondingTimeouts
respondingTimeouts := configuration.RespondingTimeouts{
IdleTimeout: flaeg.Duration(configuration.DefaultIdleTimeout),
}
// default ForwardingTimeouts
forwardingTimeouts := configuration.ForwardingTimeouts{
DialTimeout: flaeg.Duration(configuration.DefaultDialTimeout),
}
defaultConfiguration := configuration.GlobalConfiguration{
Docker: &defaultDocker,
File: &defaultFile,
Web: &defaultWeb,
Marathon: &defaultMarathon,
Consul: &defaultConsul,
ConsulCatalog: &defaultConsulCatalog,
Etcd: &defaultEtcd,
Zookeeper: &defaultZookeeper,
Boltdb: &defaultBoltDb,
Kubernetes: &defaultKubernetes,
Mesos: &defaultMesos,
ECS: &defaultECS,
Rancher: &defaultRancher,
Eureka: &defaultEureka,
DynamoDB: &defaultDynamoDB,
Retry: &configuration.Retry{},
HealthCheck: &healthCheck,
AccessLog: &defaultAccessLog,
RespondingTimeouts: &respondingTimeouts,
ForwardingTimeouts: &forwardingTimeouts,
}
return &TraefikConfiguration{
GlobalConfiguration: defaultConfiguration,
}
}
// NewTraefikConfiguration creates a TraefikConfiguration with default values
func NewTraefikConfiguration() *TraefikConfiguration {
return &TraefikConfiguration{
GlobalConfiguration: configuration.GlobalConfiguration{
GraceTimeOut: flaeg.Duration(10 * time.Second),
AccessLogsFile: "",
TraefikLogsFile: "",
LogLevel: "ERROR",
EntryPoints: map[string]*configuration.EntryPoint{},
Constraints: types.Constraints{},
DefaultEntryPoints: []string{},
ProvidersThrottleDuration: flaeg.Duration(2 * time.Second),
MaxIdleConnsPerHost: 200,
IdleTimeout: flaeg.Duration(0),
HealthCheck: &configuration.HealthCheckConfig{
Interval: flaeg.Duration(configuration.DefaultHealthCheckInterval),
},
CheckNewVersion: true,
},
ConfigFile: "",
}
}

View File

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

63
cmd/traefik/version.go Normal file
View File

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

View File

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

View File

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

View File

@@ -1,24 +1,18 @@
package configuration
import (
"crypto/tls"
"fmt"
"io/ioutil"
"os"
"strings"
"time"
"github.com/containous/flaeg"
"github.com/containous/traefik-extra-service-fabric"
"github.com/containous/traefik/acme"
"github.com/containous/traefik/api"
"github.com/containous/traefik/log"
"github.com/containous/traefik/middlewares/tracing"
"github.com/containous/traefik/middlewares/tracing/datadog"
"github.com/containous/traefik/middlewares/tracing/jaeger"
"github.com/containous/traefik/middlewares/tracing/zipkin"
"github.com/containous/traefik/ping"
acmeprovider "github.com/containous/traefik/provider/acme"
"github.com/containous/traefik/provider/boltdb"
"github.com/containous/traefik/provider/consul"
"github.com/containous/traefik/provider/consulcatalog"
"github.com/containous/traefik/provider/docker"
"github.com/containous/traefik/provider/dynamodb"
"github.com/containous/traefik/provider/ecs"
@@ -29,18 +23,12 @@ import (
"github.com/containous/traefik/provider/marathon"
"github.com/containous/traefik/provider/mesos"
"github.com/containous/traefik/provider/rancher"
"github.com/containous/traefik/provider/rest"
"github.com/containous/traefik/provider/web"
"github.com/containous/traefik/provider/zk"
"github.com/containous/traefik/tls"
"github.com/containous/traefik/types"
"github.com/pkg/errors"
lego "github.com/xenolf/lego/acme"
)
const (
// DefaultInternalEntryPointName the name of the default internal entry point
DefaultInternalEntryPointName = "traefik"
// DefaultHealthCheckInterval is the default health check interval.
DefaultHealthCheckInterval = 30 * time.Second
@@ -49,31 +37,20 @@ const (
// DefaultIdleTimeout before closing an idle connection.
DefaultIdleTimeout = 180 * time.Second
// DefaultGraceTimeout controls how long Traefik serves pending requests
// prior to shutting down.
DefaultGraceTimeout = 10 * time.Second
// DefaultAcmeCAServer is the default ACME API endpoint
DefaultAcmeCAServer = "https://acme-v02.api.letsencrypt.org/directory"
)
// GlobalConfiguration holds global configuration (with providers, etc.).
// It's populated from the traefik configuration file passed as an argument to the binary.
type GlobalConfiguration struct {
LifeCycle *LifeCycle `description:"Timeouts influencing the server life cycle" export:"true"`
GraceTimeOut flaeg.Duration `short:"g" description:"(Deprecated) Duration to give active requests a chance to finish before Traefik stops" export:"true"` // Deprecated
Debug bool `short:"d" description:"Enable debug mode" export:"true"`
CheckNewVersion bool `description:"Periodically check if a new version has been released" export:"true"`
SendAnonymousUsage bool `description:"send periodically anonymous usage statistics" export:"true"`
AccessLogsFile string `description:"(Deprecated) Access logs file" export:"true"` // Deprecated
AccessLog *types.AccessLog `description:"Access log settings" export:"true"`
TraefikLogsFile string `description:"(Deprecated) Traefik logs file. Stdout is used when omitted or empty" export:"true"` // Deprecated
TraefikLog *types.TraefikLog `description:"Traefik log settings" export:"true"`
Tracing *tracing.Tracing `description:"OpenTracing configuration" export:"true"`
LogLevel string `short:"l" description:"Log level" export:"true"`
EntryPoints EntryPoints `description:"Entrypoints definition using format: --entryPoints='Name:http Address::8000 Redirect.EntryPoint:https' --entryPoints='Name:https Address::4442 TLS:tests/traefik.crt,tests/traefik.key;prod/traefik.crt,prod/traefik.key'" export:"true"`
Cluster *types.Cluster
GraceTimeOut flaeg.Duration `short:"g" description:"Duration to give active requests a chance to finish before Traefik stops" export:"true"`
Debug bool `short:"d" description:"Enable debug mode" export:"true"`
CheckNewVersion bool `description:"Periodically check if a new version has been released" export:"true"`
AccessLogsFile string `description:"(Deprecated) Access logs file" export:"true"` // Deprecated
AccessLog *types.AccessLog `description:"Access log settings" export:"true"`
TraefikLogsFile string `description:"Traefik logs file. Stdout is used when omitted or empty" export:"true"`
LogLevel string `short:"l" description:"Log level" export:"true"`
EntryPoints EntryPoints `description:"Entrypoints definition using format: --entryPoints='Name:http Address::8000 Redirect.EntryPoint:https' --entryPoints='Name:https Address::4442 TLS:tests/traefik.crt,tests/traefik.key;prod/traefik.crt,prod/traefik.key'" export:"true"`
Cluster *types.Cluster `description:"Enable clustering" export:"true"`
Constraints types.Constraints `description:"Filter services by constraint, matching with service tags" export:"true"`
ACME *acme.ACME `description:"Enable ACME (Let's Encrypt): automatic SSL" export:"true"`
DefaultEntryPoints DefaultEntryPoints `description:"Entrypoints to be used by frontends that do not specify any entrypoint" export:"true"`
@@ -81,19 +58,17 @@ type GlobalConfiguration struct {
MaxIdleConnsPerHost int `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used" export:"true"`
IdleTimeout flaeg.Duration `description:"(Deprecated) maximum amount of time an idle (keep-alive) connection will remain idle before closing itself." export:"true"` // Deprecated
InsecureSkipVerify bool `description:"Disable SSL certificate verification" export:"true"`
RootCAs tls.FilesOrContents `description:"Add cert file for self-signed certificate"`
RootCAs RootCAs `description:"Add cert file for self-signed certificate"`
Retry *Retry `description:"Enable retry sending request if network error" export:"true"`
HealthCheck *HealthCheckConfig `description:"Health check parameters" export:"true"`
RespondingTimeouts *RespondingTimeouts `description:"Timeouts for incoming requests to the Traefik instance" export:"true"`
ForwardingTimeouts *ForwardingTimeouts `description:"Timeouts for requests forwarded to the backend servers" export:"true"`
AllowMinWeightZero bool `description:"Allow weight to take 0 as minimum real value." export:"true"` // Deprecated
KeepTrailingSlash bool `description:"Do not remove trailing slash." export:"true"` // Deprecated
Web *WebCompatibility `description:"(Deprecated) Enable Web backend with default settings" export:"true"` // Deprecated
Docker *docker.Provider `description:"Enable Docker backend with default settings" export:"true"`
File *file.Provider `description:"Enable File backend with default settings" export:"true"`
Web *web.Provider `description:"Enable Web backend with default settings" export:"true"`
Marathon *marathon.Provider `description:"Enable Marathon backend with default settings" export:"true"`
Consul *consul.Provider `description:"Enable Consul backend with default settings" export:"true"`
ConsulCatalog *consulcatalog.Provider `description:"Enable Consul catalog backend with default settings" export:"true"`
ConsulCatalog *consul.CatalogProvider `description:"Enable Consul catalog backend with default settings" export:"true"`
Etcd *etcd.Provider `description:"Enable Etcd backend with default settings" export:"true"`
Zookeeper *zk.Provider `description:"Enable Zookeeper backend with default settings" export:"true"`
Boltdb *boltdb.Provider `description:"Enable Boltdb backend with default settings" export:"true"`
@@ -103,77 +78,11 @@ type GlobalConfiguration struct {
ECS *ecs.Provider `description:"Enable ECS backend with default settings" export:"true"`
Rancher *rancher.Provider `description:"Enable Rancher backend with default settings" export:"true"`
DynamoDB *dynamodb.Provider `description:"Enable DynamoDB backend with default settings" export:"true"`
ServiceFabric *servicefabric.Provider `description:"Enable Service Fabric backend with default settings" export:"true"`
Rest *rest.Provider `description:"Enable Rest backend with default settings" export:"true"`
API *api.Handler `description:"Enable api/dashboard" export:"true"`
Metrics *types.Metrics `description:"Enable a metrics exporter" export:"true"`
Ping *ping.Handler `description:"Enable ping" export:"true"`
HostResolver *HostResolverConfig `description:"Enable CNAME Flattening" export:"true"`
}
// WebCompatibility is a configuration to handle compatibility with deprecated web provider options
type WebCompatibility struct {
Address string `description:"(Deprecated) Web administration port" export:"true"`
CertFile string `description:"(Deprecated) SSL certificate" export:"true"`
KeyFile string `description:"(Deprecated) SSL certificate" export:"true"`
ReadOnly bool `description:"(Deprecated) Enable read only API" export:"true"`
Statistics *types.Statistics `description:"(Deprecated) Enable more detailed statistics" export:"true"`
Metrics *types.Metrics `description:"(Deprecated) Enable a metrics exporter" export:"true"`
Path string `description:"(Deprecated) Root path for dashboard and API" export:"true"`
Auth *types.Auth `export:"true"`
Debug bool `export:"true"`
}
func (gc *GlobalConfiguration) handleWebDeprecation() {
if gc.Web != nil {
log.Warn("web provider configuration is deprecated, you should use these options : api, rest provider, ping and metrics")
if gc.API != nil || gc.Metrics != nil || gc.Ping != nil || gc.Rest != nil {
log.Warn("web option is ignored if you use it with one of these options : api, rest provider, ping or metrics")
return
}
gc.EntryPoints[DefaultInternalEntryPointName] = &EntryPoint{
Address: gc.Web.Address,
Auth: gc.Web.Auth,
}
if gc.Web.CertFile != "" {
gc.EntryPoints[DefaultInternalEntryPointName].TLS = &tls.TLS{
Certificates: []tls.Certificate{
{
CertFile: tls.FileOrContent(gc.Web.CertFile),
KeyFile: tls.FileOrContent(gc.Web.KeyFile),
},
},
}
}
if gc.API == nil {
gc.API = &api.Handler{
EntryPoint: DefaultInternalEntryPointName,
Statistics: gc.Web.Statistics,
Dashboard: true,
}
}
if gc.Ping == nil {
gc.Ping = &ping.Handler{
EntryPoint: DefaultInternalEntryPointName,
}
}
if gc.Metrics == nil {
gc.Metrics = gc.Web.Metrics
}
if !gc.Debug {
gc.Debug = gc.Web.Debug
}
}
}
// SetEffectiveConfiguration adds missing configuration parameters derived from existing ones.
// It also takes care of maintaining backwards compatibility.
func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
func (gc *GlobalConfiguration) SetEffectiveConfiguration() {
if len(gc.EntryPoints) == 0 {
gc.EntryPoints = map[string]*EntryPoint{"http": {
Address: ":80",
@@ -182,112 +91,15 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
gc.DefaultEntryPoints = []string{"http"}
}
gc.handleWebDeprecation()
if (gc.API != nil && gc.API.EntryPoint == DefaultInternalEntryPointName) ||
(gc.Ping != nil && gc.Ping.EntryPoint == DefaultInternalEntryPointName) ||
(gc.Metrics != nil && gc.Metrics.Prometheus != nil && gc.Metrics.Prometheus.EntryPoint == DefaultInternalEntryPointName) ||
(gc.Rest != nil && gc.Rest.EntryPoint == DefaultInternalEntryPointName) {
if _, ok := gc.EntryPoints[DefaultInternalEntryPointName]; !ok {
gc.EntryPoints[DefaultInternalEntryPointName] = &EntryPoint{Address: ":8080"}
}
}
// ForwardedHeaders must be remove in the next breaking version
for entryPointName := range gc.EntryPoints {
entryPoint := gc.EntryPoints[entryPointName]
// ForwardedHeaders must be remove in the next breaking version
if entryPoint.ForwardedHeaders == nil {
entryPoint.ForwardedHeaders = &ForwardedHeaders{Insecure: true}
}
if len(entryPoint.WhitelistSourceRange) > 0 {
log.Warnf("Deprecated configuration found: %s. Please use %s.", "whiteListSourceRange", "whiteList.sourceRange")
if entryPoint.WhiteList == nil {
entryPoint.WhiteList = &types.WhiteList{
SourceRange: entryPoint.WhitelistSourceRange,
}
entryPoint.WhitelistSourceRange = nil
}
}
if entryPoint.TLS != nil && entryPoint.TLS.DefaultCertificate == nil && len(entryPoint.TLS.Certificates) > 0 {
log.Infof("No tls.defaultCertificate given for %s: using the first item in tls.certificates as a fallback.", entryPointName)
entryPoint.TLS.DefaultCertificate = &entryPoint.TLS.Certificates[0]
}
}
// Make sure LifeCycle isn't nil to spare nil checks elsewhere.
if gc.LifeCycle == nil {
gc.LifeCycle = &LifeCycle{}
}
// Prefer legacy grace timeout parameter for backwards compatibility reasons.
if gc.GraceTimeOut > 0 {
log.Warn("top-level grace period configuration has been deprecated -- please use lifecycle grace period")
gc.LifeCycle.GraceTimeOut = gc.GraceTimeOut
}
if gc.Docker != nil {
if len(gc.Docker.Filename) != 0 && gc.Docker.TemplateVersion != 2 {
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
gc.Docker.TemplateVersion = 1
} else {
gc.Docker.TemplateVersion = 2
}
}
if gc.Marathon != nil {
if len(gc.Marathon.Filename) != 0 && gc.Marathon.TemplateVersion != 2 {
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
gc.Marathon.TemplateVersion = 1
} else {
gc.Marathon.TemplateVersion = 2
}
}
if gc.Mesos != nil {
if len(gc.Mesos.Filename) != 0 && gc.Mesos.TemplateVersion != 2 {
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
gc.Mesos.TemplateVersion = 1
} else {
gc.Mesos.TemplateVersion = 2
}
}
if gc.Eureka != nil {
if gc.Eureka.Delay != 0 {
log.Warn("Delay has been deprecated -- please use RefreshSeconds")
gc.Eureka.RefreshSeconds = gc.Eureka.Delay
}
}
if gc.ECS != nil {
if len(gc.ECS.Filename) != 0 && gc.ECS.TemplateVersion != 2 {
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
gc.ECS.TemplateVersion = 1
} else {
gc.ECS.TemplateVersion = 2
}
}
if gc.ConsulCatalog != nil {
if len(gc.ConsulCatalog.Filename) != 0 && gc.ConsulCatalog.TemplateVersion != 2 {
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
gc.ConsulCatalog.TemplateVersion = 1
} else {
gc.ConsulCatalog.TemplateVersion = 2
}
}
if gc.Rancher != nil {
if len(gc.Rancher.Filename) != 0 && gc.Rancher.TemplateVersion != 2 {
log.Warn("Template version 1 is deprecated, please use version 2, see TemplateVersion.")
gc.Rancher.TemplateVersion = 1
} else {
gc.Rancher.TemplateVersion = 2
}
// Ensure backwards compatibility for now
if len(gc.Rancher.AccessKey) > 0 ||
len(gc.Rancher.Endpoint) > 0 ||
@@ -309,197 +121,13 @@ func (gc *GlobalConfiguration) SetEffectiveConfiguration(configFile string) {
}
}
if gc.API != nil {
gc.API.Debug = gc.Debug
if gc.Debug {
gc.LogLevel = "DEBUG"
}
if gc.Web != nil && (gc.Web.Path == "" || !strings.HasSuffix(gc.Web.Path, "/")) {
gc.Web.Path += "/"
}
if gc.File != nil {
gc.File.TraefikFile = configFile
}
gc.initACMEProvider()
gc.initTracing()
}
func (gc *GlobalConfiguration) initTracing() {
if gc.Tracing != nil {
switch gc.Tracing.Backend {
case jaeger.Name:
if gc.Tracing.Jaeger == nil {
gc.Tracing.Jaeger = &jaeger.Config{
SamplingServerURL: "http://localhost:5778/sampling",
SamplingType: "const",
SamplingParam: 1.0,
LocalAgentHostPort: "127.0.0.1:6831",
}
}
if gc.Tracing.Zipkin != nil {
log.Warn("Zipkin configuration will be ignored")
gc.Tracing.Zipkin = nil
}
if gc.Tracing.DataDog != nil {
log.Warn("DataDog configuration will be ignored")
gc.Tracing.DataDog = nil
}
case zipkin.Name:
if gc.Tracing.Zipkin == nil {
gc.Tracing.Zipkin = &zipkin.Config{
HTTPEndpoint: "http://localhost:9411/api/v1/spans",
SameSpan: false,
ID128Bit: true,
Debug: false,
}
}
if gc.Tracing.Jaeger != nil {
log.Warn("Jaeger configuration will be ignored")
gc.Tracing.Jaeger = nil
}
if gc.Tracing.DataDog != nil {
log.Warn("DataDog configuration will be ignored")
gc.Tracing.DataDog = nil
}
case datadog.Name:
if gc.Tracing.DataDog == nil {
gc.Tracing.DataDog = &datadog.Config{
LocalAgentHostPort: "localhost:8126",
GlobalTag: "",
Debug: false,
}
}
if gc.Tracing.Zipkin != nil {
log.Warn("Zipkin configuration will be ignored")
gc.Tracing.Zipkin = nil
}
if gc.Tracing.Jaeger != nil {
log.Warn("Jaeger configuration will be ignored")
gc.Tracing.Jaeger = nil
}
default:
log.Warnf("Unknown tracer %q", gc.Tracing.Backend)
return
}
}
}
func (gc *GlobalConfiguration) initACMEProvider() {
if gc.ACME != nil {
gc.ACME.CAServer = getSafeACMECAServer(gc.ACME.CAServer)
if gc.ACME.DNSChallenge != nil && gc.ACME.HTTPChallenge != nil {
log.Warn("Unable to use DNS challenge and HTTP challenge at the same time. Fallback to DNS challenge.")
gc.ACME.HTTPChallenge = nil
}
if gc.ACME.DNSChallenge != nil && gc.ACME.TLSChallenge != nil {
log.Warn("Unable to use DNS challenge and TLS challenge at the same time. Fallback to DNS challenge.")
gc.ACME.TLSChallenge = nil
}
if gc.ACME.HTTPChallenge != nil && gc.ACME.TLSChallenge != nil {
log.Warn("Unable to use HTTP challenge and TLS challenge at the same time. Fallback to TLS challenge.")
gc.ACME.HTTPChallenge = nil
}
for _, domain := range gc.ACME.Domains {
if domain.Main != lego.UnFqdn(domain.Main) {
log.Warnf("FQDN detected, please remove the trailing dot: %s", domain.Main)
}
for _, san := range domain.SANs {
if san != lego.UnFqdn(san) {
log.Warnf("FQDN detected, please remove the trailing dot: %s", san)
}
}
}
// TODO: to remove in the future
if len(gc.ACME.StorageFile) > 0 && len(gc.ACME.Storage) == 0 {
log.Warn("ACME.StorageFile is deprecated, use ACME.Storage instead")
gc.ACME.Storage = gc.ACME.StorageFile
}
if len(gc.ACME.DNSProvider) > 0 {
log.Warn("ACME.DNSProvider is deprecated, use ACME.DNSChallenge instead")
gc.ACME.DNSChallenge = &acmeprovider.DNSChallenge{Provider: gc.ACME.DNSProvider, DelayBeforeCheck: gc.ACME.DelayDontCheckDNS}
}
if gc.ACME.OnDemand {
log.Warn("ACME.OnDemand is deprecated")
}
}
}
// InitACMEProvider create an acme provider from the ACME part of globalConfiguration
func (gc *GlobalConfiguration) InitACMEProvider() (*acmeprovider.Provider, error) {
if gc.ACME != nil {
if len(gc.ACME.Storage) == 0 {
// Delete the ACME configuration to avoid starting ACME in cluster mode
gc.ACME = nil
return nil, errors.New("unable to initialize ACME provider with no storage location for the certificates")
}
// TODO: Remove when Provider ACME will replace totally ACME
// If provider file, use Provider ACME instead of ACME
if gc.Cluster == nil {
provider := &acmeprovider.Provider{}
provider.Configuration = &acmeprovider.Configuration{
KeyType: gc.ACME.KeyType,
OnHostRule: gc.ACME.OnHostRule,
OnDemand: gc.ACME.OnDemand,
Email: gc.ACME.Email,
Storage: gc.ACME.Storage,
HTTPChallenge: gc.ACME.HTTPChallenge,
DNSChallenge: gc.ACME.DNSChallenge,
TLSChallenge: gc.ACME.TLSChallenge,
Domains: gc.ACME.Domains,
ACMELogging: gc.ACME.ACMELogging,
CAServer: gc.ACME.CAServer,
EntryPoint: gc.ACME.EntryPoint,
}
store := acmeprovider.NewLocalStore(provider.Storage)
provider.Store = store
acme.ConvertToNewFormat(provider.Storage)
gc.ACME = nil
return provider, nil
}
}
return nil, nil
}
func getSafeACMECAServer(caServerSrc string) string {
if len(caServerSrc) == 0 {
return DefaultAcmeCAServer
}
if strings.HasPrefix(caServerSrc, "https://acme-v01.api.letsencrypt.org") {
caServer := strings.Replace(caServerSrc, "v01", "v02", 1)
log.Warnf("The CA server %[1]q refers to a v01 endpoint of the ACME API, please change to %[2]q. Fallback to %[2]q.", caServerSrc, caServer)
return caServer
}
if strings.HasPrefix(caServerSrc, "https://acme-staging.api.letsencrypt.org") {
caServer := strings.Replace(caServerSrc, "https://acme-staging.api.letsencrypt.org", "https://acme-staging-v02.api.letsencrypt.org", 1)
log.Warnf("The CA server %[1]q refers to a v01 endpoint of the ACME API, please change to %[2]q. Fallback to %[2]q.", caServerSrc, caServer)
return caServer
}
return caServerSrc
}
// ValidateConfiguration validate that configuration is coherent
func (gc *GlobalConfiguration) ValidateConfiguration() {
if gc.ACME != nil {
if _, ok := gc.EntryPoints[gc.ACME.EntryPoint]; !ok {
log.Fatalf("Unknown entrypoint %q for ACME configuration", gc.ACME.EntryPoint)
} else {
if gc.EntryPoints[gc.ACME.EntryPoint].TLS == nil {
log.Fatalf("Entrypoint %q has no TLS configuration for ACME configuration", gc.ACME.EntryPoint)
}
}
}
}
// DefaultEntryPoints holds default entry points
@@ -527,12 +155,12 @@ func (dep *DefaultEntryPoints) Set(value string) error {
// Get return the EntryPoints map
func (dep *DefaultEntryPoints) Get() interface{} {
return *dep
return DefaultEntryPoints(*dep)
}
// SetValue sets the EntryPoints map with val
func (dep *DefaultEntryPoints) SetValue(val interface{}) {
*dep = val.(DefaultEntryPoints)
*dep = DefaultEntryPoints(val.(DefaultEntryPoints))
}
// Type is type of the struct
@@ -540,6 +168,339 @@ func (dep *DefaultEntryPoints) Type() string {
return "defaultentrypoints"
}
// RootCAs hold the CA we want to have in root
type RootCAs []FileOrContent
// FileOrContent hold a file path or content
type FileOrContent string
func (f FileOrContent) String() string {
return string(f)
}
func (f FileOrContent) Read() ([]byte, error) {
var content []byte
if _, err := os.Stat(f.String()); err == nil {
content, err = ioutil.ReadFile(f.String())
if err != nil {
return nil, err
}
} else {
content = []byte(f)
}
return content, nil
}
// String is the method to format the flag's value, part of the flag.Value interface.
// The String method's output will be used in diagnostics.
func (r *RootCAs) String() string {
sliceOfString := make([]string, len([]FileOrContent(*r)))
for key, value := range *r {
sliceOfString[key] = value.String()
}
return strings.Join(sliceOfString, ",")
}
// Set is the method to set the flag value, part of the flag.Value interface.
// Set's argument is a string to be parsed to set the flag.
// It's a comma-separated list, so we split it.
func (r *RootCAs) Set(value string) error {
rootCAs := strings.Split(value, ",")
if len(rootCAs) == 0 {
return fmt.Errorf("bad RootCAs format: %s", value)
}
for _, rootCA := range rootCAs {
*r = append(*r, FileOrContent(rootCA))
}
return nil
}
// Get return the EntryPoints map
func (r *RootCAs) Get() interface{} {
return RootCAs(*r)
}
// SetValue sets the EntryPoints map with val
func (r *RootCAs) SetValue(val interface{}) {
*r = RootCAs(val.(RootCAs))
}
// Type is type of the struct
func (r *RootCAs) Type() string {
return "rootcas"
}
// EntryPoints holds entry points configuration of the reverse proxy (ip, port, TLS...)
type EntryPoints map[string]*EntryPoint
// String is the method to format the flag's value, part of the flag.Value interface.
// The String method's output will be used in diagnostics.
func (ep *EntryPoints) String() string {
return fmt.Sprintf("%+v", *ep)
}
// Set is the method to set the flag value, part of the flag.Value interface.
// Set's argument is a string to be parsed to set the flag.
// It's a comma-separated list, so we split it.
func (ep *EntryPoints) Set(value string) error {
result := parseEntryPointsConfiguration(value)
var configTLS *TLS
if len(result["tls"]) > 0 {
certs := Certificates{}
if err := certs.Set(result["tls"]); err != nil {
return err
}
configTLS = &TLS{
Certificates: certs,
}
} else if len(result["tls_acme"]) > 0 {
configTLS = &TLS{
Certificates: Certificates{},
}
}
if len(result["ca"]) > 0 {
files := strings.Split(result["ca"], ",")
configTLS.ClientCAFiles = files
}
var redirect *Redirect
if len(result["redirect_entrypoint"]) > 0 || len(result["redirect_regex"]) > 0 || len(result["redirect_replacement"]) > 0 {
redirect = &Redirect{
EntryPoint: result["redirect_entrypoint"],
Regex: result["redirect_regex"],
Replacement: result["redirect_replacement"],
}
}
whiteListSourceRange := []string{}
if len(result["whitelistsourcerange"]) > 0 {
whiteListSourceRange = strings.Split(result["whitelistsourcerange"], ",")
}
compress := toBool(result, "compress")
var proxyProtocol *ProxyProtocol
ppTrustedIPs := result["proxyprotocol_trustedips"]
if len(result["proxyprotocol_insecure"]) > 0 || len(ppTrustedIPs) > 0 {
proxyProtocol = &ProxyProtocol{
Insecure: toBool(result, "proxyprotocol_insecure"),
}
if len(ppTrustedIPs) > 0 {
proxyProtocol.TrustedIPs = strings.Split(ppTrustedIPs, ",")
}
}
// TODO must be changed to false by default in the next breaking version.
forwardedHeaders := &ForwardedHeaders{Insecure: true}
if _, ok := result["forwardedheaders_insecure"]; ok {
forwardedHeaders.Insecure = toBool(result, "forwardedheaders_insecure")
}
fhTrustedIPs := result["forwardedheaders_trustedips"]
if len(fhTrustedIPs) > 0 {
// TODO must be removed in the next breaking version.
forwardedHeaders.Insecure = toBool(result, "forwardedheaders_insecure")
forwardedHeaders.TrustedIPs = strings.Split(fhTrustedIPs, ",")
}
if proxyProtocol != nil && proxyProtocol.Insecure {
log.Warn("ProxyProtocol.Insecure:true is dangerous. Please use 'ProxyProtocol.TrustedIPs:IPs' and remove 'ProxyProtocol.Insecure:true'")
}
(*ep)[result["name"]] = &EntryPoint{
Address: result["address"],
TLS: configTLS,
Redirect: redirect,
Compress: compress,
WhitelistSourceRange: whiteListSourceRange,
ProxyProtocol: proxyProtocol,
ForwardedHeaders: forwardedHeaders,
}
return nil
}
func parseEntryPointsConfiguration(raw string) map[string]string {
sections := strings.Fields(raw)
config := make(map[string]string)
for _, part := range sections {
field := strings.SplitN(part, ":", 2)
name := strings.ToLower(strings.Replace(field[0], ".", "_", -1))
if len(field) > 1 {
config[name] = field[1]
} else {
if strings.EqualFold(name, "TLS") {
config["tls_acme"] = "TLS"
} else {
config[name] = ""
}
}
}
return config
}
func toBool(conf map[string]string, key string) bool {
if val, ok := conf[key]; ok {
return strings.EqualFold(val, "true") ||
strings.EqualFold(val, "enable") ||
strings.EqualFold(val, "on")
}
return false
}
// Get return the EntryPoints map
func (ep *EntryPoints) Get() interface{} {
return EntryPoints(*ep)
}
// SetValue sets the EntryPoints map with val
func (ep *EntryPoints) SetValue(val interface{}) {
*ep = EntryPoints(val.(EntryPoints))
}
// Type is type of the struct
func (ep *EntryPoints) Type() string {
return "entrypoints"
}
// EntryPoint holds an entry point configuration of the reverse proxy (ip, port, TLS...)
type EntryPoint struct {
Network string
Address string
TLS *TLS `export:"true"`
Redirect *Redirect `export:"true"`
Auth *types.Auth `export:"true"`
WhitelistSourceRange []string
Compress bool `export:"true"`
ProxyProtocol *ProxyProtocol `export:"true"`
ForwardedHeaders *ForwardedHeaders `export:"true"`
}
// Redirect configures a redirection of an entry point to another, or to an URL
type Redirect struct {
EntryPoint string
Regex string
Replacement string
}
// TLS configures TLS for an entry point
type TLS struct {
MinVersion string `export:"true"`
CipherSuites []string
Certificates Certificates
ClientCAFiles []string
}
// MinVersion Map of allowed TLS minimum versions
var MinVersion = map[string]uint16{
`VersionTLS10`: tls.VersionTLS10,
`VersionTLS11`: tls.VersionTLS11,
`VersionTLS12`: tls.VersionTLS12,
}
// CipherSuites Map of TLS CipherSuites from crypto/tls
// Available CipherSuites defined at https://golang.org/pkg/crypto/tls/#pkg-constants
var CipherSuites = map[string]uint16{
`TLS_RSA_WITH_RC4_128_SHA`: tls.TLS_RSA_WITH_RC4_128_SHA,
`TLS_RSA_WITH_3DES_EDE_CBC_SHA`: tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
`TLS_RSA_WITH_AES_128_CBC_SHA`: tls.TLS_RSA_WITH_AES_128_CBC_SHA,
`TLS_RSA_WITH_AES_256_CBC_SHA`: tls.TLS_RSA_WITH_AES_256_CBC_SHA,
`TLS_RSA_WITH_AES_128_CBC_SHA256`: tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
`TLS_RSA_WITH_AES_128_GCM_SHA256`: tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
`TLS_RSA_WITH_AES_256_GCM_SHA384`: tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
`TLS_ECDHE_ECDSA_WITH_RC4_128_SHA`: tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
`TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA`: tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
`TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA`: tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
`TLS_ECDHE_RSA_WITH_RC4_128_SHA`: tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
`TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA`: tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
`TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA`: tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
`TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA`: tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
`TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256`: tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
`TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256`: tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
`TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256`: tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
`TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256`: tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
`TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384`: tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
`TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384`: tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
`TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305`: tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
`TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305`: tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
}
// Certificates defines traefik certificates type
// Certs and Keys could be either a file path, or the file content itself
type Certificates []Certificate
//CreateTLSConfig creates a TLS config from Certificate structures
func (certs *Certificates) CreateTLSConfig() (*tls.Config, error) {
config := &tls.Config{}
config.Certificates = []tls.Certificate{}
certsSlice := []Certificate(*certs)
for _, v := range certsSlice {
var err error
certContent, err := v.CertFile.Read()
if err != nil {
return nil, err
}
keyContent, err := v.KeyFile.Read()
if err != nil {
return nil, err
}
cert, err := tls.X509KeyPair(certContent, keyContent)
if err != nil {
return nil, err
}
config.Certificates = append(config.Certificates, cert)
}
return config, nil
}
// String is the method to format the flag's value, part of the flag.Value interface.
// The String method's output will be used in diagnostics.
func (certs *Certificates) String() string {
if len(*certs) == 0 {
return ""
}
var result []string
for _, certificate := range *certs {
result = append(result, certificate.CertFile.String()+","+certificate.KeyFile.String())
}
return strings.Join(result, ";")
}
// Set is the method to set the flag value, part of the flag.Value interface.
// Set's argument is a string to be parsed to set the flag.
// It's a comma-separated list, so we split it.
func (certs *Certificates) Set(value string) error {
certificates := strings.Split(value, ";")
for _, certificate := range certificates {
files := strings.Split(certificate, ",")
if len(files) != 2 {
return fmt.Errorf("bad certificates format: %s", value)
}
*certs = append(*certs, Certificate{
CertFile: FileOrContent(files[0]),
KeyFile: FileOrContent(files[1]),
})
}
return nil
}
// Type is type of the struct
func (certs *Certificates) Type() string {
return "certificates"
}
// Certificate holds a SSL cert/key pair
// Certs and Key could be either a file path, or the file content itself
type Certificate struct {
CertFile FileOrContent
KeyFile FileOrContent
}
// Retry contains request retry config
type Retry struct {
Attempts int `description:"Number of attempts" export:"true"`
@@ -563,16 +524,14 @@ type ForwardingTimeouts struct {
ResponseHeaderTimeout flaeg.Duration `description:"The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists" export:"true"`
}
// LifeCycle contains configurations relevant to the lifecycle (such as the
// shutdown phase) of Traefik.
type LifeCycle struct {
RequestAcceptGraceTimeout flaeg.Duration `description:"Duration to keep accepting requests before Traefik initiates the graceful shutdown procedure"`
GraceTimeOut flaeg.Duration `description:"Duration to give active requests a chance to finish before Traefik stops"`
// ProxyProtocol contains Proxy-Protocol configuration
type ProxyProtocol struct {
Insecure bool
TrustedIPs []string
}
// HostResolverConfig contain configuration for CNAME Flattening
type HostResolverConfig struct {
CnameFlattening bool `description:"A flag to enable/disable CNAME flattening" export:"true"`
ResolvConfig string `description:"resolv.conf used for DNS resolving" export:"true"`
ResolvDepth int `description:"The maximal depth of DNS recursive resolving" export:"true"`
// ForwardedHeaders Trust client forwarding headers
type ForwardedHeaders struct {
Insecure bool
TrustedIPs []string
}

View File

@@ -2,267 +2,292 @@ package configuration
import (
"testing"
"time"
"github.com/containous/flaeg"
"github.com/containous/traefik/acme"
"github.com/containous/traefik/middlewares/tracing"
"github.com/containous/traefik/middlewares/tracing/jaeger"
"github.com/containous/traefik/middlewares/tracing/zipkin"
"github.com/containous/traefik/provider"
acmeprovider "github.com/containous/traefik/provider/acme"
"github.com/containous/traefik/provider/file"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const defaultConfigFile = "traefik.toml"
func TestSetEffectiveConfigurationGraceTimeout(t *testing.T) {
func Test_parseEntryPointsConfiguration(t *testing.T) {
testCases := []struct {
desc string
legacyGraceTimeout time.Duration
lifeCycleGraceTimeout time.Duration
wantGraceTimeout time.Duration
name string
value string
expectedResult map[string]string
}{
{
desc: "legacy grace timeout given only",
legacyGraceTimeout: 5 * time.Second,
wantGraceTimeout: 5 * time.Second,
},
{
desc: "legacy and life cycle grace timeouts given",
legacyGraceTimeout: 5 * time.Second,
lifeCycleGraceTimeout: 12 * time.Second,
wantGraceTimeout: 5 * time.Second,
},
{
desc: "legacy grace timeout omitted",
legacyGraceTimeout: 0,
lifeCycleGraceTimeout: 12 * time.Second,
wantGraceTimeout: 12 * time.Second,
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
gc := &GlobalConfiguration{
GraceTimeOut: flaeg.Duration(test.legacyGraceTimeout),
}
if test.lifeCycleGraceTimeout > 0 {
gc.LifeCycle = &LifeCycle{
GraceTimeOut: flaeg.Duration(test.lifeCycleGraceTimeout),
}
}
gc.SetEffectiveConfiguration(defaultConfigFile)
assert.Equal(t, test.wantGraceTimeout, time.Duration(gc.LifeCycle.GraceTimeOut))
})
}
}
func TestSetEffectiveConfigurationFileProviderFilename(t *testing.T) {
testCases := []struct {
desc string
fileProvider *file.Provider
wantFileProviderFilename string
wantFileProviderTraefikFile string
}{
{
desc: "no filename for file provider given",
fileProvider: &file.Provider{},
wantFileProviderFilename: "",
wantFileProviderTraefikFile: defaultConfigFile,
},
{
desc: "filename for file provider given",
fileProvider: &file.Provider{BaseProvider: provider.BaseProvider{Filename: "other.toml"}},
wantFileProviderFilename: "other.toml",
wantFileProviderTraefikFile: defaultConfigFile,
},
{
desc: "directory for file provider given",
fileProvider: &file.Provider{Directory: "/"},
wantFileProviderFilename: "",
wantFileProviderTraefikFile: defaultConfigFile,
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
gc := &GlobalConfiguration{
File: test.fileProvider,
}
gc.SetEffectiveConfiguration(defaultConfigFile)
assert.Equal(t, test.wantFileProviderFilename, gc.File.Filename)
assert.Equal(t, test.wantFileProviderTraefikFile, gc.File.TraefikFile)
})
}
}
func TestSetEffectiveConfigurationTracing(t *testing.T) {
testCases := []struct {
desc string
tracing *tracing.Tracing
expected *tracing.Tracing
}{
{
desc: "no tracing configuration",
tracing: &tracing.Tracing{},
expected: &tracing.Tracing{},
},
{
desc: "tracing bad backend name",
tracing: &tracing.Tracing{
Backend: "powpow",
},
expected: &tracing.Tracing{
Backend: "powpow",
name: "all parameters",
value: "Name:foo TLS:goo TLS CA:car Redirect.EntryPoint:RedirectEntryPoint Redirect.Regex:RedirectRegex Redirect.Replacement:RedirectReplacement Compress:true WhiteListSourceRange:WhiteListSourceRange ProxyProtocol.TrustedIPs:192.168.0.1 ProxyProtocol.Insecure:false Address::8000",
expectedResult: map[string]string{
"name": "foo",
"address": ":8000",
"ca": "car",
"tls": "goo",
"tls_acme": "TLS",
"redirect_entrypoint": "RedirectEntryPoint",
"redirect_regex": "RedirectRegex",
"redirect_replacement": "RedirectReplacement",
"whitelistsourcerange": "WhiteListSourceRange",
"proxyprotocol_trustedips": "192.168.0.1",
"proxyprotocol_insecure": "false",
"compress": "true",
},
},
{
desc: "tracing jaeger backend name",
tracing: &tracing.Tracing{
Backend: "jaeger",
Zipkin: &zipkin.Config{
HTTPEndpoint: "http://localhost:9411/api/v1/spans",
SameSpan: false,
ID128Bit: true,
Debug: false,
},
},
expected: &tracing.Tracing{
Backend: "jaeger",
Jaeger: &jaeger.Config{
SamplingServerURL: "http://localhost:5778/sampling",
SamplingType: "const",
SamplingParam: 1.0,
LocalAgentHostPort: "127.0.0.1:6831",
},
Zipkin: nil,
name: "compress on",
value: "name:foo Compress:on",
expectedResult: map[string]string{
"name": "foo",
"compress": "on",
},
},
{
desc: "tracing zipkin backend name",
tracing: &tracing.Tracing{
Backend: "zipkin",
Jaeger: &jaeger.Config{
SamplingServerURL: "http://localhost:5778/sampling",
SamplingType: "const",
SamplingParam: 1.0,
LocalAgentHostPort: "127.0.0.1:6831",
},
},
expected: &tracing.Tracing{
Backend: "zipkin",
Jaeger: nil,
Zipkin: &zipkin.Config{
HTTPEndpoint: "http://localhost:9411/api/v1/spans",
SameSpan: false,
ID128Bit: true,
Debug: false,
},
},
},
{
desc: "tracing zipkin backend name value override",
tracing: &tracing.Tracing{
Backend: "zipkin",
Jaeger: &jaeger.Config{
SamplingServerURL: "http://localhost:5778/sampling",
SamplingType: "const",
SamplingParam: 1.0,
LocalAgentHostPort: "127.0.0.1:6831",
},
Zipkin: &zipkin.Config{
HTTPEndpoint: "http://powpow:9411/api/v1/spans",
SameSpan: true,
ID128Bit: true,
Debug: true,
},
},
expected: &tracing.Tracing{
Backend: "zipkin",
Jaeger: nil,
Zipkin: &zipkin.Config{
HTTPEndpoint: "http://powpow:9411/api/v1/spans",
SameSpan: true,
ID128Bit: true,
Debug: true,
},
name: "TLS",
value: "Name:foo TLS:goo TLS",
expectedResult: map[string]string{
"name": "foo",
"tls": "goo",
"tls_acme": "TLS",
},
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
gc := &GlobalConfiguration{
Tracing: test.tracing,
}
conf := parseEntryPointsConfiguration(test.value)
gc.SetEffectiveConfiguration(defaultConfigFile)
assert.Equal(t, test.expected, gc.Tracing)
assert.Len(t, conf, len(test.expectedResult))
assert.Equal(t, test.expectedResult, conf)
})
}
}
func TestInitACMEProvider(t *testing.T) {
func Test_toBool(t *testing.T) {
testCases := []struct {
desc string
acmeConfiguration *acme.ACME
expectedConfiguration *acmeprovider.Provider
noError bool
name string
value string
key string
expectedBool bool
}{
{
desc: "No ACME configuration",
acmeConfiguration: nil,
expectedConfiguration: nil,
noError: true,
name: "on",
value: "on",
key: "foo",
expectedBool: true,
},
{
desc: "ACME configuration with storage",
acmeConfiguration: &acme.ACME{Storage: "foo/acme.json"},
expectedConfiguration: &acmeprovider.Provider{Configuration: &acmeprovider.Configuration{Storage: "foo/acme.json"}},
noError: true,
name: "true",
value: "true",
key: "foo",
expectedBool: true,
},
{
desc: "ACME configuration with no storage",
acmeConfiguration: &acme.ACME{},
expectedConfiguration: nil,
noError: false,
name: "enable",
value: "enable",
key: "foo",
expectedBool: true,
},
{
name: "arbitrary string",
value: "bar",
key: "foo",
expectedBool: false,
},
{
name: "no existing entry",
value: "bar",
key: "fii",
expectedBool: false,
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
gc := &GlobalConfiguration{
ACME: test.acmeConfiguration,
conf := map[string]string{
"foo": test.value,
}
configuration, err := gc.InitACMEProvider()
result := toBool(conf, test.key)
assert.True(t, (err == nil) == test.noError)
if test.expectedConfiguration == nil {
assert.Nil(t, configuration)
} else {
assert.Equal(t, test.expectedConfiguration.Storage, configuration.Storage)
}
assert.Equal(t, test.expectedBool, result)
})
}
}
func TestEntryPoints_Set(t *testing.T) {
testCases := []struct {
name string
expression string
expectedEntryPointName string
expectedEntryPoint *EntryPoint
}{
{
name: "all parameters camelcase",
expression: "Name:foo Address::8000 TLS:goo,gii TLS CA:car Redirect.EntryPoint:RedirectEntryPoint Redirect.Regex:RedirectRegex Redirect.Replacement:RedirectReplacement Compress:true WhiteListSourceRange:Range ProxyProtocol.TrustedIPs:192.168.0.1 ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
Address: ":8000",
Redirect: &Redirect{
EntryPoint: "RedirectEntryPoint",
Regex: "RedirectRegex",
Replacement: "RedirectReplacement",
},
Compress: true,
ProxyProtocol: &ProxyProtocol{
TrustedIPs: []string{"192.168.0.1"},
},
ForwardedHeaders: &ForwardedHeaders{
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
},
WhitelistSourceRange: []string{"Range"},
TLS: &TLS{
ClientCAFiles: []string{"car"},
Certificates: Certificates{
{
CertFile: FileOrContent("goo"),
KeyFile: FileOrContent("gii"),
},
},
},
},
},
{
name: "all parameters lowercase",
expression: "name:foo address::8000 tls:goo,gii tls ca:car redirect.entryPoint:RedirectEntryPoint redirect.regex:RedirectRegex redirect.replacement:RedirectReplacement compress:true whiteListSourceRange:Range proxyProtocol.trustedIPs:192.168.0.1 forwardedHeaders.trustedIPs:10.0.0.3/24,20.0.0.3/24",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
Address: ":8000",
Redirect: &Redirect{
EntryPoint: "RedirectEntryPoint",
Regex: "RedirectRegex",
Replacement: "RedirectReplacement",
},
Compress: true,
ProxyProtocol: &ProxyProtocol{
TrustedIPs: []string{"192.168.0.1"},
},
ForwardedHeaders: &ForwardedHeaders{
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
},
WhitelistSourceRange: []string{"Range"},
TLS: &TLS{
ClientCAFiles: []string{"car"},
Certificates: Certificates{
{
CertFile: FileOrContent("goo"),
KeyFile: FileOrContent("gii"),
},
},
},
},
},
{
name: "default",
expression: "Name:foo",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
WhitelistSourceRange: []string{},
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
},
},
{
name: "ForwardedHeaders insecure true",
expression: "Name:foo ForwardedHeaders.Insecure:true",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
WhitelistSourceRange: []string{},
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
},
},
{
name: "ForwardedHeaders insecure false",
expression: "Name:foo ForwardedHeaders.Insecure:false",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
WhitelistSourceRange: []string{},
ForwardedHeaders: &ForwardedHeaders{Insecure: false},
},
},
{
name: "ForwardedHeaders TrustedIPs",
expression: "Name:foo ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
WhitelistSourceRange: []string{},
ForwardedHeaders: &ForwardedHeaders{
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
},
},
},
{
name: "ProxyProtocol insecure true",
expression: "Name:foo ProxyProtocol.Insecure:true",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
WhitelistSourceRange: []string{},
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
ProxyProtocol: &ProxyProtocol{Insecure: true},
},
},
{
name: "ProxyProtocol insecure false",
expression: "Name:foo ProxyProtocol.Insecure:false",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
WhitelistSourceRange: []string{},
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
ProxyProtocol: &ProxyProtocol{},
},
},
{
name: "ProxyProtocol TrustedIPs",
expression: "Name:foo ProxyProtocol.TrustedIPs:10.0.0.3/24,20.0.0.3/24",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
WhitelistSourceRange: []string{},
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
ProxyProtocol: &ProxyProtocol{
TrustedIPs: []string{"10.0.0.3/24", "20.0.0.3/24"},
},
},
},
{
name: "compress on",
expression: "Name:foo Compress:on",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
Compress: true,
WhitelistSourceRange: []string{},
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
},
},
{
name: "compress true",
expression: "Name:foo Compress:true",
expectedEntryPointName: "foo",
expectedEntryPoint: &EntryPoint{
Compress: true,
WhitelistSourceRange: []string{},
ForwardedHeaders: &ForwardedHeaders{Insecure: true},
},
},
}
for _, test := range testCases {
test := test
t.Run(test.name, func(t *testing.T) {
t.Parallel()
eps := EntryPoints{}
err := eps.Set(test.expression)
require.NoError(t, err)
ep := eps[test.expectedEntryPointName]
assert.EqualValues(t, test.expectedEntryPoint, ep)
})
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -42,18 +42,6 @@ set -o nounset
USAGE="$(basename "$0") <path to acme> <destination cert directory>"
# Platform variations
case "$(uname)" in
'Linux')
# On Linux, -d should always work. --decode does not work with Alpine's busybox-binary
CMD_DECODE_BASE64="base64 -d"
;;
*)
# Max OS-X supports --decode and -D, but --decode may be supported by other platforms as well.
CMD_DECODE_BASE64="base64 --decode"
;;
esac
# Allow us to exit on a missing jq binary
exit_jq() {
echo "
@@ -104,7 +92,7 @@ fi
jq=$(command -v jq) || exit_jq
priv=$(${jq} -e -r '.Account.PrivateKey' "${acmefile}") || bad_acme
priv=$(${jq} -e -r '.PrivateKey' "${acmefile}") || bad_acme
if [ ! -n "${priv}" ]; then
echo "
@@ -142,11 +130,9 @@ trap 'umask ${oldumask}' EXIT
#
# and sed:
# echo "-----BEGIN RSA PRIVATE KEY-----" > "${pdir}/letsencrypt.key"
# echo ${priv} | sed -E 's/(.{64})/\1\n/g' >> "${pdir}/letsencrypt.key"
# sed -i '$ d' "${pdir}/letsencrypt.key"
# echo "-----END RSA PRIVATE KEY-----" >> "${pdir}/letsencrypt.key"
# openssl rsa -noout -in "${pdir}/letsencrypt.key" -check # To check if the key is valid
# echo ${priv} | sed 's/(.{64})/\1\n/g' >> "${pdir}/letsencrypt.key"
# echo "-----END RSA PRIVATE KEY-----" > "${pdir}/letsencrypt.key"
#
# In the end, openssl was chosen because most users will need this script
# *because* of openssl combined with the fact that it will refuse to write the
# key if it does not parse out correctly. The other mechanisms were left as
@@ -155,16 +141,11 @@ echo -e "-----BEGIN RSA PRIVATE KEY-----\n${priv}\n-----END RSA PRIVATE KEY-----
| openssl rsa -inform pem -out "${pdir}/letsencrypt.key"
# Process the certificates for each of the domains in acme.json
for domain in $(jq -r '.Certificates[].Domain.Main' ${acmefile}); do
for domain in $(jq -r '.DomainsCertificate.Certs[].Certificate.Domain' acme.json); do
# Traefik stores a cert bundle for each domain. Within this cert
# bundle there is both proper the certificate and the Let's Encrypt CA
echo "Extracting cert bundle for ${domain}"
cert=$(jq -e -r --arg domain "$domain" '.Certificates[] |
select (.Domain.Main == $domain )| .Certificate' ${acmefile}) || bad_acme
echo "${cert}" | ${CMD_DECODE_BASE64} > "${cdir}/${domain}.crt"
echo "Extracting private key for ${domain}"
key=$(jq -e -r --arg domain "$domain" '.Certificates[] |
select (.Domain.Main == $domain )| .Key' ${acmefile}) || bad_acme
echo "${key}" | ${CMD_DECODE_BASE64} > "${pdir}/${domain}.key"
cert=$(jq -e -r --arg domain "$domain" '.DomainsCertificate.Certs[].Certificate |
select (.Domain == $domain )| .Certificate' ${acmefile}) || bad_acme
echo "${cert}" | base64 --decode > "${cdir}/${domain}.pem"
done

23
docs/archive.md Normal file
View File

@@ -0,0 +1,23 @@
## Current versions documentation
- [Latest stable](https://docs.traefik.io)
- [Experimental](https://master--traefik-docs.netlify.com/)
## Future version documentation
- [v1.5 RC](http://v1-5.archive.docs.traefik.io/)
## Previous versions documentation
- [v1.4 aka Roquefort](http://v1-4.archive.docs.traefik.io/)
- [v1.3 aka Raclette](http://v1-3.archive.docs.traefik.io/)
- [v1.2 aka Morbier](http://v1-2.archive.docs.traefik.io/)
- [v1.1 aka Camembert](http://v1-1.archive.docs.traefik.io/)
## More
[Change log](https://github.com/containous/traefik/blob/master/CHANGELOG.md)

View File

@@ -14,12 +14,12 @@ Let's take our example from the [overview](/#overview) again:
> ![Architecture](img/architecture.png)
Let's zoom on Traefik and have an overview of its internal architecture:
Let's zoom on Træfik and have an overview of its internal architecture:
![Architecture](img/internal.png)
- Incoming requests end on [entrypoints](#entrypoints), as the name suggests, they are the network entry points into Traefik (listening port, SSL, traffic redirection...).
- Incoming requests end on [entrypoints](#entrypoints), as the name suggests, they are the network entry points into Træfik (listening port, SSL, traffic redirection...).
- Traffic is then forwarded to a matching [frontend](#frontends). A frontend defines routes from [entrypoints](#entrypoints) to [backends](#backends).
Routes are created using requests fields (`Host`, `Path`, `Headers`...) and can match or not a request.
- The [frontend](#frontends) will then send the request to a [backend](#backends). A backend can be composed by one or more [servers](#servers), and by a load-balancing strategy.
@@ -27,7 +27,7 @@ Routes are created using requests fields (`Host`, `Path`, `Headers`...) and can
### Entrypoints
Entrypoints are the network entry points into Traefik.
Entrypoints are the network entry points into Træfik.
They can be defined using:
- a port (80, 443...)
@@ -62,9 +62,7 @@ And here is another example with client certificate authentication:
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]
[entryPoints.https.tls.ClientCA]
files = ["tests/clientca1.crt", "tests/clientca2.crt"]
optional = false
clientCAFiles = ["tests/clientca1.crt", "tests/clientca2.crt"]
[[entryPoints.https.tls.certificates]]
certFile = "tests/traefik.crt"
keyFile = "tests/traefik.key"
@@ -88,18 +86,15 @@ Following is the list of existing modifier rules:
- `AddPrefix: /products`: Add path prefix to the existing request path prior to forwarding the request to the backend.
- `ReplacePath: /serverless-path`: Replaces the path and adds the old path to the `X-Replaced-Path` header. Useful for mapping to AWS Lambda or Google Cloud Functions.
- `ReplacePathRegex: ^/api/v2/(.*) /api/$1`: Replaces the path with a regular expression and adds the old path to the `X-Replaced-Path` header. Separate the regular expression and the replacement by a space.
#### Matchers
Matcher rules determine if a particular request should be forwarded to a backend.
The associativity rule is the following:
- `,` is the `OR` operator (works **only inside a matcher**, ex: `Host:foo.com,bar.com`).
- i.e., forward a request if any rule matches.
- Does not work for `Headers` and `HeadersRegexp`.
- `;` is the `AND` operator (works **only between matchers**, ex: `Host:foo.com;Path:/bar`)
- i.e., forward a request if all rules match
Separate multiple rule values by `,` (comma) in order to enable ANY semantics (i.e., forward a request if any rule matches).
Does not work for `Headers` and `HeadersRegexp`.
Separate multiple rule values by `;` (semicolon) in order to enable ALL semantics (i.e., forward a request if all rules match).
Following is the list of existing matcher rules along with examples:
@@ -124,7 +119,7 @@ In order to use regular expressions with Host and Path matchers, you must declar
The variable has no special meaning; however, it is required by the [gorilla/mux](https://github.com/gorilla/mux) dependency which embeds the regular expression and defines the syntax.
You can optionally enable `passHostHeader` to forward client `Host` header to the backend.
You can also optionally configure the `passTLSClientCert` option to pass the Client certificates to the backend in a specific header.
You can also optionally enable `passTLSCert` to forward TLS Client certificates to the backend.
##### Path Matcher Usage Guidelines
@@ -159,8 +154,7 @@ Here is an example of frontends definition:
[frontends.frontend2]
backend = "backend1"
passHostHeader = true
[frontends.frontend2.passTLSClientCert]
pem = true
passTLSCert = true
priority = 10
entrypoints = ["https"] # overrides defaultEntryPoints
[frontends.frontend2.routes.test_1]
@@ -173,7 +167,7 @@ Here is an example of frontends definition:
- Three frontends are defined: `frontend1`, `frontend2` and `frontend3`
- `frontend1` will forward the traffic to the `backend2` if the rule `Host:test.localhost,test2.localhost` is matched
- `frontend2` will forward the traffic to the `backend1` if the rule `HostRegexp:localhost,{subdomain:[a-z]+}.localhost` is matched (forwarding client `Host` header to the backend)
- `frontend2` will forward the traffic to the `backend1` if the rule `Host:localhost,{subdomain:[a-z]+}.localhost` is matched (forwarding client `Host` header to the backend)
- `frontend3` will forward the traffic to the `backend2` if the rules `Host:test3.localhost` **AND** `Path:/test` are matched
#### Combining multiple rules
@@ -236,37 +230,33 @@ The following rules are both `Matchers` and `Modifiers`, so the `Matcher` portio
#### Priorities
By default, routes will be sorted (in descending order) using rules length (to avoid path overlap):
`PathPrefix:/foo;Host:foo.com` (length == 28) will be matched before `PathPrefixStrip:/foobar` (length == 23) will be matched before `PathPrefix:/foo,/bar` (length == 20).
`PathPrefix:/12345` will be matched before `PathPrefix:/1234` that will be matched before `PathPrefix:/1`.
You can customize priority by frontend. The priority value override the rule length during sorting:
You can customize priority by frontend:
```toml
[frontends]
[frontends.frontend1]
backend = "backend1"
priority = 20
priority = 10
passHostHeader = true
[frontends.frontend1.routes.test_1]
rule = "PathPrefix:/to"
[frontends.frontend2]
priority = 5
backend = "backend2"
passHostHeader = true
[frontends.frontend2.routes.test_1]
rule = "PathPrefix:/toto"
```
Here, `frontend1` will be matched before `frontend2` (`20 > 16`).
Here, `frontend1` will be matched before `frontend2` (`10 > 5`).
#### Custom headers
Custom headers can be configured through the frontends, to add headers to either requests or responses that match the frontend's rules.
This allows for setting headers such as `X-Script-Name` to be added to the request, or custom headers to be added to the response.
!!! warning
If the custom header name is the same as one header name of the request or response, it will be replaced.
In this example, all matches to the path `/cheese` will have the `X-Script-Name` header added to the proxied request and the `X-Custom-Response-Header` header added to the response.
```toml
[frontends]
[frontends.frontend1]
@@ -279,20 +269,7 @@ In this example, all matches to the path `/cheese` will have the `X-Script-Name`
rule = "PathPrefixStrip:/cheese"
```
In this second example, all matches to the path `/cheese` will have the `X-Script-Name` header added to the proxied request, the `X-Custom-Request-Header` header removed from the request, and the `X-Custom-Response-Header` header removed from the response.
```toml
[frontends]
[frontends.frontend1]
backend = "backend1"
[frontends.frontend1.headers.customresponseheaders]
X-Custom-Response-Header = ""
[frontends.frontend1.headers.customrequestheaders]
X-Script-Name = "test"
X-Custom-Request-Header = ""
[frontends.frontend1.routes.test_1]
rule = "PathPrefixStrip:/cheese"
```
In this example, all matches to the path `/cheese` will have the `X-Script-Name` header added to the proxied request, and the `X-Custom-Response-Header` added to the response.
#### Security headers
@@ -326,55 +303,12 @@ In this example, traffic routed through the first frontend will have the `X-Fram
A backend is responsible to load-balance the traffic coming from one or more frontends to a set of http servers.
#### Servers
Servers are simply defined using a `url`. You can also apply a custom `weight` to each server (this will be used by load-balancing).
!!! note
Paths in `url` are ignored. Use `Modifier` to specify paths instead.
Here is an example of backends and servers definition:
```toml
[backends]
[backends.backend1]
# ...
[backends.backend1.servers.server1]
url = "http://172.17.0.2:80"
weight = 10
[backends.backend1.servers.server2]
url = "http://172.17.0.3:80"
weight = 1
[backends.backend2]
# ...
[backends.backend2.servers.server1]
url = "https://172.17.0.4:443"
weight = 1
[backends.backend2.servers.server2]
url = "https://172.17.0.5:443"
weight = 2
[backends.backend3]
# ...
[backends.backend3.servers.server1]
url = "h2c://172.17.0.6:80"
weight = 1
```
- Two backends are defined: `backend1` and `backend2`
- `backend1` will forward the traffic to two servers: `172.17.0.2:80` with weight `10` and `172.17.0.3:80` with weight `1`.
- `backend2` will forward the traffic to two servers: `172.17.0.4:443` with weight `1` and `172.17.0.5:443` with weight `2` both using TLS.
- `backend3` will forward the traffic to: `172.17.0.6:80` with weight `1` using HTTP2 without TLS.
#### Load-balancing
Various methods of load-balancing are supported:
- `wrr`: Weighted Round Robin.
- `wrr`: Weighted Round Robin
- `drr`: Dynamic Round Robin: increases weights on servers that perform better than others.
It also rolls back to original weights if the servers have changed.
#### Circuit breakers
A circuit breaker can also be applied to a backend, preventing high loads on failing servers.
Initial state is Standby. CB observes the statistics and does not modify the request.
In case the condition matches, CB enters Tripped state, where it responds with predefined code or redirects to another frontend.
@@ -388,33 +322,16 @@ It can be configured using:
For example:
- `NetworkErrorRatio() > 0.5`: watch error ratio over 10 second sliding window for a frontend.
- `NetworkErrorRatio() > 0.5`: watch error ratio over 10 second sliding window for a frontend
- `LatencyAtQuantileMS(50.0) > 50`: watch latency at quantile in milliseconds.
- `ResponseCodeRatio(500, 600, 0, 600) > 0.5`: ratio of response codes in ranges [500-600) and [0-600).
- `ResponseCodeRatio(500, 600, 0, 600) > 0.5`: ratio of response codes in range [500-600) to [0-600)
Here is an example of backends and servers definition:
To proactively prevent backends from being overwhelmed with high load, a maximum connection limit can
also be applied to each backend.
```toml
[backends]
[backends.backend1]
[backends.backend1.circuitbreaker]
expression = "NetworkErrorRatio() > 0.5"
[backends.backend1.servers.server1]
url = "http://172.17.0.2:80"
weight = 10
[backends.backend1.servers.server2]
url = "http://172.17.0.3:80"
weight = 1
```
- `backend1` will forward the traffic to two servers: `http://172.17.0.2:80"` with weight `10` and `http://172.17.0.3:80` with weight `1` using default `wrr` load-balancing strategy.
- a circuit breaker is added on `backend1` using the expression `NetworkErrorRatio() > 0.5`: watch error ratio over 10 second sliding window
#### Maximum connections
To proactively prevent backends from being overwhelmed with high load, a maximum connection limit can also be applied to each backend.
Maximum connections can be configured by specifying an integer value for `maxconn.amount` and `maxconn.extractorfunc` which is a strategy used to determine how to categorize requests in order to evaluate the maximum connections.
Maximum connections can be configured by specifying an integer value for `maxconn.amount` and
`maxconn.extractorfunc` which is a strategy used to determine how to categorize requests in order to
evaluate the maximum connections.
For example:
```toml
@@ -423,14 +340,13 @@ For example:
[backends.backend1.maxconn]
amount = 10
extractorfunc = "request.host"
# ...
```
- `backend1` will return `HTTP code 429 Too Many Requests` if there are already 10 requests in progress for the same Host header.
- Another possible value for `extractorfunc` is `client.ip` which will categorize requests based on client source ip.
- Lastly `extractorfunc` can take the value of `request.header.ANY_HEADER` which will categorize requests based on `ANY_HEADER` that you provide.
#### Sticky sessions
### Sticky sessions
Sticky sessions are supported with both load balancers.
When sticky sessions are enabled, a cookie is set on the initial request.
@@ -438,6 +354,7 @@ The default cookie name is an abbreviation of a sha1 (ex: `_1d52e`).
On subsequent requests, the client will be directed to the backend stored in the cookie if it is still healthy.
If not, a new backend will be assigned.
```toml
[backends]
[backends.backend1]
@@ -461,14 +378,15 @@ The deprecated way:
sticky = true
```
#### Health Check
### Health Check
A health check can be configured in order to remove a backend from LB rotation as long as it keeps returning HTTP status codes other than `2xx` or `3xx` to HTTP GET requests periodically carried out by Traefik.
The check is defined by a path appended to the backend URL and an interval (given in a format understood by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration)) specifying how often the health check should be executed (the default being 30 seconds).
A health check can be configured in order to remove a backend from LB rotation as long as it keeps returning HTTP status codes other than `200 OK` to HTTP GET requests periodically carried out by Traefik.
The check is defined by a pathappended to the backend URL and an interval (given in a format understood by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration)) specifying how often the health check should be executed (the default being 30 seconds).
Each backend must respond to the health check within 5 seconds.
By default, the port of the backend server is used, however, this may be overridden.
A recovering backend returning `2xx` or `3xx` responses again is being returned to the LB rotation pool.
A recovering backend returning 200 OK responses again is being returned to the
LB rotation pool.
For example:
```toml
@@ -479,7 +397,7 @@ For example:
interval = "10s"
```
To use a different port for the health check:
To use a different port for the healthcheck:
```toml
[backends]
[backends.backend1]
@@ -489,43 +407,55 @@ To use a different port for the health check:
port = 8080
```
### Servers
Servers are simply defined using a `url`. You can also apply a custom `weight` to each server (this will be used by load-balancing).
!!! note
Paths in `url` are ignored. Use `Modifier` to specify paths instead.
Here is an example of backends and servers definition:
To use a different scheme for the health check:
```toml
[backends]
[backends.backend1]
[backends.backend1.healthcheck]
path = "/health"
interval = "10s"
scheme = "http"
[backends.backend1.circuitbreaker]
expression = "NetworkErrorRatio() > 0.5"
[backends.backend1.servers.server1]
url = "http://172.17.0.2:80"
weight = 10
[backends.backend1.servers.server2]
url = "http://172.17.0.3:80"
weight = 1
[backends.backend2]
[backends.backend2.LoadBalancer]
method = "drr"
[backends.backend2.servers.server1]
url = "http://172.17.0.4:80"
weight = 1
[backends.backend2.servers.server2]
url = "http://172.17.0.5:80"
weight = 2
```
Additional http headers and hostname to health check request can be specified, for instance:
```toml
[backends]
[backends.backend1]
[backends.backend1.healthcheck]
path = "/health"
interval = "10s"
hostname = "myhost.com"
port = 8080
[backends.backend1.healthcheck.headers]
My-Custom-Header = "foo"
My-Header = "bar"
```
- Two backends are defined: `backend1` and `backend2`
- `backend1` will forward the traffic to two servers: `http://172.17.0.2:80"` with weight `10` and `http://172.17.0.3:80` with weight `1` using default `wrr` load-balancing strategy.
- `backend2` will forward the traffic to two servers: `http://172.17.0.4:80"` with weight `1` and `http://172.17.0.5:80` with weight `2` using `drr` load-balancing strategy.
- a circuit breaker is added on `backend1` using the expression `NetworkErrorRatio() > 0.5`: watch error ratio over 10 second sliding window
## Configuration
Traefik's configuration has two parts:
Træfik's configuration has two parts:
- The [static Traefik configuration](/basics#static-traefik-configuration) which is loaded only at the beginning.
- The [dynamic Traefik configuration](/basics#dynamic-traefik-configuration) which can be hot-reloaded (no need to restart the process).
- The [static Træfik configuration](/basics#static-trfk-configuration) which is loaded only at the beginning.
- The [dynamic Træfik configuration](/basics#dynamic-trfk-configuration) which can be hot-reloaded (no need to restart the process).
### Static Traefik configuration
### Static Træfik configuration
The static configuration is the global configuration which is setting up connections to configuration backends and entrypoints.
Traefik can be configured using many configuration sources with the following precedence order.
Træfik can be configured using many configuration sources with the following precedence order.
Each item takes precedence over the item below it:
- [Key-value store](/basics/#key-value-stores)
@@ -535,13 +465,13 @@ Each item takes precedence over the item below it:
It means that arguments override configuration file, and key-value store overrides arguments.
!!! note
!!! note
the provider-enabling argument parameters (e.g., `--docker`) set all default values for the specific provider.
It must not be used if a configuration source with less precedence wants to set a non-default provider value.
#### Configuration file
By default, Traefik will try to find a `traefik.toml` in the following places:
By default, Træfik will try to find a `traefik.toml` in the following places:
- `/etc/traefik/`
- `$HOME/.traefik/`
@@ -567,7 +497,7 @@ Note that all default values will be displayed as well.
#### Key-value stores
Traefik supports several Key-value stores:
Træfik supports several Key-value stores:
- [Consul](https://consul.io)
- [etcd](https://coreos.com/etcd/)
@@ -576,18 +506,17 @@ Traefik supports several Key-value stores:
Please refer to the [User Guide Key-value store configuration](/user-guide/kv-config/) section to get documentation on it.
### Dynamic Traefik configuration
### Dynamic Træfik configuration
The dynamic configuration concerns :
- [Frontends](/basics/#frontends)
- [Backends](/basics/#backends)
- [Servers](/basics/#servers)
- HTTPS Certificates
Traefik can hot-reload those rules which could be provided by [multiple configuration backends](/configuration/commons).
Træfik can hot-reload those rules which could be provided by [multiple configuration backends](/configuration/commons).
We only need to enable `watch` option to make Traefik watch configuration backend changes and generate its configuration automatically.
We only need to enable `watch` option to make Træfik watch configuration backend changes and generate its configuration automatically.
Routes to services will be created and updated instantly at any changes.
Please refer to the [configuration backends](/configuration/commons) section to get documentation on it.
@@ -601,10 +530,10 @@ Usage:
traefik [command] [--flag=flag_argument]
```
List of Traefik available commands with description :
List of Træfik available commands with description :
- `version` : Print version
- `storeconfig` : Store the static Traefik configuration into a Key-value stores. Please refer to the [Store Traefik configuration](/user-guide/kv-config/#store-configuration-in-key-value-store) section to get documentation on it.
- `storeconfig` : Store the static Traefik configuration into a Key-value stores. Please refer to the [Store Træfik configuration](/user-guide/kv-config/#store-trfk-configuration) section to get documentation on it.
- `bug`: The easiest way to submit a pre-filled issue.
- `healthcheck`: Calls Traefik `/ping` to check health.
@@ -620,16 +549,11 @@ Each command is described at the beginning of the help section:
```bash
traefik --help
# or
docker run traefik[:version] --help
# ex: docker run traefik:1.5 --help
```
### Command: bug
Here is the easiest way to submit a pre-filled issue on [Traefik GitHub](https://github.com/containous/traefik).
Here is the easiest way to submit a pre-filled issue on [Træfik GitHub](https://github.com/containous/traefik).
```bash
traefik bug
@@ -644,7 +568,7 @@ This command allows to check the health of Traefik. Its exit status is `0` if Tr
This can be used with Docker [HEALTHCHECK](https://docs.docker.com/engine/reference/builder/#healthcheck) instruction or any other health check orchestration mechanism.
!!! note
The [`ping`](/configuration/ping) must be enabled to allow the `healthcheck` command to call `/ping`.
The [`web` provider](/configuration/backends/web) must be enabled to allow `/ping` calls by the `healthcheck` command.
```bash
traefik healthcheck
@@ -652,119 +576,3 @@ traefik healthcheck
```bash
OK: http://:8082/ping
```
## Collected Data
**This feature is disabled by default.**
You can read the public proposal on this topic [here](https://github.com/containous/traefik/issues/2369).
### Why ?
In order to help us learn more about how Traefik is being used and improve it, we collect anonymous usage statistics from running instances.
Those data help us prioritize our developments and focus on what's more important (for example, which configuration backend is used and which is not used).
### What ?
Once a day (the first call begins 10 minutes after the start of Traefik), we collect:
- the Traefik version
- a hash of the configuration
- an **anonymous version** of the static configuration:
- token, user name, password, URL, IP, domain, email, etc, are removed
!!! note
We do not collect the dynamic configuration (frontends & backends).
!!! note
We do not collect data behind the scenes to run advertising programs or to sell such data to third-party.
#### Here is an example
- Source configuration:
```toml
[entryPoints]
[entryPoints.http]
address = ":80"
[api]
[Docker]
endpoint = "tcp://10.10.10.10:2375"
domain = "foo.bir"
exposedByDefault = true
swarmMode = true
[Docker.TLS]
ca = "dockerCA"
cert = "dockerCert"
key = "dockerKey"
insecureSkipVerify = true
[ECS]
domain = "foo.bar"
exposedByDefault = true
clusters = ["foo-bar"]
region = "us-west-2"
accessKeyID = "AccessKeyID"
secretAccessKey = "SecretAccessKey"
```
- Obfuscated and anonymous configuration:
```toml
[entryPoints]
[entryPoints.http]
address = ":80"
[api]
[Docker]
endpoint = "xxxx"
domain = "xxxx"
exposedByDefault = true
swarmMode = true
[Docker.TLS]
ca = "xxxx"
cert = "xxxx"
key = "xxxx"
insecureSkipVerify = false
[ECS]
domain = "xxxx"
exposedByDefault = true
clusters = []
region = "us-west-2"
accessKeyID = "xxxx"
secretAccessKey = "xxxx"
```
### Show me the code !
If you want to dig into more details, here is the source code of the collecting system: [collector.go](https://github.com/containous/traefik/blob/master/collector/collector.go)
By default we anonymize all configuration fields, except fields tagged with `export=true`.
### How to enable this ?
You can enable the collecting system by:
- adding this line in the configuration TOML file:
```toml
# Send anonymous usage data
#
# Optional
# Default: false
#
sendAnonymousUsage = true
```
- adding this flag in the CLI:
```bash
./traefik --sendAnonymousUsage=true
```

View File

@@ -15,7 +15,7 @@ I used 4 VMs for the tests with the following configuration:
1. One VM used to launch the benchmarking tool [wrk](https://github.com/wg/wrk)
2. One VM for Traefik (v1.0.0-beta.416) / nginx (v1.4.6)
3. Two VMs for 2 backend servers in go [whoami](https://github.com/containous/whoami/)
3. Two VMs for 2 backend servers in go [whoami](https://github.com/emilevauge/whoamI/)
Each VM has been tuned using the following limits:
@@ -118,7 +118,7 @@ server {
Here is the `traefik.toml` file used:
```toml
maxIdleConnsPerHost = 100000
MaxIdleConnsPerHost = 100000
defaultEntryPoints = ["http"]
[entryPoints]

View File

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

View File

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

View File

@@ -1,13 +1,13 @@
# BoltDB Provider
# BoltDB Backend
Traefik can be configured to use BoltDB as a provider.
Træfik can be configured to use BoltDB as a backend configuration.
```toml
################################################################
# BoltDB Provider
# BoltDB configuration backend
################################################################
# Enable BoltDB Provider.
# Enable BoltDB configuration backend.
[boltdb]
# BoltDB file.
@@ -53,7 +53,7 @@ filename = "boltdb.tmpl"
# ca = "/etc/ssl/ca.crt"
# cert = "/etc/ssl/boltdb.crt"
# key = "/etc/ssl/boltdb.key"
# insecureSkipVerify = true
# insecureskipverify = true
```
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).

View File

@@ -1,13 +1,15 @@
# Consul Key-Value Provider
# Consul Backend
Traefik can be configured to use Consul as a provider.
## Consul Key-Value backend
Træfik can be configured to use Consul as a backend configuration.
```toml
################################################################
# Consul KV Provider
# Consul KV configuration backend
################################################################
# Enable Consul KV Provider.
# Enable Consul KV configuration backend.
[consul]
# Consul server endpoint.
@@ -53,9 +55,82 @@ prefix = "traefik"
# ca = "/etc/ssl/ca.crt"
# cert = "/etc/ssl/consul.crt"
# key = "/etc/ssl/consul.key"
# insecureSkipVerify = true
# insecureskipverify = true
```
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
Please refer to the [Key Value storage structure](/user-guide/kv-config/#key-value-storage-structure) section to get documentation on Traefik KV structure.
## Consul Catalog backend
Træfik can be configured to use service discovery catalog of Consul as a backend configuration.
```toml
################################################################
# Consul Catalog configuration backend
################################################################
# Enable Consul Catalog configuration backend.
[consulCatalog]
# Consul server endpoint.
#
# Required
# Default: "127.0.0.1:8500"
#
endpoint = "127.0.0.1:8500"
# Expose Consul catalog services by default in Traefik.
#
# Optional
# Default: true
#
exposedByDefault = false
# Prefix for Consul catalog tags.
#
# Optional
# Default: "traefik"
#
prefix = "traefik"
# Default frontEnd Rule for Consul services.
#
# The format is a Go Template with:
# - ".ServiceName", ".Domain" and ".Attributes" available
# - "getTag(name, tags, defaultValue)", "hasTag(name, tags)" and "getAttribute(name, tags, defaultValue)" functions are available
# - "getAttribute(...)" function uses prefixed tag names based on "prefix" value
#
# Optional
# Default: "Host:{{.ServiceName}}.{{.Domain}}"
#
#frontEndRule = "Host:{{.ServiceName}}.{{Domain}}"
```
This backend will create routes matching on hostname based on the service name used in Consul.
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
### Tags
Additional settings can be defined using Consul Catalog tags.
| Tag | Description |
|-----------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.enable=false` | Disable this container in Træfik |
| `traefik.protocol=https` | Override the default `http` protocol |
| `traefik.backend.weight=10` | Assign this weight to the container |
| `traefik.backend.circuitbreaker=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend, ex: `NetworkErrorRatio() > 0.` |
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend. Must be used in conjunction with the below label to take effect. |
| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by. Must be used in conjunction with the above label to take effect. |
| `traefik.frontend.rule=Host:test.traefik.io` | Override the default frontend rule (Default: `Host:{{.ServiceName}}.{{.Domain}}`). |
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
| `traefik.frontend.priority=10` | Override default frontend priority |
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`. Overrides `defaultEntryPoints`. |
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
| `traefik.backend.loadbalancer=drr` | override the default `wrr` load balancer algorithm |
| `traefik.backend.loadbalancer.stickiness=true` | enable backend sticky sessions |
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
| `traefik.backend.loadbalancer.sticky=true` | enable backend sticky sessions (DEPRECATED) |

View File

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

View File

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

View File

@@ -1,15 +1,15 @@
# DynamoDB Provider
# DynamoDB Backend
Traefik can be configured to use Amazon DynamoDB as a provider.
Træfik can be configured to use Amazon DynamoDB as a backend configuration.
## Configuration
```toml
################################################################
# DynamoDB Provider
# DynamoDB configuration backend
################################################################
# Enable DynamoDB Provider.
# Enable DynamoDB configuration backend.
[dynamodb]
# Region to use when connecting to AWS.
@@ -39,13 +39,13 @@ watch = true
#
refreshSeconds = 15
# Access Key ID to use when connecting to AWS.
# AccessKeyID to use when connecting to AWS.
#
# Optional
#
accessKeyID = "abc"
# Secret Access Key to use when connecting to AWS.
# SecretAccessKey to use when connecting to AWS.
#
# Optional
#
@@ -68,3 +68,4 @@ Items in the `dynamodb` table must have three attributes:
See `types/types.go` for details.
The presence or absence of this attribute determines its type.
So an item should never have both a `frontend` and a `backend` attribute.

View File

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

View File

@@ -1,13 +1,13 @@
# Etcd Provider
# Etcd Backend
Traefik can be configured to use Etcd as a provider.
Træfik can be configured to use Etcd as a backend configuration.
```toml
################################################################
# Etcd Provider
# Etcd configuration backend
################################################################
# Enable Etcd Provider.
# Enable Etcd configuration backend.
[etcd]
# Etcd server endpoint.
@@ -31,16 +31,6 @@ watch = true
#
prefix = "/traefik"
# Force to use API V3 (otherwise still use API V2)
#
# Deprecated
#
# Optional
# Default: false
#
useAPIV3 = true
# Override default configuration template.
# For advanced users :)
#
@@ -63,13 +53,9 @@ useAPIV3 = true
# ca = "/etc/ssl/ca.crt"
# cert = "/etc/ssl/etcd.crt"
# key = "/etc/ssl/etcd.key"
# insecureSkipVerify = true
# insecureskipverify = true
```
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
Please refer to the [Key Value storage structure](/user-guide/kv-config/#key-value-storage-structure) section to get documentation on Traefik KV structure.
!!! note
The option `useAPIV3` allows using Etcd API V3 only if it's set to true.
This option is **deprecated** and API V2 won't be supported in the future.

View File

@@ -1,13 +1,13 @@
# Eureka Provider
# Eureka Backend
Traefik can be configured to use Eureka as a provider.
Træfik can be configured to use Eureka as a backend configuration.
```toml
################################################################
# Eureka Provider
# Eureka configuration backend
################################################################
# Enable Eureka Provider.
# Enable Eureka configuration backend.
[eureka]
# Eureka server endpoint.
@@ -21,7 +21,7 @@ endpoint = "http://my.eureka.server/eureka"
# Optional
# Default: 30s
#
refreshSeconds = "1m"
delay = "1m"
# Override default configuration template.
# For advanced users :)

View File

@@ -1,210 +1,16 @@
# File Provider
# File Backends
Traefik can be configured with a file.
Like any other reverse proxy, Træfik can be configured with a file.
## Reference
You have three choices:
```toml
[file]
- [Simple](/configuration/backends/file/#simple)
- [Rules in a Separate File](/configuration/backends/file/#rules-in-a-separate-file)
- [Multiple `.toml` Files](/configuration/backends/file/#multiple-toml-files)
# Backends
[backends]
To enable the file backend, you must either pass the `--file` option to the Træfik binary or put the `[file]` section (with or without inner settings) in the configuration file.
[backends.backend1]
[backends.backend1.servers]
[backends.backend1.servers.server0]
url = "http://10.10.10.1:80"
weight = 1
[backends.backend1.servers.server1]
url = "http://10.10.10.2:80"
weight = 2
# ...
[backends.backend1.circuitBreaker]
expression = "NetworkErrorRatio() > 0.5"
[backends.backend1.responseForwarding]
flushInterval = "10ms"
[backends.backend1.loadBalancer]
method = "drr"
[backends.backend1.loadBalancer.stickiness]
cookieName = "foobar"
[backends.backend1.maxConn]
amount = 10
extractorfunc = "request.host"
[backends.backend1.healthCheck]
path = "/health"
port = 88
interval = "30s"
scheme = "http"
hostname = "myhost.com"
[backends.backend1.healthcheck.headers]
My-Custom-Header = "foo"
My-Header = "bar"
[backends.backend2]
# ...
# Frontends
[frontends]
[frontends.frontend1]
entryPoints = ["http", "https"]
backend = "backend1"
passHostHeader = true
priority = 42
# Use frontends.frontend1.auth.basic below instead
basicAuth = [
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
]
[frontends.frontend1.passTLSClientCert]
# Pass the escaped pem in a `X-Forwarded-Ssl-Client-Cert` header
pem = true
# Pass the escaped client cert infos selected below in a `X-Forwarded-Ssl-Client-Cert-Infos` header
# The unescaped header is like `Subject="C=%s,ST=%s,L=%s,O=%s,CN=%s",NB=%d,NA=%d,SAN=%s`
# It there is more than one certificates, their are separated by a `;`
[frontends.frontend-server.passTLSClientCert.infos]
notBefore = true
notAfter = true
[frontends.frontend-server.passTLSClientCert.infos.subject]
country = true
province = true
locality = true
organization = true
commonName = true
serialNumber = true
[frontends.frontend1.auth]
headerField = "X-WebAuth-User"
[frontends.frontend1.auth.basic]
removeHeader = true
users = [
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
]
usersFile = "/path/to/.htpasswd"
[frontends.frontend1.auth.digest]
removeHeader = true
users = [
"test:traefik:a2688e031edb4be6a3797f3882655c05",
"test2:traefik:518845800f9e2bfb1f1f740ec24f074e",
]
usersFile = "/path/to/.htdigest"
[frontends.frontend1.auth.forward]
address = "https://authserver.com/auth"
trustForwardHeader = true
authResponseHeaders = ["X-Auth-User"]
[frontends.frontend1.auth.forward.tls]
ca = "path/to/local.crt"
caOptional = true
cert = "path/to/foo.cert"
key = "path/to/foo.key"
insecureSkipVerify = true
[frontends.frontend1.whiteList]
sourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
useXForwardedFor = true
[frontends.frontend1.routes]
[frontends.frontend1.routes.route0]
rule = "Host:test.localhost"
[frontends.frontend1.routes.Route1]
rule = "Method:GET"
# ...
[frontends.frontend1.headers]
allowedHosts = ["foobar", "foobar"]
hostsProxyHeaders = ["foobar", "foobar"]
SSLRedirect = true
SSLTemporaryRedirect = true
SSLHost = "foobar"
STSSeconds = 42
STSIncludeSubdomains = true
STSPreload = true
forceSTSHeader = true
frameDeny = true
customFrameOptionsValue = "foobar"
contentTypeNosniff = true
browserXSSFilter = true
contentSecurityPolicy = "foobar"
publicKey = "foobar"
referrerPolicy = "foobar"
isDevelopment = true
[frontends.frontend1.headers.customRequestHeaders]
X-Foo-Bar-01 = "foobar"
X-Foo-Bar-02 = "foobar"
# ...
[frontends.frontend1.headers.customResponseHeaders]
X-Foo-Bar-03 = "foobar"
X-Foo-Bar-04 = "foobar"
# ...
[frontends.frontend1.headers.SSLProxyHeaders]
X-Foo-Bar-05 = "foobar"
X-Foo-Bar-06 = "foobar"
# ...
[frontends.frontend1.errors]
[frontends.frontend1.errors.errorPage0]
status = ["500-599"]
backend = "error"
query = "/{status}.html"
[frontends.frontend1.errors.errorPage1]
status = ["404", "403"]
backend = "error"
query = "/{status}.html"
# ...
[frontends.frontend1.ratelimit]
extractorfunc = "client.ip"
[frontends.frontend1.ratelimit.rateset.rateset1]
period = "10s"
average = 100
burst = 200
[frontends.frontend1.ratelimit.rateset.rateset2]
period = "3s"
average = 5
burst = 10
# ...
[frontends.frontend1.redirect]
entryPoint = "https"
regex = "^http://localhost/(.*)"
replacement = "http://mydomain/$1"
permanent = true
[frontends.frontend2]
# ...
# HTTPS certificates
[[tls]]
entryPoints = ["https"]
[tls.certificate]
certFile = "path/to/my.cert"
keyFile = "path/to/my.key"
[[tls]]
# ...
```
## Configuration Mode
You have two choices:
- [Rules in Traefik configuration file](/configuration/backends/file/#rules-in-traefik-configuration-file)
- [Rules in dedicated files](/configuration/backends/file/#rules-in-dedicated-files)
To enable the file backend, you must either pass the `--file` option to the Traefik binary or put the `[file]` section (with or without inner settings) in the configuration file.
The configuration file allows managing both backends/frontends and HTTPS certificates (which are not [Let's Encrypt](https://letsencrypt.org) certificates generated through Traefik).
TOML templating can be used if rules are not defined in the Traefik configuration file.
### Rules in Traefik Configuration File
## Simple
Add your configuration at the end of the global configuration file `traefik.toml`:
@@ -213,142 +19,152 @@ defaultEntryPoints = ["http", "https"]
[entryPoints]
[entryPoints.http]
# ...
address = ":80"
[entryPoints.http.redirect]
entryPoint = "https"
[entryPoints.https]
# ...
address = ":443"
[entryPoints.https.tls]
[[entryPoints.https.tls.certificates]]
CertFile = "integration/fixtures/https/snitest.com.cert"
KeyFile = "integration/fixtures/https/snitest.com.key"
[[entryPoints.https.tls.certificates]]
CertFile = "integration/fixtures/https/snitest.org.cert"
KeyFile = "integration/fixtures/https/snitest.org.key"
[file]
# rules
[backends]
[backends.backend1]
# ...
[backends.backend1.circuitbreaker]
expression = "NetworkErrorRatio() > 0.5"
[backends.backend1.servers.server1]
url = "http://172.17.0.2:80"
weight = 10
[backends.backend1.servers.server2]
url = "http://172.17.0.3:80"
weight = 1
[backends.backend2]
# ...
[backends.backend2.maxconn]
amount = 10
extractorfunc = "request.host"
[backends.backend2.LoadBalancer]
method = "drr"
[backends.backend2.servers.server1]
url = "http://172.17.0.4:80"
weight = 1
[backends.backend2.servers.server2]
url = "http://172.17.0.5:80"
weight = 2
[frontends]
[frontends.frontend1]
# ...
backend = "backend2"
[frontends.frontend1.routes.test_1]
rule = "Host:test.localhost"
[frontends.frontend2]
# ...
backend = "backend1"
passHostHeader = true
priority = 10
# restrict access to this frontend to the specified list of IPv4/IPv6 CIDR Nets
# an unset or empty list allows all Source-IPs to access
# if one of the Net-Specifications are invalid, the whole list is invalid
# and allows all Source-IPs to access.
whitelistSourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
entrypoints = ["https"] # overrides defaultEntryPoints
[frontends.frontend2.routes.test_1]
rule = "Host:{subdomain:[a-z]+}.localhost"
[frontends.frontend3]
# ...
# HTTPS certificate
[[tls]]
# ...
[[tls]]
# ...
entrypoints = ["http", "https"] # overrides defaultEntryPoints
backend = "backend2"
rule = "Path:/test"
```
!!! note
If `tls.entryPoints` is not defined, the certificate is attached to all the `defaultEntryPoints` with a TLS configuration.
## Rules in a Separate File
!!! note
Adding certificates directly to the entryPoint is still maintained but certificates declared in this way cannot be managed dynamically.
It's recommended to use the file provider to declare certificates.
!!! warning
TOML templating cannot be used if rules are defined in the Traefik configuration file.
### Rules in Dedicated Files
Traefik allows defining rules in one or more separate files.
#### One Separate File
You have to specify the file path in the `file.filename` option.
Put your rules in a separate file, for example `rules.toml`:
```toml
# traefik.toml
defaultEntryPoints = ["http", "https"]
[entryPoints]
[entryPoints.http]
# ...
address = ":80"
[entryPoints.http.redirect]
entryPoint = "https"
[entryPoints.https]
# ...
address = ":443"
[entryPoints.https.tls]
[[entryPoints.https.tls.certificates]]
CertFile = "integration/fixtures/https/snitest.com.cert"
KeyFile = "integration/fixtures/https/snitest.com.key"
[[entryPoints.https.tls.certificates]]
CertFile = "integration/fixtures/https/snitest.org.cert"
KeyFile = "integration/fixtures/https/snitest.org.key"
[file]
filename = "rules.toml"
watch = true
filename = "rules.toml"
```
The option `file.watch` allows Traefik to watch file changes automatically.
#### Multiple Separated Files
You could have multiple `.toml` files in a directory (and recursively in its sub-directories):
```toml
[file]
directory = "/path/to/config/"
watch = true
```
The option `file.watch` allows Traefik to watch file changes automatically.
#### Separate Files Content
If you are defining rules in one or more separate files, you can use two formats.
##### Simple Format
Backends, Frontends and TLS certificates are defined one at time, as described in the file `rules.toml`:
```toml
# rules.toml
[backends]
[backends.backend1]
# ...
[backends.backend1.circuitbreaker]
expression = "NetworkErrorRatio() > 0.5"
[backends.backend1.servers.server1]
url = "http://172.17.0.2:80"
weight = 10
[backends.backend1.servers.server2]
url = "http://172.17.0.3:80"
weight = 1
[backends.backend2]
# ...
[backends.backend2.maxconn]
amount = 10
extractorfunc = "request.host"
[backends.backend2.LoadBalancer]
method = "drr"
[backends.backend2.servers.server1]
url = "http://172.17.0.4:80"
weight = 1
[backends.backend2.servers.server2]
url = "http://172.17.0.5:80"
weight = 2
[frontends]
[frontends.frontend1]
# ...
backend = "backend2"
[frontends.frontend1.routes.test_1]
rule = "Host:test.localhost"
[frontends.frontend2]
# ...
backend = "backend1"
passHostHeader = true
priority = 10
entrypoints = ["https"] # overrides defaultEntryPoints
[frontends.frontend2.routes.test_1]
rule = "Host:{subdomain:[a-z]+}.localhost"
[frontends.frontend3]
# ...
# HTTPS certificate
[[tls]]
# ...
[[tls]]
# ...
entrypoints = ["http", "https"] # overrides defaultEntryPoints
backend = "backend2"
rule = "Path:/test"
```
##### TOML Templating
## Multiple `.toml` Files
!!! warning
TOML templating can only be used **if rules are defined in one or more separate files**.
Templating will not work in the Traefik configuration file.
Traefik allows using TOML templating.
Thus, it's possible to define easily lot of Backends, Frontends and TLS certificates as described in the file `template-rules.toml` :
You could have multiple `.toml` files in a directory:
```toml
# template-rules.toml
[backends]
{{ range $i, $e := until 100 }}
[backends.backend{{ $e }}]
#...
{{ end }}
[frontends]
{{ range $i, $e := until 100 }}
[frontends.frontend{{ $e }}]
#...
{{ end }}
# HTTPS certificate
{{ range $i, $e := until 100 }}
[[tls]]
#...
{{ end }}
[file]
directory = "/path/to/config/"
```
If you want Træfik to watch file changes automatically, just add:
```toml
[file]
watch = true
```

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,92 +0,0 @@
# Rest Provider
Traefik can be configured:
- using a RESTful api.
## Configuration
```toml
# Enable REST Provider.
[rest]
# Name of the related entry point
#
# Optional
# Default: "traefik"
#
entryPoint = "traefik"
```
## API
| Path | Method | Description |
|------------------------------|--------|-----------------|
| `/api/providers/web` | `PUT` | update provider |
| `/api/providers/rest` | `PUT` | update provider |
!!! warning
For compatibility reason, when you activate the rest provider, you can use `web` or `rest` as `provider` value.
```shell
curl -XPUT @file "http://localhost:8080/api/providers/rest"
```
with `@file`:
```json
{
"frontends": {
"frontend2": {
"routes": {
"test_2": {
"rule": "Path:/test"
}
},
"backend": "backend1"
},
"frontend1": {
"routes": {
"test_1": {
"rule": "Host:test.localhost"
}
},
"backend": "backend2"
}
},
"backends": {
"backend2": {
"loadBalancer": {
"method": "drr"
},
"servers": {
"server2": {
"weight": 2,
"URL": "http://172.17.0.5:80"
},
"server1": {
"weight": 1,
"url": "http://172.17.0.4:80"
}
}
},
"backend1": {
"loadBalancer": {
"method": "wrr"
},
"circuitBreaker": {
"expression": "NetworkErrorRatio() > 0.5"
},
"servers": {
"server2": {
"weight": 1,
"url": "http://172.17.0.3:80"
},
"server1": {
"weight": 10,
"url": "http://172.17.0.2:80"
}
}
}
}
}
```

View File

@@ -1,158 +0,0 @@
# Azure Service Fabric Provider
Traefik can be configured to use Azure Service Fabric as a provider.
See [this repository for an example deployment package and further documentation.](https://aka.ms/traefikonsf)
## Azure Service Fabric
```toml
################################################################
# Azure Service Fabric Provider
################################################################
# Enable Azure Service Fabric Provider
[serviceFabric]
# Azure Service Fabric Management Endpoint
#
# Required
#
clusterManagementUrl = "https://localhost:19080"
# Azure Service Fabric Management Endpoint API Version
#
# Required
# Default: "3.0"
#
apiVersion = "3.0"
# Azure Service Fabric Polling Interval (in seconds)
#
# Required
# Default: 10
#
refreshSeconds = 10
# Enable TLS connection.
#
# Optional
#
# [serviceFabric.tls]
# ca = "/etc/ssl/ca.crt"
# cert = "/etc/ssl/servicefabric.crt"
# key = "/etc/ssl/servicefabric.key"
# insecureSkipVerify = true
```
## Labels
The provider uses labels to configure how services are exposed through Traefik.
These can be set using Extensions and the Property Manager API
#### Extensions
Set labels with extensions through the services `ServiceManifest.xml` file.
Here is an example of an extension setting Traefik labels:
```xml
<StatelessServiceType ServiceTypeName="WebServiceType">
<Extensions>
<Extension Name="Traefik">
<Labels xmlns="http://schemas.microsoft.com/2015/03/fabact-no-schema">
<Label Key="traefik.frontend.rule.example2">PathPrefixStrip: /a/path/to/strip</Label>
<Label Key="traefik.enable">true</Label>
<Label Key="traefik.frontend.passHostHeader">true</Label>
</Labels>
</Extension>
</Extensions>
</StatelessServiceType>
```
#### Property Manager
Set Labels with the property manager API to overwrite and add labels, while your service is running.
Here is an example of adding a frontend rule using the property manager API.
```shell
curl -X PUT \
'http://localhost:19080/Names/GettingStartedApplication2/WebService/$/GetProperty?api-version=6.0&IncludeValues=true' \
-d '{
"PropertyName": "traefik.frontend.rule.default",
"Value": {
"Kind": "String",
"Data": "PathPrefixStrip: /a/path/to/strip"
},
"CustomTypeId": "LabelType"
}'
```
!!! note
This functionality will be released in a future version of the [sfctl](https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-application-lifecycle-sfctl) tool.
## Available Labels
Labels, set through extensions or the property manager, can be used on services to override default behavior.
| Label | Description |
|------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.enable=false` | Disable this container in Traefik |
| `traefik.backend.circuitbreaker.expression=EXPR` | Create a [circuit breaker](/basics/#backends) to be used against the backend |
| `traefik.servicefabric.groupname` | Group all services with the same name into a single backend in Traefik |
| `traefik.servicefabric.groupweight` | Set the weighting of the current services nodes in the backend group |
| `traefik.servicefabric.enablelabeloverrides` | Toggle whether labels can be overridden using the Service Fabric Property Manager API |
| `traefik.backend.healthcheck.path=/health` | Enable health check for the backend, hitting the container at `path`. |
| `traefik.backend.healthcheck.port=8080` | Allow to use a different port for the health check. |
| `traefik.backend.healthcheck.interval=1s` | Define the health check interval. |
| `traefik.backend.healthcheck.hostname=foobar.com` | Define the health check hostname. |
| `traefik.backend.healthcheck.headers=EXPR` | Define the health check request headers <br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.backend.loadbalancer.method=drr` | Override the default `wrr` load balancer algorithm |
| `traefik.backend.loadbalancer.stickiness=true` | Enable backend sticky sessions |
| `traefik.backend.loadbalancer.stickiness.cookieName=NAME` | Manually set the cookie name for sticky sessions |
| `traefik.backend.loadbalancer.sticky=true` | Enable backend sticky sessions (DEPRECATED) |
| `traefik.backend.maxconn.amount=10` | Set a maximum number of connections to the backend.<br>Must be used in conjunction with the below label to take effect. |
| `traefik.backend.maxconn.extractorfunc=client.ip` | Set the function to be used against the request to determine what to limit maximum connections to the backend by.<br>Must be used in conjunction with the above label to take effect. |
| `traefik.backend.weight=10` | Assign this weight to the container |
| `traefik.frontend.auth.basic=EXPR` | Sets basic authentication for that frontend in CSV format: `User:Hash,User:Hash` |
| `traefik.frontend.entryPoints=http,https` | Assign this frontend to entry points `http` and `https`.<br>Overrides `defaultEntryPoints` |
| `traefik.frontend.passHostHeader=true` | Forward client `Host` header to the backend. |
| `traefik.frontend.passTLSCert=true` | Forward TLS Client certificates to the backend. |
| `traefik.frontend.priority=10` | Override default frontend priority |
| `traefik.frontend.redirect.entryPoint=https` | Enables Redirect to another entryPoint for that frontend (e.g. HTTPS) |
| `traefik.frontend.redirect.regex=^http://localhost/(.*)` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.replacement`. |
| `traefik.frontend.redirect.replacement=http://mydomain/$1` | Redirect to another URL for that frontend.<br>Must be set with `traefik.frontend.redirect.regex`. |
| `traefik.frontend.redirect.permanent=true` | Return 301 instead of 302. |
| `traefik.frontend.rule=EXPR` | Override the default frontend rule. Defaults to SF address. |
| `traefik.frontend.whiteList.sourceRange=RANGE` | List of IP-Ranges which are allowed to access.<br>An unset or empty list allows all Source-IPs to access.<br>If one of the Net-Specifications are invalid, the whole list is invalid and allows all Source-IPs to access. |
| `traefik.frontend.whiteList.useXForwardedFor=true` | Use `X-Forwarded-For` header as valid source of IP for the white list. |
### Custom Headers
| Label | Description |
|-------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.frontend.headers.customRequestHeaders=EXPR ` | Provides the container with custom request headers that will be appended to each request forwarded to the container.<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.frontend.headers.customResponseHeaders=EXPR` | Appends the headers to each response returned by the container, before forwarding the response to the client.<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
### Security Headers
| Label | Description |
|----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `traefik.frontend.headers.allowedHosts=EXPR` | Provides a list of allowed hosts that requests will be processed.<br>Format: `Host1,Host2` |
| `traefik.frontend.headers.hostsProxyHeaders=EXPR ` | Provides a list of headers that the proxied hostname may be stored.<br>Format: `HEADER1,HEADER2` |
| `traefik.frontend.headers.SSLRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent. |
| `traefik.frontend.headers.SSLTemporaryRedirect=true` | Forces the frontend to redirect to SSL if a non-SSL request is sent, but by sending a 302 instead of a 301. |
| `traefik.frontend.headers.SSLHost=HOST` | This setting configures the hostname that redirects will be based on. Default is "", which is the same host as the request. |
| `traefik.frontend.headers.SSLProxyHeaders=EXPR` | Header combinations that would signify a proper SSL Request (Such as `X-Forwarded-For:https`).<br>Format: <code>HEADER:value&vert;&vert;HEADER2:value2</code> |
| `traefik.frontend.headers.STSSeconds=315360000` | Sets the max-age of the STS header. |
| `traefik.frontend.headers.STSIncludeSubdomains=true` | Adds the `IncludeSubdomains` section of the STS header. |
| `traefik.frontend.headers.STSPreload=true` | Adds the preload flag to the STS header. |
| `traefik.frontend.headers.forceSTSHeader=false` | Adds the STS header to non-SSL requests. |
| `traefik.frontend.headers.frameDeny=false` | Adds the `X-Frame-Options` header with the value of `DENY`. |
| `traefik.frontend.headers.customFrameOptionsValue=VALUE` | Overrides the `X-Frame-Options` header with the custom value. |
| `traefik.frontend.headers.contentTypeNosniff=true` | Adds the `X-Content-Type-Options` header with the value `nosniff`. |
| `traefik.frontend.headers.browserXSSFilter=true` | Adds the X-XSS-Protection header with the value `1; mode=block`. |
| `traefik.frontend.headers.customBrowserXSSValue=VALUE` | Set custom value for X-XSS-Protection header. This overrides the BrowserXssFilter option. |
| `traefik.frontend.headers.contentSecurityPolicy=VALUE` | Adds CSP Header with the custom value. |
| `traefik.frontend.headers.publicKey=VALUE` | Adds HPKP header. |
| `traefik.frontend.headers.referrerPolicy=VALUE` | Adds referrer policy header. |
| `traefik.frontend.headers.isDevelopment=false` | This will cause the `AllowedHosts`, `SSLRedirect`, and `STSSeconds`/`STSIncludeSubdomains` options to be ignored during development.<br>When deploying to production, be sure to set this to false. |

View File

@@ -1,9 +1,6 @@
# Web Provider
# Web Backend
!!! danger "DEPRECATED"
The web provider is deprecated, please use the [api](/configuration/api.md), the [ping](/configuration/ping.md), the [metrics](/configuration/metrics) and the [rest](/configuration/backends/rest.md) provider.
Traefik can be configured:
Træfik can be configured:
- using a RESTful api.
- to use a monitoring system (like Prometheus, DataDog or StatD, ...).
@@ -12,7 +9,7 @@ Traefik can be configured:
## Configuration
```toml
# Enable Web Provider.
# Enable web backend.
[web]
# Web administration port.
@@ -35,14 +32,6 @@ address = ":8080"
# Default: false
#
readOnly = true
# Set the root path for webui and API
#
# Deprecated
# Optional
#
# path = "/mypath"
#
```
## Web UI
@@ -54,13 +43,13 @@ readOnly = true
### Authentication
!!! note
The `/ping` path of the API is excluded from authentication (since 1.4).
The `/ping` path of the api is excluded from authentication (since 1.4).
#### Basic Authentication
Passwords can be encoded in MD5, SHA1 and BCrypt: you can use `htpasswd` to generate those ones.
Users can be specified directly in the TOML file, or indirectly by referencing an external file;
Users can be specified directly in the toml file, or indirectly by referencing an external file;
if both are provided, the two are merged, with external file contents having precedence.
```toml
@@ -79,7 +68,7 @@ usersFile = "/path/to/.htpasswd"
You can use `htdigest` to generate those ones.
Users can be specified directly in the TOML file, or indirectly by referencing an external file;
Users can be specified directly in the toml file, or indirectly by referencing an external file;
if both are provided, the two are merged, with external file contents having precedence
```toml
@@ -88,7 +77,7 @@ Users can be specified directly in the TOML file, or indirectly by referencing a
# To enable digest auth on the webui with 2 user/realm/pass: test:traefik:test and test2:traefik:test2
[web.auth.digest]
users = ["test:traefik:a2688e031edb4be6a3797f3882655c05", "test2:traefik:518845800f9e2bfb1f1f740ec24f074e"]
users = ["test:traefik:a2688e031edb4be6a3797f3882655c05 ", "test2:traefik:518845800f9e2bfb1f1f740ec24f074e"]
usersFile = "/path/to/.htdigest"
# ...
@@ -113,7 +102,7 @@ You can enable Traefik to export internal metrics to different monitoring system
# Optional
# Default: [0.1, 0.3, 1.2, 5]
buckets=[0.1,0.3,1.2,5.0]
# ...
```
@@ -169,52 +158,6 @@ pushinterval = "10s"
# ...
```
### InfluxDB
```toml
[web]
# ...
# InfluxDB metrics exporter type
[web.metrics.influxdb]
# InfluxDB's address.
#
# Required
# Default: "localhost:8089"
#
address = "localhost:8089"
# InfluxDB's address protocol (udp or http)
#
# Required
# Default: "udp"
#
protocol = "udp"
# InfluxDB push interval
#
# Optional
# Default: "10s"
#
pushinterval = "10s"
# InfluxDB database used when protocol is http
#
# Optional
# Default: ""
#
database = ""
# InfluxDB retention policy used when protocol is http
#
# Optional
# Default: ""
#
retentionpolicy = ""
# ...
```
## Statistics
@@ -239,9 +182,9 @@ recentErrors = 10
| Path | Method | Description |
|-----------------------------------------------------------------|:-------------:|----------------------------------------------------------------------------------------------------|
| `/` | `GET` | Provides a simple HTML frontend of Traefik |
| `/ping` | `GET`, `HEAD` | A simple endpoint to check for Traefik process liveness. Return a code `200` with the content: `OK` |
| `/health` | `GET` | JSON health metrics |
| `/` | `GET` | Provides a simple HTML frontend of Træfik |
| `/ping` | `GET`, `HEAD` | A simple endpoint to check for Træfik process liveness. Return a code `200` with the content: `OK` |
| `/health` | `GET` | json health metrics |
| `/api` | `GET` | Configuration for all providers |
| `/api/providers` | `GET` | Providers |
| `/api/providers/{provider}` | `GET`, `PUT` | Get or update provider |
@@ -264,7 +207,7 @@ curl -sv "http://localhost:8080/ping"
```
```shell
* Trying ::1...
* Connected to localhost (::1) port 8080 (\#0)
* Connected to localhost (::1) port 8080 (#0)
> GET /ping HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.43.0
@@ -275,7 +218,7 @@ curl -sv "http://localhost:8080/ping"
< Content-Length: 2
< Content-Type: text/plain; charset=utf-8
<
* Connection \#0 to host localhost left intact
* Connection #0 to host localhost left intact
OK
```
@@ -286,11 +229,11 @@ curl -s "http://localhost:8080/health" | jq .
```
```json
{
// Traefik PID
// Træfik PID
"pid": 2458,
// Traefik server uptime (formated time)
// Træfik server uptime (formated time)
"uptime": "39m6.885931127s",
// Traefik server uptime in seconds
// Træfik server uptime in seconds
"uptime_sec": 2346.885931127,
// current server date
"time": "2015-10-07 18:32:24.362238909 +0200 CEST",
@@ -300,7 +243,7 @@ curl -s "http://localhost:8080/health" | jq .
"status_code_count": {
"502": 1
},
// count HTTP response status code since Traefik started
// count HTTP response status code since Træfik started
"total_status_code_count": {
"200": 7,
"404": 21,
@@ -329,7 +272,7 @@ curl -s "http://localhost:8080/health" | jq .
"status": "Internal Server Error",
// request HTTP method
"method": "GET",
// request host name
// request hostname
"host": "localhost",
// request path
"path": "/path",
@@ -404,100 +347,3 @@ curl -s "http://localhost:8080/api" | jq .
}
}
```
### Deprecation compatibility
#### Address
As the web provider is deprecated, you can handle the `Address` option like this:
```toml
defaultEntryPoints = ["http"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.foo]
address = ":8082"
[entryPoints.bar]
address = ":8083"
[ping]
entryPoint = "foo"
[api]
entryPoint = "bar"
```
In the above example, you would access a regular path, administration panel, and health-check as follows:
* Regular path: `http://hostname:80/path`
* Admin Panel: `http://hostname:8083/`
* Ping URL: `http://hostname:8082/ping`
In the above example, it is _very_ important to create a named dedicated entry point, and do **not** include it in `defaultEntryPoints`.
Otherwise, you are likely to expose _all_ services via that entry point.
#### Path
As the web provider is deprecated, you can handle the `Path` option like this:
```toml
defaultEntryPoints = ["http"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.foo]
address = ":8080"
[entryPoints.bar]
address = ":8081"
# Activate API and Dashboard
[api]
entryPoint = "bar"
dashboard = true
[file]
[backends]
[backends.backend1]
[backends.backend1.servers.server1]
url = "http://127.0.0.1:8081"
[frontends]
[frontends.frontend1]
entryPoints = ["foo"]
backend = "backend1"
[frontends.frontend1.routes.test_1]
rule = "PathPrefixStrip:/yourprefix;PathPrefix:/yourprefix"
```
#### Authentication
As the web provider is deprecated, you can handle the `auth` option like this:
```toml
defaultEntryPoints = ["http"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.foo]
address=":8080"
[entryPoints.foo.auth]
[entryPoints.foo.auth.basic]
users = [
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
]
[api]
entrypoint="foo"
```
For more information, see [entry points](/configuration/entrypoints/) .

View File

@@ -1,13 +1,13 @@
# Zookeeper Provider
# Zookeeper Backend
Traefik can be configured to use Zookeeper as a provider.
Træfik can be configured to use Zookeeper as a backend configuration.
```toml
################################################################
# Zookeeper Provider
# Zookeeper configuration backend
################################################################
# Enable Zookeeper Provider.
# Enable Zookeeperconfiguration backend.
[zookeeper]
# Zookeeper server endpoint.
@@ -27,9 +27,9 @@ watch = true
# Prefix used for KV store.
#
# Optional
# Default: "traefik"
# Default: "/traefik"
#
prefix = "traefik"
prefix = "/traefik"
# Override default configuration template.
# For advanced users :)
@@ -53,9 +53,9 @@ prefix = "traefik"
# ca = "/etc/ssl/ca.crt"
# cert = "/etc/ssl/zookeeper.crt"
# key = "/etc/ssl/zookeeper.key"
# insecureSkipVerify = true
# insecureskipverify = true
```
To enable constraints see [provider-specific constraints section](/configuration/commons/#provider-specific).
To enable constraints see [backend-specific constraints section](/configuration/commons/#backend-specific).
Please refer to the [Key Value storage structure](/user-guide/kv-config/#key-value-storage-structure) section to get documentation on Traefik KV structure.

View File

@@ -3,23 +3,14 @@
## Main Section
```toml
# DEPRECATED - for general usage instruction see [lifeCycle.graceTimeOut].
#
# If both the deprecated option and the new one are given, the deprecated one
# takes precedence.
# A value of zero is equivalent to omitting the parameter, causing
# [lifeCycle.graceTimeOut] to be effective. Pass zero to the new option in
# order to disable the grace period.
# Duration to give active requests a chance to finish before Traefik stops.
#
# Optional
# Default: "0s"
# Default: "10s"
#
# graceTimeOut = "10s"
# Enable debug mode.
# This will install HTTP handlers to expose Go expvars under /debug/vars and
# pprof profiling data under /debug/pprof/.
# The log level will be set to DEBUG unless `logLevel` is specified.
#
# Optional
# Default: false
@@ -33,26 +24,19 @@
#
# checkNewVersion = false
# Tells traefik whether it should keep the trailing slashes in the paths (e.g. /paths/) or redirect to the no trailing slash paths instead (/paths).
#
# Optional
# Default: false
#
# keepTrailingSlash = false
# Providers throttle duration.
# Backends throttle duration.
#
# Optional
# Default: "2s"
#
# providersThrottleDuration = "2s"
# ProvidersThrottleDuration = "2s"
# Controls the maximum idle (keep-alive) connections to keep per-host.
#
# Optional
# Default: 200
#
# maxIdleConnsPerHost = 200
# MaxIdleConnsPerHost = 200
# If set to true invalid SSL certificates are accepted for backends.
# This disables detection of man-in-the-middle attacks so should only be used on secure backend networks.
@@ -60,14 +44,14 @@
# Optional
# Default: false
#
# insecureSkipVerify = true
# InsecureSkipVerify = true
# Register Certificates in the rootCA.
# Register Certificates in the RootCA.
#
# Optional
# Default: []
#
# rootCAs = [ "/mycert.cert" ]
# RootCAs = [ "/mycert.cert" ]
# Entrypoints to be used by frontends that do not specify any entrypoint.
# Each frontend can specify its own entrypoints.
@@ -76,15 +60,6 @@
# Default: ["http"]
#
# defaultEntryPoints = ["http", "https"]
# Allow the use of 0 as server weight.
# - false: a weight 0 means internally a weight of 1.
# - true: a weight 0 means internally a weight of 0 (a server with a weight of 0 is removed from the available servers).
#
# Optional
# Default: false
#
# AllowMinWeightZero = true
```
- `graceTimeOut`: Duration to give active requests a chance to finish before Traefik stops.
@@ -92,49 +67,30 @@ Can be provided in a format supported by [time.ParseDuration](https://golang.org
If no units are provided, the value is parsed assuming seconds.
**Note:** in this time frame no new requests are accepted.
- `providersThrottleDuration`: Providers throttle duration: minimum duration in seconds between 2 events from providers before applying a new configuration.
- `ProvidersThrottleDuration`: Backends throttle duration: minimum duration in seconds between 2 events from providers before applying a new configuration.
It avoids unnecessary reloads if multiples events are sent in a short amount of time.
Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw values (digits).
If no units are provided, the value is parsed assuming seconds.
- `maxIdleConnsPerHost`: Controls the maximum idle (keep-alive) connections to keep per-host.
- `MaxIdleConnsPerHost`: Controls the maximum idle (keep-alive) connections to keep per-host.
If zero, `DefaultMaxIdleConnsPerHost` from the Go standard library net/http module is used.
If you encounter 'too many open files' errors, you can either increase this value or change the `ulimit`.
- `insecureSkipVerify` : If set to true invalid SSL certificates are accepted for backends.
- `InsecureSkipVerify` : If set to true invalid SSL certificates are accepted for backends.
**Note:** This disables detection of man-in-the-middle attacks so should only be used on secure backend networks.
- `rootCAs`: Register Certificates in the RootCA. This certificates will be use for backends calls.
- `RootCAs`: Register Certificates in the RootCA. This certificates will be use for backends calls.
**Note** You can use file path or cert content directly
- `defaultEntryPoints`: Entrypoints to be used by frontends that do not specify any entrypoint.
Each frontend can specify its own entrypoints.
- `keepTrailingSlash`: Tells Træfik whether it should keep the trailing slashes that might be present in the paths of incoming requests (true), or if it should redirect to the slashless version of the URL (default behavior: false)
!!! note
Beware that the value of `keepTrailingSlash` can have a significant impact on the way your frontend rules are interpreted.
The table below tries to sum up several behaviors depending on requests/configurations.
The current default behavior is deprecated and kept for compatibility reasons.
As a consequence, we encourage you to set `keepTrailingSlash` to true.
| Incoming request | keepTrailingSlash | Path:{value} | Behavior
|----------------------|-------------------|--------------|----------------------------|
| http://foo.com/path/ | false | Path:/path/ | Proceeds with the request |
| http://foo.com/path/ | false | Path:/path | 301 to http://foo.com/path |
| http://foo.com/path | false | Path:/path/ | Proceeds with the request |
| http://foo.com/path | false | Path:/path | Proceeds with the request |
| http://foo.com/path/ | true | Path:/path/ | Proceeds with the request |
| http://foo.com/path/ | true | Path:/path | 404 |
| http://foo.com/path | true | Path:/path/ | 404 |
| http://foo.com/path | true | Path:/path | Proceeds with the request |
## Constraints
In a micro-service architecture, with a central service discovery, setting constraints limits Traefik scope to a smaller number of routes.
In a micro-service architecture, with a central service discovery, setting constraints limits Træfik scope to a smaller number of routes.
Traefik filters services according to service attributes/tags set in your providers.
Træfik filters services according to service attributes/tags set in your configuration backends.
Supported filters:
@@ -162,15 +118,14 @@ constraints = ["tag==us-*"]
constraints = ["tag!=us-*", "tag!=asia-*"]
```
### provider-specific
### Backend-specific
Supported Providers:
Supported backends:
- Docker
- Consul K/V
- BoltDB
- Zookeeper
- ECS
- Etcd
- Consul Catalog
- Rancher
@@ -178,18 +133,79 @@ Supported Providers:
- Kubernetes (using a provider-specific mechanism based on label selectors)
```toml
# Provider-specific constraint
# Backend-specific constraint
[consulCatalog]
# ...
constraints = ["tag==api"]
# Provider-specific constraint
# Backend-specific constraint
[marathon]
# ...
constraints = ["tag==api", "tag!=v*-beta"]
```
## Logs Definition
### Traefik logs
```toml
# Traefik logs file
# If not defined, logs to stdout
traefikLogsFile = "log/traefik.log"
# Log level
#
# Optional
# Default: "ERROR"
#
# Accepted values, in order of severity: "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "PANIC"
# Messages at and above the selected level will be logged.
#
logLevel = "ERROR"
```
### Access Logs
Access logs are written when `[accessLog]` is defined.
By default it will write to stdout and produce logs in the textual Common Log Format (CLF), extended with additional fields.
To enable access logs using the default settings just add the `[accessLog]` entry.
```toml
[accessLog]
```
To write the logs into a logfile specify the `filePath`.
```toml
[accessLog]
filePath = "/path/to/access.log"
```
To write JSON format logs, specify `json` as the format:
```toml
[accessLog]
filePath = "/path/to/access.log"
format = "json"
```
Deprecated way (before 1.4):
```toml
# Access logs file
#
# DEPRECATED - see [accessLog] lower down
#
accessLogsFile = "log/access.log"
```
### Log Rotation
Traefik will close and reopen its log files, assuming they're configured, on receipt of a USR1 signal.
This allows the logs to be rotated and processed by an external program, such as `logrotate`.
!!! note
This does not work on Windows due to the lack of USR signals.
## Custom Error pages
Custom error pages can be returned, in lieu of the default, according to frontend-configured ranges of HTTP Status codes.
@@ -226,52 +242,9 @@ Instead, the query parameter can also be set to some generic error page like so:
Now the `500s.html` error page is returned for the configured code range.
The configured status code ranges are inclusive; that is, in the above example, the `500s.html` page will be returned for status codes `500` through, and including, `599`.
Custom error pages are easiest to implement using the file provider.
For dynamic providers, the corresponding template file needs to be customized accordingly and referenced in the Traefik configuration.
## Rate limiting
Rate limiting can be configured per frontend.
Multiple sets of rates can be added to each frontend, but the time periods must be unique.
```toml
[frontends]
[frontends.frontend1]
# ...
[frontends.frontend1.ratelimit]
extractorfunc = "client.ip"
[frontends.frontend1.ratelimit.rateset.rateset1]
period = "10s"
average = 100
burst = 200
[frontends.frontend1.ratelimit.rateset.rateset2]
period = "3s"
average = 5
burst = 10
```
In the above example, frontend1 is configured to limit requests by the client's ip address.
An average of 5 requests every 3 seconds is allowed and an average of 100 requests every 10 seconds.
These can "burst" up to 10 and 200 in each period respectively.
## Buffering
In some cases request/buffering can be enabled for a specific backend.
By enabling this, Traefik will read the entire request into memory (possibly buffering large requests into disk) and will reject requests that are over a specified limit.
This may help services deal with large data (multipart/form-data for example) more efficiently and should minimise time spent when sending data to a backend server.
For more information please check [oxy/buffer](http://godoc.org/github.com/vulcand/oxy/buffer) documentation.
Example configuration:
```toml
[backends]
[backends.backend1]
[backends.backend1.buffering]
maxRequestBodyBytes = 10485760
memRequestBodyBytes = 2097152
maxResponseBodyBytes = 10485760
memResponseBodyBytes = 2097152
retryExpression = "IsNetworkError() && Attempts() <= 2"
```
## Retry Configuration
@@ -308,38 +281,6 @@ Given provider-specific support, the value may be overridden on a per-backend ba
Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw values (digits).
If no units are provided, the value is parsed assuming seconds.
## Life Cycle
Controls the behavior of Traefik during the shutdown phase.
```toml
[lifeCycle]
# Duration to keep accepting requests prior to initiating the graceful
# termination period (as defined by the `graceTimeOut` option). This
# option is meant to give downstream load-balancers sufficient time to
# take Traefik out of rotation.
# Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw values (digits).
# If no units are provided, the value is parsed assuming seconds.
# The zero duration disables the request accepting grace period, i.e.,
# Traefik will immediately proceed to the grace period.
#
# Optional
# Default: 0
#
# requestAcceptGraceTimeout = "10s"
# Duration to give active requests a chance to finish before Traefik stops.
# Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw values (digits).
# If no units are provided, the value is parsed assuming seconds.
# Note: in this time frame no new requests are accepted.
#
# Optional
# Default: "10s"
#
# graceTimeOut = "10s"
```
## Timeouts
### Responding Timeouts
@@ -422,70 +363,38 @@ If no units are provided, the value is parsed assuming seconds.
### Idle Timeout (deprecated)
Use [respondingTimeouts](/configuration/commons/#responding-timeouts) instead of `idleTimeout`.
Use [respondingTimeouts](/configuration/commons/#responding-timeouts) instead of `IdleTimeout`.
In the case both settings are configured, the deprecated option will be overwritten.
`idleTimeout` is the maximum amount of time an idle (keep-alive) connection will remain idle before closing itself.
`IdleTimeout` is the maximum amount of time an idle (keep-alive) connection will remain idle before closing itself.
This is set to enforce closing of stale client connections.
Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw values (digits).
If no units are provided, the value is parsed assuming seconds.
```toml
# idleTimeout
# IdleTimeout
#
# DEPRECATED - see [respondingTimeouts] section.
#
# Optional
# Default: "180s"
#
idleTimeout = "360s"
IdleTimeout = "360s"
```
## Host Resolver
`hostResolver` are used for request host matching process.
```toml
[hostResolver]
# cnameFlattening is a trigger to flatten request host, assuming it is a CNAME record
#
# Optional
# Default : false
#
cnameFlattening = true
# resolvConf is dns resolving configuration file, the default is /etc/resolv.conf
#
# Optional
# Default : "/etc/resolv.conf"
#
# resolvConf = "/etc/resolv.conf"
# resolvDepth is the maximum CNAME recursive lookup
#
# Optional
# Default : 5
#
# resolvDepth = 5
```
- To allow serving secure https request and generate the SSL using ACME while `cnameFlattening` is active.
The `acme` configuration for `HTTP-01` challenge and `onDemand` is mandatory.
Refer to [ACME configuration](/configuration/acme) for more information.
## Override Default Configuration Template
!!! warning
For advanced users only.
Supported by all providers except: File Provider, Web Provider and DynamoDB Provider.
Supported by all backends except: File backend, Web backend and DynamoDB backend.
```toml
[provider_name]
[backend_name]
# Override default provider configuration template. For advanced users :)
# Override default configuration template. For advanced users :)
#
# Optional
# Default: ""

View File

@@ -1,155 +1,5 @@
# Entry Points Definition
## Reference
### TOML
```toml
defaultEntryPoints = ["http", "https"]
# ...
# ...
[entryPoints]
[entryPoints.http]
address = ":80"
compress = true
[entryPoints.http.whitelist]
sourceRange = ["10.42.0.0/16", "152.89.1.33/32", "afed:be44::/16"]
useXForwardedFor = true
[entryPoints.http.tls]
minVersion = "VersionTLS12"
cipherSuites = [
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_RSA_WITH_AES_256_GCM_SHA384"
]
[[entryPoints.http.tls.certificates]]
certFile = "path/to/my.cert"
keyFile = "path/to/my.key"
[[entryPoints.http.tls.certificates]]
certFile = "path/to/other.cert"
keyFile = "path/to/other.key"
# ...
[entryPoints.http.tls.clientCA]
files = ["path/to/ca1.crt", "path/to/ca2.crt"]
optional = false
[entryPoints.http.redirect]
entryPoint = "https"
regex = "^http://localhost/(.*)"
replacement = "http://mydomain/$1"
permanent = true
[entryPoints.http.auth]
headerField = "X-WebAuth-User"
[entryPoints.http.auth.basic]
removeHeader = true
users = [
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
]
usersFile = "/path/to/.htpasswd"
[entryPoints.http.auth.digest]
removeHeader = true
users = [
"test:traefik:a2688e031edb4be6a3797f3882655c05",
"test2:traefik:518845800f9e2bfb1f1f740ec24f074e",
]
usersFile = "/path/to/.htdigest"
[entryPoints.http.auth.forward]
address = "https://authserver.com/auth"
trustForwardHeader = true
authResponseHeaders = ["X-Auth-User"]
[entryPoints.http.auth.forward.tls]
ca = "path/to/local.crt"
caOptional = true
cert = "path/to/foo.cert"
key = "path/to/foo.key"
insecureSkipVerify = true
[entryPoints.http.proxyProtocol]
insecure = true
trustedIPs = ["10.10.10.1", "10.10.10.2"]
[entryPoints.http.forwardedHeaders]
trustedIPs = ["10.10.10.1", "10.10.10.2"]
[entryPoints.https]
# ...
```
### CLI
For more information about the CLI, see the documentation about [Traefik command](/basics/#traefik).
```shell
--entryPoints='Name:http Address::80'
--entryPoints='Name:https Address::443 TLS'
```
!!! note
Whitespace is used as option separator and `,` is used as value separator for the list.
The names of the options are case-insensitive.
In compose file the entrypoint syntax is different:
```yaml
traefik:
image: traefik
command:
- --defaultentrypoints=powpow
- "--entryPoints=Name:powpow Address::42 Compress:true"
```
or
```yaml
traefik:
image: traefik
command: --defaultentrypoints=powpow --entryPoints='Name:powpow Address::42 Compress:true'
```
#### All available options:
```ini
Name:foo
Address::80
TLS:/my/path/foo.cert,/my/path/foo.key;/my/path/goo.cert,/my/path/goo.key;/my/path/hoo.cert,/my/path/hoo.key
TLS
TLS.MinVersion:VersionTLS11
TLS.CipherSuites:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA384
TLS.SniStrict:true
TLS.DefaultCertificate.Cert:path/to/foo.cert
TLS.DefaultCertificate.Key:path/to/foo.key
CA:car
CA.Optional:true
Redirect.EntryPoint:https
Redirect.Regex:http://localhost/(.*)
Redirect.Replacement:http://mydomain/$1
Redirect.Permanent:true
Compress:true
WhiteList.SourceRange:10.42.0.0/16,152.89.1.33/32,afed:be44::/16
WhiteList.UseXForwardedFor:true
ProxyProtocol.TrustedIPs:192.168.0.1
ProxyProtocol.Insecure:true
ForwardedHeaders.TrustedIPs:10.0.0.3/24,20.0.0.3/24
Auth.Basic.Users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/,test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0
Auth.Basic.Removeheader:true
Auth.Digest.Users:test:traefik:a2688e031edb4be6a3797f3882655c05,test2:traefik:518845800f9e2bfb1f1f740ec24f074e
Auth.Digest.Removeheader:true
Auth.HeaderField:X-WebAuth-User
Auth.Forward.Address:https://authserver.com/auth
Auth.Forward.AuthResponseHeaders:X-Auth,X-Test,X-Secret
Auth.Forward.TrustForwardHeader:true
Auth.Forward.TLS.CA:path/to/local.crt
Auth.Forward.TLS.CAOptional:true
Auth.Forward.TLS.Cert:path/to/foo.cert
Auth.Forward.TLS.Key:path/to/foo.key
Auth.Forward.TLS.InsecureSkipVerify:true
```
## Basic
```toml
# Entrypoints definition
#
@@ -177,16 +27,13 @@ To redirect an http entrypoint to an https entrypoint (with SNI support).
address = ":443"
[entryPoints.https.tls]
[[entryPoints.https.tls.certificates]]
certFile = "integration/fixtures/https/snitest.com.cert"
keyFile = "integration/fixtures/https/snitest.com.key"
CertFile = "integration/fixtures/https/snitest.com.cert"
KeyFile = "integration/fixtures/https/snitest.com.key"
[[entryPoints.https.tls.certificates]]
certFile = "integration/fixtures/https/snitest.org.cert"
keyFile = "integration/fixtures/https/snitest.org.key"
CertFile = "integration/fixtures/https/snitest.org.cert"
KeyFile = "integration/fixtures/https/snitest.org.key"
```
!!! note
Please note that `regex` and `replacement` do not have to be set in the `redirect` structure if an entrypoint is defined for the redirection (they will not be used in this case).
## Rewriting URL
To redirect an entrypoint rewriting the URL.
@@ -200,47 +47,13 @@ To redirect an entrypoint rewriting the URL.
replacement = "http://mydomain/$1"
```
!!! note
Please note that `regex` and `replacement` do not have to be set in the `redirect` structure if an `entrypoint` is defined for the redirection (they will not be used in this case).
Care should be taken when defining replacement expand variables: `$1x` is equivalent to `${1x}`, not `${1}x` (see [Regexp.Expand](https://golang.org/pkg/regexp/#Regexp.Expand)), so use `${1}` syntax.
Regular expressions and replacements can be tested using online tools such as [Go Playground](https://play.golang.org/p/mWU9p-wk2ru) or the [Regex101](https://regex101.com/r/58sIgx/2).
## TLS
### Static Certificates
Define an entrypoint with SNI support.
```toml
[entryPoints]
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]
[[entryPoints.https.tls.certificates]]
certFile = "integration/fixtures/https/snitest.com.cert"
keyFile = "integration/fixtures/https/snitest.com.key"
```
!!! note
If an empty TLS configuration is provided, default self-signed certificates are generated.
### Dynamic Certificates
If you need to add or remove TLS certificates while Traefik is started, Dynamic TLS certificates are supported using the [file provider](/configuration/backends/file).
## TLS Mutual Authentication
TLS Mutual Authentication can be `optional` or not.
If it's `optional`, Traefik will authorize connection with certificates not signed by a specified Certificate Authority (CA).
Otherwise, Traefik will only accept clients that present a certificate signed by a specified Certificate Authority (CA).
Only accept clients that present a certificate signed by a specified Certificate Authority (CA).
`ClientCAFiles` can be configured with multiple `CA:s` in the same file or use multiple files containing one or several `CA:s`.
The `CA:s` has to be in PEM format.
By default, `ClientCAFiles` is not optional, all clients will be required to present a valid cert.
All clients will be required to present a valid cert.
The requirement will apply to all server certs in the entrypoint.
In the example below both `snitest.com` and `snitest.org` will require client certs
@@ -250,28 +63,23 @@ In the example below both `snitest.com` and `snitest.org` will require client ce
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]
[entryPoints.https.tls.ClientCA]
files = ["tests/clientca1.crt", "tests/clientca2.crt"]
optional = false
ClientCAFiles = ["tests/clientca1.crt", "tests/clientca2.crt"]
[[entryPoints.https.tls.certificates]]
certFile = "integration/fixtures/https/snitest.com.cert"
keyFile = "integration/fixtures/https/snitest.com.key"
CertFile = "integration/fixtures/https/snitest.com.cert"
KeyFile = "integration/fixtures/https/snitest.com.key"
[[entryPoints.https.tls.certificates]]
certFile = "integration/fixtures/https/snitest.org.cert"
keyFile = "integration/fixtures/https/snitest.org.key"
CertFile = "integration/fixtures/https/snitest.org.cert"
KeyFile = "integration/fixtures/https/snitest.org.key"
```
!!! note
The deprecated argument `ClientCAFiles` allows adding Client CA files which are mandatory.
If this parameter exists, the new ones are not checked.
## Authentication
### Basic Authentication
Passwords can be encoded in MD5, SHA1 and BCrypt: you can use `htpasswd` to generate them.
Passwords can be encoded in MD5, SHA1 and BCrypt: you can use `htpasswd` to generate those ones.
Users can be specified directly in the TOML file, or indirectly by referencing an external file;
Users can be specified directly in the toml file, or indirectly by referencing an external file;
if both are provided, the two are merged, with external file contents having precedence.
```toml
@@ -284,37 +92,11 @@ Users can be specified directly in the TOML file, or indirectly by referencing a
usersFile = "/path/to/.htpasswd"
```
Optionally, you can:
- pass authenticated user to application via headers
```toml
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.http.auth]
headerField = "X-WebAuth-User" # <-- header for the authenticated user
[entryPoints.http.auth.basic]
users = ["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"]
```
- remove the Authorization header
```toml
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.http.auth]
[entryPoints.http.auth.basic]
removeHeader = true # <-- remove the Authorization header
users = ["test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"]
```
### Digest Authentication
You can use `htdigest` to generate them.
You can use `htdigest` to generate those ones.
Users can be specified directly in the TOML file, or indirectly by referencing an external file;
Users can be specified directly in the toml file, or indirectly by referencing an external file;
if both are provided, the two are merged, with external file contents having precedence
```toml
@@ -322,43 +104,17 @@ Users can be specified directly in the TOML file, or indirectly by referencing a
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.http.auth.digest]
users = ["test:traefik:a2688e031edb4be6a3797f3882655c05", "test2:traefik:518845800f9e2bfb1f1f740ec24f074e"]
[entryPoints.http.auth.basic]
users = ["test:traefik:a2688e031edb4be6a3797f3882655c05 ", "test2:traefik:518845800f9e2bfb1f1f740ec24f074e"]
usersFile = "/path/to/.htdigest"
```
Optionally, you can!
- pass authenticated user to application via headers.
```toml
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.http.auth]
headerField = "X-WebAuth-User" # <-- header for the authenticated user
[entryPoints.http.auth.digest]
users = ["test:traefik:a2688e031edb4be6a3797f3882655c05", "test2:traefik:518845800f9e2bfb1f1f740ec24f074e"]
```
- remove the Authorization header.
```toml
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.http.auth]
[entryPoints.http.auth.digest]
removeHeader = true # <-- remove the Authorization header
users = ["test:traefik:a2688e031edb4be6a3797f3882655c05", "test2:traefik:518845800f9e2bfb1f1f740ec24f074e"]
```
### Forward Authentication
This configuration will first forward the request to `http://authserver.com/auth`.
If the response code is 2XX, access is granted and the original request is performed.
Otherwise, the response from the authentication server is returned.
Otherwise, the response from the auth server is returned.
```toml
[entryPoints]
@@ -367,7 +123,7 @@ Otherwise, the response from the authentication server is returned.
# To enable forward auth on an entrypoint
[entryPoints.http.auth.forward]
address = "https://authserver.com/auth"
# Trust existing X-Forwarded-* headers.
# Useful with another reverse proxy in front of Traefik.
#
@@ -375,27 +131,19 @@ Otherwise, the response from the authentication server is returned.
# Default: false
#
trustForwardHeader = true
# Copy headers from the authentication server to the request.
# Enable forward auth TLS connection.
#
# Optional
#
authResponseHeaders = ["X-Auth-User", "X-Secret"]
# Enable forward auth TLS connection.
#
# Optional
#
[entryPoints.http.auth.forward.tls]
ca = "path/to/local.crt"
caOptional = true
cert = "path/to/foo.cert"
key = "path/to/foo.key"
[entryPoints.http.auth.forward.tls]
cert = "authserver.crt"
key = "authserver.key"
```
## Specify Minimum TLS Version
To specify an https entry point with a minimum TLS version, and specifying an array of cipher suites (from [crypto/tls](https://godoc.org/crypto/tls#pkg-constants)).
To specify an https entry point with a minimum TLS version, and specifying an array of cipher suites (from crypto/tls).
```toml
[entryPoints]
@@ -403,10 +151,7 @@ To specify an https entry point with a minimum TLS version, and specifying an ar
address = ":443"
[entryPoints.https.tls]
minVersion = "VersionTLS12"
cipherSuites = [
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_RSA_WITH_AES_256_GCM_SHA384"
]
cipherSuites = ["TLS_RSA_WITH_AES_256_GCM_SHA384"]
[[entryPoints.https.tls.certificates]]
certFile = "integration/fixtures/https/snitest.com.cert"
keyFile = "integration/fixtures/https/snitest.com.key"
@@ -415,40 +160,6 @@ To specify an https entry point with a minimum TLS version, and specifying an ar
keyFile = "integration/fixtures/https/snitest.org.key"
```
## Strict SNI Checking
To enable strict SNI checking, so that connections cannot be made if a matching certificate does not exist.
```toml
[entryPoints]
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]
sniStrict = true
[[entryPoints.https.tls.certificates]]
certFile = "integration/fixtures/https/snitest.com.cert"
keyFile = "integration/fixtures/https/snitest.com.key"
```
## Default Certificate
To enable a default certificate to serve, so that connections without SNI or without a matching domain will be served this certificate.
```toml
[entryPoints]
[entryPoints.https]
address = ":443"
[entryPoints.https.tls]
[entryPoints.https.tls.defaultCertificate]
certFile = "integration/fixtures/https/snitest.com.cert"
keyFile = "integration/fixtures/https/snitest.com.key"
```
!!! note
There can only be one `defaultCertificate` set per entrypoint.
Use a single set of square brackets `[ ]`, instead of the two needed for normal certificates.
If no default certificate is provided, a self-signed certificate will be generated by Traefik, and used instead.
## Compression
To enable compression support using gzip format.
@@ -466,18 +177,15 @@ Responses are compressed when:
* And the `Accept-Encoding` request header contains `gzip`
* And the response is not already compressed, i.e. the `Content-Encoding` response header is not already set.
## White Listing
## Whitelisting
To enable IP white listing at the entry point level.
To enable IP whitelisting at the entrypoint level.
```toml
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.http.whiteList]
sourceRange = ["127.0.0.1/32", "192.168.1.7"]
# useXForwardedFor = true
address = ":80"
whiteListSourceRange = ["127.0.0.1/32", "192.168.1.7"]
```
## ProxyProtocol
@@ -486,8 +194,8 @@ To enable [ProxyProtocol](https://www.haproxy.org/download/1.8/doc/proxy-protoco
Only IPs in `trustedIPs` will lead to remote client address replacement: you should declare your load-balancer IP or CIDR range here (in testing environment, you can trust everyone using `insecure = true`).
!!! danger
When queuing Traefik behind another load-balancer, be sure to carefully configure Proxy Protocol on both sides.
Otherwise, it could introduce a security risk in your system by forging requests.
When queuing Træfik behind another load-balancer, be sure to carefully configure Proxy Protocol on both sides.
Otherwise, it could introduce a security risk in your system by forging requests.
```toml
[entryPoints]

View File

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

View File

@@ -1,129 +0,0 @@
# Metrics Definition
## Prometheus
```toml
# Metrics definition
[metrics]
#...
# To enable Traefik to export internal metrics to Prometheus
[metrics.prometheus]
# Name of the related entry point
#
# Optional
# Default: "traefik"
#
entryPoint = "traefik"
# Buckets for latency metrics
#
# Optional
# Default: [0.1, 0.3, 1.2, 5]
#
buckets = [0.1,0.3,1.2,5.0]
# ...
```
## DataDog
```toml
# Metrics definition
[metrics]
#...
# DataDog metrics exporter type
[metrics.datadog]
# DataDog's address.
#
# Required
# Default: "localhost:8125"
#
address = "localhost:8125"
# DataDog push interval
#
# Optional
# Default: "10s"
#
pushInterval = "10s"
# ...
```
## StatsD
```toml
# Metrics definition
[metrics]
#...
# StatsD metrics exporter type
[metrics.statsd]
# StatD's address.
#
# Required
# Default: "localhost:8125"
#
address = "localhost:8125"
# StatD push interval
#
# Optional
# Default: "10s"
#
pushInterval = "10s"
# ...
```
## InfluxDB
```toml
[metrics]
# ...
# InfluxDB metrics exporter type
[metrics.influxdb]
# InfluxDB's address.
#
# Required
# Default: "localhost:8089"
#
address = "localhost:8089"
# InfluxDB's address protocol (udp or http)
#
# Required
# Default: "udp"
#
protocol = "udp"
# InfluxDB push interval
#
# Optional
# Default: "10s"
#
pushinterval = "10s"
# InfluxDB database used when protocol is http
#
# Optional
# Default: ""
#
database = ""
# InfluxDB retention policy used when protocol is http
#
# Optional
# Default: ""
#
retentionpolicy = ""
# ...
```

View File

@@ -1,91 +0,0 @@
# Ping Definition
## Configuration
```toml
# Ping definition
[ping]
# Name of the related entry point
#
# Optional
# Default: "traefik"
#
entryPoint = "traefik"
```
| Path | Method | Description |
|---------|---------------|----------------------------------------------------------------------------------------------------|
| `/ping` | `GET`, `HEAD` | A simple endpoint to check for Traefik process liveness. Return a code `200` with the content: `OK` |
!!! warning
Even if you have authentication configured on entry point, the `/ping` path of the api is excluded from authentication.
## Examples
The `/ping` health-check URL is enabled with the command-line `--ping` or config file option `[ping]`.
Thus, if you have a regular path for `/foo` and an entrypoint on `:80`, you would access them as follows:
* Regular path: `http://hostname:80/foo`
* Dashboard: `http://hostname:8080/`
* Ping URL: `http://hostname:8080/ping`
However, for security reasons, you may want to be able to expose the `/ping` health-check URL to outside health-checkers, e.g. an Internet service or cloud load-balancer, _without_ exposing your dashboard's port.
In many environments, the security staff may not _allow_ you to expose it.
You have two options:
* Enable `/ping` on a regular entry point
* Enable `/ping` on a dedicated port
### Ping health check on a regular entry point
To proxy `/ping` from a regular entry point to the administration one without exposing the dashboard, do the following:
```toml
defaultEntryPoints = ["http"]
[entryPoints]
[entryPoints.http]
address = ":80"
[ping]
entryPoint = "http"
```
The above link `ping` on the `http` entry point and then expose it on port `80`
### Enable ping health check on dedicated port
If you do not want to or cannot expose the health-check on a regular entry point - e.g. your security rules do not allow it, or you have a conflicting path - then you can enable health-check on its own entry point.
Use the following configuration:
```toml
defaultEntryPoints = ["http"]
[entryPoints]
[entryPoints.http]
address = ":80"
[entryPoints.ping]
address = ":8082"
[ping]
entryPoint = "ping"
```
The above is similar to the previous example, but instead of enabling `/ping` on the _default_ entry point, we enable it on a _dedicated_ entry point.
In the above example, you would access a regular path and health-check as follows:
* Regular path: `http://hostname:80/foo`
* Ping URL: `http://hostname:8082/ping`
Note the dedicated port `:8082` for `/ping`.
In the above example, it is _very_ important to create a named dedicated entry point, and do **not** include it in `defaultEntryPoints`.
Otherwise, you are likely to expose _all_ services via this entry point.
### Using ping for external Load-balancer rotation health check
If you are running traefik behind a external Load-balancer, and want to configure rotation health check on the Load-balancer to take a traefik instance out of rotation gracefully, you can configure [lifecycle.requestAcceptGraceTimeout](/configuration/commons.md#life-cycle) and the ping endpoint will return `503` response on traefik server termination, so that the Load-balancer can take the terminating traefik instance out of rotation, before it stops responding.

View File

@@ -1,159 +0,0 @@
# Tracing
The tracing system allows developers to visualize call flows in their infrastructure.
We use [OpenTracing](http://opentracing.io). It is an open standard designed for distributed tracing.
Traefik supports three tracing backends: Jaeger, Zipkin and DataDog.
## Jaeger
```toml
# Tracing definition
[tracing]
# Backend name used to send tracing data
#
# Default: "jaeger"
#
backend = "jaeger"
# Service name used in Jaeger backend
#
# Default: "traefik"
#
serviceName = "traefik"
# Span name limit allows for name truncation in case of very long Frontend/Backend names
# This can prevent certain tracing providers to drop traces that exceed their length limits
#
# Default: 0 - no truncation will occur
#
spanNameLimit = 0
[tracing.jaeger]
# Sampling Server URL is the address of jaeger-agent's HTTP sampling server
#
# Default: "http://localhost:5778/sampling"
#
samplingServerURL = "http://localhost:5778/sampling"
# Sampling Type specifies the type of the sampler: const, probabilistic, rateLimiting
#
# Default: "const"
#
samplingType = "const"
# Sampling Param is a value passed to the sampler.
# Valid values for Param field are:
# - for "const" sampler, 0 or 1 for always false/true respectively
# - for "probabilistic" sampler, a probability between 0 and 1
# - for "rateLimiting" sampler, the number of spans per second
#
# Default: 1.0
#
samplingParam = 1.0
# Local Agent Host Port instructs reporter to send spans to jaeger-agent at this address
#
# Default: "127.0.0.1:6831"
#
localAgentHostPort = "127.0.0.1:6831"
```
!!! warning
Traefik is only able to send data over compact thrift protocol to the [Jaeger agent](https://www.jaegertracing.io/docs/deployment/#agent).
## Zipkin
```toml
# Tracing definition
[tracing]
# Backend name used to send tracing data
#
# Default: "jaeger"
#
backend = "zipkin"
# Service name used in Zipkin backend
#
# Default: "traefik"
#
serviceName = "traefik"
# Span name limit allows for name truncation in case of very long Frontend/Backend names
# This can prevent certain tracing providers to drop traces that exceed their length limits
#
# Default: 0 - no truncation will occur
#
spanNameLimit = 150
[tracing.zipkin]
# Zipking HTTP endpoint used to send data
#
# Default: "http://localhost:9411/api/v1/spans"
#
httpEndpoint = "http://localhost:9411/api/v1/spans"
# Enable Zipkin debug
#
# Default: false
#
debug = false
# Use ZipKin SameSpan RPC style traces
#
# Default: false
#
sameSpan = false
# Use ZipKin 128 bit root span IDs
#
# Default: true
#
id128Bit = true
```
## DataDog
```toml
# Tracing definition
[tracing]
# Backend name used to send tracing data
#
# Default: "jaeger"
#
backend = "datadog"
# Service name used in DataDog backend
#
# Default: "traefik"
#
serviceName = "traefik"
# Span name limit allows for name truncation in case of very long Frontend/Backend names
# This can prevent certain tracing providers to drop traces that exceed their length limits
#
# Default: 0 - no truncation will occur
#
spanNameLimit = 100
[tracing.datadog]
# Local Agent Host Port instructs reporter to send spans to datadog-tracing-agent at this address
#
# Default: "127.0.0.1:8126"
#
localAgentHostPort = "127.0.0.1:8126"
# Enable DataDog debug
#
# Default: false
#
debug = false
# Apply shared tag in a form of Key:Value to all the traces
#
# Default: ""
#
globalTag = ""
```

BIN
docs/img/apollo-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

BIN
docs/img/asteris.logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

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