forked from SW/traefik
Compare commits
65 Commits
v2.9.0-rc3
...
v2.9.6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d97d3a6726 | ||
|
|
a8df674dcf | ||
|
|
abd569701f | ||
|
|
7e3fe48b80 | ||
|
|
778188ed34 | ||
|
|
88603810a8 | ||
|
|
c7647b4938 | ||
|
|
af71443b61 | ||
|
|
18d66d7432 | ||
|
|
7c72780820 | ||
|
|
68e8eb2435 | ||
|
|
81a5b1b4c8 | ||
|
|
52e6ce95cf | ||
|
|
d547718fdd | ||
|
|
af4e74c39d | ||
|
|
f6b7940b76 | ||
|
|
f1b91a119d | ||
|
|
35d8281f4d | ||
|
|
00de5c711a | ||
|
|
b935c80dbd | ||
|
|
22c6630412 | ||
|
|
b2c4221429 | ||
|
|
97de552e06 | ||
|
|
454f552691 | ||
|
|
7258048403 | ||
|
|
15f7472091 | ||
|
|
a041a6b198 | ||
|
|
7582da9650 | ||
|
|
ccbbd0d766 | ||
|
|
93212125e3 | ||
|
|
be3b798dd6 | ||
|
|
8128d6ca26 | ||
|
|
14ab1514dc | ||
|
|
40242294d8 | ||
|
|
996eccf5b7 | ||
|
|
e9de061b84 | ||
|
|
0ca1c8aac3 | ||
|
|
2c550c284d | ||
|
|
87815586be | ||
|
|
09d6383621 | ||
|
|
39b0077725 | ||
|
|
e2a9caf760 | ||
|
|
bc79796c38 | ||
|
|
b1db81d8ac | ||
|
|
ae7db879d9 | ||
|
|
dd34905ea9 | ||
|
|
3812e6f3cb | ||
|
|
627175694d | ||
|
|
82cf6c9577 | ||
|
|
63a1186d3e | ||
|
|
f75f636e27 | ||
|
|
615dc7fd35 | ||
|
|
52b6b057f0 | ||
|
|
7b3faef4b3 | ||
|
|
7758880f3f | ||
|
|
d04903edb2 | ||
|
|
a63d5c95a8 | ||
|
|
bb66950197 | ||
|
|
c4cc30ccc6 | ||
|
|
9cd54baca4 | ||
|
|
7ac687a0a9 | ||
|
|
83ae1021f6 | ||
|
|
67e3bc6380 | ||
|
|
4b291b2cf8 | ||
|
|
89870ad539 |
8
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
8
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -6,12 +6,14 @@ body:
|
||||
attributes:
|
||||
label: Welcome!
|
||||
description: |
|
||||
The issue tracker is for reporting bugs and feature requests only. For end-user related support questions, please refer to one of the following:
|
||||
- the Traefik community forum: https://community.traefik.io/
|
||||
The issue tracker is for reporting bugs and feature requests only.
|
||||
For end-user related support questions, please use the [Traefik community forum](https://community.traefik.io/).
|
||||
|
||||
The configurations between 1.X and 2.X are NOT compatible. Please have a look [here](https://doc.traefik.io/traefik/getting-started/configuration-overview/).
|
||||
All new/updated issues are triaged regularly by the maintainers.
|
||||
All issues closed by a bot are subsequently double-checked by the maintainers.
|
||||
|
||||
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
|
||||
|
||||
options:
|
||||
- label: Yes, I've searched similar issues on [GitHub](https://github.com/traefik/traefik/issues) and didn't find any.
|
||||
required: true
|
||||
|
||||
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -3,11 +3,11 @@ PLEASE READ THIS MESSAGE.
|
||||
|
||||
Documentation fixes or enhancements:
|
||||
- for Traefik v1: use branch v1.7
|
||||
- for Traefik v2: use branch v2.8
|
||||
- for Traefik v2: use branch v2.9
|
||||
|
||||
Bug fixes:
|
||||
- for Traefik v1: use branch v1.7
|
||||
- for Traefik v2: use branch v2.8
|
||||
- for Traefik v2: use branch v2.9
|
||||
|
||||
Enhancements:
|
||||
- for Traefik v1: we only accept bug fixes
|
||||
|
||||
2
.github/workflows/build.yaml
vendored
2
.github/workflows/build.yaml
vendored
@@ -56,7 +56,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
|
||||
2
.github/workflows/test-unit.yaml
vendored
2
.github/workflows/test-unit.yaml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
|
||||
10
.github/workflows/validate.yaml
vendored
10
.github/workflows/validate.yaml
vendored
@@ -7,8 +7,8 @@ on:
|
||||
|
||||
env:
|
||||
GO_VERSION: 1.19
|
||||
GOLANGCI_LINT_VERSION: v1.49.0
|
||||
MISSSPELL_VERSION: v0.3.4
|
||||
GOLANGCI_LINT_VERSION: v1.50.0
|
||||
MISSSPELL_VERSION: v0.4.0
|
||||
IN_DOCKER: ""
|
||||
|
||||
jobs:
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCI_LINT_VERSION}
|
||||
|
||||
- name: Install missspell ${{ env.MISSSPELL_VERSION }}
|
||||
run: curl -sfL https://raw.githubusercontent.com/client9/misspell/master/install-misspell.sh | sh -s -- -b $(go env GOPATH)/bin ${MISSSPELL_VERSION}
|
||||
run: curl -sfL https://raw.githubusercontent.com/golangci/misspell/master/install-misspell.sh | sh -s -- -b $(go env GOPATH)/bin ${MISSSPELL_VERSION}
|
||||
|
||||
- name: Avoid generating webui
|
||||
run: touch webui/static/index.html
|
||||
@@ -73,7 +73,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
|
||||
246
.golangci.toml
246
.golangci.toml
@@ -1,246 +0,0 @@
|
||||
[run]
|
||||
timeout = "10m"
|
||||
skip-files = []
|
||||
skip-dirs = [
|
||||
"pkg/provider/kubernetes/crd/generated/",
|
||||
]
|
||||
|
||||
[linters-settings]
|
||||
|
||||
[linters-settings.govet]
|
||||
check-shadowing = false
|
||||
|
||||
[linters-settings.golint]
|
||||
min-confidence = 0.0
|
||||
|
||||
[linters-settings.gocyclo]
|
||||
min-complexity = 14.0
|
||||
|
||||
[linters-settings.goconst]
|
||||
min-len = 3.0
|
||||
min-occurrences = 4.0
|
||||
|
||||
[linters-settings.misspell]
|
||||
locale = "US"
|
||||
|
||||
[linters-settings.funlen]
|
||||
lines = 230 # default 60
|
||||
statements = 120 # default 40
|
||||
|
||||
[linters-settings.forbidigo]
|
||||
forbid = [
|
||||
'^print(ln)?$',
|
||||
'^spew\.Print(f|ln)?$',
|
||||
'^spew\.Dump$',
|
||||
]
|
||||
|
||||
[linters-settings.depguard]
|
||||
list-type = "blacklist"
|
||||
include-go-root = false
|
||||
packages = ["github.com/pkg/errors"]
|
||||
|
||||
[linters-settings.godox]
|
||||
keywords = ["FIXME"]
|
||||
|
||||
[linters-settings.importas]
|
||||
corev1 = "k8s.io/api/core/v1"
|
||||
networkingv1beta1 = "k8s.io/api/networking/v1beta1"
|
||||
extensionsv1beta1 = "k8s.io/api/extensions/v1beta1"
|
||||
metav1 = "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
kubeerror = "k8s.io/apimachinery/pkg/api/errors"
|
||||
composeapi = "github.com/docker/compose/v2/pkg/api"
|
||||
|
||||
[linters-settings.revive]
|
||||
[[linters-settings.revive.rules]]
|
||||
name = "struct-tag"
|
||||
[[linters-settings.rules]]
|
||||
name = "blank-imports"
|
||||
[[linters-settings.rules]]
|
||||
name = "context-as-argument"
|
||||
[[linters-settings.rules]]
|
||||
name = "context-keys-type"
|
||||
[[linters-settings.rules]]
|
||||
name = "dot-imports"
|
||||
[[linters-settings.rules]]
|
||||
name = "error-return"
|
||||
[[linters-settings.rules]]
|
||||
name = "error-strings"
|
||||
[[linters-settings.rules]]
|
||||
name = "error-naming"
|
||||
[[linters-settings.rules]]
|
||||
name = "exported"
|
||||
[[linters-settings.rules]]
|
||||
name = "if-return"
|
||||
[[linters-settings.rules]]
|
||||
name = "increment-decrement"
|
||||
[[linters-settings.rules]]
|
||||
name = "var-naming"
|
||||
[[linters-settings.rules]]
|
||||
name = "var-declaration"
|
||||
[[linters-settings.rules]]
|
||||
name = "package-comments"
|
||||
[[linters-settings.rules]]
|
||||
name = "range"
|
||||
[[linters-settings.rules]]
|
||||
name = "receiver-naming"
|
||||
[[linters-settings.rules]]
|
||||
name = "time-naming"
|
||||
[[linters-settings.rules]]
|
||||
name = "unexported-return"
|
||||
[[linters-settings.rules]]
|
||||
name = "indent-error-flow"
|
||||
[[linters-settings.rules]]
|
||||
name = "errorf"
|
||||
[[linters-settings.rules]]
|
||||
name = "empty-block"
|
||||
[[linters-settings.rules]]
|
||||
name = "superfluous-else"
|
||||
[[linters-settings.rules]]
|
||||
name = "unused-parameter"
|
||||
[[linters-settings.rules]]
|
||||
name = "unreachable-code"
|
||||
[[linters-settings.rules]]
|
||||
name = "redefines-builtin-id"
|
||||
|
||||
[linters-settings.gomoddirectives]
|
||||
replace-allow-list = [
|
||||
"github.com/abbot/go-http-auth",
|
||||
"github.com/go-check/check",
|
||||
"github.com/gorilla/mux",
|
||||
"github.com/mailgun/minheap",
|
||||
"github.com/mailgun/multibuf",
|
||||
"github.com/jaguilar/vt100",
|
||||
]
|
||||
|
||||
[linters]
|
||||
enable-all = true
|
||||
disable = [
|
||||
"scopelint", # Deprecated
|
||||
"interfacer", # Deprecated
|
||||
"maligned", # Deprecated
|
||||
"golint", # Deprecated
|
||||
"exhaustivestruct",# Deprecated
|
||||
"nosnakecase", # Deprecated
|
||||
"ifshort", # Deprecated
|
||||
"structcheck", # Deprecated
|
||||
"varcheck", # Deprecated
|
||||
"deadcode", # Deprecated
|
||||
"execinquery", # Not relevant (SQL)
|
||||
"sqlclosecheck", # Not relevant (SQL)
|
||||
"rowserrcheck", # Not relevant (SQL)
|
||||
"lll", # Not relevant
|
||||
"gocyclo", # FIXME must be fixed
|
||||
"cyclop", # Duplicate of gocyclo
|
||||
"gocognit", # Too strict
|
||||
"nestif", # Too many false-positive.
|
||||
"prealloc", # Too many false-positive.
|
||||
"makezero", # Not relevant
|
||||
"dupl", # Too strict
|
||||
"gosec", # Too strict
|
||||
"gochecknoinits",
|
||||
"gochecknoglobals",
|
||||
"wsl", # Too strict
|
||||
"nlreturn", # Not relevant
|
||||
"gomnd", # Too strict
|
||||
"stylecheck", # skip because report issues related to some generated files.
|
||||
"testpackage", # Too strict
|
||||
"tparallel", # Not relevant
|
||||
"paralleltest", # Not relevant
|
||||
"exhaustive", # Not relevant
|
||||
"exhaustruct", # duplicate of exhaustivestruct
|
||||
"goerr113", # Too strict
|
||||
"wrapcheck", # Too strict
|
||||
"noctx", # Too strict
|
||||
"bodyclose", # Too many false-positive and panics.
|
||||
"forcetypeassert", # Too strict
|
||||
"tagliatelle", # Not compatible with current tags.
|
||||
"varnamelen", # not relevant
|
||||
"nilnil", # not relevant
|
||||
"ireturn", # not relevant
|
||||
"contextcheck", # too many false-positive
|
||||
"containedctx", # too many false-positive
|
||||
"maintidx", # kind of duplicate of gocyclo
|
||||
"nonamedreturns", # not relevant
|
||||
]
|
||||
|
||||
[issues]
|
||||
exclude-use-default = false
|
||||
max-per-linter = 0
|
||||
max-same-issues = 0
|
||||
exclude = [
|
||||
"Error return value of .((os\\.)?std(out|err)\\..*|.*Close|.*Flush|os\\.Remove(All)?|.*printf?|os\\.(Un)?Setenv). is not checked",
|
||||
"should have a package comment, unless it's in another file for this package",
|
||||
"SA1019: http.CloseNotifier has been deprecated", # FIXME must be fixed
|
||||
"SA1019: cfg.SSLRedirect is deprecated",
|
||||
"SA1019: cfg.SSLTemporaryRedirect is deprecated",
|
||||
"SA1019: cfg.SSLHost is deprecated",
|
||||
"SA1019: cfg.SSLForceHost is deprecated",
|
||||
"SA1019: cfg.FeaturePolicy is deprecated",
|
||||
"SA1019: c.Providers.ConsulCatalog.Namespace is deprecated",
|
||||
"SA1019: c.Providers.Consul.Namespace is deprecated",
|
||||
]
|
||||
[[issues.exclude-rules]]
|
||||
path = "(.+)_test.go"
|
||||
linters = ["goconst", "funlen", "godot", "nosnakecase"]
|
||||
[[issues.exclude-rules]]
|
||||
path = "(.+)_test.go"
|
||||
text = " always receives "
|
||||
linters = [ "unparam" ]
|
||||
[[issues.exclude-rules]]
|
||||
path = "integration/.+_test.go"
|
||||
text = "Error return value of `cmd\\.Process\\.Kill` is not checked"
|
||||
[[issues.exclude-rules]]
|
||||
path = "integration/(consul_catalog_test|constraint_test).go"
|
||||
text = "Error return value of `(s.deregisterService|s.deregisterAgentService)` is not checked"
|
||||
[[issues.exclude-rules]]
|
||||
path = "integration/grpc_test.go"
|
||||
text = "Error return value of `closer` is not checked"
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/h2c/h2c.go"
|
||||
text = "Error return value of `rw.Write` is not checked"
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/provider/docker/builder_test.go"
|
||||
text = "(U1000: func )?`(.+)` is unused"
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/provider/kubernetes/builder_(endpoint|service)_test.go"
|
||||
text = "(U1000: func )?`(.+)` is unused"
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/server/service/bufferpool.go"
|
||||
text = "SA6002: argument should be pointer-like to avoid allocations"
|
||||
[[issues.exclude-rules]]
|
||||
path = "cmd/configuration.go"
|
||||
text = "string `traefik` has (\\d) occurrences, make it a constant"
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/server/middleware/middlewares.go"
|
||||
text = "Function 'buildConstructor' has too many statements"
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/tracing/haystack/logger.go"
|
||||
linters = ["goprintffuncname"]
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/tracing/tracing.go"
|
||||
text = "printf-like formatting function 'SetErrorWithEvent' should be named 'SetErrorWithEventf'"
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/log/deprecated.go"
|
||||
linters = ["godot"]
|
||||
[[issues.exclude-rules]]
|
||||
path = "(.+)\\.go"
|
||||
text = "struct-tag: unknown option 'inline' in JSON tag"
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/server/router/tcp/manager.go"
|
||||
text = "Function 'buildEntryPointHandler' is too long (.+)"
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/tls/tlsmanager_test.go"
|
||||
text = "SA1019: config.ClientCAs.Subjects has been deprecated since Go 1.18"
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/types/tls_test.go"
|
||||
text = "SA1019: tlsConfig.RootCAs.Subjects has been deprecated since Go 1.18"
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/provider/kubernetes/(crd|gateway)/client.go"
|
||||
linters = ["interfacebloat"]
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/metrics/metrics.go"
|
||||
linters = ["interfacebloat"]
|
||||
[[issues.exclude-rules]]
|
||||
path = "pkg/provider/acme/provider.go"
|
||||
text = "\\(\\*Provider\\)\\.resolveCertificate - result 0 \\(\\*github.com/go-acme/lego/v4/certificate.Resource\\) is never used"
|
||||
|
||||
189
.golangci.yml
Normal file
189
.golangci.yml
Normal file
@@ -0,0 +1,189 @@
|
||||
run:
|
||||
timeout: 10m
|
||||
skip-files: []
|
||||
skip-dirs:
|
||||
- pkg/provider/kubernetes/crd/generated/
|
||||
|
||||
linters-settings:
|
||||
govet:
|
||||
check-shadowing: false
|
||||
golint:
|
||||
min-confidence: 0
|
||||
gocyclo:
|
||||
min-complexity: 14
|
||||
goconst:
|
||||
min-len: 3
|
||||
min-occurrences: 4
|
||||
misspell:
|
||||
locale: US
|
||||
funlen:
|
||||
lines: -1
|
||||
statements: 120
|
||||
forbidigo:
|
||||
forbid:
|
||||
- ^print(ln)?$
|
||||
- ^spew\.Print(f|ln)?$
|
||||
- ^spew\.Dump$
|
||||
depguard:
|
||||
list-type: denylist
|
||||
include-go-root: false
|
||||
packages:
|
||||
- github.com/pkg/errors
|
||||
godox:
|
||||
keywords:
|
||||
- FIXME
|
||||
importas:
|
||||
corev1: k8s.io/api/core/v1
|
||||
networkingv1beta1: k8s.io/api/networking/v1beta1
|
||||
extensionsv1beta1: k8s.io/api/extensions/v1beta1
|
||||
metav1: k8s.io/apimachinery/pkg/apis/meta/v1
|
||||
kubeerror: k8s.io/apimachinery/pkg/api/errors
|
||||
composeapi: github.com/docker/compose/v2/pkg/api
|
||||
revive:
|
||||
rules:
|
||||
- name: struct-tag
|
||||
rules:
|
||||
- name: blank-imports
|
||||
- name: context-as-argument
|
||||
- name: context-keys-type
|
||||
- name: dot-imports
|
||||
- name: error-return
|
||||
- name: error-strings
|
||||
- name: error-naming
|
||||
- name: exported
|
||||
- name: if-return
|
||||
- name: increment-decrement
|
||||
- name: var-naming
|
||||
- name: var-declaration
|
||||
- name: package-comments
|
||||
- name: range
|
||||
- name: receiver-naming
|
||||
- name: time-naming
|
||||
- name: unexported-return
|
||||
- name: indent-error-flow
|
||||
- name: errorf
|
||||
- name: empty-block
|
||||
- name: superfluous-else
|
||||
- name: unused-parameter
|
||||
- name: unreachable-code
|
||||
- name: redefines-builtin-id
|
||||
gomoddirectives:
|
||||
replace-allow-list:
|
||||
- github.com/abbot/go-http-auth
|
||||
- github.com/go-check/check
|
||||
- github.com/gorilla/mux
|
||||
- github.com/mailgun/minheap
|
||||
- github.com/mailgun/multibuf
|
||||
- github.com/jaguilar/vt100
|
||||
|
||||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
- deadcode # deprecated
|
||||
- exhaustivestruct # deprecated
|
||||
- golint # deprecated
|
||||
- ifshort # deprecated
|
||||
- interfacer # deprecated
|
||||
- maligned # deprecated
|
||||
- nosnakecase # deprecated
|
||||
- scopelint # deprecated
|
||||
- scopelint # deprecated
|
||||
- structcheck # deprecated
|
||||
- varcheck # deprecated
|
||||
- sqlclosecheck # not relevant (SQL)
|
||||
- rowserrcheck # not relevant (SQL)
|
||||
- execinquery # not relevant (SQL)
|
||||
- cyclop # duplicate of gocyclo
|
||||
- lll # Not relevant
|
||||
- gocyclo # FIXME must be fixed
|
||||
- gocognit # Too strict
|
||||
- nestif # Too many false-positive.
|
||||
- prealloc # Too many false-positive.
|
||||
- makezero # Not relevant
|
||||
- dupl # Too strict
|
||||
- gosec # Too strict
|
||||
- gochecknoinits
|
||||
- gochecknoglobals
|
||||
- wsl # Too strict
|
||||
- nlreturn # Not relevant
|
||||
- gomnd # Too strict
|
||||
- stylecheck # skip because report issues related to some generated files.
|
||||
- testpackage # Too strict
|
||||
- tparallel # Not relevant
|
||||
- paralleltest # Not relevant
|
||||
- exhaustive # Not relevant
|
||||
- exhaustruct # Not relevant
|
||||
- goerr113 # Too strict
|
||||
- wrapcheck # Too strict
|
||||
- noctx # Too strict
|
||||
- bodyclose # too many false-positive
|
||||
- forcetypeassert # Too strict
|
||||
- tagliatelle # Too strict
|
||||
- varnamelen # Not relevant
|
||||
- nilnil # Not relevant
|
||||
- ireturn # Not relevant
|
||||
- contextcheck # too many false-positive
|
||||
- containedctx # too many false-positive
|
||||
- maintidx # kind of duplicate of gocyclo
|
||||
- nonamedreturns # Too strict
|
||||
|
||||
issues:
|
||||
exclude-use-default: false
|
||||
max-per-linter: 0
|
||||
max-same-issues: 0
|
||||
exclude:
|
||||
- 'Error return value of .((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked'
|
||||
- "should have a package comment, unless it's in another file for this package"
|
||||
- 'SA1019: http.CloseNotifier has been deprecated' # FIXME must be fixed
|
||||
- 'SA1019: cfg.SSLRedirect is deprecated'
|
||||
- 'SA1019: cfg.SSLTemporaryRedirect is deprecated'
|
||||
- 'SA1019: cfg.SSLHost is deprecated'
|
||||
- 'SA1019: cfg.SSLForceHost is deprecated'
|
||||
- 'SA1019: cfg.FeaturePolicy is deprecated'
|
||||
- 'SA1019: c.Providers.ConsulCatalog.Namespace is deprecated'
|
||||
- 'SA1019: c.Providers.Consul.Namespace is deprecated'
|
||||
exclude-rules:
|
||||
- path: '(.+)_test.go'
|
||||
linters:
|
||||
- goconst
|
||||
- funlen
|
||||
- godot
|
||||
- path: '(.+)_test.go'
|
||||
text: ' always receives '
|
||||
linters:
|
||||
- unparam
|
||||
- path: '(.+)\.go'
|
||||
text: 'struct-tag: unknown option ''inline'' in JSON tag'
|
||||
linters:
|
||||
- revive
|
||||
- path: pkg/server/service/bufferpool.go
|
||||
text: 'SA6002: argument should be pointer-like to avoid allocations'
|
||||
- path: pkg/server/middleware/middlewares.go
|
||||
text: "Function 'buildConstructor' has too many statements"
|
||||
linters:
|
||||
- funlen
|
||||
- path: pkg/tracing/haystack/logger.go
|
||||
linters:
|
||||
- goprintffuncname
|
||||
- path: pkg/tracing/tracing.go
|
||||
text: "printf-like formatting function 'SetErrorWithEvent' should be named 'SetErrorWithEventf'"
|
||||
linters:
|
||||
- goprintffuncname
|
||||
- path: pkg/tls/tlsmanager_test.go
|
||||
text: 'SA1019: config.ClientCAs.Subjects has been deprecated since Go 1.18'
|
||||
- path: pkg/types/tls_test.go
|
||||
text: 'SA1019: tlsConfig.RootCAs.Subjects has been deprecated since Go 1.18'
|
||||
- path: pkg/provider/kubernetes/(crd|gateway)/client.go
|
||||
linters:
|
||||
- interfacebloat
|
||||
- path: pkg/metrics/metrics.go
|
||||
linters:
|
||||
- interfacebloat
|
||||
- path: integration/healthcheck_test.go
|
||||
text: 'Duplicate words \(wsp2,\) found'
|
||||
linters:
|
||||
- dupword
|
||||
- path: pkg/types/domain_test.go
|
||||
text: 'Duplicate words \(sub\) found'
|
||||
linters:
|
||||
- dupword
|
||||
@@ -22,22 +22,23 @@ builds:
|
||||
- openbsd
|
||||
goarch:
|
||||
- amd64
|
||||
- 386
|
||||
- '386'
|
||||
- arm
|
||||
- arm64
|
||||
- ppc64le
|
||||
- s390x
|
||||
goarm:
|
||||
- 7
|
||||
- 6
|
||||
- 5
|
||||
- '7'
|
||||
- '6'
|
||||
ignore:
|
||||
- goos: darwin
|
||||
goarch: 386
|
||||
goarch: '386'
|
||||
- goos: openbsd
|
||||
goarch: arm
|
||||
- goos: openbsd
|
||||
goarch: arm64
|
||||
- goos: freebsd
|
||||
goarch: arm
|
||||
- goos: freebsd
|
||||
goarch: arm64
|
||||
- goos: windows
|
||||
|
||||
@@ -25,7 +25,7 @@ global_job_config:
|
||||
- export "PATH=${GOPATH}/bin:${PATH}"
|
||||
- mkdir -vp "${SEMAPHORE_GIT_DIR}" "${GOPATH}/bin"
|
||||
- export GOPROXY=https://proxy.golang.org,direct
|
||||
- curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b "${GOPATH}/bin" v1.49.0
|
||||
- curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b "${GOPATH}/bin" v1.50.0
|
||||
- curl -sSfL https://gist.githubusercontent.com/traefiker/6d7ac019c11d011e4f131bb2cca8900e/raw/goreleaser.sh | bash -s -- -b "${GOPATH}/bin"
|
||||
- checkout
|
||||
- cache restore traefik-$(checksum go.sum)
|
||||
|
||||
165
CHANGELOG.md
165
CHANGELOG.md
@@ -1,3 +1,166 @@
|
||||
## [v2.9.6](https://github.com/traefik/traefik/tree/v2.9.6) (2022-12-07)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.9.5...v2.9.6)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Update go-acme/lego to v4.9.1 ([#9550](https://github.com/traefik/traefik/pull/9550) by [ldez](https://github.com/ldez))
|
||||
- **[k8s/crd]** Support of allowEmptyServices in TraefikService ([#9424](https://github.com/traefik/traefik/pull/9424) by [jeromeguiard](https://github.com/jeromeguiard))
|
||||
- **[logs]** Remove logs of the request ([#9574](https://github.com/traefik/traefik/pull/9574) by [ldez](https://github.com/ldez))
|
||||
- **[plugins]** Increase the timeout on plugin download ([#9529](https://github.com/traefik/traefik/pull/9529) by [ldez](https://github.com/ldez))
|
||||
- **[server]** Update golang.org/x/net ([#9582](https://github.com/traefik/traefik/pull/9582) by [ldez](https://github.com/ldez))
|
||||
- **[tls]** Handle broken TLS conf better ([#9572](https://github.com/traefik/traefik/pull/9572) by [mpl](https://github.com/mpl))
|
||||
- **[tracing]** Update DataDog tracing dependency to v1.43.1 ([#9526](https://github.com/traefik/traefik/pull/9526) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[webui]** Add missing serialNumber passTLSClientCert option to middleware panel ([#9539](https://github.com/traefik/traefik/pull/9539) by [rtribotte](https://github.com/rtribotte))
|
||||
|
||||
**Documentation:**
|
||||
- **[docker]** Add networking example ([#9542](https://github.com/traefik/traefik/pull/9542) by [Janik-Haag](https://github.com/Janik-Haag))
|
||||
- **[hub]** Add information about the Hub Agent ([#9560](https://github.com/traefik/traefik/pull/9560) by [nmengin](https://github.com/nmengin))
|
||||
- **[k8s/helm]** Update Helm installation section ([#9564](https://github.com/traefik/traefik/pull/9564) by [mloiseleur](https://github.com/mloiseleur))
|
||||
- **[middleware]** Clarify PathPrefix matcher greediness ([#9519](https://github.com/traefik/traefik/pull/9519) by [mpl](https://github.com/mpl))
|
||||
|
||||
## [v2.9.5](https://github.com/traefik/traefik/tree/v2.9.5) (2022-11-17)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.9.4...v2.9.5)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[logs,middleware]** Create a new capture instance for each incoming request ([#9510](https://github.com/traefik/traefik/pull/9510) by [sdelicata](https://github.com/sdelicata))
|
||||
|
||||
**Documentation:**
|
||||
- **[k8s/helm]** Update helm repository ([#9506](https://github.com/traefik/traefik/pull/9506) by [charlie-haley](https://github.com/charlie-haley))
|
||||
- Enhance wording of building-testing page ([#9509](https://github.com/traefik/traefik/pull/9509) by [svx](https://github.com/svx))
|
||||
- Add link descriptions and update wording ([#9507](https://github.com/traefik/traefik/pull/9507) by [svx](https://github.com/svx))
|
||||
- Removes the experimental tag on the Traefik Hub header ([#9498](https://github.com/traefik/traefik/pull/9498) by [tfny](https://github.com/tfny))
|
||||
|
||||
## [v2.9.4](https://github.com/traefik/traefik/tree/v2.9.4) (2022-10-27)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.9.1...v2.9.4)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Update go-acme/lego to v4.9.0 ([#9413](https://github.com/traefik/traefik/pull/9413) by [tony-defa](https://github.com/tony-defa))
|
||||
- **[kv,redis]** Fix Redis configuration type ([#9435](https://github.com/traefik/traefik/pull/9435) by [ldez](https://github.com/ldez))
|
||||
- **[logs,middleware,metrics]** Handle capture on redefined http.responseWriters ([#9440](https://github.com/traefik/traefik/pull/9440) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[middleware,k8s]** Remove raw cert escape in PassTLSClientCert middleware ([#9412](https://github.com/traefik/traefik/pull/9412) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[plugins]** Update Yaegi to v0.14.3 ([#9468](https://github.com/traefik/traefik/pull/9468) by [ldez](https://github.com/ldez))
|
||||
- Remove side effect on default transport tests ([#9460](https://github.com/traefik/traefik/pull/9460) by [sdelicata](https://github.com/sdelicata))
|
||||
|
||||
**Documentation:**
|
||||
- **[k8s]** Fix links to gateway API guides ([#9445](https://github.com/traefik/traefik/pull/9445) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- Simplify dashboard rule example ([#9454](https://github.com/traefik/traefik/pull/9454) by [sosoba](https://github.com/sosoba))
|
||||
- Add v2.9 to release page ([#9438](https://github.com/traefik/traefik/pull/9438) by [kevinpollet](https://github.com/kevinpollet))
|
||||
|
||||
## [v2.9.3](https://github.com/traefik/traefik/tree/v2.9.3) (2022-10-27)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.9.1...v2.9.3)
|
||||
|
||||
Release canceled.
|
||||
|
||||
## [v2.9.2](https://github.com/traefik/traefik/tree/v2.9.2) (2022-10-27)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.9.1...v2.9.2)
|
||||
|
||||
Release canceled.
|
||||
|
||||
## [v2.9.1](https://github.com/traefik/traefik/tree/v2.9.1) (2022-10-03)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.9.0-rc1...v2.9.1)
|
||||
|
||||
**Enhancements:**
|
||||
- **[acme,tls]** ACME Default Certificate ([#9189](https://github.com/traefik/traefik/pull/9189) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[consul,etcd,zk,kv,redis]** Update valkeyrie to v1.0.0 ([#9316](https://github.com/traefik/traefik/pull/9316) by [ldez](https://github.com/ldez))
|
||||
- **[consulcatalog,nomad]** Support Nomad canary deployment ([#9216](https://github.com/traefik/traefik/pull/9216) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[consulcatalog]** Move consulcatalog provider to only use health apis ([#9140](https://github.com/traefik/traefik/pull/9140) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- **[docker]** Add support for reaching containers using host networking on Podman ([#9190](https://github.com/traefik/traefik/pull/9190) by [freundTech](https://github.com/freundTech))
|
||||
- **[docker]** Use IPv6 address ([#9183](https://github.com/traefik/traefik/pull/9183) by [tomMoulard](https://github.com/tomMoulard))
|
||||
- **[docker]** Add allowEmptyServices for Docker provider ([#8690](https://github.com/traefik/traefik/pull/8690) by [jvasseur](https://github.com/jvasseur))
|
||||
- **[ecs]** Add support for ECS Anywhere ([#9324](https://github.com/traefik/traefik/pull/9324) by [tuxpower](https://github.com/tuxpower))
|
||||
- **[healthcheck]** Add a method option to the service Health Check ([#9165](https://github.com/traefik/traefik/pull/9165) by [ddtmachado](https://github.com/ddtmachado))
|
||||
- **[http3]** Upgrade quic-go to v0.28.0 ([#9187](https://github.com/traefik/traefik/pull/9187) by [tomMoulard](https://github.com/tomMoulard))
|
||||
- **[http]** Start polling HTTP provider at the beginning ([#9116](https://github.com/traefik/traefik/pull/9116) by [moutoum](https://github.com/moutoum))
|
||||
- **[k8s/crd,plugins]** Load plugin configuration field value from Kubernetes Secret ([#9103](https://github.com/traefik/traefik/pull/9103) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[logs,tcp]** Quiet down TCP RST packet error on read operation ([#9007](https://github.com/traefik/traefik/pull/9007) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[metrics]** Add traffic size metrics ([#9208](https://github.com/traefik/traefik/pull/9208) by [tomMoulard](https://github.com/tomMoulard))
|
||||
- **[middleware,pilot]** Remove Pilot support ([#9330](https://github.com/traefik/traefik/pull/9330) by [ldez](https://github.com/ldez))
|
||||
- **[rules,tcp]** Support ALPN for TCP + TLS routers ([#8913](https://github.com/traefik/traefik/pull/8913) by [sh7dm](https://github.com/sh7dm))
|
||||
- **[tcp,service,udp]** Make the loadbalancers servers order random ([#9037](https://github.com/traefik/traefik/pull/9037) by [qmloong](https://github.com/qmloong))
|
||||
- **[tls]** Change default TLS options for more security ([#8951](https://github.com/traefik/traefik/pull/8951) by [ddtmachado](https://github.com/ddtmachado))
|
||||
- **[tracing]** Add Datadog GlobalTags support ([#9266](https://github.com/traefik/traefik/pull/9266) by [sdelicata](https://github.com/sdelicata))
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Fix ACME panic ([#9365](https://github.com/traefik/traefik/pull/9365) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- Prepare release v2.9.0 ([#9409](https://github.com/traefik/traefik/pull/9409) by [tomMoulard](https://github.com/tomMoulard))
|
||||
- **[metrics]** Rework metrics overview page ([#9366](https://github.com/traefik/traefik/pull/9366) by [ddtmachado](https://github.com/ddtmachado))
|
||||
- Prepare release v2.9.0-rc5 ([#9402](https://github.com/traefik/traefik/pull/9402) by [ldez](https://github.com/ldez))
|
||||
- Prepare release v2.9.0-rc4 ([#9372](https://github.com/traefik/traefik/pull/9372) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- Prepare release v2.9.0-rc3 ([#9344](https://github.com/traefik/traefik/pull/9344) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- Prepare release v2.9.0-rc2 ([6c2c561](https://github.com/traefik/traefik/commit/6c2c561d8f935d76ccd07d28e1455c7768adc153) by [ldez](https://github.com/ldez))
|
||||
- Prepare release v2.9.0-rc1 ([#9334](https://github.com/traefik/traefik/pull/9334) by [rtribotte](https://github.com/rtribotte))
|
||||
|
||||
**Misc:**
|
||||
- Merge current v2.8 into v2.9 ([#9400](https://github.com/traefik/traefik/pull/9400) by [ldez](https://github.com/ldez))
|
||||
- Merge current v2.8 into v2.9 ([#9371](https://github.com/traefik/traefik/pull/9371) by [ldez](https://github.com/ldez))
|
||||
- Merge current v2.8 into v2.9 ([#9367](https://github.com/traefik/traefik/pull/9367) by [ldez](https://github.com/ldez))
|
||||
- Merge current v2.8 into v2.9 ([#9350](https://github.com/traefik/traefik/pull/9350) by [ldez](https://github.com/ldez))
|
||||
- Merge current v2.8 into v2.9 ([#9343](https://github.com/traefik/traefik/pull/9343) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- Merge v2.8.5 into master ([#9329](https://github.com/traefik/traefik/pull/9329) by [rtribotte](https://github.com/rtribotte))
|
||||
- Merge current v2.8 into master ([#9291](https://github.com/traefik/traefik/pull/9291) by [rtribotte](https://github.com/rtribotte))
|
||||
- Merge current v2.8 into master ([#9265](https://github.com/traefik/traefik/pull/9265) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- Merge current v2.8 into master ([#9209](https://github.com/traefik/traefik/pull/9209) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- Merge current v2.8 into master ([#9146](https://github.com/traefik/traefik/pull/9146) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- Merge current v2.8 into master ([#9135](https://github.com/traefik/traefik/pull/9135) by [kevinpollet](https://github.com/kevinpollet))
|
||||
|
||||
## [v2.9.0](https://github.com/traefik/traefik/tree/v2.9.0) (2022-10-03)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.9.0-rc1...v2.9.0)
|
||||
|
||||
Release canceled.
|
||||
|
||||
## [v2.9.0-rc5](https://github.com/traefik/traefik/tree/v2.9.0-rc5) (2022-09-30)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.9.0-rc4...v2.9.0-rc5)
|
||||
|
||||
**Misc:**
|
||||
- Merge current v2.8 into v2.9 ([#9400](https://github.com/traefik/traefik/pull/9400) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v2.8.8](https://github.com/traefik/traefik/tree/v2.8.8) (2022-09-30)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.8.7...v2.8.8)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[server]** Update golang.org/x/net to latest version ([#9398](https://github.com/traefik/traefik/pull/9398) by [tspearconquest](https://github.com/tspearconquest))
|
||||
|
||||
**Documentation:**
|
||||
- **[docker]** Fix watch option description for Docker provider ([#9391](https://github.com/traefik/traefik/pull/9391) by [bhuisgen](https://github.com/bhuisgen))
|
||||
- **[ecs]** Fix autoDiscoverClusters option documentation for ECS provider ([#9392](https://github.com/traefik/traefik/pull/9392) by [johnpekcan](https://github.com/johnpekcan))
|
||||
- **[k8s]** Improve documentation for publishedService and IP options ([#9380](https://github.com/traefik/traefik/pull/9380) by [samip5](https://github.com/samip5))
|
||||
|
||||
## [v2.9.0-rc4](https://github.com/traefik/traefik/tree/v2.9.0-rc4) (2022-09-23)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.9.0-rc3...v2.9.0-rc4)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Fix ACME panic ([#9365](https://github.com/traefik/traefik/pull/9365) by [ldez](https://github.com/ldez))
|
||||
|
||||
**Documentation:**
|
||||
- **[metrics]** Rework metrics overview page ([#9366](https://github.com/traefik/traefik/pull/9366) by [ddtmachado](https://github.com/ddtmachado))
|
||||
|
||||
**Misc:**
|
||||
- Merge current v2.8 into v2.9 ([#9371](https://github.com/traefik/traefik/pull/9371) by [ldez](https://github.com/ldez))
|
||||
- Merge current v2.8 into v2.9 ([#9367](https://github.com/traefik/traefik/pull/9367) by [ldez](https://github.com/ldez))
|
||||
- Merge current v2.8 into v2.9 ([#9350](https://github.com/traefik/traefik/pull/9350) by [ldez](https://github.com/ldez))
|
||||
|
||||
## [v2.8.7](https://github.com/traefik/traefik/tree/v2.8.7) (2022-09-23)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.8.5...v2.8.7)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[consulcatalog]** Fix UDP loadbalancer tags not being used with Consul Catalog ([#9357](https://github.com/traefik/traefik/pull/9357) by [t3hchipmunk](https://github.com/t3hchipmunk))
|
||||
- **[docker,rancher,ecs,provider]** Simplify AddServer algorithm ([#9358](https://github.com/traefik/traefik/pull/9358) by [ldez](https://github.com/ldez))
|
||||
- **[plugins]** Allow empty plugin configuration ([#9338](https://github.com/traefik/traefik/pull/9338) by [ldez](https://github.com/ldez))
|
||||
- **[rules]** Fix query parameter matching with equal ([#9369](https://github.com/traefik/traefik/pull/9369) by [ldez](https://github.com/ldez))
|
||||
- **[server]** Optimize websocket headers handling ([#9360](https://github.com/traefik/traefik/pull/9360) by [juliens](https://github.com/juliens))
|
||||
|
||||
**Documentation:**
|
||||
- **[ecs]** Add documentation for ECS constraints option ([#9354](https://github.com/traefik/traefik/pull/9354) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[k8s/gatewayapi]** Fix link to RouteNamespaces ([#9349](https://github.com/traefik/traefik/pull/9349) by [ldez](https://github.com/ldez))
|
||||
- Add documentation for json schema usage to validate config in the FAQ ([#9340](https://github.com/traefik/traefik/pull/9340) by [rtribotte](https://github.com/rtribotte))
|
||||
- Add a note on case insensitive regex matching ([#9322](https://github.com/traefik/traefik/pull/9322) by [NEwa-05](https://github.com/NEwa-05))
|
||||
|
||||
## [v2.8.6](https://github.com/traefik/traefik/tree/v2.8.6) (2022-09-23)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.8.5...v2.8.6)
|
||||
|
||||
Release canceled.
|
||||
|
||||
## [v2.9.0-rc3](https://github.com/traefik/traefik/tree/v2.9.0-rc3) (2022-09-16)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.9.0-rc2...v2.9.0-rc3)
|
||||
|
||||
@@ -6271,5 +6434,3 @@ Same changelog as v2.0.3.
|
||||
- Fix travis tag check [\#422](https://github.com/traefik/traefik/pull/422) ([emilevauge](https://github.com/emilevauge))
|
||||
- log info about TOML configuration file using [\#420](https://github.com/traefik/traefik/pull/420) ([cocap10](https://github.com/cocap10))
|
||||
- Doc about skipping some integration tests with '-check.f ConsulCatalogSuite' [\#418](https://github.com/traefik/traefik/pull/418) ([samber](https://github.com/samber))
|
||||
|
||||
\* *This Change Log was automatically generated by [gcg](https://github.com/ldez/gcg)*
|
||||
|
||||
2
Makefile
2
Makefile
@@ -189,7 +189,7 @@ generate-genconf:
|
||||
.PHONY: release-packages
|
||||
release-packages: generate-webui build-dev-image
|
||||
rm -rf dist
|
||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) goreleaser release --skip-publish --timeout="90m"
|
||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) goreleaser release --skip-publish -p 4 --timeout="90m"
|
||||
$(if $(IN_DOCKER),$(DOCKER_RUN_TRAEFIK_NOTTY)) tar cfz dist/traefik-${VERSION}.src.tar.gz \
|
||||
--exclude-vcs \
|
||||
--exclude .idea \
|
||||
|
||||
@@ -13,10 +13,10 @@ RUN mkdir -p /usr/local/bin \
|
||||
| tar -xzC /usr/local/bin --transform 's#^.+/##x'
|
||||
|
||||
# Download golangci-lint binary to bin folder in $GOPATH
|
||||
RUN curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash -s -- -b $GOPATH/bin v1.49.0
|
||||
RUN curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash -s -- -b $GOPATH/bin v1.50.0
|
||||
|
||||
# Download misspell binary to bin folder in $GOPATH
|
||||
RUN curl -sfL https://raw.githubusercontent.com/client9/misspell/master/install-misspell.sh | bash -s -- -b $GOPATH/bin v0.3.4
|
||||
RUN curl -sfL https://raw.githubusercontent.com/golangci/misspell/master/install-misspell.sh | bash -s -- -b $GOPATH/bin v0.4.0
|
||||
|
||||
# Download goreleaser binary to bin folder in $GOPATH
|
||||
RUN curl -sfL https://gist.githubusercontent.com/traefiker/6d7ac019c11d011e4f131bb2cca8900e/raw/goreleaser.sh | sh
|
||||
|
||||
@@ -31,7 +31,6 @@ import (
|
||||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
"github.com/traefik/traefik/v2/pkg/metrics"
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares/accesslog"
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares/capture"
|
||||
"github.com/traefik/traefik/v2/pkg/provider/acme"
|
||||
"github.com/traefik/traefik/v2/pkg/provider/aggregator"
|
||||
"github.com/traefik/traefik/v2/pkg/provider/hub"
|
||||
@@ -45,7 +44,7 @@ import (
|
||||
"github.com/traefik/traefik/v2/pkg/tracing/jaeger"
|
||||
"github.com/traefik/traefik/v2/pkg/types"
|
||||
"github.com/traefik/traefik/v2/pkg/version"
|
||||
"github.com/vulcand/oxy/roundrobin"
|
||||
"github.com/vulcand/oxy/v2/roundrobin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -260,9 +259,8 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
||||
|
||||
accessLog := setupAccessLog(staticConfiguration.AccessLog)
|
||||
tracer := setupTracing(staticConfiguration.Tracing)
|
||||
captureMiddleware := setupCapture(staticConfiguration)
|
||||
|
||||
chainBuilder := middleware.NewChainBuilder(metricsRegistry, accessLog, tracer, captureMiddleware)
|
||||
chainBuilder := middleware.NewChainBuilder(metricsRegistry, accessLog, tracer)
|
||||
routerFactory := server.NewRouterFactory(*staticConfiguration, managerFactory, tlsManager, chainBuilder, pluginBuilder, metricsRegistry)
|
||||
|
||||
// Watcher
|
||||
@@ -565,13 +563,6 @@ func setupTracing(conf *static.Tracing) *tracing.Tracing {
|
||||
return tracer
|
||||
}
|
||||
|
||||
func setupCapture(staticConfiguration *static.Configuration) *capture.Handler {
|
||||
if staticConfiguration.AccessLog == nil && staticConfiguration.Metrics == nil {
|
||||
return nil
|
||||
}
|
||||
return &capture.Handler{}
|
||||
}
|
||||
|
||||
func configureLogging(staticConfiguration *static.Configuration) {
|
||||
// configure default log flags
|
||||
stdlog.SetFlags(stdlog.Lshortfile | stdlog.LstdFlags)
|
||||
|
||||
@@ -8,17 +8,22 @@ description: "Compile and test your own Traefik Proxy! Learn how to build your o
|
||||
Compile and Test Your Own Traefik!
|
||||
{: .subtitle }
|
||||
|
||||
So you want to build your own Traefik binary from the sources?
|
||||
You want to build your own Traefik binary from the sources?
|
||||
Let's see how.
|
||||
|
||||
## Building
|
||||
|
||||
You need either [Docker](https://github.com/docker/docker) and `make` (Method 1), or `go` (Method 2) in order to build Traefik.
|
||||
You need either [Docker](https://github.com/docker/docker "Link to website of Docker") and `make` (Method 1), or [Go](https://go.dev/ "Link to website of Go") (Method 2) in order to build Traefik.
|
||||
For changes to its dependencies, the `dep` dependency management tool is required.
|
||||
|
||||
### Method 1: Using `Docker` and `Makefile`
|
||||
|
||||
Run make with the `binary` target.
|
||||
|
||||
```bash
|
||||
make binary
|
||||
```
|
||||
|
||||
This will create binaries for the Linux platform in the `dist` folder.
|
||||
|
||||
In case when you run build on CI, you may probably want to run docker in non-interactive mode. To achieve that define `DOCKER_NON_INTERACTIVE=true` environment variable.
|
||||
@@ -160,7 +165,7 @@ TESTFLAGS="-check.f MyTestSuite.My" make test-integration
|
||||
TESTFLAGS="-check.f MyTestSuite.*Test" make test-integration
|
||||
```
|
||||
|
||||
More: https://labix.org/gocheck
|
||||
Check [gocheck](https://labix.org/gocheck "Link to website of gocheck") for more information.
|
||||
|
||||
### Method 2: `go`
|
||||
|
||||
|
||||
@@ -15,10 +15,14 @@ Let's see how.
|
||||
|
||||
### General
|
||||
|
||||
This [documentation](https://doc.traefik.io/traefik/) is built with [mkdocs](https://mkdocs.org/).
|
||||
This [documentation](https://doc.traefik.io/traefik/ "Link to the official Traefik documentation") is built with [MkDocs](https://mkdocs.org/ "Link to website of MkDocs").
|
||||
|
||||
### Method 1: `Docker` and `make`
|
||||
|
||||
Please make sure you have the following requirements installed:
|
||||
|
||||
- [Docker](https://www.docker.com/ "Link to website of Docker")
|
||||
|
||||
You can build the documentation and test it locally (with live reloading), using the `docs-serve` target:
|
||||
|
||||
```bash
|
||||
@@ -43,9 +47,12 @@ $ make docs-build
|
||||
...
|
||||
```
|
||||
|
||||
### Method 2: `mkdocs`
|
||||
### Method 2: `MkDocs`
|
||||
|
||||
First, make sure you have `python` and `pip` installed.
|
||||
Please make sure you have the following requirements installed:
|
||||
|
||||
- [Python](https://www.python.org/ "Link to website of Python")
|
||||
- [pip](https://pypi.org/project/pip/ "Link to the website of pip on PyPI")
|
||||
|
||||
```bash
|
||||
$ python --version
|
||||
@@ -54,7 +61,7 @@ $ pip --version
|
||||
pip 1.5.2
|
||||
```
|
||||
|
||||
Then, install mkdocs with `pip`.
|
||||
Then, install MkDocs with `pip`.
|
||||
|
||||
```bash
|
||||
pip install --user -r requirements.txt
|
||||
@@ -87,7 +94,7 @@ Running ["HtmlCheck", "ImageCheck", "ScriptCheck", "LinkCheck"] on /app/site/bas
|
||||
|
||||
!!! note "Clean & Verify"
|
||||
|
||||
If you've made changes to the documentation, it's safter to clean it before verifying it.
|
||||
If you've made changes to the documentation, it's safer to clean it before verifying it.
|
||||
|
||||
```bash
|
||||
$ make docs
|
||||
|
||||
@@ -6,16 +6,17 @@ Below is a non-exhaustive list of versions and their maintenance status:
|
||||
|
||||
| Version | Release Date | Active Support | Security Support |
|
||||
|---------|--------------|--------------------|------------------|
|
||||
| 2.8 | Jun 29, 2022 | Yes | Yes |
|
||||
| 2.7 | May 24, 2022 | Ended Jun 29, 2022 | No |
|
||||
| 2.6 | Jan 24, 2022 | Ended May 24, 2022 | No |
|
||||
| 2.5 | Aug 17, 2021 | Ended Jan 24, 2022 | No |
|
||||
| 2.4 | Jan 19, 2021 | Ended Aug 17, 2021 | No |
|
||||
| 2.3 | Sep 23, 2020 | Ended Jan 19, 2021 | No |
|
||||
| 2.2 | Mar 25, 2020 | Ended Sep 23, 2020 | No |
|
||||
| 2.1 | Dec 11, 2019 | Ended Mar 25, 2020 | No |
|
||||
| 2.0 | Sep 16, 2019 | Ended Dec 11, 2019 | No |
|
||||
| 1.7 | Sep 24, 2018 | Ended Dec 31, 2021 | Contact Support |
|
||||
| 2.9 | Oct 03, 2022 | Yes | Yes |
|
||||
| 2.8 | Jun 29, 2022 | Ended Oct 03, 2022 | No |
|
||||
| 2.7 | May 24, 2022 | Ended Jun 29, 2022 | No |
|
||||
| 2.6 | Jan 24, 2022 | Ended May 24, 2022 | No |
|
||||
| 2.5 | Aug 17, 2021 | Ended Jan 24, 2022 | No |
|
||||
| 2.4 | Jan 19, 2021 | Ended Aug 17, 2021 | No |
|
||||
| 2.3 | Sep 23, 2020 | Ended Jan 19, 2021 | No |
|
||||
| 2.2 | Mar 25, 2020 | Ended Sep 23, 2020 | No |
|
||||
| 2.1 | Dec 11, 2019 | Ended Mar 25, 2020 | No |
|
||||
| 2.0 | Sep 16, 2019 | Ended Dec 11, 2019 | No |
|
||||
| 1.7 | Sep 24, 2018 | Ended Dec 31, 2021 | Contact Support |
|
||||
|
||||
??? example "Active Support / Security Support"
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ You can install Traefik with the following flavors:
|
||||
|
||||
Choose one of the [official Docker images](https://hub.docker.com/_/traefik) and run it with one sample configuration file:
|
||||
|
||||
* [YAML](https://raw.githubusercontent.com/traefik/traefik/v2.8/traefik.sample.yml)
|
||||
* [TOML](https://raw.githubusercontent.com/traefik/traefik/v2.8/traefik.sample.toml)
|
||||
* [YAML](https://raw.githubusercontent.com/traefik/traefik/v2.9/traefik.sample.yml)
|
||||
* [TOML](https://raw.githubusercontent.com/traefik/traefik/v2.9/traefik.sample.toml)
|
||||
|
||||
```bash
|
||||
docker run -d -p 8080:8080 -p 80:80 \
|
||||
@@ -44,13 +44,13 @@ Traefik can be installed in Kubernetes using the Helm chart from <https://github
|
||||
|
||||
Ensure that the following requirements are met:
|
||||
|
||||
* Kubernetes 1.14+
|
||||
* Helm version 3.x is [installed](https://helm.sh/docs/intro/install/)
|
||||
* Kubernetes 1.16+
|
||||
* Helm version 3.9+ is [installed](https://helm.sh/docs/intro/install/)
|
||||
|
||||
Add Traefik's chart repository to Helm:
|
||||
Add Traefik Labs chart repository to Helm:
|
||||
|
||||
```bash
|
||||
helm repo add traefik https://helm.traefik.io/traefik
|
||||
helm repo add traefik https://traefik.github.io/charts
|
||||
```
|
||||
|
||||
You can update the chart repository by running:
|
||||
@@ -68,6 +68,9 @@ helm install traefik traefik/traefik
|
||||
!!! tip "Helm Features"
|
||||
|
||||
All [Helm features](https://helm.sh/docs/intro/using_helm/) are supported.
|
||||
|
||||
Examples are provided [here](https://github.com/traefik/traefik-helm-chart/blob/master/EXAMPLES.md).
|
||||
|
||||
For instance, installing the chart in a dedicated namespace:
|
||||
|
||||
```bash tab="Install in a Dedicated Namespace"
|
||||
@@ -83,8 +86,7 @@ helm install traefik traefik/traefik
|
||||
as with [any helm chart](https://helm.sh/docs/intro/using_helm/#customizing-the-chart-before-installing).
|
||||
{: #helm-custom-values }
|
||||
|
||||
The values are not (yet) documented, but are self-explanatory:
|
||||
you can look at the [default `values.yaml`](https://github.com/traefik/traefik-helm-chart/blob/master/traefik/values.yaml) file to explore possibilities.
|
||||
All parameters are documented in the default [`values.yaml`](https://github.com/traefik/traefik-helm-chart/blob/master/traefik/values.yaml).
|
||||
|
||||
You can also set Traefik command line flags using `additionalArguments`.
|
||||
Example of installation with logging set to `DEBUG`:
|
||||
|
||||
@@ -50,7 +50,12 @@ Now that we have a Traefik instance up and running, we will deploy new services.
|
||||
Edit your `docker-compose.yml` file and add the following at the end of your file.
|
||||
|
||||
```yaml
|
||||
# ...
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
|
||||
...
|
||||
|
||||
whoami:
|
||||
# A container that exposes an API to show its IP address
|
||||
image: traefik/whoami
|
||||
|
||||
@@ -293,112 +293,117 @@ For example, `CF_API_EMAIL_FILE=/run/secrets/traefik_cf-api-email` could be used
|
||||
|
||||
For complete details, refer to your provider's _Additional configuration_ link.
|
||||
|
||||
| Provider Name | Provider Code | Environment Variables | |
|
||||
|----------------------------------------------------------------------------------------------------|----------------|---------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------|
|
||||
| [ACME DNS](https://github.com/joohoi/acme-dns) | `acme-dns` | `ACME_DNS_API_BASE`, `ACME_DNS_STORAGE_PATH` | [Additional configuration](https://go-acme.github.io/lego/dns/acme-dns) |
|
||||
| [Alibaba Cloud](https://www.alibabacloud.com) | `alidns` | `ALICLOUD_ACCESS_KEY`, `ALICLOUD_SECRET_KEY`, `ALICLOUD_REGION_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/alidns) |
|
||||
| [all-inkl](https://all-inkl.com) | `allinkl` | `ALL_INKL_LOGIN`, `ALL_INKL_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/allinkl) |
|
||||
| [ArvanCloud](https://www.arvancloud.com/en) | `arvancloud` | `ARVANCLOUD_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/arvancloud) |
|
||||
| [Auroradns](https://www.pcextreme.com/dns-health-checks) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` | [Additional configuration](https://go-acme.github.io/lego/dns/auroradns) |
|
||||
| [Autodns](https://www.internetx.com/domains/autodns/) | `autodns` | `AUTODNS_API_USER`, `AUTODNS_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/autodns) |
|
||||
| [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP`, `[AZURE_METADATA_ENDPOINT]` | [Additional configuration](https://go-acme.github.io/lego/dns/azure) |
|
||||
| [Bindman](https://github.com/labbsr0x/bindman-dns-webhook) | `bindman` | `BINDMAN_MANAGER_ADDRESS` | [Additional configuration](https://go-acme.github.io/lego/dns/bindman) |
|
||||
| [Blue Cat](https://www.bluecatnetworks.com/) | `bluecat` | `BLUECAT_SERVER_URL`, `BLUECAT_USER_NAME`, `BLUECAT_PASSWORD`, `BLUECAT_CONFIG_NAME`, `BLUECAT_DNS_VIEW` | [Additional configuration](https://go-acme.github.io/lego/dns/bluecat) |
|
||||
| [Checkdomain](https://www.checkdomain.de/) | `checkdomain` | `CHECKDOMAIN_TOKEN`, | [Additional configuration](https://go-acme.github.io/lego/dns/checkdomain/) |
|
||||
| [CloudDNS](https://vshosting.eu/) | `clouddns` | `CLOUDDNS_CLIENT_ID`, `CLOUDDNS_EMAIL`, `CLOUDDNS_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/clouddns) |
|
||||
| [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CF_API_EMAIL`, `CF_API_KEY` [^5] or `CF_DNS_API_TOKEN`, `[CF_ZONE_API_TOKEN]` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudflare) |
|
||||
| [ClouDNS](https://www.cloudns.net/) | `cloudns` | `CLOUDNS_AUTH_ID`, `CLOUDNS_AUTH_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudns) |
|
||||
| [CloudXNS](https://www.cloudxns.net) | `cloudxns` | `CLOUDXNS_API_KEY`, `CLOUDXNS_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudxns) |
|
||||
| [ConoHa](https://www.conoha.jp) | `conoha` | `CONOHA_TENANT_ID`, `CONOHA_API_USERNAME`, `CONOHA_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/conoha) |
|
||||
| [Constellix](https://constellix.com) | `constellix` | `CONSTELLIX_API_KEY`, `CONSTELLIX_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/constellix) |
|
||||
| [deSEC](https://desec.io) | `desec` | `DESEC_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/desec) |
|
||||
| [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/digitalocean) |
|
||||
| [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` | [Additional configuration](https://go-acme.github.io/lego/dns/dnsmadeeasy) |
|
||||
| [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/dnsimple) |
|
||||
| [DNSPod](https://www.dnspod.com/) | `dnspod` | `DNSPOD_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/dnspod) |
|
||||
| [Domain Offensive (do.de)](https://www.do.de/) | `dode` | `DODE_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/dode) |
|
||||
| [Domeneshop](https://domene.shop) | `domeneshop` | `DOMENESHOP_API_TOKEN`, `DOMENESHOP_API_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/domeneshop) |
|
||||
| [DreamHost](https://www.dreamhost.com/) | `dreamhost` | `DREAMHOST_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/dreamhost) |
|
||||
| [Duck DNS](https://www.duckdns.org/) | `duckdns` | `DUCKDNS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/duckdns) |
|
||||
| [Dyn](https://dyn.com) | `dyn` | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/dyn) |
|
||||
| [Dynu](https://www.dynu.com) | `dynu` | `DYNU_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/dynu) |
|
||||
| [EasyDNS](https://easydns.com/) | `easydns` | `EASYDNS_TOKEN`, `EASYDNS_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/easydns) |
|
||||
| [EdgeDNS](https://www.akamai.com/) | `edgedns` | `AKAMAI_CLIENT_TOKEN`, `AKAMAI_CLIENT_SECRET`, `AKAMAI_ACCESS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/edgedns) |
|
||||
| [Epik](https://www.epik.com) | `epik` | `EPIK_SIGNATURE` | [Additional configuration](https://go-acme.github.io/lego/dns/epik) |
|
||||
| [Exoscale](https://www.exoscale.com) | `exoscale` | `EXOSCALE_API_KEY`, `EXOSCALE_API_SECRET`, `EXOSCALE_ENDPOINT` | [Additional configuration](https://go-acme.github.io/lego/dns/exoscale) |
|
||||
| [Fast DNS](https://www.akamai.com/) | `fastdns` | `AKAMAI_CLIENT_TOKEN`, `AKAMAI_CLIENT_SECRET`, `AKAMAI_ACCESS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/edgedns) |
|
||||
| [Freemyip.com](https://freemyip.com) | `freemyip` | `FREEMYIP_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/freemyip) |
|
||||
| [G-Core Lab](https://gcorelabs.com/dns/) | `gcore` | `GCORE_PERMANENT_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/gcore) |
|
||||
| [Gandi v5](https://doc.livedns.gandi.net) | `gandiv5` | `GANDIV5_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/gandiv5) |
|
||||
| [Gandi](https://www.gandi.net) | `gandi` | `GANDI_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/gandi) |
|
||||
| [Glesys](https://glesys.com/) | `glesys` | `GLESYS_API_USER`, `GLESYS_API_KEY`, `GLESYS_DOMAIN` | [Additional configuration](https://go-acme.github.io/lego/dns/glesys) |
|
||||
| [GoDaddy](https://godaddy.com/) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/godaddy) |
|
||||
| [Google Cloud DNS](https://cloud.google.com/dns/docs/) | `gcloud` | `GCE_PROJECT`, Application Default Credentials [^2] [^3], [`GCE_SERVICE_ACCOUNT_FILE`] | [Additional configuration](https://go-acme.github.io/lego/dns/gcloud) |
|
||||
| [Hetzner](https://hetzner.com) | `hetzner` | `HETZNER_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/hetzner) |
|
||||
| [hosting.de](https://www.hosting.de) | `hostingde` | `HOSTINGDE_API_KEY`, `HOSTINGDE_ZONE_NAME` | [Additional configuration](https://go-acme.github.io/lego/dns/hostingde) |
|
||||
| [Hosttech](https://www.hosttech.eu) | `hosttech` | `HOSTTECH_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/hosttech) |
|
||||
| [Hurricane Electric](https://dns.he.net) | `hurricane` | `HURRICANE_TOKENS` [^6] | [Additional configuration](https://go-acme.github.io/lego/dns/hurricane) |
|
||||
| [HyperOne](https://www.hyperone.com) | `hyperone` | `HYPERONE_PASSPORT_LOCATION`, `HYPERONE_LOCATION_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/hyperone) |
|
||||
| [IBM Cloud (SoftLayer)](https://www.ibm.com/cloud/) | `ibmcloud` | `SOFTLAYER_USERNAME`, `SOFTLAYER_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/ibmcloud) |
|
||||
| [IIJ DNS Platform Service](https://www.iij.ad.jp) | `iijdpf` | `IIJ_DPF_API_TOKEN` , `IIJ_DPF_DPM_SERVICE_CODE` | [Additional configuration](https://go-acme.github.io/lego/dns/iijdpf) |
|
||||
| [IIJ](https://www.iij.ad.jp/) | `iij` | `IIJ_API_ACCESS_KEY`, `IIJ_API_SECRET_KEY`, `IIJ_DO_SERVICE_CODE` | [Additional configuration](https://go-acme.github.io/lego/dns/iij) |
|
||||
| [Infoblox](https://www.infoblox.com/) | `infoblox` | `INFOBLOX_USERNAME`, `INFOBLOX_PASSWORD`, `INFOBLOX_HOST` | [Additional configuration](https://go-acme.github.io/lego/dns/infoblox) |
|
||||
| [Infomaniak](https://www.infomaniak.com) | `infomaniak` | `INFOMANIAK_ACCESS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/infomaniak) |
|
||||
| [Internet.bs](https://internetbs.net) | `internetbs` | `INTERNET_BS_API_KEY`, `INTERNET_BS_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/internetbs) |
|
||||
| [INWX](https://www.inwx.de/en) | `inwx` | `INWX_USERNAME`, `INWX_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/inwx) |
|
||||
| [ionos](https://ionos.com/) | `ionos` | `IONOS_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/ionos) |
|
||||
| [iwantmyname](https://iwantmyname.com) | `iwantmyname` | `IWANTMYNAME_USERNAME` , `IWANTMYNAME_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/iwantmyname) |
|
||||
| [Joker.com](https://joker.com) | `joker` | `JOKER_API_MODE` with `JOKER_API_KEY` or `JOKER_USERNAME`, `JOKER_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/joker) |
|
||||
| [Lightsail](https://aws.amazon.com/lightsail/) | `lightsail` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `DNS_ZONE` | [Additional configuration](https://go-acme.github.io/lego/dns/lightsail) |
|
||||
| [Linode v4](https://www.linode.com) | `linode` | `LINODE_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/linode) |
|
||||
| [Liquid Web](https://www.liquidweb.com/) | `liquidweb` | `LIQUID_WEB_PASSWORD`, `LIQUID_WEB_USERNAME`, `LIQUID_WEB_ZONE` | [Additional configuration](https://go-acme.github.io/lego/dns/liquidweb) |
|
||||
| [Loopia](https://loopia.com/) | `loopia` | `LOOPIA_API_PASSWORD`, `LOOPIA_API_USER` | [Additional configuration](https://go-acme.github.io/lego/dns/loopia) |
|
||||
| [LuaDNS](https://luadns.com) | `luadns` | `LUADNS_API_USERNAME`, `LUADNS_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/luadns) |
|
||||
| [MyDNS.jp](https://www.mydns.jp/) | `mydnsjp` | `MYDNSJP_MASTER_ID`, `MYDNSJP_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/mydnsjp) |
|
||||
| [Mythic Beasts](https://www.mythic-beasts.com) | `mythicbeasts` | `MYTHICBEASTS_USER_NAME`, `MYTHICBEASTS_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/mythicbeasts) |
|
||||
| [name.com](https://www.name.com/) | `namedotcom` | `NAMECOM_USERNAME`, `NAMECOM_API_TOKEN`, `NAMECOM_SERVER` | [Additional configuration](https://go-acme.github.io/lego/dns/namedotcom) |
|
||||
| [Namecheap](https://www.namecheap.com) | `namecheap` | `NAMECHEAP_API_USER`, `NAMECHEAP_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/namecheap) |
|
||||
| [Namesilo](https://www.namesilo.com/) | `namesilo` | `NAMESILO_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/namesilo) |
|
||||
| [Netcup](https://www.netcup.eu/) | `netcup` | `NETCUP_CUSTOMER_NUMBER`, `NETCUP_API_KEY`, `NETCUP_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/netcup) |
|
||||
| [Netlify](https://www.netlify.com) | `netlify` | `NETLIFY_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/netlify) |
|
||||
| [Nicmanager](https://www.nicmanager.com) | `nicmanager` | `NICMANAGER_API_EMAIL`, `NICMANAGER_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/nicmanager) |
|
||||
| [NIFCloud](https://cloud.nifty.com/service/dns.htm) | `nifcloud` | `NIFCLOUD_ACCESS_KEY_ID`, `NIFCLOUD_SECRET_ACCESS_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/nifcloud) |
|
||||
| [Njalla](https://njal.la) | `njalla` | `NJALLA_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/njalla) |
|
||||
| [NS1](https://ns1.com/) | `ns1` | `NS1_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/ns1) |
|
||||
| [Open Telekom Cloud](https://cloud.telekom.de) | `otc` | `OTC_DOMAIN_NAME`, `OTC_USER_NAME`, `OTC_PASSWORD`, `OTC_PROJECT_NAME`, `OTC_IDENTITY_ENDPOINT` | [Additional configuration](https://go-acme.github.io/lego/dns/otc) |
|
||||
| [Openstack Designate](https://docs.openstack.org/designate) | `designate` | `OS_AUTH_URL`, `OS_USERNAME`, `OS_PASSWORD`, `OS_TENANT_NAME`, `OS_REGION_NAME` | [Additional configuration](https://go-acme.github.io/lego/dns/designate) |
|
||||
| [Oracle Cloud](https://cloud.oracle.com/home) | `oraclecloud` | `OCI_COMPARTMENT_OCID`, `OCI_PRIVKEY_FILE`, `OCI_PRIVKEY_PASS`, `OCI_PUBKEY_FINGERPRINT`, `OCI_REGION`, `OCI_TENANCY_OCID`, `OCI_USER_OCID` | [Additional configuration](https://go-acme.github.io/lego/dns/oraclecloud) |
|
||||
| [OVH](https://www.ovh.com) | `ovh` | `OVH_ENDPOINT`, `OVH_APPLICATION_KEY`, `OVH_APPLICATION_SECRET`, `OVH_CONSUMER_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/ovh) |
|
||||
| [Porkbun](https://porkbun.com/) | `porkbun` | `PORKBUN_SECRET_API_KEY`, `PORKBUN_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/porkbun) |
|
||||
| [PowerDNS](https://www.powerdns.com) | `pdns` | `PDNS_API_KEY`, `PDNS_API_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/pdns) |
|
||||
| [Rackspace](https://www.rackspace.com/cloud/dns) | `rackspace` | `RACKSPACE_USER`, `RACKSPACE_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/rackspace) |
|
||||
| [reg.ru](https://www.reg.ru) | `regru` | `REGRU_USERNAME`, `REGRU_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/regru) |
|
||||
| [RFC2136](https://tools.ietf.org/html/rfc2136) | `rfc2136` | `RFC2136_TSIG_KEY`, `RFC2136_TSIG_SECRET`, `RFC2136_TSIG_ALGORITHM`, `RFC2136_NAMESERVER` | [Additional configuration](https://go-acme.github.io/lego/dns/rfc2136) |
|
||||
| [RimuHosting](https://rimuhosting.com) | `rimuhosting` | `RIMUHOSTING_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/rimuhosting) |
|
||||
| [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. | [Additional configuration](https://go-acme.github.io/lego/dns/route53) |
|
||||
| [Sakura Cloud](https://cloud.sakura.ad.jp/) | `sakuracloud` | `SAKURACLOUD_ACCESS_TOKEN`, `SAKURACLOUD_ACCESS_TOKEN_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/sakuracloud) |
|
||||
| [Scaleway](https://www.scaleway.com) | `scaleway` | `SCALEWAY_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/scaleway) |
|
||||
| [Selectel](https://selectel.ru/en/) | `selectel` | `SELECTEL_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/selectel) |
|
||||
| [Servercow](https://servercow.de) | `servercow` | `SERVERCOW_USERNAME`, `SERVERCOW_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/servercow) |
|
||||
| [Simply.com](https://www.simply.com/en/domains/) | `simply` | `SIMPLY_ACCOUNT_NAME`, `SIMPLY_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/simply) |
|
||||
| [Sonic](https://www.sonic.com/) | `sonic` | `SONIC_USER_ID`, `SONIC_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/sonic) |
|
||||
| [Stackpath](https://www.stackpath.com/) | `stackpath` | `STACKPATH_CLIENT_ID`, `STACKPATH_CLIENT_SECRET`, `STACKPATH_STACK_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/stackpath) |
|
||||
| [Tencent Cloud DNS](https://cloud.tencent.com/product/cns) | `tencentcloud` | `TENCENTCLOUD_SECRET_ID`, `TENCENTCLOUD_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/tencentcloud) |
|
||||
| [TransIP](https://www.transip.nl/) | `transip` | `TRANSIP_ACCOUNT_NAME`, `TRANSIP_PRIVATE_KEY_PATH` | [Additional configuration](https://go-acme.github.io/lego/dns/transip) |
|
||||
| [UKFast SafeDNS](https://www.ans.co.uk/cloud-and-infrastructure/dedicated-servers/dns-management/) | `safedns` | `SAFEDNS_AUTH_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/safedns) |
|
||||
| [VegaDNS](https://github.com/shupp/VegaDNS-API) | `vegadns` | `SECRET_VEGADNS_KEY`, `SECRET_VEGADNS_SECRET`, `VEGADNS_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/vegadns) |
|
||||
| [Vercel](https://vercel.com) | `vercel` | `VERCEL_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/vercel) |
|
||||
| [Versio](https://www.versio.nl/domeinnamen) | `versio` | `VERSIO_USERNAME`, `VERSIO_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/versio) |
|
||||
| [VinylDNS](https://www.vinyldns.io) | `vinyldns` | `VINYLDNS_ACCESS_KEY`, `VINYLDNS_SECRET_KEY`, `VINYLDNS_HOST` | [Additional configuration](https://go-acme.github.io/lego/dns/vinyldns) |
|
||||
| [Vscale](https://vscale.io/) | `vscale` | `VSCALE_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/vscale) |
|
||||
| [VULTR](https://www.vultr.com) | `vultr` | `VULTR_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/vultr) |
|
||||
| [WEDOS](https://www.wedos.com) | `wedos` | `WEDOS_USERNAME`, `WEDOS_WAPI_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/wedos) |
|
||||
| [Yandex](https://yandex.com) | `yandex` | `YANDEX_PDD_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/yandex) |
|
||||
| [Zone.ee](https://www.zone.ee) | `zoneee` | `ZONEEE_API_USER`, `ZONEEE_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/zoneee) |
|
||||
| [Zonomi](https://zonomi.com) | `zonomi` | `ZONOMI_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/zonomi) |
|
||||
| External Program | `exec` | `EXEC_PATH` | [Additional configuration](https://go-acme.github.io/lego/dns/exec) |
|
||||
| HTTP request | `httpreq` | `HTTPREQ_ENDPOINT`, `HTTPREQ_MODE`, `HTTPREQ_USERNAME`, `HTTPREQ_PASSWORD` [^1] | [Additional configuration](https://go-acme.github.io/lego/dns/httpreq) |
|
||||
| manual | `manual` | none, but you need to run Traefik interactively [^4], turn on debug log to see instructions and press <kbd>Enter</kbd>. | |
|
||||
| Provider Name | Provider Code | Environment Variables | |
|
||||
|----------------------------------------------------------------------------------------------------|--------------------|---------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------|
|
||||
| [ACME DNS](https://github.com/joohoi/acme-dns) | `acme-dns` | `ACME_DNS_API_BASE`, `ACME_DNS_STORAGE_PATH` | [Additional configuration](https://go-acme.github.io/lego/dns/acme-dns) |
|
||||
| [Alibaba Cloud](https://www.alibabacloud.com) | `alidns` | `ALICLOUD_ACCESS_KEY`, `ALICLOUD_SECRET_KEY`, `ALICLOUD_REGION_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/alidns) |
|
||||
| [all-inkl](https://all-inkl.com) | `allinkl` | `ALL_INKL_LOGIN`, `ALL_INKL_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/allinkl) |
|
||||
| [ArvanCloud](https://www.arvancloud.com/en) | `arvancloud` | `ARVANCLOUD_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/arvancloud) |
|
||||
| [Auroradns](https://www.pcextreme.com/dns-health-checks) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` | [Additional configuration](https://go-acme.github.io/lego/dns/auroradns) |
|
||||
| [Autodns](https://www.internetx.com/domains/autodns/) | `autodns` | `AUTODNS_API_USER`, `AUTODNS_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/autodns) |
|
||||
| [Azure](https://azure.microsoft.com/services/dns/) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP`, `[AZURE_METADATA_ENDPOINT]` | [Additional configuration](https://go-acme.github.io/lego/dns/azure) |
|
||||
| [Bindman](https://github.com/labbsr0x/bindman-dns-webhook) | `bindman` | `BINDMAN_MANAGER_ADDRESS` | [Additional configuration](https://go-acme.github.io/lego/dns/bindman) |
|
||||
| [Blue Cat](https://www.bluecatnetworks.com/) | `bluecat` | `BLUECAT_SERVER_URL`, `BLUECAT_USER_NAME`, `BLUECAT_PASSWORD`, `BLUECAT_CONFIG_NAME`, `BLUECAT_DNS_VIEW` | [Additional configuration](https://go-acme.github.io/lego/dns/bluecat) |
|
||||
| [Checkdomain](https://www.checkdomain.de/) | `checkdomain` | `CHECKDOMAIN_TOKEN`, | [Additional configuration](https://go-acme.github.io/lego/dns/checkdomain/) |
|
||||
| [Civo](https://www.civo.com/) | `civo` | `CIVO_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/civo) |
|
||||
| [CloudDNS](https://vshosting.eu/) | `clouddns` | `CLOUDDNS_CLIENT_ID`, `CLOUDDNS_EMAIL`, `CLOUDDNS_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/clouddns) |
|
||||
| [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CF_API_EMAIL`, `CF_API_KEY` [^5] or `CF_DNS_API_TOKEN`, `[CF_ZONE_API_TOKEN]` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudflare) |
|
||||
| [ClouDNS](https://www.cloudns.net/) | `cloudns` | `CLOUDNS_AUTH_ID`, `CLOUDNS_AUTH_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudns) |
|
||||
| [CloudXNS](https://www.cloudxns.net) | `cloudxns` | `CLOUDXNS_API_KEY`, `CLOUDXNS_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudxns) |
|
||||
| [ConoHa](https://www.conoha.jp) | `conoha` | `CONOHA_TENANT_ID`, `CONOHA_API_USERNAME`, `CONOHA_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/conoha) |
|
||||
| [Constellix](https://constellix.com) | `constellix` | `CONSTELLIX_API_KEY`, `CONSTELLIX_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/constellix) |
|
||||
| [deSEC](https://desec.io) | `desec` | `DESEC_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/desec) |
|
||||
| [DigitalOcean](https://www.digitalocean.com) | `digitalocean` | `DO_AUTH_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/digitalocean) |
|
||||
| [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` | [Additional configuration](https://go-acme.github.io/lego/dns/dnsmadeeasy) |
|
||||
| [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/dnsimple) |
|
||||
| [DNSPod](https://www.dnspod.com/) | `dnspod` | `DNSPOD_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/dnspod) |
|
||||
| [Domain Offensive (do.de)](https://www.do.de/) | `dode` | `DODE_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/dode) |
|
||||
| [Domeneshop](https://domene.shop) | `domeneshop` | `DOMENESHOP_API_TOKEN`, `DOMENESHOP_API_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/domeneshop) |
|
||||
| [DreamHost](https://www.dreamhost.com/) | `dreamhost` | `DREAMHOST_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/dreamhost) |
|
||||
| [Duck DNS](https://www.duckdns.org/) | `duckdns` | `DUCKDNS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/duckdns) |
|
||||
| [Dyn](https://dyn.com) | `dyn` | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/dyn) |
|
||||
| [Dynu](https://www.dynu.com) | `dynu` | `DYNU_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/dynu) |
|
||||
| [EasyDNS](https://easydns.com/) | `easydns` | `EASYDNS_TOKEN`, `EASYDNS_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/easydns) |
|
||||
| [EdgeDNS](https://www.akamai.com/) | `edgedns` | `AKAMAI_CLIENT_TOKEN`, `AKAMAI_CLIENT_SECRET`, `AKAMAI_ACCESS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/edgedns) |
|
||||
| [Epik](https://www.epik.com) | `epik` | `EPIK_SIGNATURE` | [Additional configuration](https://go-acme.github.io/lego/dns/epik) |
|
||||
| [Exoscale](https://www.exoscale.com) | `exoscale` | `EXOSCALE_API_KEY`, `EXOSCALE_API_SECRET`, `EXOSCALE_ENDPOINT` | [Additional configuration](https://go-acme.github.io/lego/dns/exoscale) |
|
||||
| [Fast DNS](https://www.akamai.com/) | `fastdns` | `AKAMAI_CLIENT_TOKEN`, `AKAMAI_CLIENT_SECRET`, `AKAMAI_ACCESS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/edgedns) |
|
||||
| [Freemyip.com](https://freemyip.com) | `freemyip` | `FREEMYIP_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/freemyip) |
|
||||
| [G-Core Lab](https://gcorelabs.com/dns/) | `gcore` | `GCORE_PERMANENT_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/gcore) |
|
||||
| [Gandi v5](https://doc.livedns.gandi.net) | `gandiv5` | `GANDIV5_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/gandiv5) |
|
||||
| [Gandi](https://www.gandi.net) | `gandi` | `GANDI_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/gandi) |
|
||||
| [Glesys](https://glesys.com/) | `glesys` | `GLESYS_API_USER`, `GLESYS_API_KEY`, `GLESYS_DOMAIN` | [Additional configuration](https://go-acme.github.io/lego/dns/glesys) |
|
||||
| [GoDaddy](https://godaddy.com/) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/godaddy) |
|
||||
| [Google Cloud DNS](https://cloud.google.com/dns/docs/) | `gcloud` | `GCE_PROJECT`, Application Default Credentials [^2] [^3], [`GCE_SERVICE_ACCOUNT_FILE`] | [Additional configuration](https://go-acme.github.io/lego/dns/gcloud) |
|
||||
| [Hetzner](https://hetzner.com) | `hetzner` | `HETZNER_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/hetzner) |
|
||||
| [hosting.de](https://www.hosting.de) | `hostingde` | `HOSTINGDE_API_KEY`, `HOSTINGDE_ZONE_NAME` | [Additional configuration](https://go-acme.github.io/lego/dns/hostingde) |
|
||||
| [Hosttech](https://www.hosttech.eu) | `hosttech` | `HOSTTECH_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/hosttech) |
|
||||
| [Hurricane Electric](https://dns.he.net) | `hurricane` | `HURRICANE_TOKENS` [^6] | [Additional configuration](https://go-acme.github.io/lego/dns/hurricane) |
|
||||
| [HyperOne](https://www.hyperone.com) | `hyperone` | `HYPERONE_PASSPORT_LOCATION`, `HYPERONE_LOCATION_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/hyperone) |
|
||||
| [IBM Cloud (SoftLayer)](https://www.ibm.com/cloud/) | `ibmcloud` | `SOFTLAYER_USERNAME`, `SOFTLAYER_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/ibmcloud) |
|
||||
| [IIJ DNS Platform Service](https://www.iij.ad.jp) | `iijdpf` | `IIJ_DPF_API_TOKEN` , `IIJ_DPF_DPM_SERVICE_CODE` | [Additional configuration](https://go-acme.github.io/lego/dns/iijdpf) |
|
||||
| [IIJ](https://www.iij.ad.jp/) | `iij` | `IIJ_API_ACCESS_KEY`, `IIJ_API_SECRET_KEY`, `IIJ_DO_SERVICE_CODE` | [Additional configuration](https://go-acme.github.io/lego/dns/iij) |
|
||||
| [Infoblox](https://www.infoblox.com/) | `infoblox` | `INFOBLOX_USERNAME`, `INFOBLOX_PASSWORD`, `INFOBLOX_HOST` | [Additional configuration](https://go-acme.github.io/lego/dns/infoblox) |
|
||||
| [Infomaniak](https://www.infomaniak.com) | `infomaniak` | `INFOMANIAK_ACCESS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/infomaniak) |
|
||||
| [Internet.bs](https://internetbs.net) | `internetbs` | `INTERNET_BS_API_KEY`, `INTERNET_BS_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/internetbs) |
|
||||
| [INWX](https://www.inwx.de/en) | `inwx` | `INWX_USERNAME`, `INWX_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/inwx) |
|
||||
| [ionos](https://ionos.com/) | `ionos` | `IONOS_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/ionos) |
|
||||
| [iwantmyname](https://iwantmyname.com) | `iwantmyname` | `IWANTMYNAME_USERNAME` , `IWANTMYNAME_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/iwantmyname) |
|
||||
| [Joker.com](https://joker.com) | `joker` | `JOKER_API_MODE` with `JOKER_API_KEY` or `JOKER_USERNAME`, `JOKER_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/joker) |
|
||||
| [Lightsail](https://aws.amazon.com/lightsail/) | `lightsail` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `DNS_ZONE` | [Additional configuration](https://go-acme.github.io/lego/dns/lightsail) |
|
||||
| [Linode v4](https://www.linode.com) | `linode` | `LINODE_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/linode) |
|
||||
| [Liquid Web](https://www.liquidweb.com/) | `liquidweb` | `LIQUID_WEB_PASSWORD`, `LIQUID_WEB_USERNAME`, `LIQUID_WEB_ZONE` | [Additional configuration](https://go-acme.github.io/lego/dns/liquidweb) |
|
||||
| [Loopia](https://loopia.com/) | `loopia` | `LOOPIA_API_PASSWORD`, `LOOPIA_API_USER` | [Additional configuration](https://go-acme.github.io/lego/dns/loopia) |
|
||||
| [LuaDNS](https://luadns.com) | `luadns` | `LUADNS_API_USERNAME`, `LUADNS_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/luadns) |
|
||||
| [MyDNS.jp](https://www.mydns.jp/) | `mydnsjp` | `MYDNSJP_MASTER_ID`, `MYDNSJP_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/mydnsjp) |
|
||||
| [Mythic Beasts](https://www.mythic-beasts.com) | `mythicbeasts` | `MYTHICBEASTS_USER_NAME`, `MYTHICBEASTS_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/mythicbeasts) |
|
||||
| [name.com](https://www.name.com/) | `namedotcom` | `NAMECOM_USERNAME`, `NAMECOM_API_TOKEN`, `NAMECOM_SERVER` | [Additional configuration](https://go-acme.github.io/lego/dns/namedotcom) |
|
||||
| [Namecheap](https://www.namecheap.com) | `namecheap` | `NAMECHEAP_API_USER`, `NAMECHEAP_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/namecheap) |
|
||||
| [Namesilo](https://www.namesilo.com/) | `namesilo` | `NAMESILO_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/namesilo) |
|
||||
| [NearlyFreeSpeech.NET](https://www.nearlyfreespeech.net/) | `nearlyfreespeech` | `NEARLYFREESPEECH_API_KEY`, `NEARLYFREESPEECH_LOGIN` | [Additional configuration](https://go-acme.github.io/lego/dns/nearlyfreespeech) |
|
||||
| [Netcup](https://www.netcup.eu/) | `netcup` | `NETCUP_CUSTOMER_NUMBER`, `NETCUP_API_KEY`, `NETCUP_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/netcup) |
|
||||
| [Netlify](https://www.netlify.com) | `netlify` | `NETLIFY_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/netlify) |
|
||||
| [Nicmanager](https://www.nicmanager.com) | `nicmanager` | `NICMANAGER_API_EMAIL`, `NICMANAGER_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/nicmanager) |
|
||||
| [NIFCloud](https://cloud.nifty.com/service/dns.htm) | `nifcloud` | `NIFCLOUD_ACCESS_KEY_ID`, `NIFCLOUD_SECRET_ACCESS_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/nifcloud) |
|
||||
| [Njalla](https://njal.la) | `njalla` | `NJALLA_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/njalla) |
|
||||
| [NS1](https://ns1.com/) | `ns1` | `NS1_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/ns1) |
|
||||
| [Open Telekom Cloud](https://cloud.telekom.de) | `otc` | `OTC_DOMAIN_NAME`, `OTC_USER_NAME`, `OTC_PASSWORD`, `OTC_PROJECT_NAME`, `OTC_IDENTITY_ENDPOINT` | [Additional configuration](https://go-acme.github.io/lego/dns/otc) |
|
||||
| [Openstack Designate](https://docs.openstack.org/designate) | `designate` | `OS_AUTH_URL`, `OS_USERNAME`, `OS_PASSWORD`, `OS_TENANT_NAME`, `OS_REGION_NAME` | [Additional configuration](https://go-acme.github.io/lego/dns/designate) |
|
||||
| [Oracle Cloud](https://cloud.oracle.com/home) | `oraclecloud` | `OCI_COMPARTMENT_OCID`, `OCI_PRIVKEY_FILE`, `OCI_PRIVKEY_PASS`, `OCI_PUBKEY_FINGERPRINT`, `OCI_REGION`, `OCI_TENANCY_OCID`, `OCI_USER_OCID` | [Additional configuration](https://go-acme.github.io/lego/dns/oraclecloud) |
|
||||
| [OVH](https://www.ovh.com) | `ovh` | `OVH_ENDPOINT`, `OVH_APPLICATION_KEY`, `OVH_APPLICATION_SECRET`, `OVH_CONSUMER_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/ovh) |
|
||||
| [Porkbun](https://porkbun.com/) | `porkbun` | `PORKBUN_SECRET_API_KEY`, `PORKBUN_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/porkbun) |
|
||||
| [PowerDNS](https://www.powerdns.com) | `pdns` | `PDNS_API_KEY`, `PDNS_API_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/pdns) |
|
||||
| [Rackspace](https://www.rackspace.com/cloud/dns) | `rackspace` | `RACKSPACE_USER`, `RACKSPACE_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/rackspace) |
|
||||
| [reg.ru](https://www.reg.ru) | `regru` | `REGRU_USERNAME`, `REGRU_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/regru) |
|
||||
| [RFC2136](https://tools.ietf.org/html/rfc2136) | `rfc2136` | `RFC2136_TSIG_KEY`, `RFC2136_TSIG_SECRET`, `RFC2136_TSIG_ALGORITHM`, `RFC2136_NAMESERVER` | [Additional configuration](https://go-acme.github.io/lego/dns/rfc2136) |
|
||||
| [RimuHosting](https://rimuhosting.com) | `rimuhosting` | `RIMUHOSTING_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/rimuhosting) |
|
||||
| [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. | [Additional configuration](https://go-acme.github.io/lego/dns/route53) |
|
||||
| [Sakura Cloud](https://cloud.sakura.ad.jp/) | `sakuracloud` | `SAKURACLOUD_ACCESS_TOKEN`, `SAKURACLOUD_ACCESS_TOKEN_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/sakuracloud) |
|
||||
| [Scaleway](https://www.scaleway.com) | `scaleway` | `SCALEWAY_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/scaleway) |
|
||||
| [Selectel](https://selectel.ru/en/) | `selectel` | `SELECTEL_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/selectel) |
|
||||
| [Servercow](https://servercow.de) | `servercow` | `SERVERCOW_USERNAME`, `SERVERCOW_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/servercow) |
|
||||
| [Simply.com](https://www.simply.com/en/domains/) | `simply` | `SIMPLY_ACCOUNT_NAME`, `SIMPLY_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/simply) |
|
||||
| [Sonic](https://www.sonic.com/) | `sonic` | `SONIC_USER_ID`, `SONIC_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/sonic) |
|
||||
| [Stackpath](https://www.stackpath.com/) | `stackpath` | `STACKPATH_CLIENT_ID`, `STACKPATH_CLIENT_SECRET`, `STACKPATH_STACK_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/stackpath) |
|
||||
| [Tencent Cloud DNS](https://cloud.tencent.com/product/cns) | `tencentcloud` | `TENCENTCLOUD_SECRET_ID`, `TENCENTCLOUD_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/tencentcloud) |
|
||||
| [TransIP](https://www.transip.nl/) | `transip` | `TRANSIP_ACCOUNT_NAME`, `TRANSIP_PRIVATE_KEY_PATH` | [Additional configuration](https://go-acme.github.io/lego/dns/transip) |
|
||||
| [UKFast SafeDNS](https://www.ans.co.uk/cloud-and-infrastructure/dedicated-servers/dns-management/) | `safedns` | `SAFEDNS_AUTH_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/safedns) |
|
||||
| [Variomedia](https://www.variomedia.de/) | `variomedia` | `VARIOMEDIA_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/variomedia) |
|
||||
| [VegaDNS](https://github.com/shupp/VegaDNS-API) | `vegadns` | `SECRET_VEGADNS_KEY`, `SECRET_VEGADNS_SECRET`, `VEGADNS_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/vegadns) |
|
||||
| [Vercel](https://vercel.com) | `vercel` | `VERCEL_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/vercel) |
|
||||
| [Versio](https://www.versio.nl/domeinnamen) | `versio` | `VERSIO_USERNAME`, `VERSIO_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/versio) |
|
||||
| [VinylDNS](https://www.vinyldns.io) | `vinyldns` | `VINYLDNS_ACCESS_KEY`, `VINYLDNS_SECRET_KEY`, `VINYLDNS_HOST` | [Additional configuration](https://go-acme.github.io/lego/dns/vinyldns) |
|
||||
| [VK Cloud](https://mcs.mail.ru/) | `vkcloud` | `VK_CLOUD_PASSWORD`, `VK_CLOUD_PROJECT_ID`, `VK_CLOUD_USERNAME` | [Additional configuration](https://go-acme.github.io/lego/dns/vkcloud) |
|
||||
| [Vscale](https://vscale.io/) | `vscale` | `VSCALE_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/vscale) |
|
||||
| [VULTR](https://www.vultr.com) | `vultr` | `VULTR_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/vultr) |
|
||||
| [WEDOS](https://www.wedos.com) | `wedos` | `WEDOS_USERNAME`, `WEDOS_WAPI_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/wedos) |
|
||||
| [Yandex Cloud](https://cloud.yandex.com/en/) | `yandexcloud` | `YANDEX_CLOUD_FOLDER_ID`, `YANDEX_CLOUD_IAM_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/yandexcloud) |
|
||||
| [Yandex](https://yandex.com) | `yandex` | `YANDEX_PDD_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/yandex) |
|
||||
| [Zone.ee](https://www.zone.ee) | `zoneee` | `ZONEEE_API_USER`, `ZONEEE_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/zoneee) |
|
||||
| [Zonomi](https://zonomi.com) | `zonomi` | `ZONOMI_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/zonomi) |
|
||||
| External Program | `exec` | `EXEC_PATH` | [Additional configuration](https://go-acme.github.io/lego/dns/exec) |
|
||||
| HTTP request | `httpreq` | `HTTPREQ_ENDPOINT`, `HTTPREQ_MODE`, `HTTPREQ_USERNAME`, `HTTPREQ_PASSWORD` [^1] | [Additional configuration](https://go-acme.github.io/lego/dns/httpreq) |
|
||||
| manual | `manual` | none, but you need to run Traefik interactively [^4], turn on debug log to see instructions and press <kbd>Enter</kbd>. | |
|
||||
|
||||
[^1]: More information about the HTTP message format can be found [here](https://go-acme.github.io/lego/dns/httpreq/).
|
||||
[^2]: [Providing credentials to your application](https://cloud.google.com/docs/authentication/production).
|
||||
|
||||
@@ -16,10 +16,10 @@ PassTLSClientCert adds the selected data from the passed client TLS certificate
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
Pass the escaped pem in the `X-Forwarded-Tls-Client-Cert` header.
|
||||
Pass the pem in the `X-Forwarded-Tls-Client-Cert` header.
|
||||
|
||||
```yaml tab="Docker"
|
||||
# Pass the escaped pem in the `X-Forwarded-Tls-Client-Cert` header.
|
||||
# Pass the pem in the `X-Forwarded-Tls-Client-Cert` header.
|
||||
labels:
|
||||
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.pem=true"
|
||||
```
|
||||
@@ -35,7 +35,7 @@ spec:
|
||||
```
|
||||
|
||||
```yaml tab="Consul Catalog"
|
||||
# Pass the escaped pem in the `X-Forwarded-Tls-Client-Cert` header
|
||||
# Pass the pem in the `X-Forwarded-Tls-Client-Cert` header
|
||||
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.pem=true"
|
||||
```
|
||||
|
||||
@@ -46,13 +46,13 @@ spec:
|
||||
```
|
||||
|
||||
```yaml tab="Rancher"
|
||||
# Pass the escaped pem in the `X-Forwarded-Tls-Client-Cert` header.
|
||||
# Pass the pem in the `X-Forwarded-Tls-Client-Cert` header.
|
||||
labels:
|
||||
- "traefik.http.middlewares.test-passtlsclientcert.passtlsclientcert.pem=true"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Pass the escaped pem in the `X-Forwarded-Tls-Client-Cert` header.
|
||||
# Pass the pem in the `X-Forwarded-Tls-Client-Cert` header.
|
||||
http:
|
||||
middlewares:
|
||||
test-passtlsclientcert:
|
||||
@@ -61,13 +61,13 @@ http:
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Pass the escaped pem in the `X-Forwarded-Tls-Client-Cert` header.
|
||||
# Pass the pem in the `X-Forwarded-Tls-Client-Cert` header.
|
||||
[http.middlewares]
|
||||
[http.middlewares.test-passtlsclientcert.passTLSClientCert]
|
||||
pem = true
|
||||
```
|
||||
|
||||
??? example "Pass the escaped pem in the `X-Forwarded-Tls-Client-Cert` header"
|
||||
??? example "Pass the pem in the `X-Forwarded-Tls-Client-Cert` header"
|
||||
|
||||
```yaml tab="Docker"
|
||||
# Pass all the available info in the `X-Forwarded-Tls-Client-Cert-Info` header
|
||||
@@ -254,12 +254,12 @@ http:
|
||||
|
||||
PassTLSClientCert can add two headers to the request:
|
||||
|
||||
- `X-Forwarded-Tls-Client-Cert` that contains the escaped pem.
|
||||
- `X-Forwarded-Tls-Client-Cert` that contains the pem.
|
||||
- `X-Forwarded-Tls-Client-Cert-Info` that contains all the selected certificate information in an escaped string.
|
||||
|
||||
!!! info
|
||||
|
||||
* Each header value is a string that has been escaped in order to be a valid URL query.
|
||||
* `X-Forwarded-Tls-Client-Cert-Info` header value is a string that has been escaped in order to be a valid URL query.
|
||||
* These options only work accordingly to the [MutualTLS configuration](../../https/tls.md#client-authentication-mtls).
|
||||
That is to say, only the certificates that match the `clientAuth.clientAuthType` policy are passed.
|
||||
|
||||
@@ -371,7 +371,7 @@ The following example shows a complete certificate and explains each of the midd
|
||||
|
||||
### `pem`
|
||||
|
||||
The `pem` option sets the `X-Forwarded-Tls-Client-Cert` header with the escaped certificate.
|
||||
The `pem` option sets the `X-Forwarded-Tls-Client-Cert` header with the certificate.
|
||||
|
||||
In the example, it is the part between `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----` delimiters:
|
||||
|
||||
|
||||
@@ -444,8 +444,8 @@ To enable HTTP/3 on an EntryPoint, please check out the [HTTP/3 configuration](.
|
||||
|
||||
### Kubernetes Gateway API Provider
|
||||
|
||||
In `v2.6`, the [Kubernetes Gateway API provider](../providers/kubernetes-gateway.md) now only supports the version [v1alpha2](https://gateway-api.sigs.k8s.io/v1alpha2/guides/getting-started/) of the specification and
|
||||
[route namespaces](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.RouteNamespaces) selectors, which requires Traefik to fetch and watch the cluster namespaces.
|
||||
In `v2.6`, the [Kubernetes Gateway API provider](../providers/kubernetes-gateway.md) now only supports the version [v1alpha2](https://gateway-api.sigs.k8s.io/v1alpha2/guides/) of the specification and
|
||||
[route namespaces](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.RouteNamespaces) selectors, which requires Traefik to fetch and watch the cluster namespaces.
|
||||
Therefore, the [RBAC](../reference/dynamic-configuration/kubernetes-gateway.md#rbac) and [CRD](../reference/dynamic-configuration/kubernetes-gateway.md#definitions) definitions must be updated.
|
||||
|
||||
## v2.6.0 to v2.6.1
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
---
|
||||
title: "Traefik Metrics Overview"
|
||||
description: "Traefik Proxy supports four metrics backend systems: Datadog, InfluxDB, Prometheus, and StatsD. Read the full documentation to get started."
|
||||
description: "Traefik Proxy supports these metrics backend systems: Datadog, InfluxDB, Prometheus, and StatsD. Read the full documentation to get started."
|
||||
---
|
||||
|
||||
# Metrics
|
||||
|
||||
Traefik supports 4 metrics backends:
|
||||
Traefik supports these metrics backends:
|
||||
|
||||
- [Datadog](./datadog.md)
|
||||
- [InfluxDB](./influxdb.md)
|
||||
@@ -15,569 +15,188 @@ Traefik supports 4 metrics backends:
|
||||
|
||||
## Global Metrics
|
||||
|
||||
| Metric | DataDog | InfluxDB / InfluxDB2 | Prometheus | StatsD |
|
||||
|-------------------------------------------------------------------------|---------|----------------------|------------|--------|
|
||||
| [Configuration reloads](#configuration-reloads) | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Last Configuration Reload Success](#last-configuration-reload-success) | ✓ | ✓ | ✓ | ✓ |
|
||||
| [TLS certificates expiration](#tls-certificates-expiration) | ✓ | ✓ | ✓ | ✓ |
|
||||
| Metric | Type | Description |
|
||||
|---------------------------------------------|---------|---------------------------------------------------------|
|
||||
| Config reload total | Count | The total count of configuration reloads. |
|
||||
| Config reload last success | Gauge | The timestamp of the last configuration reload success. |
|
||||
| TLS certificates not after | Gauge | The expiration date of certificates. |
|
||||
|
||||
### Configuration Reloads
|
||||
|
||||
The total count of configuration reloads.
|
||||
```prom tab="Prometheus"
|
||||
traefik_config_reloads_total
|
||||
traefik_config_last_reload_success
|
||||
traefik_tls_certs_not_after
|
||||
```
|
||||
|
||||
```dd tab="Datadog"
|
||||
config.reload.total
|
||||
config.reload.lastSuccessTimestamp
|
||||
tls.certs.notAfterTimestamp
|
||||
```
|
||||
|
||||
```influxdb tab="InfluxDB / InfluxDB2"
|
||||
traefik.config.reload.total
|
||||
```
|
||||
|
||||
```prom tab="Prometheus"
|
||||
traefik_config_reloads_total
|
||||
traefik.config.reload.lastSuccessTimestamp
|
||||
traefik.tls.certs.notAfterTimestamp
|
||||
```
|
||||
|
||||
```statsd tab="StatsD"
|
||||
# Default prefix: "traefik"
|
||||
{prefix}.config.reload.total
|
||||
```
|
||||
|
||||
### Last Configuration Reload Success
|
||||
|
||||
The timestamp of the last configuration reload success.
|
||||
|
||||
```dd tab="Datadog"
|
||||
config.reload.lastSuccessTimestamp
|
||||
```
|
||||
|
||||
```influxdb tab="InfluxDB / InfluxDB2"
|
||||
traefik.config.reload.lastSuccessTimestamp
|
||||
```
|
||||
|
||||
```prom tab="Prometheus"
|
||||
traefik_config_last_reload_success
|
||||
```
|
||||
|
||||
```statsd tab="StatsD"
|
||||
# Default prefix: "traefik"
|
||||
{prefix}.config.reload.lastSuccessTimestamp
|
||||
```
|
||||
|
||||
### TLS certificates expiration
|
||||
|
||||
The expiration date of certificates.
|
||||
|
||||
[Labels](#labels): `cn`, `sans`, `serial`.
|
||||
|
||||
```dd tab="Datadog"
|
||||
tls.certs.notAfterTimestamp
|
||||
```
|
||||
|
||||
```influxdb tab="InfluxDB / InfluxDB2"
|
||||
traefik.tls.certs.notAfterTimestamp
|
||||
```
|
||||
|
||||
```prom tab="Prometheus"
|
||||
traefik_tls_certs_not_after
|
||||
```
|
||||
|
||||
```statsd tab="StatsD"
|
||||
# Default prefix: "traefik"
|
||||
{prefix}.tls.certs.notAfterTimestamp
|
||||
```
|
||||
|
||||
## EntryPoint Metrics
|
||||
|
||||
| Metric | DataDog | InfluxDB / InfluxDB2 | Prometheus | StatsD |
|
||||
|-----------------------------------------------------------|---------|----------------------|------------|--------|
|
||||
| [HTTP Requests Count](#http-requests-count) | ✓ | ✓ | ✓ | ✓ |
|
||||
| [HTTPS Requests Count](#https-requests-count) | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Request Duration Histogram](#request-duration-histogram) | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Open Connections Count](#open-connections-count) | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Requests Bytes Count](#requests-bytes-count) | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Responses Bytes Count](#responses-bytes-count) | ✓ | ✓ | ✓ | ✓ |
|
||||
| Metric | Type | [Labels](#labels) | Description |
|
||||
|-----------------------|-----------|--------------------------------------------|---------------------------------------------------------------------|
|
||||
| Requests total | Count | `code`, `method`, `protocol`, `entrypoint` | The total count of HTTP requests received by an entrypoint. |
|
||||
| Requests TLS total | Count | `tls_version`, `tls_cipher`, `entrypoint` | The total count of HTTPS requests received by an entrypoint. |
|
||||
| Request duration | Histogram | `code`, `method`, `protocol`, `entrypoint` | Request processing duration histogram on an entrypoint. |
|
||||
| Open connections | Count | `method`, `protocol`, `entrypoint` | The current count of open connections on an entrypoint. |
|
||||
| Requests bytes total | Count | `code`, `method`, `protocol`, `entrypoint` | The total size of HTTP requests in bytes handled by an entrypoint. |
|
||||
| Responses bytes total | Count | `code`, `method`, `protocol`, `entrypoint` | The total size of HTTP responses in bytes handled by an entrypoint. |
|
||||
|
||||
### HTTP Requests Count
|
||||
|
||||
The total count of HTTP requests received by an entrypoint.
|
||||
|
||||
[Labels](#labels): `code`, `method`, `protocol`, `entrypoint`.
|
||||
```prom tab="Prometheus"
|
||||
traefik_entrypoint_requests_total
|
||||
traefik_entrypoint_requests_tls_total
|
||||
traefik_entrypoint_request_duration_seconds
|
||||
traefik_entrypoint_open_connections
|
||||
traefik_entrypoint_requests_bytes_total
|
||||
traefik_entrypoint_responses_bytes_total
|
||||
```
|
||||
|
||||
```dd tab="Datadog"
|
||||
entrypoint.request.total
|
||||
entrypoint.request.tls.total
|
||||
entrypoint.request.duration
|
||||
entrypoint.connections.open
|
||||
entrypoint.requests.bytes.total
|
||||
entrypoint.responses.bytes.total
|
||||
```
|
||||
|
||||
```influxdb tab="InfluxDB / InfluxDB2"
|
||||
traefik.entrypoint.requests.total
|
||||
```
|
||||
|
||||
```prom tab="Prometheus"
|
||||
traefik_entrypoint_requests_total
|
||||
traefik.entrypoint.requests.tls.total
|
||||
traefik.entrypoint.request.duration
|
||||
traefik.entrypoint.connections.open
|
||||
traefik.entrypoint.requests.bytes.total
|
||||
traefik.entrypoint.responses.bytes.total
|
||||
```
|
||||
|
||||
```statsd tab="StatsD"
|
||||
# Default prefix: "traefik"
|
||||
{prefix}.entrypoint.request.total
|
||||
```
|
||||
|
||||
### HTTPS Requests Count
|
||||
|
||||
The total count of HTTPS requests received by an entrypoint.
|
||||
|
||||
[Labels](#labels): `tls_version`, `tls_cipher`, `entrypoint`.
|
||||
|
||||
```dd tab="Datadog"
|
||||
entrypoint.request.tls.total
|
||||
```
|
||||
|
||||
```influxdb tab="InfluxDB / InfluxDB2"
|
||||
traefik.entrypoint.requests.tls.total
|
||||
```
|
||||
|
||||
```prom tab="Prometheus"
|
||||
traefik_entrypoint_requests_tls_total
|
||||
```
|
||||
|
||||
```statsd tab="StatsD"
|
||||
# Default prefix: "traefik"
|
||||
{prefix}.entrypoint.request.tls.total
|
||||
```
|
||||
|
||||
### Request Duration Histogram
|
||||
|
||||
Request processing duration histogram on an entrypoint.
|
||||
|
||||
[Labels](#labels): `code`, `method`, `protocol`, `entrypoint`.
|
||||
|
||||
```dd tab="Datadog"
|
||||
entrypoint.request.duration
|
||||
```
|
||||
|
||||
```influxdb tab="InfluxDB / InfluxDB2"
|
||||
traefik.entrypoint.request.duration
|
||||
```
|
||||
|
||||
```prom tab="Prometheus"
|
||||
traefik_entrypoint_request_duration_seconds
|
||||
```
|
||||
|
||||
```statsd tab="StatsD"
|
||||
# Default prefix: "traefik"
|
||||
{prefix}.entrypoint.request.duration
|
||||
```
|
||||
|
||||
### Open Connections Count
|
||||
|
||||
The current count of open connections on an entrypoint.
|
||||
|
||||
[Labels](#labels): `method`, `protocol`, `entrypoint`.
|
||||
|
||||
```dd tab="Datadog"
|
||||
entrypoint.connections.open
|
||||
```
|
||||
|
||||
```influxdb tab="InfluxDB / InfluxDB2"
|
||||
traefik.entrypoint.connections.open
|
||||
```
|
||||
|
||||
```prom tab="Prometheus"
|
||||
traefik_entrypoint_open_connections
|
||||
```
|
||||
|
||||
```statsd tab="StatsD"
|
||||
# Default prefix: "traefik"
|
||||
{prefix}.entrypoint.connections.open
|
||||
```
|
||||
|
||||
### Requests Bytes Count
|
||||
|
||||
The total size of HTTP requests in bytes handled by an entrypoint.
|
||||
|
||||
[Labels](#labels): `code`, `method`, `protocol`, `entrypoint`.
|
||||
|
||||
```dd tab="Datadog"
|
||||
entrypoint.requests.bytes.total
|
||||
```
|
||||
|
||||
```influxdb tab="InfluxDB / InfluxDB2"
|
||||
traefik.entrypoint.requests.bytes.total
|
||||
```
|
||||
|
||||
```prom tab="Prometheus"
|
||||
traefik_entrypoint_requests_bytes_total
|
||||
```
|
||||
|
||||
```statsd tab="StatsD"
|
||||
# Default prefix: "traefik"
|
||||
{prefix}.entrypoint.requests.bytes.total
|
||||
```
|
||||
|
||||
### Responses Bytes Count
|
||||
|
||||
The total size of HTTP responses in bytes handled by an entrypoint.
|
||||
|
||||
[Labels](#labels): `code`, `method`, `protocol`, `entrypoint`.
|
||||
|
||||
```dd tab="Datadog"
|
||||
entrypoint.responses.bytes.total
|
||||
```
|
||||
|
||||
```influxdb tab="InfluxDB / InfluxDB2"
|
||||
traefik.entrypoint.responses.bytes.total
|
||||
```
|
||||
|
||||
```prom tab="Prometheus"
|
||||
traefik_entrypoint_responses_bytes_total
|
||||
```
|
||||
|
||||
```statsd tab="StatsD"
|
||||
# Default prefix: "traefik"
|
||||
{prefix}.entrypoint.responses.bytes.total
|
||||
```
|
||||
|
||||
## Router Metrics
|
||||
|
||||
| Metric | DataDog | InfluxDB / InfluxDB2 | Prometheus | StatsD |
|
||||
|-------------------------------------------------------------|---------|----------------------|------------|--------|
|
||||
| [HTTP Requests Count](#http-requests-count_1) | ✓ | ✓ | ✓ | ✓ |
|
||||
| [HTTPS Requests Count](#https-requests-count_1) | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Request Duration Histogram](#request-duration-histogram_1) | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Open Connections Count](#open-connections-count_1) | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Requests Bytes Count](#requests-bytes-count_1) | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Responses Bytes Count](#responses-bytes-count_1) | ✓ | ✓ | ✓ | ✓ |
|
||||
| Metric | Type | [Labels](#labels) | Description |
|
||||
|-----------------------|-----------|---------------------------------------------------|----------------------------------------------------------------|
|
||||
| Requests total | Count | `code`, `method`, `protocol`, `router`, `service` | The total count of HTTP requests handled by a router. |
|
||||
| Requests TLS total | Count | `tls_version`, `tls_cipher`, `router`, `service` | The total count of HTTPS requests handled by a router. |
|
||||
| Request duration | Histogram | `code`, `method`, `protocol`, `router`, `service` | Request processing duration histogram on a router. |
|
||||
| Open connections | Count | `method`, `protocol`, `router`, `service` | The current count of open connections on a router. |
|
||||
| Requests bytes total | Count | `code`, `method`, `protocol`, `router`, `service` | The total size of HTTP requests in bytes handled by a router. |
|
||||
| Responses bytes total | Count | `code`, `method`, `protocol`, `router`, `service` | The total size of HTTP responses in bytes handled by a router. |
|
||||
|
||||
### HTTP Requests Count
|
||||
|
||||
The total count of HTTP requests handled by a router.
|
||||
|
||||
[Labels](#labels): `code`, `method`, `protocol`, `router`, `service`.
|
||||
```prom tab="Prometheus"
|
||||
traefik_router_requests_total
|
||||
traefik_router_requests_tls_total
|
||||
traefik_router_request_duration_seconds
|
||||
traefik_router_open_connections
|
||||
traefik_router_requests_bytes_total
|
||||
traefik_router_responses_bytes_total
|
||||
```
|
||||
|
||||
```dd tab="Datadog"
|
||||
router.request.total
|
||||
router.request.tls.total
|
||||
router.request.duration
|
||||
router.connections.open
|
||||
router.requests.bytes.total
|
||||
router.responses.bytes.total
|
||||
```
|
||||
|
||||
```influxdb tab="InfluxDB / InfluxDB2"
|
||||
traefik.router.requests.total
|
||||
```
|
||||
|
||||
```prom tab="Prometheus"
|
||||
traefik_router_requests_total
|
||||
traefik.router.requests.tls.total
|
||||
traefik.router.request.duration
|
||||
traefik.router.connections.open
|
||||
traefik.router.requests.bytes.total
|
||||
traefik.router.responses.bytes.total
|
||||
```
|
||||
|
||||
```statsd tab="StatsD"
|
||||
# Default prefix: "traefik"
|
||||
{prefix}.router.request.total
|
||||
```
|
||||
|
||||
### HTTPS Requests Count
|
||||
|
||||
The total count of HTTPS requests handled by a router.
|
||||
|
||||
[Labels](#labels): `tls_version`, `tls_cipher`, `router`, `service`.
|
||||
|
||||
```dd tab="Datadog"
|
||||
router.request.tls.total
|
||||
```
|
||||
|
||||
```influxdb tab="InfluxDB / InfluxDB2"
|
||||
traefik.router.requests.tls.total
|
||||
```
|
||||
|
||||
```prom tab="Prometheus"
|
||||
traefik_router_requests_tls_total
|
||||
```
|
||||
|
||||
```statsd tab="StatsD"
|
||||
# Default prefix: "traefik"
|
||||
{prefix}.router.request.tls.total
|
||||
```
|
||||
|
||||
### Request Duration Histogram
|
||||
|
||||
Request processing duration histogram on a router.
|
||||
|
||||
[Labels](#labels): `code`, `method`, `protocol`, `router`, `service`.
|
||||
|
||||
```dd tab="Datadog"
|
||||
router.request.duration
|
||||
```
|
||||
|
||||
```influxdb tab="InfluxDB / InfluxDB2"
|
||||
traefik.router.request.duration
|
||||
```
|
||||
|
||||
```prom tab="Prometheus"
|
||||
traefik_router_request_duration_seconds
|
||||
```
|
||||
|
||||
```statsd tab="StatsD"
|
||||
# Default prefix: "traefik"
|
||||
{prefix}.router.request.duration
|
||||
```
|
||||
|
||||
### Open Connections Count
|
||||
|
||||
The current count of open connections on a router.
|
||||
|
||||
[Labels](#labels): `method`, `protocol`, `router`, `service`.
|
||||
|
||||
```dd tab="Datadog"
|
||||
router.connections.open
|
||||
```
|
||||
|
||||
```influxdb tab="InfluxDB / InfluxDB2"
|
||||
traefik.router.connections.open
|
||||
```
|
||||
|
||||
```prom tab="Prometheus"
|
||||
traefik_router_open_connections
|
||||
```
|
||||
|
||||
```statsd tab="StatsD"
|
||||
# Default prefix: "traefik"
|
||||
{prefix}.router.connections.open
|
||||
```
|
||||
|
||||
### Requests Bytes Count
|
||||
|
||||
The total size of HTTP requests in bytes handled by a router.
|
||||
|
||||
[Labels](#labels): `code`, `method`, `protocol`, `router`, `service`.
|
||||
|
||||
```dd tab="Datadog"
|
||||
router.requests.bytes.total
|
||||
```
|
||||
|
||||
```influxdb tab="InfluxDB / InfluxDB2"
|
||||
traefik.router.requests.bytes.total
|
||||
```
|
||||
|
||||
```prom tab="Prometheus"
|
||||
traefik_router_requests_bytes_total
|
||||
```
|
||||
|
||||
```statsd tab="StatsD"
|
||||
# Default prefix: "traefik"
|
||||
{prefix}.router.requests.bytes.total
|
||||
```
|
||||
|
||||
### Responses Bytes Count
|
||||
|
||||
The total size of HTTP responses in bytes handled by a router.
|
||||
|
||||
[Labels](#labels): `code`, `method`, `protocol`, `router`, `service`.
|
||||
|
||||
```dd tab="Datadog"
|
||||
router.responses.bytes.total
|
||||
```
|
||||
|
||||
```influxdb tab="InfluxDB / InfluxDB2"
|
||||
traefik.router.responses.bytes.total
|
||||
```
|
||||
|
||||
```prom tab="Prometheus"
|
||||
traefik_router_responses_bytes_total
|
||||
```
|
||||
|
||||
```statsd tab="StatsD"
|
||||
# Default prefix: "traefik"
|
||||
{prefix}.router.responses.bytes.total
|
||||
```
|
||||
|
||||
## Service Metrics
|
||||
|
||||
| Metric | DataDog | InfluxDB / InfluxDB2 | Prometheus | StatsD |
|
||||
|-------------------------------------------------------------|---------|----------------------|------------|--------|
|
||||
| [HTTP Requests Count](#http-requests-count_2) | ✓ | ✓ | ✓ | ✓ |
|
||||
| [HTTPS Requests Count](#https-requests-count_2) | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Request Duration Histogram](#request-duration-histogram_2) | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Open Connections Count](#open-connections-count_2) | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Requests Retries Count](#requests-retries-count) | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Service Server UP](#service-server-up) | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Requests Bytes Count](#requests-bytes-count_2) | ✓ | ✓ | ✓ | ✓ |
|
||||
| [Responses outgoing traffic](#responses-bytes-count_2) | ✓ | ✓ | ✓ | ✓ |
|
||||
| Metric | Type | Labels | Description |
|
||||
|-----------------------|-----------|-----------------------------------------|-------------------------------------------------------------|
|
||||
| Requests total | Count | `code`, `method`, `protocol`, `service` | The total count of HTTP requests processed on a service. |
|
||||
| Requests TLS total | Count | `tls_version`, `tls_cipher`, `service` | The total count of HTTPS requests processed on a service. |
|
||||
| Request duration | Histogram | `code`, `method`, `protocol`, `service` | Request processing duration histogram on a service. |
|
||||
| Open connections | Count | `method`, `protocol`, `service` | The current count of open connections on a service. |
|
||||
| Retries total | Count | `service` | The count of requests retries on a service. |
|
||||
| Server UP | Gauge | `service`, `url` | Current service's server status, 0 for a down or 1 for up. |
|
||||
| Requests bytes total | Count | `code`, `method`, `protocol`, `service` | The total size of requests in bytes received by a service. |
|
||||
| Responses bytes total | Count | `code`, `method`, `protocol`, `service` | The total size of responses in bytes returned by a service. |
|
||||
|
||||
### HTTP Requests Count
|
||||
|
||||
The total count of HTTP requests processed on a service.
|
||||
|
||||
[Labels](#labels): `code`, `method`, `protocol`, `service`.
|
||||
```prom tab="Prometheus"
|
||||
traefik_service_requests_total
|
||||
traefik_service_requests_tls_total
|
||||
traefik_service_request_duration_seconds
|
||||
traefik_service_open_connections
|
||||
traefik_service_retries_total
|
||||
traefik_service_server_up
|
||||
traefik_service_requests_bytes_total
|
||||
traefik_service_responses_bytes_total
|
||||
```
|
||||
|
||||
```dd tab="Datadog"
|
||||
service.request.total
|
||||
router.service.tls.total
|
||||
service.request.duration
|
||||
service.connections.open
|
||||
service.retries.total
|
||||
service.server.up
|
||||
service.requests.bytes.total
|
||||
service.responses.bytes.total
|
||||
```
|
||||
|
||||
```influxdb tab="InfluxDB / InfluxDB2"
|
||||
traefik.service.requests.total
|
||||
```
|
||||
|
||||
```prom tab="Prometheus"
|
||||
traefik_service_requests_total
|
||||
traefik.service.requests.tls.total
|
||||
traefik.service.request.duration
|
||||
traefik.service.connections.open
|
||||
traefik.service.retries.total
|
||||
traefik.service.server.up
|
||||
traefik.service.requests.bytes.total
|
||||
traefik.service.responses.bytes.total
|
||||
```
|
||||
|
||||
```statsd tab="StatsD"
|
||||
# Default prefix: "traefik"
|
||||
{prefix}.service.request.total
|
||||
```
|
||||
|
||||
### HTTPS Requests Count
|
||||
|
||||
The total count of HTTPS requests processed on a service.
|
||||
|
||||
[Labels](#labels): `tls_version`, `tls_cipher`, `service`.
|
||||
|
||||
```dd tab="Datadog"
|
||||
router.service.tls.total
|
||||
```
|
||||
|
||||
```influxdb tab="InfluxDB / InfluxDB2"
|
||||
traefik.service.requests.tls.total
|
||||
```
|
||||
|
||||
```prom tab="Prometheus"
|
||||
traefik_service_requests_tls_total
|
||||
```
|
||||
|
||||
```statsd tab="StatsD"
|
||||
# Default prefix: "traefik"
|
||||
{prefix}.service.request.tls.total
|
||||
```
|
||||
|
||||
### Request Duration Histogram
|
||||
|
||||
Request processing duration histogram on a service.
|
||||
|
||||
[Labels](#labels): `code`, `method`, `protocol`, `service`.
|
||||
|
||||
```dd tab="Datadog"
|
||||
service.request.duration
|
||||
```
|
||||
|
||||
```influxdb tab="InfluxDB / InfluxDB2"
|
||||
traefik.service.request.duration
|
||||
```
|
||||
|
||||
```prom tab="Prometheus"
|
||||
traefik_service_request_duration_seconds
|
||||
```
|
||||
|
||||
```statsd tab="StatsD"
|
||||
# Default prefix: "traefik"
|
||||
{prefix}.service.request.duration
|
||||
```
|
||||
|
||||
### Open Connections Count
|
||||
|
||||
The current count of open connections on a service.
|
||||
|
||||
[Labels](#labels): `method`, `protocol`, `service`.
|
||||
|
||||
```dd tab="Datadog"
|
||||
service.connections.open
|
||||
```
|
||||
|
||||
```influxdb tab="InfluxDB / InfluxDB2"
|
||||
traefik.service.connections.open
|
||||
```
|
||||
|
||||
```prom tab="Prometheus"
|
||||
traefik_service_open_connections
|
||||
```
|
||||
|
||||
```statsd tab="StatsD"
|
||||
# Default prefix: "traefik"
|
||||
{prefix}.service.connections.open
|
||||
```
|
||||
|
||||
### Requests Retries Count
|
||||
|
||||
The count of requests retries on a service.
|
||||
|
||||
[Labels](#labels): `service`.
|
||||
|
||||
```dd tab="Datadog"
|
||||
service.retries.total
|
||||
```
|
||||
|
||||
```influxdb tab="InfluxDB / InfluxDB2"
|
||||
traefik.service.retries.total
|
||||
```
|
||||
|
||||
```prom tab="Prometheus"
|
||||
traefik_service_retries_total
|
||||
```
|
||||
|
||||
```statsd tab="StatsD"
|
||||
# Default prefix: "traefik"
|
||||
{prefix}.service.retries.total
|
||||
```
|
||||
|
||||
### Service Server UP
|
||||
|
||||
Current service's server status, described by a gauge with a value of 0 for a down server or a value of 1 for an up server.
|
||||
|
||||
[Labels](#labels): `service`, `url`.
|
||||
|
||||
```dd tab="Datadog"
|
||||
service.server.up
|
||||
```
|
||||
|
||||
```influxdb tab="InfluxDB / InfluxDB2"
|
||||
traefik.service.server.up
|
||||
```
|
||||
|
||||
```prom tab="Prometheus"
|
||||
traefik_service_server_up
|
||||
```
|
||||
|
||||
```statsd tab="StatsD"
|
||||
# Default prefix: "traefik"
|
||||
{prefix}.service.server.up
|
||||
```
|
||||
|
||||
### Requests Bytes Count
|
||||
|
||||
The total size of requests in bytes received by a service.
|
||||
|
||||
[Labels](#labels): `code`, `method`, `protocol`, `service`.
|
||||
|
||||
```dd tab="Datadog"
|
||||
service.requests.bytes.total
|
||||
```
|
||||
|
||||
```influxdb tab="InfluxDB / InfluxDB2"
|
||||
traefik.service.requests.bytes.total
|
||||
```
|
||||
|
||||
```prom tab="Prometheus"
|
||||
traefik_service_requests_bytes_total
|
||||
```
|
||||
|
||||
```statsd tab="StatsD"
|
||||
# Default prefix: "traefik"
|
||||
{prefix}.service.requests.bytes.total
|
||||
```
|
||||
|
||||
### Responses Bytes Count
|
||||
|
||||
The total size of responses in bytes returned by a service.
|
||||
|
||||
[Labels](#labels): `code`, `method`, `protocol`, `service`.
|
||||
|
||||
```dd tab="Datadog"
|
||||
service.responses.bytes.total
|
||||
```
|
||||
|
||||
```influxdb tab="InfluxDB / InfluxDB2"
|
||||
traefik.service.responses.bytes.total
|
||||
```
|
||||
|
||||
```prom tab="Prometheus"
|
||||
traefik_service_responses_bytes_total
|
||||
```
|
||||
|
||||
```statsd tab="StatsD"
|
||||
# Default prefix: "traefik"
|
||||
{prefix}.service.responses.bytes.total
|
||||
```
|
||||
|
||||
|
||||
@@ -93,12 +93,12 @@ rule = "Host(`traefik.example.com`)"
|
||||
|
||||
```bash tab="Path Prefix Rule"
|
||||
# The dashboard can be accessed on http://example.com/dashboard/ or http://traefik.example.com/dashboard/
|
||||
rule = "PathPrefix(`/api`) || PathPrefix(`/dashboard`)"
|
||||
rule = "PathPrefix(`/api`, `/dashboard`)"
|
||||
```
|
||||
|
||||
```bash tab="Combination of Rules"
|
||||
# The dashboard can be accessed on http://traefik.example.com/dashboard/
|
||||
rule = "Host(`traefik.example.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"
|
||||
rule = "Host(`traefik.example.com`) && PathPrefix(`/api`, `/dashboard`)"
|
||||
```
|
||||
|
||||
??? example "Dashboard Dynamic Configuration Examples"
|
||||
|
||||
@@ -539,7 +539,7 @@ providers:
|
||||
|
||||
_Optional, Default=true_
|
||||
|
||||
Watch Docker Swarm events.
|
||||
Watch Docker events.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
|
||||
@@ -70,7 +70,7 @@ _Optional, Default=false_
|
||||
|
||||
Search for services in cluster list.
|
||||
|
||||
- If set to `true` service discovery is disabled on configured clusters, but enabled for all other clusters.
|
||||
- If set to `true` service discovery is enabled for all clusters.
|
||||
- If set to `false` service discovery is enabled on configured clusters only.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
@@ -123,6 +123,7 @@ providers:
|
||||
_Optional, Default=["default"]_
|
||||
|
||||
Search for services in cluster list.
|
||||
This option is ignored if `autoDiscoverClusters` is set to `true`.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
@@ -169,6 +170,70 @@ providers:
|
||||
# ...
|
||||
```
|
||||
|
||||
### `constraints`
|
||||
|
||||
_Optional, Default=""_
|
||||
|
||||
The `constraints` option can be set to an expression that Traefik matches against the container labels (task),
|
||||
to determine whether to create any route for that container.
|
||||
If none of the container labels match the expression, no route for that container is created.
|
||||
If the expression is empty, all detected containers are included.
|
||||
|
||||
The expression syntax is based on the `Label("key", "value")`, and `LabelRegex("key", "value")` functions,
|
||||
as well as the usual boolean logic, as shown in examples below.
|
||||
|
||||
??? example "Constraints Expression Examples"
|
||||
|
||||
```toml
|
||||
# Includes only containers having a label with key `a.label.name` and value `foo`
|
||||
constraints = "Label(`a.label.name`, `foo`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# Excludes containers having any label with key `a.label.name` and value `foo`
|
||||
constraints = "!Label(`a.label.name`, `value`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# With logical AND.
|
||||
constraints = "Label(`a.label.name`, `valueA`) && Label(`another.label.name`, `valueB`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# With logical OR.
|
||||
constraints = "Label(`a.label.name`, `valueA`) || Label(`another.label.name`, `valueB`)"
|
||||
```
|
||||
|
||||
```toml
|
||||
# With logical AND and OR, with precedence set by parentheses.
|
||||
constraints = "Label(`a.label.name`, `valueA`) && (Label(`another.label.name`, `valueB`) || Label(`yet.another.label.name`, `valueC`))"
|
||||
```
|
||||
|
||||
```toml
|
||||
# Includes only containers having a label with key `a.label.name` and a value matching the `a.+` regular expression.
|
||||
constraints = "LabelRegex(`a.label.name`, `a.+`)"
|
||||
```
|
||||
|
||||
For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#restrict-the-scope-of-service-discovery).
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
ecs:
|
||||
constraints: "Label(`a.label.name`,`foo`)"
|
||||
# ...
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[providers.ecs]
|
||||
constraints = "Label(`a.label.name`,`foo`)"
|
||||
# ...
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.ecs.constraints=Label(`a.label.name`,`foo`)
|
||||
# ...
|
||||
```
|
||||
|
||||
### `defaultRule`
|
||||
|
||||
_Optional, Default=```Host(`{{ normalize .Name }}`)```_
|
||||
|
||||
@@ -78,7 +78,7 @@ This provider is proposed as an experimental feature and partially supports the
|
||||
|
||||
The Kubernetes Gateway API project provides several guides on how to use the APIs.
|
||||
These guides can help you to go further than the example above.
|
||||
The [getting started guide](https://gateway-api.sigs.k8s.io/v1alpha2/guides/getting-started/) details how to install the CRDs from their repository.
|
||||
The [getting started guide](https://gateway-api.sigs.k8s.io/v1alpha2/guides/) details how to install the CRDs from their repository.
|
||||
|
||||
!!! note ""
|
||||
|
||||
|
||||
@@ -374,7 +374,7 @@ providers:
|
||||
|
||||
_Optional, Default: ""_
|
||||
|
||||
IP used for Kubernetes Ingress endpoints.
|
||||
This IP will get copied to Ingress `status.loadbalancer.ip`, and currently only supports one IP value (IPv4 or IPv6).
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
providers:
|
||||
@@ -398,7 +398,9 @@ providers:
|
||||
|
||||
_Optional, Default: ""_
|
||||
|
||||
Published Kubernetes Service to copy status from.
|
||||
The Kubernetes service to copy status from.
|
||||
When using third parties tools like External-DNS, this option can be used to copy the service `loadbalancer.status` (containing the service's endpoints IPs) to the ingresses.
|
||||
|
||||
Format: `namespace/servicename`.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
@@ -500,6 +502,6 @@ providers:
|
||||
### Further
|
||||
|
||||
To learn more about the various aspects of the Ingress specification that Traefik supports,
|
||||
many examples of Ingresses definitions are located in the test [examples](https://github.com/traefik/traefik/tree/v2.8/pkg/provider/kubernetes/ingress/fixtures) of the Traefik repository.
|
||||
many examples of Ingresses definitions are located in the test [examples](https://github.com/traefik/traefik/tree/v2.9/pkg/provider/kubernetes/ingress/fixtures) of the Traefik repository.
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
||||
@@ -213,6 +213,7 @@ you can do so in two different ways:
|
||||
List of providers that support these features:
|
||||
|
||||
- [Docker](./docker.md#exposedbydefault)
|
||||
- [ECS](./ecs.md#exposedbydefault)
|
||||
- [Consul Catalog](./consul-catalog.md#exposedbydefault)
|
||||
- [Nomad](./nomad.md#exposedbydefault)
|
||||
- [Rancher](./rancher.md#exposedbydefault)
|
||||
@@ -223,6 +224,7 @@ List of providers that support these features:
|
||||
List of providers that support constraints:
|
||||
|
||||
- [Docker](./docker.md#constraints)
|
||||
- [ECS](./ecs.md#constraints)
|
||||
- [Consul Catalog](./consul-catalog.md#constraints)
|
||||
- [Nomad](./nomad.md#constraints)
|
||||
- [Rancher](./rancher.md#constraints)
|
||||
|
||||
@@ -1300,7 +1300,7 @@ spec:
|
||||
type: object
|
||||
pem:
|
||||
description: PEM sets the X-Forwarded-Tls-Client-Cert header with
|
||||
the escaped certificate.
|
||||
the certificate.
|
||||
type: boolean
|
||||
type: object
|
||||
plugin:
|
||||
|
||||
@@ -723,7 +723,7 @@ spec:
|
||||
type: object
|
||||
pem:
|
||||
description: PEM sets the X-Forwarded-Tls-Client-Cert header with
|
||||
the escaped certificate.
|
||||
the certificate.
|
||||
type: boolean
|
||||
type: object
|
||||
plugin:
|
||||
|
||||
@@ -553,7 +553,7 @@ TLS key
|
||||
Use the ip address from the bound port, rather than from the inner network. (Default: ```false```)
|
||||
|
||||
`--providers.docker.watch`:
|
||||
Watch Docker Swarm events. (Default: ```true```)
|
||||
Watch Docker events. (Default: ```true```)
|
||||
|
||||
`--providers.ecs`:
|
||||
Enable AWS ECS backend with default settings. (Default: ```false```)
|
||||
|
||||
@@ -553,7 +553,7 @@ TLS key
|
||||
Use the ip address from the bound port, rather than from the inner network. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_DOCKER_WATCH`:
|
||||
Watch Docker Swarm events. (Default: ```true```)
|
||||
Watch Docker events. (Default: ```true```)
|
||||
|
||||
`TRAEFIK_PROVIDERS_ECS`:
|
||||
Enable AWS ECS backend with default settings. (Default: ```false```)
|
||||
|
||||
@@ -276,10 +276,11 @@ The table below lists all the available matchers:
|
||||
|
||||
!!! info "Path Vs PathPrefix"
|
||||
|
||||
Use `Path` if your service listens on the exact path only. For instance, `Path: /products` would match `/products` but not `/products/shoes`.
|
||||
Use `Path` if your service listens on the exact path only. For instance, ```Path(`/products`)``` would match `/products` but not `/products/shoes`.
|
||||
|
||||
Use a `*Prefix*` matcher if your service listens on a particular base path but also serves requests on sub-paths.
|
||||
For instance, `PathPrefix: /products` would match `/products` but also `/products/shoes` and `/products/shirts`.
|
||||
For instance, ```PathPrefix(`/products`)``` would match `/products` and `/products/shoes`,
|
||||
as well as `/productsforsale`, and `/productsforsale/shoes`.
|
||||
Since the path is forwarded as-is, your service is expected to listen on `/products`.
|
||||
|
||||
!!! info "ClientIP matcher"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Traefik Hub (Experimental)
|
||||
# Traefik Hub
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -29,6 +29,12 @@ This agent can:
|
||||
* The Traefik Hub Agent must be installed to connect to the Traefik Hub platform.
|
||||
* Activate this feature in the experimental section of the static configuration.
|
||||
|
||||
!!! information "Configuration Discovery"
|
||||
|
||||
According to installation options, the Traefik Hub Agent listens to the Docker or Kubernetes API to discover containers/services.
|
||||
|
||||
It doesn't support the routers discovered by Traefik Proxy using other providers, e.g., using the File provider.
|
||||
|
||||
!!! example "Minimal Static Configuration to Activate Traefik Hub for Docker"
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
|
||||
@@ -16,6 +16,35 @@ This will also be used as a starting point for the other docker-compose guides.
|
||||
--8<-- "content/user-guides/docker-compose/basic-example/docker-compose.yml"
|
||||
```
|
||||
|
||||
??? Networking
|
||||
|
||||
The Traefik container has to be attached to the same network as the containers to be exposed.
|
||||
If no networks are specified in the docker-compose file, Docker creates a default one that allows Traefik to reach the containers defined in the same file.
|
||||
You can [customize the network](https://docs.docker.com/compose/networking/#specify-custom-networks) as described in the example below.
|
||||
You can use a [pre-existing network](https://docs.docker.com/compose/networking/#use-a-pre-existing-network) too.
|
||||
|
||||
```yaml
|
||||
version: "3.3"
|
||||
|
||||
networks:
|
||||
traefiknet: {}
|
||||
|
||||
services:
|
||||
|
||||
traefik:
|
||||
image: "traefik:v2.9"
|
||||
...
|
||||
networks:
|
||||
- traefiknet
|
||||
|
||||
whoami:
|
||||
image: "traefik/whoami"
|
||||
...
|
||||
networks:
|
||||
- traefiknet
|
||||
|
||||
```
|
||||
|
||||
- Replace `whoami.localhost` by your **own domain** within the `traefik.http.routers.whoami.rule` label of the `whoami` service.
|
||||
- Run `docker-compose up -d` within the folder where you created the previous file.
|
||||
- Wait a bit and visit `http://your_own_domain` to confirm everything went fine.
|
||||
|
||||
135
go.mod
135
go.mod
@@ -3,12 +3,12 @@ module github.com/traefik/traefik/v2
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.1.0
|
||||
github.com/BurntSushi/toml v1.2.1
|
||||
github.com/ExpediaDotCom/haystack-client-go v0.0.0-20190315171017-e7edbdf53a61
|
||||
github.com/Masterminds/sprig/v3 v3.2.2
|
||||
github.com/abbot/go-http-auth v0.0.0-00010101000000-000000000000
|
||||
github.com/aws/aws-sdk-go v1.44.47
|
||||
github.com/cenkalti/backoff/v4 v4.1.1
|
||||
github.com/cenkalti/backoff/v4 v4.1.3
|
||||
github.com/compose-spec/compose-go v1.0.3
|
||||
github.com/containous/alice v0.0.0-20181107144136-d83ebdd94cbd
|
||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
|
||||
@@ -19,7 +19,7 @@ require (
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/fatih/structs v1.1.0
|
||||
github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2
|
||||
github.com/go-acme/lego/v4 v4.7.0
|
||||
github.com/go-acme/lego/v4 v4.9.1
|
||||
github.com/go-check/check v0.0.0-00010101000000-000000000000
|
||||
github.com/go-kit/kit v0.10.1-0.20200915143503-439c4d2ed3ea
|
||||
github.com/golang/protobuf v1.5.2
|
||||
@@ -28,25 +28,25 @@ require (
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/hashicorp/consul v1.10.4
|
||||
github.com/hashicorp/consul/api v1.14.0
|
||||
github.com/hashicorp/go-hclog v0.16.2
|
||||
github.com/hashicorp/go-hclog v1.2.0
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/hashicorp/go-version v1.3.0
|
||||
github.com/hashicorp/go-version v1.6.0
|
||||
github.com/hashicorp/nomad/api v0.0.0-20220506174431-b5665129cd1f
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.7.0
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d
|
||||
github.com/instana/go-sensor v1.38.3
|
||||
github.com/klauspost/compress v1.14.2
|
||||
github.com/kvtools/consul v1.0.1
|
||||
github.com/kvtools/etcdv3 v1.0.1
|
||||
github.com/kvtools/redis v1.0.1
|
||||
github.com/klauspost/compress v1.15.0
|
||||
github.com/kvtools/consul v1.0.2
|
||||
github.com/kvtools/etcdv3 v1.0.2
|
||||
github.com/kvtools/redis v1.0.2
|
||||
github.com/kvtools/valkeyrie v1.0.0
|
||||
github.com/kvtools/zookeeper v1.0.1
|
||||
github.com/kvtools/zookeeper v1.0.2
|
||||
github.com/lucas-clemente/quic-go v0.28.1
|
||||
github.com/mailgun/ttlmap v0.0.0-20170619185759-c1c17f74874f
|
||||
github.com/miekg/dns v1.1.47
|
||||
github.com/miekg/dns v1.1.50
|
||||
github.com/mitchellh/copystructure v1.0.0
|
||||
github.com/mitchellh/hashstructure v1.0.0
|
||||
github.com/mitchellh/mapstructure v1.4.3
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/opentracing/opentracing-go v1.2.0
|
||||
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5
|
||||
github.com/openzipkin/zipkin-go v0.2.2
|
||||
@@ -56,27 +56,27 @@ require (
|
||||
github.com/prometheus/client_golang v1.12.2-0.20220704083116-e8f91604d835
|
||||
github.com/prometheus/client_model v0.2.0
|
||||
github.com/rancher/go-rancher-metadata v0.0.0-20200311180630-7f4c936a06ac
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154
|
||||
github.com/traefik/paerser v0.1.9
|
||||
github.com/traefik/yaegi v0.14.2
|
||||
github.com/traefik/yaegi v0.14.3
|
||||
github.com/uber/jaeger-client-go v2.30.0+incompatible
|
||||
github.com/uber/jaeger-lib v2.2.0+incompatible
|
||||
github.com/unrolled/render v1.0.2
|
||||
github.com/unrolled/secure v1.0.9
|
||||
github.com/vdemeester/shakers v0.1.0
|
||||
github.com/vulcand/oxy v1.4.1
|
||||
github.com/vulcand/oxy/v2 v2.0.0-20221121151423-d5cb734e4467
|
||||
github.com/vulcand/predicate v1.2.0
|
||||
go.elastic.co/apm v1.13.1
|
||||
go.elastic.co/apm/module/apmot v1.13.1
|
||||
golang.org/x/mod v0.4.2
|
||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e
|
||||
golang.org/x/text v0.3.7
|
||||
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11
|
||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2
|
||||
google.golang.org/grpc v1.38.0
|
||||
gopkg.in/DataDog/dd-trace-go.v1 v1.38.1
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4
|
||||
golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10
|
||||
golang.org/x/text v0.5.0
|
||||
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65
|
||||
golang.org/x/tools v0.1.12
|
||||
google.golang.org/grpc v1.41.0
|
||||
gopkg.in/DataDog/dd-trace-go.v1 v1.43.1
|
||||
gopkg.in/fsnotify.v1 v1.4.7
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.22.1
|
||||
@@ -94,10 +94,10 @@ require (
|
||||
github.com/Azure/azure-sdk-for-go v40.3.0+incompatible // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.19 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.13 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.8 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.2 // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.24 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.18 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.5 // indirect
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
|
||||
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
|
||||
@@ -106,7 +106,7 @@ require (
|
||||
github.com/DataDog/datadog-agent/pkg/obfuscate v0.0.0-20211129110424-6491aa3bf583 // indirect
|
||||
github.com/DataDog/datadog-go v4.8.2+incompatible // indirect
|
||||
github.com/DataDog/datadog-go/v5 v5.0.2 // indirect
|
||||
github.com/DataDog/sketches-go v1.0.0 // indirect
|
||||
github.com/DataDog/sketches-go v1.2.1 // indirect
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.1.1 // indirect
|
||||
@@ -116,8 +116,9 @@ require (
|
||||
github.com/Shopify/sarama v1.23.1 // indirect
|
||||
github.com/VividCortex/gohistogram v1.0.0 // indirect
|
||||
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.1.1 // indirect
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183 // indirect
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.1 // indirect
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1755 // indirect
|
||||
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
|
||||
github.com/armon/go-metrics v0.3.10 // indirect
|
||||
github.com/armon/go-radix v1.0.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
@@ -127,7 +128,8 @@ require (
|
||||
github.com/cheekybits/genny v1.0.0 // indirect
|
||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible // indirect
|
||||
github.com/circonus-labs/circonusllhist v0.1.3 // indirect
|
||||
github.com/cloudflare/cloudflare-go v0.20.0 // indirect
|
||||
github.com/civo/civogo v0.3.11 // indirect
|
||||
github.com/cloudflare/cloudflare-go v0.49.0 // indirect
|
||||
github.com/compose-spec/godotenv v1.0.0 // indirect
|
||||
github.com/containerd/cgroups v1.0.1 // indirect
|
||||
github.com/containerd/console v1.0.2 // indirect
|
||||
@@ -137,12 +139,12 @@ require (
|
||||
github.com/coreos/go-semver v0.3.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||
github.com/cpu/goacmedns v0.1.1 // indirect
|
||||
github.com/deepmap/oapi-codegen v1.8.2 // indirect
|
||||
github.com/deepmap/oapi-codegen v1.9.1 // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.0 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dimchansky/utfbom v1.1.1 // indirect
|
||||
github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e // indirect
|
||||
github.com/dnsimple/dnsimple-go v0.70.1 // indirect
|
||||
github.com/dnsimple/dnsimple-go v0.71.1 // indirect
|
||||
github.com/docker/buildx v0.5.2-0.20210422185057-908a856079fc // indirect
|
||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.6.4-0.20210125172408-38bea2ce277a // indirect
|
||||
@@ -155,11 +157,11 @@ require (
|
||||
github.com/elastic/go-sysinfo v1.1.1 // indirect
|
||||
github.com/elastic/go-windows v1.0.0 // indirect
|
||||
github.com/evanphx/json-patch v4.11.0+incompatible // indirect
|
||||
github.com/exoscale/egoscale v0.67.0 // indirect
|
||||
github.com/fatih/color v1.12.0 // indirect
|
||||
github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||
github.com/exoscale/egoscale v0.90.0 // indirect
|
||||
github.com/fatih/color v1.13.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/fvbommel/sortorder v1.0.1 // indirect
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-errors/errors v1.0.1 // indirect
|
||||
github.com/go-logfmt/logfmt v0.5.1 // indirect
|
||||
github.com/go-logr/logr v0.4.0 // indirect
|
||||
@@ -168,21 +170,21 @@ require (
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
|
||||
github.com/go-zookeeper/zk v1.0.3 // indirect
|
||||
github.com/gofrs/flock v0.8.0 // indirect
|
||||
github.com/gofrs/uuid v4.0.0+incompatible // indirect
|
||||
github.com/gogo/googleapis v1.4.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/go-cmp v0.5.7 // indirect
|
||||
github.com/google/go-cmp v0.5.8 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.0.5 // indirect
|
||||
github.com/googleapis/gnostic v0.5.5 // indirect
|
||||
github.com/gophercloud/gophercloud v0.16.0 // indirect
|
||||
github.com/gophercloud/gophercloud v1.0.0 // indirect
|
||||
github.com/gophercloud/utils v0.0.0-20210216074907-f6de111f2eae // indirect
|
||||
github.com/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 // indirect
|
||||
@@ -204,14 +206,13 @@ require (
|
||||
github.com/hashicorp/raft-autopilot v0.1.5 // indirect
|
||||
github.com/hashicorp/serf v0.9.7 // indirect
|
||||
github.com/hashicorp/yamux v0.0.0-20210826001029-26ff87cf9493 // indirect
|
||||
github.com/huandu/xstrings v1.3.1 // indirect
|
||||
github.com/huandu/xstrings v1.3.2 // indirect
|
||||
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect
|
||||
github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect
|
||||
github.com/jaguilar/vt100 v0.0.0-20150826170717-2703a27b14ea // indirect
|
||||
github.com/jarcoal/httpmock v1.0.8 // indirect
|
||||
github.com/jcchavezs/porto v0.1.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 // indirect
|
||||
@@ -223,7 +224,7 @@ require (
|
||||
github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b // indirect
|
||||
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
|
||||
github.com/labbsr0x/goh v1.0.1 // indirect
|
||||
github.com/linode/linodego v0.31.1 // indirect
|
||||
github.com/linode/linodego v1.9.1 // indirect
|
||||
github.com/liquidweb/go-lwApi v0.0.5 // indirect
|
||||
github.com/liquidweb/liquidweb-cli v0.6.9 // indirect
|
||||
github.com/liquidweb/liquidweb-go v1.6.3 // indirect
|
||||
@@ -237,8 +238,8 @@ require (
|
||||
github.com/marten-seemann/qtls-go1-17 v0.1.2 // indirect
|
||||
github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect
|
||||
github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.11 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mattn/go-shellwords v1.0.12 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||
@@ -256,7 +257,7 @@ require (
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect
|
||||
github.com/nrdcg/auroradns v1.0.1 // indirect
|
||||
github.com/nrdcg/auroradns v1.1.0 // indirect
|
||||
github.com/nrdcg/desec v0.6.0 // indirect
|
||||
github.com/nrdcg/dnspod-go v0.4.0 // indirect
|
||||
github.com/nrdcg/freemyip v0.2.0 // indirect
|
||||
@@ -267,7 +268,7 @@ require (
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||
github.com/opencontainers/runc v1.0.2 // indirect
|
||||
github.com/opencontainers/runc v1.0.3 // indirect
|
||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 // indirect
|
||||
github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect
|
||||
github.com/ovh/go-ovh v1.1.0 // indirect
|
||||
@@ -276,33 +277,38 @@ require (
|
||||
github.com/pquerna/otp v1.3.0 // indirect
|
||||
github.com/prometheus/common v0.35.0 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/sacloud/libsacloud v1.36.2 // indirect
|
||||
github.com/sacloud/api-client-go v0.2.1 // indirect
|
||||
github.com/sacloud/go-http v0.1.2 // indirect
|
||||
github.com/sacloud/iaas-api-go v1.3.2 // indirect
|
||||
github.com/sacloud/packages-go v0.0.5 // indirect
|
||||
github.com/sanathkr/go-yaml v0.0.0-20170819195128-ed9d249f429b // indirect
|
||||
github.com/santhosh-tekuri/jsonschema v1.2.4 // indirect
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f // indirect
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9 // indirect
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
|
||||
github.com/segmentio/fasthash v1.0.3 // indirect
|
||||
github.com/shopspring/decimal v1.2.0 // indirect
|
||||
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect
|
||||
github.com/softlayer/softlayer-go v1.0.3 // indirect
|
||||
github.com/softlayer/softlayer-go v1.0.6 // indirect
|
||||
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
|
||||
github.com/spf13/cast v1.3.1 // indirect
|
||||
github.com/spf13/cobra v1.2.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/objx v0.4.0 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.287 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.287 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490 // indirect
|
||||
github.com/theupdateframework/notary v0.6.1 // indirect
|
||||
github.com/tinylib/msgp v1.1.2 // indirect
|
||||
github.com/tonistiigi/fsutil v0.0.0-20201103201449-0834f99b7b85 // indirect
|
||||
github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect
|
||||
github.com/transip/gotransip/v6 v6.6.1 // indirect
|
||||
github.com/transip/gotransip/v6 v6.17.0 // indirect
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 // indirect
|
||||
github.com/vinyldns/go-vinyldns v0.9.16 // indirect
|
||||
github.com/vultr/govultr/v2 v2.16.0 // indirect
|
||||
github.com/vultr/govultr/v2 v2.17.2 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
github.com/yandex-cloud/go-genproto v0.0.0-20220805142335-27b56ddae16f // indirect
|
||||
github.com/yandex-cloud/go-sdk v0.0.0-20220805164847-cf028e604997 // indirect
|
||||
go.elastic.co/apm/module/apmhttp v1.13.1 // indirect
|
||||
go.elastic.co/fastjson v1.1.0 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.4 // indirect
|
||||
@@ -311,26 +317,29 @@ require (
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277 // indirect
|
||||
go.uber.org/ratelimit v0.2.0 // indirect
|
||||
go.uber.org/zap v1.18.1 // indirect
|
||||
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f // indirect
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e // indirect
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 // indirect
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
|
||||
golang.org/x/sys v0.3.0 // indirect
|
||||
golang.org/x/term v0.3.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/api v0.44.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect
|
||||
google.golang.org/genproto v0.0.0-20211021150943-2b146023228c // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||
gopkg.in/ns1/ns1-go.v2 v2.6.2 // indirect
|
||||
gopkg.in/ini.v1 v1.66.6 // indirect
|
||||
gopkg.in/ns1/ns1-go.v2 v2.6.5 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
howett.net/plist v0.0.0-20181124034731-591f970eefbb // indirect
|
||||
inet.af/netaddr v0.0.0-20220617031823-097006376321 // indirect
|
||||
k8s.io/klog/v2 v2.10.0 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.2 // indirect
|
||||
|
||||
60
integration/fixtures/https/https_invalid_tls_options.toml
Normal file
60
integration/fixtures/https/https_invalid_tls_options.toml
Normal file
@@ -0,0 +1,60 @@
|
||||
[global]
|
||||
checkNewVersion = false
|
||||
sendAnonymousUsage = false
|
||||
|
||||
[log]
|
||||
level = "DEBUG"
|
||||
|
||||
[entryPoints.websecure]
|
||||
address = ":4443"
|
||||
|
||||
[api]
|
||||
insecure = true
|
||||
|
||||
[providers.file]
|
||||
filename = "{{ .SelfFilename }}"
|
||||
|
||||
## dynamic configuration ##
|
||||
|
||||
[http.routers]
|
||||
|
||||
[http.routers.router1]
|
||||
entryPoints = ["websecure"]
|
||||
service = "service1"
|
||||
rule = "Host(`snitest.com`)"
|
||||
[http.routers.router1.tls]
|
||||
options = "invalidTLSOptions"
|
||||
|
||||
[http.routers.router2]
|
||||
entryPoints = ["websecure"]
|
||||
service = "service1"
|
||||
rule = "Host(`snitest.org`)"
|
||||
[http.routers.router2.tls]
|
||||
|
||||
# fallback router
|
||||
[http.routers.router3]
|
||||
entryPoints = ["websecure"]
|
||||
service = "service1"
|
||||
rule = "Path(`/`)"
|
||||
[http.routers.router3.tls]
|
||||
|
||||
[[http.services.service1.loadBalancer.servers]]
|
||||
url = "http://127.0.0.1:9010"
|
||||
|
||||
[[tls.certificates]]
|
||||
certFile = "fixtures/https/snitest.com.cert"
|
||||
keyFile = "fixtures/https/snitest.com.key"
|
||||
|
||||
[[tls.certificates]]
|
||||
certFile = "fixtures/https/snitest.org.cert"
|
||||
keyFile = "fixtures/https/snitest.org.key"
|
||||
|
||||
[tls.options]
|
||||
|
||||
[tls.options.default.clientAuth]
|
||||
# Missing caFile to have an invalid mTLS configuration.
|
||||
clientAuthType = "RequireAndVerifyClientCert"
|
||||
|
||||
[tls.options.invalidTLSOptions.clientAuth]
|
||||
# Missing caFile to have an invalid mTLS configuration.
|
||||
clientAuthType = "RequireAndVerifyClientCert"
|
||||
@@ -1300,7 +1300,7 @@ spec:
|
||||
type: object
|
||||
pem:
|
||||
description: PEM sets the X-Forwarded-Tls-Client-Cert header with
|
||||
the escaped certificate.
|
||||
the certificate.
|
||||
type: boolean
|
||||
type: object
|
||||
plugin:
|
||||
|
||||
15
integration/fixtures/simple_debug_log.toml
Normal file
15
integration/fixtures/simple_debug_log.toml
Normal file
@@ -0,0 +1,15 @@
|
||||
[global]
|
||||
checkNewVersion = false
|
||||
sendAnonymousUsage = false
|
||||
|
||||
[log]
|
||||
level = "DEBUG"
|
||||
|
||||
[api]
|
||||
insecure = true
|
||||
|
||||
[providers.docker]
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.webHost]
|
||||
address = ":8000"
|
||||
@@ -0,0 +1,66 @@
|
||||
[global]
|
||||
checkNewVersion = false
|
||||
sendAnonymousUsage = false
|
||||
|
||||
[log]
|
||||
level = "DEBUG"
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.webA]
|
||||
address = ":8001"
|
||||
[entryPoints.webB]
|
||||
address = ":8002"
|
||||
[entryPoints.webC]
|
||||
address = ":8003"
|
||||
|
||||
[api]
|
||||
insecure = true
|
||||
|
||||
[metrics]
|
||||
[metrics.prometheus]
|
||||
buckets = "0.1,0.3,1.2,5.0"
|
||||
|
||||
[providers]
|
||||
[providers.file]
|
||||
filename = "{{ .SelfFilename }}"
|
||||
|
||||
## dynamic configuration ##
|
||||
|
||||
[http.routers]
|
||||
|
||||
[http.routers.router-without]
|
||||
entrypoints = ["webA"]
|
||||
service = "service-without"
|
||||
rule = "PathPrefix(`/without`)"
|
||||
|
||||
[http.routers.router-req]
|
||||
entrypoints = ["webB"]
|
||||
service = "service-req"
|
||||
rule = "PathPrefix(`/with-req`)"
|
||||
middlewares = ["buffer-req"]
|
||||
|
||||
[http.routers.router-resp]
|
||||
entrypoints = ["webC"]
|
||||
service = "service-resp"
|
||||
rule = "PathPrefix(`/with-resp`)"
|
||||
middlewares = ["buffer-resp"]
|
||||
|
||||
[http.middlewares]
|
||||
[http.middlewares.buffer-req.buffering]
|
||||
maxRequestBodyBytes = 10
|
||||
|
||||
[http.middlewares.buffer-resp.buffering]
|
||||
maxResponseBodyBytes = 10
|
||||
|
||||
[http.services]
|
||||
[http.services.service-without.loadBalancer]
|
||||
[[http.services.service-without.loadBalancer.servers]]
|
||||
url = "http://{{ .IP }}"
|
||||
|
||||
[http.services.service-req.loadBalancer]
|
||||
[[http.services.service-req.loadBalancer.servers]]
|
||||
url = "http://{{ .IP }}"
|
||||
|
||||
[http.services.service-resp.loadBalancer]
|
||||
[[http.services.service-resp.loadBalancer.servers]]
|
||||
url = "http://{{ .IP }}"
|
||||
@@ -33,6 +33,13 @@
|
||||
[tcp.routers.to-whoami-sni-strict.tls]
|
||||
options = "bar"
|
||||
|
||||
[tcp.routers.to-whoami-invalid-tls]
|
||||
rule = "HostSNI(`whoami-i.test`)"
|
||||
service = "whoami-no-cert"
|
||||
entryPoints = [ "tcp" ]
|
||||
[tcp.routers.to-whoami-invalid-tls.tls]
|
||||
options = "invalid"
|
||||
|
||||
[tcp.services.whoami-no-cert]
|
||||
[tcp.services.whoami-no-cert.loadBalancer]
|
||||
[[tcp.services.whoami-no-cert.loadBalancer.servers]]
|
||||
@@ -45,3 +52,7 @@
|
||||
|
||||
[tls.options.bar]
|
||||
minVersion = "VersionTLS13"
|
||||
|
||||
[tls.options.invalid.clientAuth]
|
||||
# Missing CA files to have an invalid mTLS configuration.
|
||||
clientAuthType = "RequireAndVerifyClientCert"
|
||||
|
||||
@@ -5,9 +5,11 @@
|
||||
Package helloworld is a generated protocol buffer package.
|
||||
|
||||
It is generated from these files:
|
||||
|
||||
helloworld.proto
|
||||
|
||||
It has these top-level messages:
|
||||
|
||||
HelloRequest
|
||||
HelloReply
|
||||
StreamExampleRequest
|
||||
|
||||
@@ -1226,3 +1226,53 @@ func (s *HTTPSSuite) TestWithDomainFronting(c *check.C) {
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
}
|
||||
|
||||
// TestWithInvalidTLSOption verifies the behavior when using an invalid tlsOption configuration.
|
||||
func (s *HTTPSSuite) TestWithInvalidTLSOption(c *check.C) {
|
||||
backend := startTestServer("9010", http.StatusOK, "server1")
|
||||
defer backend.Close()
|
||||
|
||||
file := s.adaptFile(c, "fixtures/https/https_invalid_tls_options.toml", struct{}{})
|
||||
defer os.Remove(file)
|
||||
cmd, display := s.traefikCmd(withConfigFile(file))
|
||||
defer display(c)
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer s.killCmd(cmd)
|
||||
|
||||
// wait for Traefik
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 500*time.Millisecond, try.BodyContains("Host(`snitest.com`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
serverName string
|
||||
}{
|
||||
{
|
||||
desc: "With invalid TLS Options specified",
|
||||
serverName: "snitest.com",
|
||||
},
|
||||
{
|
||||
desc: "With invalid Default TLS Options",
|
||||
serverName: "snitest.org",
|
||||
},
|
||||
{
|
||||
desc: "With TLS Options without servername (fallback to default)",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
if test.serverName != "" {
|
||||
tlsConfig.ServerName = test.serverName
|
||||
}
|
||||
|
||||
conn, err := tls.Dial("tcp", "127.0.0.1:4443", tlsConfig)
|
||||
c.Assert(err, checker.NotNil, check.Commentf("connected to server successfully"))
|
||||
c.Assert(conn, checker.IsNil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
server:
|
||||
image: rancher/k3s:v1.18.20-k3s1
|
||||
image: rancher/k3s:v1.20.15-k3s1
|
||||
command: server --disable-agent --no-deploy coredns --no-deploy servicelb --no-deploy traefik --no-deploy local-storage --no-deploy metrics-server --log /output/k3s.log --bind-address=server --tls-san=server
|
||||
environment:
|
||||
K3S_CLUSTER_SECRET: somethingtotallyrandom
|
||||
@@ -12,7 +12,7 @@ services:
|
||||
- ./fixtures/k8s:/var/lib/rancher/k3s/server/manifests
|
||||
|
||||
node:
|
||||
image: rancher/k3s:v1.18.20-k3s1
|
||||
image: rancher/k3s:v1.20.15-k3s1
|
||||
privileged: true
|
||||
environment:
|
||||
K3S_URL: https://server:6443
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
@@ -308,7 +309,7 @@ func (s *SimpleSuite) TestMetricsPrometheusDefaultEntryPoint(c *check.C) {
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer s.killCmd(cmd)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("PathPrefix"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("PathPrefix(`/whoami`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8000/whoami", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
@@ -369,6 +370,84 @@ func (s *SimpleSuite) TestMetricsPrometheusTwoRoutersOneService(c *check.C) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestMetricsWithBufferingMiddleware checks that the buffering middleware
|
||||
// (which introduces its own response writer in the chain), does not interfere with
|
||||
// the capture middleware on which the metrics mechanism relies.
|
||||
func (s *SimpleSuite) TestMetricsWithBufferingMiddleware(c *check.C) {
|
||||
s.createComposeProject(c, "base")
|
||||
|
||||
s.composeUp(c)
|
||||
defer s.composeDown(c)
|
||||
|
||||
server := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("MORE THAN TEN BYTES IN RESPONSE"))
|
||||
}))
|
||||
|
||||
server.Start()
|
||||
defer server.Close()
|
||||
|
||||
file := s.adaptFile(c, "fixtures/simple_metrics_with_buffer_middleware.toml", struct{ IP string }{IP: strings.TrimPrefix(server.URL, "http://")})
|
||||
defer os.Remove(file)
|
||||
|
||||
cmd, output := s.traefikCmd(withConfigFile(file))
|
||||
defer output(c)
|
||||
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer s.killCmd(cmd)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("PathPrefix(`/without`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8001/without", 1*time.Second, try.StatusCodeIs(http.StatusOK))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8002/with-req", strings.NewReader("MORE THAN TEN BYTES IN REQUEST"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// The request should fail because the body is too large.
|
||||
err = try.Request(req, 1*time.Second, try.StatusCodeIs(http.StatusRequestEntityTooLarge))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// The request should fail because the response exceeds the configured limit.
|
||||
err = try.GetRequest("http://127.0.0.1:8003/with-resp", 1*time.Second, try.StatusCodeIs(http.StatusInternalServerError))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
request, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8080/metrics", nil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
response, err := http.DefaultClient.Do(request)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
|
||||
|
||||
body, err := io.ReadAll(response.Body)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// For allowed requests and responses, the entrypoint and service metrics have the same status code.
|
||||
c.Assert(string(body), checker.Contains, "traefik_entrypoint_requests_total{code=\"200\",entrypoint=\"webA\",method=\"GET\",protocol=\"http\"} 1")
|
||||
c.Assert(string(body), checker.Contains, "traefik_entrypoint_requests_bytes_total{code=\"200\",entrypoint=\"webA\",method=\"GET\",protocol=\"http\"} 0")
|
||||
c.Assert(string(body), checker.Contains, "traefik_entrypoint_responses_bytes_total{code=\"200\",entrypoint=\"webA\",method=\"GET\",protocol=\"http\"} 31")
|
||||
|
||||
c.Assert(string(body), checker.Contains, "traefik_service_requests_total{code=\"200\",method=\"GET\",protocol=\"http\",service=\"service-without@file\"} 1")
|
||||
c.Assert(string(body), checker.Contains, "traefik_service_requests_bytes_total{code=\"200\",method=\"GET\",protocol=\"http\",service=\"service-without@file\"} 0")
|
||||
c.Assert(string(body), checker.Contains, "traefik_service_responses_bytes_total{code=\"200\",method=\"GET\",protocol=\"http\",service=\"service-without@file\"} 31")
|
||||
|
||||
// For forbidden requests, the entrypoints have metrics, the services don't.
|
||||
c.Assert(string(body), checker.Contains, "traefik_entrypoint_requests_total{code=\"413\",entrypoint=\"webB\",method=\"GET\",protocol=\"http\"} 1")
|
||||
c.Assert(string(body), checker.Contains, "traefik_entrypoint_requests_bytes_total{code=\"413\",entrypoint=\"webB\",method=\"GET\",protocol=\"http\"} 0")
|
||||
c.Assert(string(body), checker.Contains, "traefik_entrypoint_responses_bytes_total{code=\"413\",entrypoint=\"webB\",method=\"GET\",protocol=\"http\"} 24")
|
||||
|
||||
// For disallowed responses, the entrypoint and service metrics don't have the same status code.
|
||||
c.Assert(string(body), checker.Contains, "traefik_entrypoint_requests_bytes_total{code=\"500\",entrypoint=\"webC\",method=\"GET\",protocol=\"http\"} 0")
|
||||
c.Assert(string(body), checker.Contains, "traefik_entrypoint_requests_total{code=\"500\",entrypoint=\"webC\",method=\"GET\",protocol=\"http\"} 1")
|
||||
c.Assert(string(body), checker.Contains, "traefik_entrypoint_responses_bytes_total{code=\"500\",entrypoint=\"webC\",method=\"GET\",protocol=\"http\"} 21")
|
||||
|
||||
c.Assert(string(body), checker.Contains, "traefik_service_requests_bytes_total{code=\"200\",method=\"GET\",protocol=\"http\",service=\"service-resp@file\"} 0")
|
||||
c.Assert(string(body), checker.Contains, "traefik_service_requests_total{code=\"200\",method=\"GET\",protocol=\"http\",service=\"service-resp@file\"} 1")
|
||||
c.Assert(string(body), checker.Contains, "traefik_service_responses_bytes_total{code=\"200\",method=\"GET\",protocol=\"http\",service=\"service-resp@file\"} 31")
|
||||
}
|
||||
|
||||
func (s *SimpleSuite) TestMultipleProviderSameBackendName(c *check.C) {
|
||||
s.createComposeProject(c, "base")
|
||||
|
||||
@@ -1300,3 +1379,36 @@ func (s *SimpleSuite) TestMuxer(c *check.C) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SimpleSuite) TestDebugLog(c *check.C) {
|
||||
s.createComposeProject(c, "base")
|
||||
|
||||
s.composeUp(c)
|
||||
defer s.composeDown(c)
|
||||
|
||||
file := s.adaptFile(c, "fixtures/simple_debug_log.toml", struct{}{})
|
||||
defer os.Remove(file)
|
||||
|
||||
cmd, output := s.cmdTraefik(withConfigFile(file))
|
||||
|
||||
err := cmd.Start()
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer s.killCmd(cmd)
|
||||
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("PathPrefix(`/whoami`)"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "http://localhost:8000/whoami", http.NoBody)
|
||||
c.Assert(err, checker.IsNil)
|
||||
req.Header.Set("Autorization", "Bearer ThisIsABearerToken")
|
||||
|
||||
response, err := http.DefaultClient.Do(req)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(response.StatusCode, checker.Equals, http.StatusOK)
|
||||
|
||||
if regexp.MustCompile("ThisIsABearerToken").MatchReader(output) {
|
||||
c.Logf("Traefik Logs: %s", output.String())
|
||||
c.Log("Found Authorization Header in Traefik DEBUG logs")
|
||||
c.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,6 +116,14 @@ func (s *TCPSuite) TestTLSOptions(c *check.C) {
|
||||
_, err = guessWhoTLSMaxVersion("127.0.0.1:8093", "whoami-d.test", true, tls.VersionTLS12)
|
||||
c.Assert(err, checker.NotNil)
|
||||
c.Assert(err.Error(), checker.Contains, "protocol version not supported")
|
||||
|
||||
// Check that we can't reach a route with an invalid mTLS configuration.
|
||||
conn, err := tls.Dial("tcp", "127.0.0.1:8093", &tls.Config{
|
||||
ServerName: "whoami-i.test",
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
c.Assert(conn, checker.IsNil)
|
||||
c.Assert(err, checker.NotNil)
|
||||
}
|
||||
|
||||
func (s *TCPSuite) TestNonTLSFallback(c *check.C) {
|
||||
|
||||
@@ -65,6 +65,6 @@ func (s *TLSClientHeadersSuite) TestTLSClientHeaders(c *check.C) {
|
||||
},
|
||||
}
|
||||
|
||||
err = try.RequestWithTransport(request, 2*time.Second, tr, try.BodyContains("Forwarded-Tls-Client-Cert: MIIDNTCCAh0CFD0QQcHXUJuKwMBYDA%2BbBExVSP26MA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZGcmFuY2UxFTATBgNVBAoMDFRyYWVmaWsgTGFiczEQMA4GA1UECwwHdHJhZWZpazENMAsGA1UEAwwEcm9vdDAeFw0yMTAxMDgxNzQ0MjRaFw0zMTAxMDYxNzQ0MjRaMFgxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZGcmFuY2UxFTATBgNVBAoMDFRyYWVmaWsgTGFiczEQMA4GA1UECwwHdHJhZWZpazEPMA0GA1UEAwwGc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvYK2z8gLPOfFLgXNWP2460aeJ9vrH47x%2FlhKLlv4amSDHDx8Cmz%2F6blOUM8XOfMRW1xx%2B%2BAgChWN9dx%2Fkf7G2xlA5grZxRvUQ6xj7AvFG9TQUA3muNh2hvm9c3IjaZBNKH27bRKuDIBvZBvXdX4NL%2FaaFy7w7v7IKxk8j4WkfB23sgyH43g4b7NqKHJugZiedFu5GALmtLbShVOFbjWcre7Wvatdw8dIBmiFJqZQT3UjIuGAgqczIShtLxo4V%2BXyVkIPmzfPrRV%2B4zoMFIFOIaj3syyxb4krPBtxhe7nz2cWvvq0wePB2y4YbAAoVY8NYpd5JsMFwZtG6Uk59ygv4QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQDaPg69wNeFNFisfBJTrscqVCTW%2BB80gMhpLdxXD%2BKO0%2FWgc5xpB%2FwLSirNtRQyxAa3%2BEEcIwJv%2Fwdh8EyjlDLSpFm%2F8ghntrKhkOfIOPDFE41M5HNfx%2FFuh5btKEenOL%2FXdapqtNUt2ZE4RrsfbL79sPYepa9kDUVi2mCbeH5ollZ0MDU68HpB2YwHbCEuQNk5W3pjYK2NaDkVnxTkfEDM1k%2B3QydO1lqB5JJmcrs59BEveTqaJ3eeh%2F0I4OOab6OkTTZ0JNjJp1573oxO%2Bfce%2FbfGud8xHY5gSN9huU7U6RsgvO7Dhmal%2FsDNl8XC8oU90hVDVXZdA7ewh4jjaoIv"))
|
||||
err = try.RequestWithTransport(request, 2*time.Second, tr, try.BodyContains("Forwarded-Tls-Client-Cert: MIIDNTCCAh0CFD0QQcHXUJuKwMBYDA+bBExVSP26MA0GCSqGSIb3DQEBCwUAMFYxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZGcmFuY2UxFTATBgNVBAoMDFRyYWVmaWsgTGFiczEQMA4GA1UECwwHdHJhZWZpazENMAsGA1UEAwwEcm9vdDAeFw0yMTAxMDgxNzQ0MjRaFw0zMTAxMDYxNzQ0MjRaMFgxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZGcmFuY2UxFTATBgNVBAoMDFRyYWVmaWsgTGFiczEQMA4GA1UECwwHdHJhZWZpazEPMA0GA1UEAwwGc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvYK2z8gLPOfFLgXNWP2460aeJ9vrH47x/lhKLlv4amSDHDx8Cmz/6blOUM8XOfMRW1xx++AgChWN9dx/kf7G2xlA5grZxRvUQ6xj7AvFG9TQUA3muNh2hvm9c3IjaZBNKH27bRKuDIBvZBvXdX4NL/aaFy7w7v7IKxk8j4WkfB23sgyH43g4b7NqKHJugZiedFu5GALmtLbShVOFbjWcre7Wvatdw8dIBmiFJqZQT3UjIuGAgqczIShtLxo4V+XyVkIPmzfPrRV+4zoMFIFOIaj3syyxb4krPBtxhe7nz2cWvvq0wePB2y4YbAAoVY8NYpd5JsMFwZtG6Uk59ygv4QIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQDaPg69wNeFNFisfBJTrscqVCTW+B80gMhpLdxXD+KO0/Wgc5xpB/wLSirNtRQyxAa3+EEcIwJv/wdh8EyjlDLSpFm/8ghntrKhkOfIOPDFE41M5HNfx/Fuh5btKEenOL/XdapqtNUt2ZE4RrsfbL79sPYepa9kDUVi2mCbeH5ollZ0MDU68HpB2YwHbCEuQNk5W3pjYK2NaDkVnxTkfEDM1k+3QydO1lqB5JJmcrs59BEveTqaJ3eeh/0I4OOab6OkTTZ0JNjJp1573oxO+fce/bfGud8xHY5gSN9huU7U6RsgvO7Dhmal/sDNl8XC8oU90hVDVXZdA7ewh4jjaoIv"))
|
||||
c.Assert(err, checker.IsNil)
|
||||
}
|
||||
|
||||
@@ -416,7 +416,7 @@ type InFlightReq struct {
|
||||
// This middleware adds the selected data from the passed client TLS certificate to a header.
|
||||
// More info: https://doc.traefik.io/traefik/v2.9/middlewares/http/passtlsclientcert/
|
||||
type PassTLSClientCert struct {
|
||||
// PEM sets the X-Forwarded-Tls-Client-Cert header with the escaped certificate.
|
||||
// PEM sets the X-Forwarded-Tls-Client-Cert header with the certificate.
|
||||
PEM bool `json:"pem,omitempty" toml:"pem,omitempty" yaml:"pem,omitempty" export:"true"`
|
||||
// Info selects the specific client certificate details you want to add to the X-Forwarded-Tls-Client-Cert-Info header.
|
||||
Info *TLSClientCertificateInfo `json:"info,omitempty" toml:"info,omitempty" yaml:"info,omitempty" export:"true"`
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
"github.com/traefik/traefik/v2/pkg/metrics"
|
||||
"github.com/traefik/traefik/v2/pkg/safe"
|
||||
"github.com/vulcand/oxy/roundrobin"
|
||||
"github.com/vulcand/oxy/v2/roundrobin"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/traefik/traefik/v2/pkg/config/runtime"
|
||||
"github.com/traefik/traefik/v2/pkg/testhelpers"
|
||||
"github.com/vulcand/oxy/roundrobin"
|
||||
"github.com/vulcand/oxy/v2/roundrobin"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -9,67 +9,67 @@ import (
|
||||
)
|
||||
|
||||
// Debug logs a message at level Debug on the standard logger.
|
||||
// Deprecated
|
||||
// Deprecated: use log.FromContext(ctx).Debug(...) instead.
|
||||
func Debug(args ...interface{}) {
|
||||
mainLogger.Debug(args...)
|
||||
}
|
||||
|
||||
// Debugf logs a message at level Debug on the standard logger.
|
||||
// Deprecated
|
||||
// Deprecated: use log.FromContext(ctx).Debugf(...) instead.
|
||||
func Debugf(format string, args ...interface{}) {
|
||||
mainLogger.Debugf(format, args...)
|
||||
}
|
||||
|
||||
// Info logs a message at level Info on the standard logger.
|
||||
// Deprecated
|
||||
// Deprecated: use log.FromContext(ctx).Info(...) instead.
|
||||
func Info(args ...interface{}) {
|
||||
mainLogger.Info(args...)
|
||||
}
|
||||
|
||||
// Infof logs a message at level Info on the standard logger.
|
||||
// Deprecated
|
||||
// Deprecated: use log.FromContext(ctx).Infof(...) instead.
|
||||
func Infof(format string, args ...interface{}) {
|
||||
mainLogger.Infof(format, args...)
|
||||
}
|
||||
|
||||
// Warn logs a message at level Warn on the standard logger.
|
||||
// Deprecated
|
||||
// Deprecated: use log.FromContext(ctx).Warn(...) instead.
|
||||
func Warn(args ...interface{}) {
|
||||
mainLogger.Warn(args...)
|
||||
}
|
||||
|
||||
// Warnf logs a message at level Warn on the standard logger.
|
||||
// Deprecated
|
||||
// Deprecated: use log.FromContext(ctx).Warnf(...) instead.
|
||||
func Warnf(format string, args ...interface{}) {
|
||||
mainLogger.Warnf(format, args...)
|
||||
}
|
||||
|
||||
// Error logs a message at level Error on the standard logger.
|
||||
// Deprecated
|
||||
// Deprecated: use log.FromContext(ctx).Error(...) instead.
|
||||
func Error(args ...interface{}) {
|
||||
mainLogger.Error(args...)
|
||||
}
|
||||
|
||||
// Errorf logs a message at level Error on the standard logger.
|
||||
// Deprecated
|
||||
// Deprecated: use log.FromContext(ctx).Errorf(...) instead.
|
||||
func Errorf(format string, args ...interface{}) {
|
||||
mainLogger.Errorf(format, args...)
|
||||
}
|
||||
|
||||
// Panic logs a message at level Panic on the standard logger.
|
||||
// Deprecated
|
||||
// Deprecated: use log.FromContext(ctx).Panic(...) instead.
|
||||
func Panic(args ...interface{}) {
|
||||
mainLogger.Panic(args...)
|
||||
}
|
||||
|
||||
// Fatal logs a message at level Fatal on the standard logger.
|
||||
// Deprecated
|
||||
// Deprecated: use log.FromContext(ctx).Fatal(...) instead.
|
||||
func Fatal(args ...interface{}) {
|
||||
mainLogger.Fatal(args...)
|
||||
}
|
||||
|
||||
// Fatalf logs a message at level Fatal on the standard logger.
|
||||
// Deprecated
|
||||
// Deprecated: use log.FromContext(ctx).Fatalf(...) instead.
|
||||
func Fatalf(format string, args ...interface{}) {
|
||||
mainLogger.Fatalf(format, args...)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares/capture"
|
||||
"github.com/vulcand/oxy/utils"
|
||||
"github.com/vulcand/oxy/v2/utils"
|
||||
)
|
||||
|
||||
// FieldApply function hook to add data in accesslog.
|
||||
|
||||
@@ -227,6 +227,15 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http
|
||||
core[ClientHost] = forwardedFor
|
||||
}
|
||||
|
||||
ctx := req.Context()
|
||||
capt, err := capture.FromContext(ctx)
|
||||
if err != nil {
|
||||
log.FromContext(log.With(ctx, log.Str(log.MiddlewareType, "AccessLogs"))).
|
||||
WithError(err).
|
||||
Errorf("Could not get Capture")
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(rw, reqWithDataTable)
|
||||
|
||||
if _, ok := core[ClientUsername]; !ok {
|
||||
@@ -237,13 +246,6 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http
|
||||
headers: rw.Header().Clone(),
|
||||
}
|
||||
|
||||
ctx := req.Context()
|
||||
capt, err := capture.FromContext(ctx)
|
||||
if err != nil {
|
||||
log.FromContext(log.With(ctx, log.Str(log.MiddlewareType, "AccessLogs"))).Errorf("Could not get Capture: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
logDataTable.DownstreamResponse.status = capt.StatusCode()
|
||||
logDataTable.DownstreamResponse.size = capt.ResponseSize()
|
||||
logDataTable.Request.size = capt.RequestSize()
|
||||
|
||||
@@ -57,7 +57,7 @@ func TestLogRotation(t *testing.T) {
|
||||
})
|
||||
|
||||
chain := alice.New()
|
||||
chain = chain.Append(capture.WrapHandler(&capture.Handler{}))
|
||||
chain = chain.Append(capture.Wrap)
|
||||
chain = chain.Append(WrapHandler(logHandler))
|
||||
handler, err := chain.Then(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
@@ -210,7 +210,7 @@ func TestLoggerHeaderFields(t *testing.T) {
|
||||
}
|
||||
|
||||
chain := alice.New()
|
||||
chain = chain.Append(capture.WrapHandler(&capture.Handler{}))
|
||||
chain = chain.Append(capture.Wrap)
|
||||
chain = chain.Append(WrapHandler(logger))
|
||||
handler, err := chain.Then(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
@@ -784,7 +784,7 @@ func doLoggingTLSOpt(t *testing.T, config *types.AccessLog, enableTLS bool) {
|
||||
}
|
||||
|
||||
chain := alice.New()
|
||||
chain = chain.Append(capture.WrapHandler(&capture.Handler{}))
|
||||
chain = chain.Append(capture.Wrap)
|
||||
chain = chain.Append(WrapHandler(logger))
|
||||
handler, err := chain.Then(http.HandlerFunc(logWriterTestHandlerFunc))
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -17,8 +17,8 @@ import (
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares"
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares/connectionheader"
|
||||
"github.com/traefik/traefik/v2/pkg/tracing"
|
||||
"github.com/vulcand/oxy/forward"
|
||||
"github.com/vulcand/oxy/utils"
|
||||
"github.com/vulcand/oxy/v2/forward"
|
||||
"github.com/vulcand/oxy/v2/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
tracingMiddleware "github.com/traefik/traefik/v2/pkg/middlewares/tracing"
|
||||
"github.com/traefik/traefik/v2/pkg/testhelpers"
|
||||
"github.com/traefik/traefik/v2/pkg/tracing"
|
||||
"github.com/vulcand/oxy/forward"
|
||||
"github.com/vulcand/oxy/v2/forward"
|
||||
)
|
||||
|
||||
func TestForwardAuthFail(t *testing.T) {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares"
|
||||
"github.com/traefik/traefik/v2/pkg/tracing"
|
||||
oxybuffer "github.com/vulcand/oxy/buffer"
|
||||
oxybuffer "github.com/vulcand/oxy/v2/buffer"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -34,7 +34,7 @@ func New(ctx context.Context, next http.Handler, config dynamic.Buffering, name
|
||||
oxybuffer.MaxRequestBodyBytes(config.MaxRequestBodyBytes),
|
||||
oxybuffer.MemResponseBodyBytes(config.MemResponseBodyBytes),
|
||||
oxybuffer.MaxResponseBodyBytes(config.MaxResponseBodyBytes),
|
||||
oxybuffer.CondSetter(len(config.RetryExpression) > 0, oxybuffer.Retry(config.RetryExpression)),
|
||||
oxybuffer.Cond(len(config.RetryExpression) > 0, oxybuffer.Retry(config.RetryExpression)),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
// For another middleware to get those attributes of a request/response, this middleware
|
||||
// should be added before in the middleware chain.
|
||||
//
|
||||
// handler, _ := NewHandler()
|
||||
// chain := alice.New().
|
||||
// Append(WrapHandler(handler)).
|
||||
// Append(capture.Wrap).
|
||||
// Append(myOtherMiddleware).
|
||||
// then(...)
|
||||
//
|
||||
@@ -33,7 +32,6 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/containous/alice"
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares"
|
||||
)
|
||||
|
||||
@@ -41,62 +39,76 @@ type key string
|
||||
|
||||
const capturedData key = "capturedData"
|
||||
|
||||
// Handler will store each request data to its context.
|
||||
type Handler struct{}
|
||||
|
||||
// WrapHandler wraps capture handler into an Alice Constructor.
|
||||
func WrapHandler(handler *Handler) alice.Constructor {
|
||||
return func(next http.Handler) (http.Handler, error) {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
handler.ServeHTTP(rw, req, next)
|
||||
}), nil
|
||||
}
|
||||
// Wrap returns a new handler that inserts a Capture into the given handler for each incoming request.
|
||||
// It satisfies the alice.Constructor type.
|
||||
func Wrap(next http.Handler) (http.Handler, error) {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
c := &Capture{}
|
||||
newRW, newReq := c.renew(rw, req)
|
||||
next.ServeHTTP(newRW, newReq)
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http.Handler) {
|
||||
c := Capture{}
|
||||
if req.Body != nil {
|
||||
readCounter := &readCounter{source: req.Body}
|
||||
c.rr = readCounter
|
||||
req.Body = readCounter
|
||||
// FromContext returns the Capture value found in ctx, or an empty Capture otherwise.
|
||||
func FromContext(ctx context.Context) (Capture, error) {
|
||||
c := ctx.Value(capturedData)
|
||||
if c == nil {
|
||||
return Capture{}, errors.New("value not found in context")
|
||||
}
|
||||
responseWriter := newResponseWriter(rw)
|
||||
c.rw = responseWriter
|
||||
ctx := context.WithValue(req.Context(), capturedData, &c)
|
||||
next.ServeHTTP(responseWriter, req.WithContext(ctx))
|
||||
capt, ok := c.(*Capture)
|
||||
if !ok {
|
||||
return Capture{}, errors.New("value stored in context is not a *Capture")
|
||||
}
|
||||
return *capt, nil
|
||||
}
|
||||
|
||||
// Capture is the object populated by the capture middleware,
|
||||
// allowing to gather information about the request and response.
|
||||
// holding probes that allow to gather information about the request and response.
|
||||
type Capture struct {
|
||||
rr *readCounter
|
||||
rw responseWriter
|
||||
}
|
||||
|
||||
// FromContext returns the Capture value found in ctx, or an empty Capture otherwise.
|
||||
func FromContext(ctx context.Context) (*Capture, error) {
|
||||
c := ctx.Value(capturedData)
|
||||
if c == nil {
|
||||
return nil, errors.New("value not found")
|
||||
}
|
||||
capt, ok := c.(*Capture)
|
||||
if !ok {
|
||||
return nil, errors.New("value stored in Context is not a *Capture")
|
||||
}
|
||||
return capt, nil
|
||||
// NeedsReset returns whether the given http.ResponseWriter is the capture's probe.
|
||||
func (c *Capture) NeedsReset(rw http.ResponseWriter) bool {
|
||||
// This comparison is naive.
|
||||
return c.rw != rw
|
||||
}
|
||||
|
||||
func (c Capture) ResponseSize() int64 {
|
||||
// Reset returns a new handler that renews the Capture's probes, and inserts
|
||||
// them when deferring to next.
|
||||
func (c *Capture) Reset(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
newRW, newReq := c.renew(rw, req)
|
||||
next.ServeHTTP(newRW, newReq)
|
||||
})
|
||||
}
|
||||
|
||||
func (c *Capture) renew(rw http.ResponseWriter, req *http.Request) (http.ResponseWriter, *http.Request) {
|
||||
ctx := context.WithValue(req.Context(), capturedData, c)
|
||||
newReq := req.WithContext(ctx)
|
||||
|
||||
if newReq.Body != nil {
|
||||
readCounter := &readCounter{source: newReq.Body}
|
||||
c.rr = readCounter
|
||||
newReq.Body = readCounter
|
||||
}
|
||||
c.rw = newResponseWriter(rw)
|
||||
|
||||
return c.rw, newReq
|
||||
}
|
||||
|
||||
func (c *Capture) ResponseSize() int64 {
|
||||
return c.rw.Size()
|
||||
}
|
||||
|
||||
func (c Capture) StatusCode() int {
|
||||
func (c *Capture) StatusCode() int {
|
||||
return c.rw.Status()
|
||||
}
|
||||
|
||||
// RequestSize returns the size of the request's body if it applies,
|
||||
// zero otherwise.
|
||||
func (c Capture) RequestSize() int64 {
|
||||
func (c *Capture) RequestSize() int64 {
|
||||
if c.rr == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -38,9 +38,8 @@ func TestCapture(t *testing.T) {
|
||||
assert.Equal(t, "bar", string(all))
|
||||
})
|
||||
|
||||
wrapped := WrapHandler(&Handler{})
|
||||
chain := alice.New()
|
||||
chain = chain.Append(wrapped)
|
||||
chain = chain.Append(Wrap)
|
||||
chain = chain.Append(wrapMiddleware)
|
||||
handlers, err := chain.Then(handler)
|
||||
require.NoError(t, err)
|
||||
@@ -142,8 +141,7 @@ func BenchmarkCapture(b *testing.B) {
|
||||
|
||||
chain := alice.New()
|
||||
if test.capture || test.body {
|
||||
captureWrapped := WrapHandler(&Handler{})
|
||||
chain = chain.Append(captureWrapped)
|
||||
chain = chain.Append(Wrap)
|
||||
}
|
||||
handlers, err := chain.Then(next)
|
||||
require.NoError(b, err)
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares"
|
||||
"github.com/traefik/traefik/v2/pkg/tracing"
|
||||
"github.com/vulcand/oxy/cbreaker"
|
||||
"github.com/vulcand/oxy/v2/cbreaker"
|
||||
)
|
||||
|
||||
const typeName = "CircuitBreaker"
|
||||
@@ -28,7 +28,7 @@ func New(ctx context.Context, next http.Handler, confCircuitBreaker dynamic.Circ
|
||||
logger.Debug("Creating middleware")
|
||||
logger.Debugf("Setting up with expression: %s", expression)
|
||||
|
||||
cbOpts := []cbreaker.CircuitBreakerOption{
|
||||
cbOpts := []cbreaker.Option{
|
||||
cbreaker.Fallback(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
tracing.SetErrorWithEvent(req, "blocked by circuit-breaker (%q)", expression)
|
||||
rw.WriteHeader(http.StatusServiceUnavailable)
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares"
|
||||
"github.com/traefik/traefik/v2/pkg/tracing"
|
||||
"github.com/traefik/traefik/v2/pkg/types"
|
||||
"github.com/vulcand/oxy/utils"
|
||||
"github.com/vulcand/oxy/v2/utils"
|
||||
)
|
||||
|
||||
// Compile time validation that the response recorder implements http interfaces correctly.
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/traefik/traefik/v2/pkg/testhelpers"
|
||||
"github.com/vulcand/oxy/roundrobin"
|
||||
"github.com/vulcand/oxy/v2/roundrobin"
|
||||
)
|
||||
|
||||
func TestEmptyBackendHandler(t *testing.T) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
"github.com/vulcand/oxy/utils"
|
||||
"github.com/vulcand/oxy/v2/utils"
|
||||
)
|
||||
|
||||
// GetSourceExtractor returns the SourceExtractor function corresponding to the given sourceMatcher.
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares"
|
||||
"github.com/traefik/traefik/v2/pkg/tracing"
|
||||
"github.com/vulcand/oxy/connlimit"
|
||||
"github.com/vulcand/oxy/v2/connlimit"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -24,6 +24,7 @@ const (
|
||||
protoWebsocket = "websocket"
|
||||
typeName = "Metrics"
|
||||
nameEntrypoint = "metrics-entrypoint"
|
||||
nameRouter = "metrics-router"
|
||||
nameService = "metrics-service"
|
||||
)
|
||||
|
||||
@@ -56,7 +57,7 @@ func NewEntryPointMiddleware(ctx context.Context, next http.Handler, registry me
|
||||
|
||||
// NewRouterMiddleware creates a new metrics middleware for a Router.
|
||||
func NewRouterMiddleware(ctx context.Context, next http.Handler, registry metrics.Registry, routerName string, serviceName string) http.Handler {
|
||||
log.FromContext(middlewares.GetLoggerCtx(ctx, nameEntrypoint, typeName)).Debug("Creating middleware")
|
||||
log.FromContext(middlewares.GetLoggerCtx(ctx, nameRouter, typeName)).Debug("Creating middleware")
|
||||
|
||||
return &metricsMiddleware{
|
||||
next: next,
|
||||
@@ -125,17 +126,25 @@ func (m *metricsMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Request)
|
||||
m.reqsTLSCounter.With(tlsLabels...).Add(1)
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
|
||||
m.next.ServeHTTP(rw, req)
|
||||
|
||||
ctx := req.Context()
|
||||
|
||||
capt, err := capture.FromContext(ctx)
|
||||
if err != nil {
|
||||
log.FromContext(middlewares.GetLoggerCtx(ctx, nameEntrypoint, typeName)).Errorf("Could not get Capture: %w", err)
|
||||
for i := 0; i < len(m.baseLabels); i += 2 {
|
||||
ctx = log.With(ctx, log.Str(m.baseLabels[i], m.baseLabels[i+1]))
|
||||
}
|
||||
log.FromContext(ctx).WithError(err).Errorf("Could not get Capture")
|
||||
return
|
||||
}
|
||||
|
||||
next := m.next
|
||||
if capt.NeedsReset(rw) {
|
||||
next = capt.Reset(m.next)
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
next.ServeHTTP(rw, req)
|
||||
|
||||
labels = append(labels, "code", strconv.Itoa(capt.StatusCode()))
|
||||
m.reqDurationHistogram.With(labels...).ObserveFromStart(start)
|
||||
m.reqsCounter.With(labels...).Add(1)
|
||||
|
||||
@@ -325,13 +325,11 @@ func writePart(ctx context.Context, content io.StringWriter, entry, prefix strin
|
||||
|
||||
// sanitize As we pass the raw certificates, remove the useless data and make it http request compliant.
|
||||
func sanitize(cert []byte) string {
|
||||
cleaned := strings.NewReplacer(
|
||||
return strings.NewReplacer(
|
||||
"-----BEGIN CERTIFICATE-----", "",
|
||||
"-----END CERTIFICATE-----", "",
|
||||
"\n", "",
|
||||
).Replace(string(cert))
|
||||
|
||||
return url.QueryEscape(cleaned)
|
||||
}
|
||||
|
||||
// getCertificates Build a string with the client certificates.
|
||||
|
||||
@@ -610,7 +610,7 @@ WqeUSNGYV//RunTeuRDAf5OxehERb1srzBXhRZ3cZdzXbgR/`,
|
||||
|
||||
content := sanitize(test.toSanitize)
|
||||
|
||||
expected := url.QueryEscape(strings.ReplaceAll(test.expected, "\n", ""))
|
||||
expected := strings.ReplaceAll(test.expected, "\n", "")
|
||||
assert.Equal(t, expected, content, "The sanitized certificates should be equal")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares"
|
||||
"github.com/traefik/traefik/v2/pkg/tracing"
|
||||
"github.com/vulcand/oxy/utils"
|
||||
"github.com/vulcand/oxy/v2/utils"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
ptypes "github.com/traefik/paerser/types"
|
||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v2/pkg/testhelpers"
|
||||
"github.com/vulcand/oxy/utils"
|
||||
"github.com/vulcand/oxy/v2/utils"
|
||||
)
|
||||
|
||||
func TestNewRateLimiter(t *testing.T) {
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/opentracing/opentracing-go/ext"
|
||||
"github.com/traefik/traefik/v2/pkg/tracing"
|
||||
"github.com/vulcand/oxy/utils"
|
||||
"github.com/vulcand/oxy/v2/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -237,7 +237,7 @@ func headersRegexp(route *mux.Route, headers ...string) error {
|
||||
func query(route *mux.Route, query ...string) error {
|
||||
var queries []string
|
||||
for _, elem := range query {
|
||||
queries = append(queries, strings.Split(elem, "=")...)
|
||||
queries = append(queries, strings.SplitN(elem, "=", 2)...)
|
||||
}
|
||||
|
||||
route.Queries(queries...)
|
||||
|
||||
@@ -252,6 +252,14 @@ func Test_addRoute(t *testing.T) {
|
||||
"http://localhost/foo?bar=baz": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Query with multiple equals",
|
||||
rule: "Query(`foo=b=ar`)",
|
||||
expected: map[string]int{
|
||||
"http://localhost/foo?foo=b=ar": http.StatusOK,
|
||||
"http://localhost/foo?foo=bar": http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Rule with simple path",
|
||||
rule: `Path("/a")`,
|
||||
@@ -473,7 +481,7 @@ func Test_addRoute(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Rule with not on multiple route with and and another not",
|
||||
desc: "Rule with not on multiple route with and another not",
|
||||
rule: `!(Host("tchouk") && !Path("/titi"))`,
|
||||
expected: map[string]int{
|
||||
"http://tchouk/titi": http.StatusOK,
|
||||
|
||||
@@ -78,7 +78,7 @@ func NewClient(opts ClientOptions) (*Client, error) {
|
||||
}
|
||||
|
||||
return &Client{
|
||||
HTTPClient: &http.Client{Timeout: 5 * time.Second},
|
||||
HTTPClient: &http.Client{Timeout: 10 * time.Second},
|
||||
baseURL: baseURL,
|
||||
|
||||
archives: archivesPath,
|
||||
|
||||
@@ -397,7 +397,7 @@ func (p *Provider) resolveDomains(ctx context.Context, domains []string, tlsStor
|
||||
return
|
||||
}
|
||||
|
||||
err = p.addCertificateForDomain(dom, cert.Certificate, cert.PrivateKey, tlsStore)
|
||||
err = p.addCertificateForDomain(dom, cert, tlsStore)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Error adding certificate for domain")
|
||||
}
|
||||
@@ -431,7 +431,7 @@ func (p *Provider) watchNewDomains(ctx context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
err = p.addCertificateForDomain(dom, cert.Certificate, cert.PrivateKey, traefiktls.DefaultTLSStoreName)
|
||||
err = p.addCertificateForDomain(dom, cert, traefiktls.DefaultTLSStoreName)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Error adding certificate for domain")
|
||||
}
|
||||
@@ -468,7 +468,7 @@ func (p *Provider) watchNewDomains(ctx context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
err = p.addCertificateForDomain(dom, cert.Certificate, cert.PrivateKey, traefiktls.DefaultTLSStoreName)
|
||||
err = p.addCertificateForDomain(dom, cert, traefiktls.DefaultTLSStoreName)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Error adding certificate for domain")
|
||||
}
|
||||
@@ -535,7 +535,7 @@ func (p *Provider) watchNewDomains(ctx context.Context) {
|
||||
domain.SANs = validDomains[1:]
|
||||
}
|
||||
|
||||
err = p.addCertificateForDomain(domain, cert.Certificate, cert.PrivateKey, traefiktls.DefaultTLSStoreName)
|
||||
err = p.addCertificateForDomain(domain, cert, traefiktls.DefaultTLSStoreName)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Error adding certificate for domain")
|
||||
}
|
||||
@@ -660,11 +660,15 @@ func (p *Provider) removeResolvingDomains(resolvingDomains []string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Provider) addCertificateForDomain(domain types.Domain, certificate, key []byte, tlsStore string) error {
|
||||
func (p *Provider) addCertificateForDomain(domain types.Domain, crt *certificate.Resource, tlsStore string) error {
|
||||
if crt == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
p.certificatesMu.Lock()
|
||||
defer p.certificatesMu.Unlock()
|
||||
|
||||
cert := Certificate{Certificate: certificate, Key: key, Domain: domain}
|
||||
cert := Certificate{Certificate: crt.Certificate, Key: crt.PrivateKey, Domain: domain}
|
||||
|
||||
certUpdated := false
|
||||
for _, domainsCertificate := range p.certificates {
|
||||
@@ -828,7 +832,7 @@ func (p *Provider) renewCertificates(ctx context.Context, renewPeriod time.Durat
|
||||
continue
|
||||
}
|
||||
|
||||
err = p.addCertificateForDomain(cert.Domain, renewedCert.Certificate, renewedCert.PrivateKey, cert.Store)
|
||||
err = p.addCertificateForDomain(cert.Domain, renewedCert, cert.Store)
|
||||
if err != nil {
|
||||
logger.WithError(err).Error("Error adding certificate for domain")
|
||||
}
|
||||
|
||||
@@ -195,29 +195,27 @@ func (p *Provider) addServerTCP(item itemData, loadBalancer *dynamic.TCPServersL
|
||||
return errors.New("load-balancer is not defined")
|
||||
}
|
||||
|
||||
var port string
|
||||
if len(loadBalancer.Servers) > 0 {
|
||||
port = loadBalancer.Servers[0].Port
|
||||
}
|
||||
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
loadBalancer.Servers = []dynamic.TCPServer{{}}
|
||||
}
|
||||
|
||||
if item.Port != "" && port == "" {
|
||||
port = item.Port
|
||||
}
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
if item.Address == "" {
|
||||
return errors.New("address is missing")
|
||||
}
|
||||
|
||||
port := loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
if port == "" {
|
||||
port = item.Port
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
loadBalancer.Servers[0].Address = net.JoinHostPort(item.Address, port)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -230,21 +228,23 @@ func (p *Provider) addServerUDP(item itemData, loadBalancer *dynamic.UDPServersL
|
||||
loadBalancer.Servers = []dynamic.UDPServer{{}}
|
||||
}
|
||||
|
||||
var port string
|
||||
if item.Port != "" {
|
||||
if item.Address == "" {
|
||||
return errors.New("address is missing")
|
||||
}
|
||||
|
||||
port := loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
if port == "" {
|
||||
port = item.Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
if item.Address == "" {
|
||||
return errors.New("address is missing")
|
||||
}
|
||||
|
||||
loadBalancer.Servers[0].Address = net.JoinHostPort(item.Address, port)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -253,11 +253,6 @@ func (p *Provider) addServer(item itemData, loadBalancer *dynamic.ServersLoadBal
|
||||
return errors.New("load-balancer is not defined")
|
||||
}
|
||||
|
||||
var port string
|
||||
if len(loadBalancer.Servers) > 0 {
|
||||
port = loadBalancer.Servers[0].Port
|
||||
}
|
||||
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
server := dynamic.Server{}
|
||||
server.SetDefaults()
|
||||
@@ -265,17 +260,19 @@ func (p *Provider) addServer(item itemData, loadBalancer *dynamic.ServersLoadBal
|
||||
loadBalancer.Servers = []dynamic.Server{server}
|
||||
}
|
||||
|
||||
if item.Port != "" && port == "" {
|
||||
port = item.Port
|
||||
if item.Address == "" {
|
||||
return errors.New("address is missing")
|
||||
}
|
||||
|
||||
port := loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
port = item.Port
|
||||
}
|
||||
|
||||
if item.Address == "" {
|
||||
return errors.New("address is missing")
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
scheme := loadBalancer.Servers[0].Scheme
|
||||
|
||||
@@ -2220,7 +2220,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Labels: map[string]string{
|
||||
"traefik.tcp.routers.foo.rule": "HostSNI(`foo.bar`)",
|
||||
"traefik.tcp.routers.foo.tls.options": "foo",
|
||||
"traefik.tcp.services.foo.loadbalancer.server.port": "80",
|
||||
"traefik.tcp.services.foo.loadbalancer.server.port": "8080",
|
||||
},
|
||||
Address: "127.0.0.1",
|
||||
Port: "80",
|
||||
@@ -2244,7 +2244,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||
Servers: []dynamic.TCPServer{
|
||||
{
|
||||
Address: "127.0.0.1:80",
|
||||
Address: "127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
@@ -2858,6 +2858,57 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "UDP service with labels only",
|
||||
ConnectAware: true,
|
||||
items: []itemData{
|
||||
{
|
||||
ID: "1",
|
||||
Node: "Node1",
|
||||
Datacenter: "dc1",
|
||||
Name: "Test",
|
||||
Namespace: "ns",
|
||||
Labels: map[string]string{
|
||||
"traefik.udp.routers.test-udp-label.service": "test-udp-label-service",
|
||||
"traefik.udp.routers.test-udp-label.entryPoints": "udp",
|
||||
"traefik.udp.services.test-udp-label-service.loadBalancer.server.port": "21116",
|
||||
},
|
||||
Address: "127.0.0.1",
|
||||
Port: "80",
|
||||
Status: api.HealthPassing,
|
||||
},
|
||||
},
|
||||
expected: &dynamic.Configuration{
|
||||
TCP: &dynamic.TCPConfiguration{
|
||||
Routers: map[string]*dynamic.TCPRouter{},
|
||||
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
||||
Services: map[string]*dynamic.TCPService{},
|
||||
},
|
||||
UDP: &dynamic.UDPConfiguration{
|
||||
Routers: map[string]*dynamic.UDPRouter{
|
||||
"test-udp-label": {
|
||||
EntryPoints: []string{"udp"},
|
||||
Service: "test-udp-label-service",
|
||||
},
|
||||
},
|
||||
Services: map[string]*dynamic.UDPService{
|
||||
"test-udp-label-service": {
|
||||
LoadBalancer: &dynamic.UDPServersLoadBalancer{
|
||||
Servers: []dynamic.UDPServer{
|
||||
{Address: "127.0.0.1:21116"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
HTTP: &dynamic.HTTPConfiguration{
|
||||
Routers: map[string]*dynamic.Router{},
|
||||
Middlewares: map[string]*dynamic.Middleware{},
|
||||
Services: map[string]*dynamic.Service{},
|
||||
ServersTransports: map[string]*dynamic.ServersTransport{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
|
||||
@@ -47,12 +47,6 @@ func nodeIP(ip string) func(*docker.ContainerJSON) {
|
||||
}
|
||||
}
|
||||
|
||||
func labels(labels map[string]string) func(*docker.ContainerJSON) {
|
||||
return func(c *docker.ContainerJSON) {
|
||||
c.Config.Labels = labels
|
||||
}
|
||||
}
|
||||
|
||||
func ports(portMap nat.PortMap) func(*docker.ContainerJSON) {
|
||||
return func(c *docker.ContainerJSON) {
|
||||
c.NetworkSettings.NetworkSettingsBase.Ports = portMap
|
||||
|
||||
@@ -196,28 +196,24 @@ func (p *Provider) addServerTCP(ctx context.Context, container dockerData, loadB
|
||||
return errors.New("load-balancer is not defined")
|
||||
}
|
||||
|
||||
var serverPort string
|
||||
if len(loadBalancer.Servers) > 0 {
|
||||
serverPort = loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
loadBalancer.Servers = []dynamic.TCPServer{{}}
|
||||
}
|
||||
|
||||
serverPort := loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
ip, port, err := p.getIPPort(ctx, container, serverPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
server := dynamic.TCPServer{}
|
||||
|
||||
loadBalancer.Servers = []dynamic.TCPServer{server}
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
loadBalancer.Servers[0].Address = net.JoinHostPort(ip, port)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -226,28 +222,24 @@ func (p *Provider) addServerUDP(ctx context.Context, container dockerData, loadB
|
||||
return errors.New("load-balancer is not defined")
|
||||
}
|
||||
|
||||
var serverPort string
|
||||
if len(loadBalancer.Servers) > 0 {
|
||||
serverPort = loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
loadBalancer.Servers = []dynamic.UDPServer{{}}
|
||||
}
|
||||
|
||||
serverPort := loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
ip, port, err := p.getIPPort(ctx, container, serverPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
server := dynamic.UDPServer{}
|
||||
|
||||
loadBalancer.Servers = []dynamic.UDPServer{server}
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
loadBalancer.Servers[0].Address = net.JoinHostPort(ip, port)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -256,17 +248,6 @@ func (p *Provider) addServer(ctx context.Context, container dockerData, loadBala
|
||||
return errors.New("load-balancer is not defined")
|
||||
}
|
||||
|
||||
var serverPort string
|
||||
if len(loadBalancer.Servers) > 0 {
|
||||
serverPort = loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
}
|
||||
|
||||
ip, port, err := p.getIPPort(ctx, container, serverPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
server := dynamic.Server{}
|
||||
server.SetDefaults()
|
||||
@@ -274,6 +255,14 @@ func (p *Provider) addServer(ctx context.Context, container dockerData, loadBala
|
||||
loadBalancer.Servers = []dynamic.Server{server}
|
||||
}
|
||||
|
||||
serverPort := loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
ip, port, err := p.getIPPort(ctx, container, serverPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ var _ provider.Provider = (*Provider)(nil)
|
||||
// Provider holds configurations of the provider.
|
||||
type Provider struct {
|
||||
Constraints string `description:"Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container." json:"constraints,omitempty" toml:"constraints,omitempty" yaml:"constraints,omitempty" export:"true"`
|
||||
Watch bool `description:"Watch Docker Swarm events." json:"watch,omitempty" toml:"watch,omitempty" yaml:"watch,omitempty" export:"true"`
|
||||
Watch bool `description:"Watch Docker events." json:"watch,omitempty" toml:"watch,omitempty" yaml:"watch,omitempty" export:"true"`
|
||||
Endpoint string `description:"Docker server endpoint. Can be a tcp or a unix socket endpoint." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"`
|
||||
DefaultRule string `description:"Default rule." json:"defaultRule,omitempty" toml:"defaultRule,omitempty" yaml:"defaultRule,omitempty"`
|
||||
TLS *types.ClientTLS `description:"Enable Docker TLS support." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`
|
||||
|
||||
@@ -185,7 +185,7 @@ func (p *Provider) filterInstance(ctx context.Context, instance ecsInstance) boo
|
||||
|
||||
matches, err := constraints.MatchLabels(instance.Labels, p.Constraints)
|
||||
if err != nil {
|
||||
logger.Errorf("Error matching constraints expression: %v", err)
|
||||
logger.Errorf("Error matching constraint expression: %v", err)
|
||||
return false
|
||||
}
|
||||
if !matches {
|
||||
@@ -201,28 +201,24 @@ func (p *Provider) addServerTCP(instance ecsInstance, loadBalancer *dynamic.TCPS
|
||||
return errors.New("load-balancer is not defined")
|
||||
}
|
||||
|
||||
var serverPort string
|
||||
if len(loadBalancer.Servers) > 0 {
|
||||
serverPort = loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
loadBalancer.Servers = []dynamic.TCPServer{{}}
|
||||
}
|
||||
|
||||
serverPort := loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
ip, port, err := p.getIPPort(instance, serverPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
server := dynamic.TCPServer{}
|
||||
|
||||
loadBalancer.Servers = []dynamic.TCPServer{server}
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
loadBalancer.Servers[0].Address = net.JoinHostPort(ip, port)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -231,28 +227,24 @@ func (p *Provider) addServerUDP(instance ecsInstance, loadBalancer *dynamic.UDPS
|
||||
return errors.New("load-balancer is not defined")
|
||||
}
|
||||
|
||||
var serverPort string
|
||||
if len(loadBalancer.Servers) > 0 {
|
||||
serverPort = loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
loadBalancer.Servers = []dynamic.UDPServer{{}}
|
||||
}
|
||||
|
||||
serverPort := loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
ip, port, err := p.getIPPort(instance, serverPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
server := dynamic.UDPServer{}
|
||||
|
||||
loadBalancer.Servers = []dynamic.UDPServer{server}
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
loadBalancer.Servers[0].Address = net.JoinHostPort(ip, port)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -261,17 +253,6 @@ func (p *Provider) addServer(instance ecsInstance, loadBalancer *dynamic.Servers
|
||||
return errors.New("load-balancer is not defined")
|
||||
}
|
||||
|
||||
var serverPort string
|
||||
if len(loadBalancer.Servers) > 0 {
|
||||
serverPort = loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
}
|
||||
|
||||
ip, port, err := p.getIPPort(instance, serverPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
server := dynamic.Server{}
|
||||
server.SetDefaults()
|
||||
@@ -279,6 +260,14 @@ func (p *Provider) addServer(instance ecsInstance, loadBalancer *dynamic.Servers
|
||||
loadBalancer.Servers = []dynamic.Server{server}
|
||||
}
|
||||
|
||||
serverPort := loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
ip, port, err := p.getIPPort(instance, serverPort)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: test.route
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
entryPoints:
|
||||
- foo
|
||||
|
||||
routes:
|
||||
- match: Host(`foo.com`) && PathPrefix(`/bar`)
|
||||
kind: Rule
|
||||
priority: 12
|
||||
services:
|
||||
- name: test-weighted
|
||||
kind: TraefikService
|
||||
- name: test-mirror
|
||||
kind: TraefikService
|
||||
middlewares:
|
||||
- name: test-errorpage
|
||||
|
||||
---
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: TraefikService
|
||||
metadata:
|
||||
name: test-weighted
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
weighted:
|
||||
services:
|
||||
- name: whoami-without-endpoints-subsets
|
||||
weight: 1
|
||||
port: 80
|
||||
|
||||
---
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: TraefikService
|
||||
metadata:
|
||||
name: test-mirror
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
mirroring:
|
||||
name: whoami-without-endpoints-subsets
|
||||
port: 80
|
||||
mirrors:
|
||||
- name: whoami-without-endpoints-subsets
|
||||
port: 80
|
||||
- name: test-weighted
|
||||
kind: TraefikService
|
||||
|
||||
---
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: test-errorpage
|
||||
namespace: default
|
||||
|
||||
spec:
|
||||
errors:
|
||||
service:
|
||||
name: whoami-without-endpoints-subsets
|
||||
port: 80
|
||||
@@ -292,7 +292,12 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
|
||||
}
|
||||
}
|
||||
|
||||
cb := configBuilder{client: client, allowCrossNamespace: p.AllowCrossNamespace, allowExternalNameServices: p.AllowExternalNameServices}
|
||||
cb := configBuilder{
|
||||
client: client,
|
||||
allowCrossNamespace: p.AllowCrossNamespace,
|
||||
allowExternalNameServices: p.AllowExternalNameServices,
|
||||
allowEmptyServices: p.AllowEmptyServices,
|
||||
}
|
||||
|
||||
for _, service := range client.GetTraefikServices() {
|
||||
err := cb.buildTraefikService(ctx, service, conf.HTTP.Services)
|
||||
@@ -578,7 +583,14 @@ func (p *Provider) createErrorPageMiddleware(client Client, namespace string, er
|
||||
Query: errorPage.Query,
|
||||
}
|
||||
|
||||
balancerServerHTTP, err := configBuilder{client: client, allowCrossNamespace: p.AllowCrossNamespace, allowExternalNameServices: p.AllowExternalNameServices}.buildServersLB(namespace, errorPage.Service.LoadBalancerSpec)
|
||||
cb := configBuilder{
|
||||
client: client,
|
||||
allowCrossNamespace: p.AllowCrossNamespace,
|
||||
allowExternalNameServices: p.AllowExternalNameServices,
|
||||
allowEmptyServices: p.AllowEmptyServices,
|
||||
}
|
||||
|
||||
balancerServerHTTP, err := cb.buildServersLB(namespace, errorPage.Service.LoadBalancerSpec)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
@@ -4037,6 +4037,91 @@ func TestLoadIngressRoutes(t *testing.T) {
|
||||
TLS: &dynamic.TLSConfiguration{},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "TraefikService, empty service allowed",
|
||||
allowEmptyServices: true,
|
||||
paths: []string{"services.yml", "with_empty_services_ts.yml"},
|
||||
expected: &dynamic.Configuration{
|
||||
UDP: &dynamic.UDPConfiguration{
|
||||
Routers: map[string]*dynamic.UDPRouter{},
|
||||
Services: map[string]*dynamic.UDPService{},
|
||||
},
|
||||
TCP: &dynamic.TCPConfiguration{
|
||||
Routers: map[string]*dynamic.TCPRouter{},
|
||||
Middlewares: map[string]*dynamic.TCPMiddleware{},
|
||||
Services: map[string]*dynamic.TCPService{},
|
||||
},
|
||||
HTTP: &dynamic.HTTPConfiguration{
|
||||
Routers: map[string]*dynamic.Router{
|
||||
"default-test-route-6b204d94623b3df4370c": {
|
||||
EntryPoints: []string{"foo"},
|
||||
Middlewares: []string{"default-test-errorpage"},
|
||||
Service: "default-test-route-6b204d94623b3df4370c",
|
||||
Rule: "Host(`foo.com`) && PathPrefix(`/bar`)",
|
||||
Priority: 12,
|
||||
},
|
||||
},
|
||||
Middlewares: map[string]*dynamic.Middleware{
|
||||
"default-test-errorpage": {
|
||||
Errors: &dynamic.ErrorPage{
|
||||
Service: "default-test-errorpage-errorpage-service",
|
||||
},
|
||||
},
|
||||
},
|
||||
Services: map[string]*dynamic.Service{
|
||||
"default-test-route-6b204d94623b3df4370c": {
|
||||
Weighted: &dynamic.WeightedRoundRobin{
|
||||
Services: []dynamic.WRRService{
|
||||
{
|
||||
Name: "default-test-weighted",
|
||||
Weight: func(i int) *int { return &i }(1),
|
||||
},
|
||||
{
|
||||
Name: "default-test-mirror",
|
||||
Weight: func(i int) *int { return &i }(1),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"default-test-errorpage-errorpage-service": {
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
},
|
||||
},
|
||||
"default-test-weighted": {
|
||||
Weighted: &dynamic.WeightedRoundRobin{
|
||||
Services: []dynamic.WRRService{
|
||||
{
|
||||
Name: "default-whoami-without-endpoints-subsets-80",
|
||||
Weight: func(i int) *int { return &i }(1),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"default-test-mirror": {
|
||||
Mirroring: &dynamic.Mirroring{
|
||||
Service: "default-whoami-without-endpoints-subsets-80",
|
||||
Mirrors: []dynamic.MirrorService{
|
||||
{
|
||||
Name: "default-whoami-without-endpoints-subsets-80",
|
||||
},
|
||||
{
|
||||
Name: "default-test-weighted",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"default-whoami-without-endpoints-subsets-80": {
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
ServersTransports: map[string]*dynamic.ServersTransport{},
|
||||
},
|
||||
TLS: &dynamic.TLSConfiguration{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
|
||||
@@ -571,7 +571,7 @@ func TestLoadConfigurationFromIngresses(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "Ingress with with port name in backend and 2 pod replica",
|
||||
desc: "Ingress with port name in backend and 2 pod replica",
|
||||
expected: &dynamic.Configuration{
|
||||
TCP: &dynamic.TCPConfiguration{},
|
||||
HTTP: &dynamic.HTTPConfiguration{
|
||||
|
||||
@@ -30,7 +30,7 @@ func (p *Provider) SetDefaults() {
|
||||
|
||||
// Init the provider.
|
||||
func (p *Provider) Init() error {
|
||||
config := redis.Config{
|
||||
config := &redis.Config{
|
||||
Username: p.Username,
|
||||
Password: p.Password,
|
||||
DB: p.DB,
|
||||
|
||||
@@ -175,29 +175,27 @@ func (p *Provider) addServerTCP(i item, lb *dynamic.TCPServersLoadBalancer) erro
|
||||
return errors.New("load-balancer is missing")
|
||||
}
|
||||
|
||||
var port string
|
||||
if len(lb.Servers) > 0 {
|
||||
port = lb.Servers[0].Port
|
||||
}
|
||||
|
||||
if len(lb.Servers) == 0 {
|
||||
lb.Servers = []dynamic.TCPServer{{}}
|
||||
}
|
||||
|
||||
if i.Port != 0 && port == "" {
|
||||
port = strconv.Itoa(i.Port)
|
||||
}
|
||||
lb.Servers[0].Port = ""
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
if i.Address == "" {
|
||||
return errors.New("address is missing")
|
||||
}
|
||||
|
||||
port := lb.Servers[0].Port
|
||||
lb.Servers[0].Port = ""
|
||||
|
||||
if port == "" && i.Port > 0 {
|
||||
port = strconv.Itoa(i.Port)
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
lb.Servers[0].Address = net.JoinHostPort(i.Address, port)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -206,29 +204,27 @@ func (p *Provider) addServerUDP(i item, lb *dynamic.UDPServersLoadBalancer) erro
|
||||
return errors.New("load-balancer is missing")
|
||||
}
|
||||
|
||||
var port string
|
||||
if len(lb.Servers) > 0 {
|
||||
port = lb.Servers[0].Port
|
||||
}
|
||||
|
||||
if len(lb.Servers) == 0 {
|
||||
lb.Servers = []dynamic.UDPServer{{}}
|
||||
}
|
||||
|
||||
if i.Port != 0 && port == "" {
|
||||
port = strconv.Itoa(i.Port)
|
||||
}
|
||||
lb.Servers[0].Port = ""
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
if i.Address == "" {
|
||||
return errors.New("address is missing")
|
||||
}
|
||||
|
||||
port := lb.Servers[0].Port
|
||||
lb.Servers[0].Port = ""
|
||||
|
||||
if port == "" && i.Port > 0 {
|
||||
port = strconv.Itoa(i.Port)
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
lb.Servers[0].Address = net.JoinHostPort(i.Address, port)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -237,11 +233,6 @@ func (p *Provider) addServer(i item, lb *dynamic.ServersLoadBalancer) error {
|
||||
return errors.New("load-balancer is missing")
|
||||
}
|
||||
|
||||
var port string
|
||||
if len(lb.Servers) > 0 {
|
||||
port = lb.Servers[0].Port
|
||||
}
|
||||
|
||||
if len(lb.Servers) == 0 {
|
||||
server := dynamic.Server{}
|
||||
server.SetDefaults()
|
||||
@@ -249,19 +240,21 @@ func (p *Provider) addServer(i item, lb *dynamic.ServersLoadBalancer) error {
|
||||
lb.Servers = []dynamic.Server{server}
|
||||
}
|
||||
|
||||
if i.Port != 0 && port == "" {
|
||||
if i.Address == "" {
|
||||
return errors.New("address is missing")
|
||||
}
|
||||
|
||||
port := lb.Servers[0].Port
|
||||
lb.Servers[0].Port = ""
|
||||
|
||||
if port == "" && i.Port > 0 {
|
||||
port = strconv.Itoa(i.Port)
|
||||
}
|
||||
lb.Servers[0].Port = ""
|
||||
|
||||
if port == "" {
|
||||
return errors.New("port is missing")
|
||||
}
|
||||
|
||||
if i.Address == "" {
|
||||
return errors.New("address is missing")
|
||||
}
|
||||
|
||||
scheme := lb.Servers[0].Scheme
|
||||
lb.Servers[0].Scheme = ""
|
||||
lb.Servers[0].URL = fmt.Sprintf("%s://%s", scheme, net.JoinHostPort(i.Address, port))
|
||||
|
||||
@@ -160,7 +160,7 @@ func (p *Provider) keepService(ctx context.Context, service rancherData) bool {
|
||||
|
||||
matches, err := constraints.MatchLabels(service.Labels, p.Constraints)
|
||||
if err != nil {
|
||||
logger.Errorf("Error matching constraints expression: %v", err)
|
||||
logger.Errorf("Error matching constraint expression: %v", err)
|
||||
return false
|
||||
}
|
||||
if !matches {
|
||||
@@ -185,23 +185,19 @@ func (p *Provider) keepService(ctx context.Context, service rancherData) bool {
|
||||
func (p *Provider) addServerTCP(ctx context.Context, service rancherData, loadBalancer *dynamic.TCPServersLoadBalancer) error {
|
||||
log.FromContext(ctx).Debugf("Trying to add servers for service %s \n", service.Name)
|
||||
|
||||
serverPort := ""
|
||||
|
||||
if loadBalancer != nil && len(loadBalancer.Servers) > 0 {
|
||||
serverPort = loadBalancer.Servers[0].Port
|
||||
if loadBalancer == nil {
|
||||
return errors.New("load-balancer is not defined")
|
||||
}
|
||||
|
||||
port := getServicePort(service)
|
||||
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
server := dynamic.TCPServer{}
|
||||
|
||||
loadBalancer.Servers = []dynamic.TCPServer{server}
|
||||
loadBalancer.Servers = []dynamic.TCPServer{{}}
|
||||
}
|
||||
|
||||
if serverPort != "" {
|
||||
port = serverPort
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
port := loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
if port == "" {
|
||||
port = getServicePort(service)
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
@@ -216,29 +212,26 @@ func (p *Provider) addServerTCP(ctx context.Context, service rancherData, loadBa
|
||||
}
|
||||
|
||||
loadBalancer.Servers = servers
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provider) addServerUDP(ctx context.Context, service rancherData, loadBalancer *dynamic.UDPServersLoadBalancer) error {
|
||||
log.FromContext(ctx).Debugf("Trying to add servers for service %s \n", service.Name)
|
||||
|
||||
serverPort := ""
|
||||
|
||||
if loadBalancer != nil && len(loadBalancer.Servers) > 0 {
|
||||
serverPort = loadBalancer.Servers[0].Port
|
||||
if loadBalancer == nil {
|
||||
return errors.New("load-balancer is not defined")
|
||||
}
|
||||
|
||||
port := getServicePort(service)
|
||||
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
server := dynamic.UDPServer{}
|
||||
|
||||
loadBalancer.Servers = []dynamic.UDPServer{server}
|
||||
loadBalancer.Servers = []dynamic.UDPServer{{}}
|
||||
}
|
||||
|
||||
if serverPort != "" {
|
||||
port = serverPort
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
port := loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
if port == "" {
|
||||
port = getServicePort(service)
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
@@ -253,14 +246,16 @@ func (p *Provider) addServerUDP(ctx context.Context, service rancherData, loadBa
|
||||
}
|
||||
|
||||
loadBalancer.Servers = servers
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Provider) addServers(ctx context.Context, service rancherData, loadBalancer *dynamic.ServersLoadBalancer) error {
|
||||
log.FromContext(ctx).Debugf("Trying to add servers for service %s \n", service.Name)
|
||||
|
||||
serverPort := getLBServerPort(loadBalancer)
|
||||
port := getServicePort(service)
|
||||
if loadBalancer == nil {
|
||||
return errors.New("load-balancer is not defined")
|
||||
}
|
||||
|
||||
if len(loadBalancer.Servers) == 0 {
|
||||
server := dynamic.Server{}
|
||||
@@ -269,9 +264,11 @@ func (p *Provider) addServers(ctx context.Context, service rancherData, loadBala
|
||||
loadBalancer.Servers = []dynamic.Server{server}
|
||||
}
|
||||
|
||||
if serverPort != "" {
|
||||
port = serverPort
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
port := loadBalancer.Servers[0].Port
|
||||
loadBalancer.Servers[0].Port = ""
|
||||
|
||||
if port == "" {
|
||||
port = getServicePort(service)
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
@@ -286,14 +283,8 @@ func (p *Provider) addServers(ctx context.Context, service rancherData, loadBala
|
||||
}
|
||||
|
||||
loadBalancer.Servers = servers
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLBServerPort(loadBalancer *dynamic.ServersLoadBalancer) string {
|
||||
if loadBalancer != nil && len(loadBalancer.Servers) > 0 {
|
||||
return loadBalancer.Servers[0].Port
|
||||
}
|
||||
return ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func getServicePort(data rancherData) string {
|
||||
|
||||
@@ -58,7 +58,7 @@ func (p *mockProvider) Init() error {
|
||||
|
||||
func TestNewConfigurationWatcher(t *testing.T) {
|
||||
routinesPool := safe.NewPool(context.Background())
|
||||
defer routinesPool.Stop()
|
||||
t.Cleanup(routinesPool.Stop)
|
||||
|
||||
pvd := &mockProvider{
|
||||
messages: []dynamic.Message{{
|
||||
@@ -115,7 +115,6 @@ func TestNewConfigurationWatcher(t *testing.T) {
|
||||
|
||||
func TestWaitForRequiredProvider(t *testing.T) {
|
||||
routinesPool := safe.NewPool(context.Background())
|
||||
defer routinesPool.Stop()
|
||||
|
||||
pvdAggregator := &mockProvider{
|
||||
wait: 5 * time.Millisecond,
|
||||
@@ -151,7 +150,9 @@ func TestWaitForRequiredProvider(t *testing.T) {
|
||||
})
|
||||
|
||||
watcher.Start()
|
||||
defer watcher.Stop()
|
||||
|
||||
t.Cleanup(watcher.Stop)
|
||||
t.Cleanup(routinesPool.Stop)
|
||||
|
||||
// give some time so that the configuration can be processed
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
@@ -162,7 +163,6 @@ func TestWaitForRequiredProvider(t *testing.T) {
|
||||
|
||||
func TestIgnoreTransientConfiguration(t *testing.T) {
|
||||
routinesPool := safe.NewPool(context.Background())
|
||||
defer routinesPool.Stop()
|
||||
|
||||
config := &dynamic.Configuration{
|
||||
HTTP: th.BuildConfiguration(
|
||||
@@ -190,7 +190,9 @@ func TestIgnoreTransientConfiguration(t *testing.T) {
|
||||
})
|
||||
|
||||
watcher.Start()
|
||||
defer watcher.Stop()
|
||||
|
||||
t.Cleanup(watcher.Stop)
|
||||
t.Cleanup(routinesPool.Stop)
|
||||
|
||||
watcher.allProvidersConfigs <- dynamic.Message{
|
||||
ProviderName: "mock",
|
||||
@@ -243,7 +245,6 @@ func TestIgnoreTransientConfiguration(t *testing.T) {
|
||||
|
||||
func TestListenProvidersThrottleProviderConfigReload(t *testing.T) {
|
||||
routinesPool := safe.NewPool(context.Background())
|
||||
defer routinesPool.Stop()
|
||||
|
||||
pvd := &mockProvider{
|
||||
wait: 10 * time.Millisecond,
|
||||
@@ -274,7 +275,9 @@ func TestListenProvidersThrottleProviderConfigReload(t *testing.T) {
|
||||
})
|
||||
|
||||
watcher.Start()
|
||||
defer watcher.Stop()
|
||||
|
||||
t.Cleanup(watcher.Stop)
|
||||
t.Cleanup(routinesPool.Stop)
|
||||
|
||||
// Give some time so that the configuration can be processed.
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
@@ -287,7 +290,6 @@ func TestListenProvidersThrottleProviderConfigReload(t *testing.T) {
|
||||
|
||||
func TestListenProvidersSkipsEmptyConfigs(t *testing.T) {
|
||||
routinesPool := safe.NewPool(context.Background())
|
||||
defer routinesPool.Stop()
|
||||
|
||||
pvd := &mockProvider{
|
||||
messages: []dynamic.Message{{ProviderName: "mock"}},
|
||||
@@ -299,7 +301,9 @@ func TestListenProvidersSkipsEmptyConfigs(t *testing.T) {
|
||||
})
|
||||
|
||||
watcher.Start()
|
||||
defer watcher.Stop()
|
||||
|
||||
t.Cleanup(watcher.Stop)
|
||||
t.Cleanup(routinesPool.Stop)
|
||||
|
||||
// give some time so that the configuration can be processed
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
@@ -307,7 +311,6 @@ func TestListenProvidersSkipsEmptyConfigs(t *testing.T) {
|
||||
|
||||
func TestListenProvidersSkipsSameConfigurationForProvider(t *testing.T) {
|
||||
routinesPool := safe.NewPool(context.Background())
|
||||
defer routinesPool.Stop()
|
||||
|
||||
message := dynamic.Message{
|
||||
ProviderName: "mock",
|
||||
@@ -331,7 +334,9 @@ func TestListenProvidersSkipsSameConfigurationForProvider(t *testing.T) {
|
||||
})
|
||||
|
||||
watcher.Start()
|
||||
defer watcher.Stop()
|
||||
|
||||
t.Cleanup(watcher.Stop)
|
||||
t.Cleanup(routinesPool.Stop)
|
||||
|
||||
// give some time so that the configuration can be processed
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
@@ -340,7 +345,6 @@ func TestListenProvidersSkipsSameConfigurationForProvider(t *testing.T) {
|
||||
|
||||
func TestListenProvidersDoesNotSkipFlappingConfiguration(t *testing.T) {
|
||||
routinesPool := safe.NewPool(context.Background())
|
||||
defer routinesPool.Stop()
|
||||
|
||||
configuration := &dynamic.Configuration{
|
||||
HTTP: th.BuildConfiguration(
|
||||
@@ -374,7 +378,9 @@ func TestListenProvidersDoesNotSkipFlappingConfiguration(t *testing.T) {
|
||||
})
|
||||
|
||||
watcher.Start()
|
||||
defer watcher.Stop()
|
||||
|
||||
t.Cleanup(watcher.Stop)
|
||||
t.Cleanup(routinesPool.Stop)
|
||||
|
||||
// give some time so that the configuration can be processed
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
@@ -407,7 +413,6 @@ func TestListenProvidersDoesNotSkipFlappingConfiguration(t *testing.T) {
|
||||
|
||||
func TestListenProvidersIgnoreSameConfig(t *testing.T) {
|
||||
routinesPool := safe.NewPool(context.Background())
|
||||
defer routinesPool.Stop()
|
||||
|
||||
configuration := &dynamic.Configuration{
|
||||
HTTP: th.BuildConfiguration(
|
||||
@@ -453,8 +458,7 @@ func TestListenProvidersIgnoreSameConfig(t *testing.T) {
|
||||
configurationReloads++
|
||||
lastConfig = conf
|
||||
|
||||
// Allows next configurations to be sent by the mock provider
|
||||
// as soon as the first configuration message is applied.
|
||||
// Allows next configurations to be sent by the mock provider as soon as the first configuration message is applied.
|
||||
once.Do(func() {
|
||||
pvd.first <- struct{}{}
|
||||
// Wait for all configuration messages to pile in
|
||||
@@ -463,7 +467,9 @@ func TestListenProvidersIgnoreSameConfig(t *testing.T) {
|
||||
})
|
||||
|
||||
watcher.Start()
|
||||
defer watcher.Stop()
|
||||
|
||||
t.Cleanup(watcher.Stop)
|
||||
t.Cleanup(routinesPool.Stop)
|
||||
|
||||
// Wait long enough
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
@@ -498,7 +504,6 @@ func TestListenProvidersIgnoreSameConfig(t *testing.T) {
|
||||
|
||||
func TestApplyConfigUnderStress(t *testing.T) {
|
||||
routinesPool := safe.NewPool(context.Background())
|
||||
defer routinesPool.Stop()
|
||||
|
||||
watcher := NewConfigurationWatcher(routinesPool, &mockProvider{}, []string{"defaultEP"}, "")
|
||||
|
||||
@@ -525,15 +530,16 @@ func TestApplyConfigUnderStress(t *testing.T) {
|
||||
})
|
||||
|
||||
watcher.Start()
|
||||
defer watcher.Stop()
|
||||
|
||||
t.Cleanup(watcher.Stop)
|
||||
t.Cleanup(routinesPool.Stop)
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// Ensure that at least two configurations have been applied
|
||||
// if we simulate being spammed configuration changes by the
|
||||
// provider(s).
|
||||
// In theory, checking at least one would be sufficient, but
|
||||
// checking for two also ensures that we're looping properly,
|
||||
// if we simulate being spammed configuration changes by the provider(s).
|
||||
// In theory, checking at least one would be sufficient,
|
||||
// but checking for two also ensures that we're looping properly,
|
||||
// and that the whole algo holds, etc.
|
||||
t.Log(configurationReloads)
|
||||
assert.GreaterOrEqual(t, configurationReloads, 2)
|
||||
@@ -541,7 +547,6 @@ func TestApplyConfigUnderStress(t *testing.T) {
|
||||
|
||||
func TestListenProvidersIgnoreIntermediateConfigs(t *testing.T) {
|
||||
routinesPool := safe.NewPool(context.Background())
|
||||
defer routinesPool.Stop()
|
||||
|
||||
configuration := &dynamic.Configuration{
|
||||
HTTP: th.BuildConfiguration(
|
||||
@@ -596,7 +601,9 @@ func TestListenProvidersIgnoreIntermediateConfigs(t *testing.T) {
|
||||
})
|
||||
|
||||
watcher.Start()
|
||||
defer watcher.Stop()
|
||||
|
||||
t.Cleanup(watcher.Stop)
|
||||
t.Cleanup(routinesPool.Stop)
|
||||
|
||||
// Wait long enough
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
@@ -631,7 +638,6 @@ func TestListenProvidersIgnoreIntermediateConfigs(t *testing.T) {
|
||||
|
||||
func TestListenProvidersPublishesConfigForEachProvider(t *testing.T) {
|
||||
routinesPool := safe.NewPool(context.Background())
|
||||
defer routinesPool.Stop()
|
||||
|
||||
configuration := &dynamic.Configuration{
|
||||
HTTP: th.BuildConfiguration(
|
||||
@@ -656,7 +662,9 @@ func TestListenProvidersPublishesConfigForEachProvider(t *testing.T) {
|
||||
})
|
||||
|
||||
watcher.Start()
|
||||
defer watcher.Stop()
|
||||
|
||||
t.Cleanup(watcher.Stop)
|
||||
t.Cleanup(routinesPool.Stop)
|
||||
|
||||
// give some time so that the configuration can be processed
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
@@ -695,7 +703,6 @@ func TestListenProvidersPublishesConfigForEachProvider(t *testing.T) {
|
||||
|
||||
func TestPublishConfigUpdatedByProvider(t *testing.T) {
|
||||
routinesPool := safe.NewPool(context.Background())
|
||||
defer routinesPool.Stop()
|
||||
|
||||
pvdConfiguration := dynamic.Configuration{
|
||||
TCP: &dynamic.TCPConfiguration{
|
||||
@@ -725,12 +732,14 @@ func TestPublishConfigUpdatedByProvider(t *testing.T) {
|
||||
watcher.AddListener(func(configuration dynamic.Configuration) {
|
||||
publishedConfigCount++
|
||||
|
||||
// Update the provider configuration published in next dynamic Message which should trigger a new publish.
|
||||
// Update the provider configuration published in next dynamic Message which should trigger a new publishing.
|
||||
pvdConfiguration.TCP.Routers["bar"] = &dynamic.TCPRouter{}
|
||||
})
|
||||
|
||||
watcher.Start()
|
||||
defer watcher.Stop()
|
||||
|
||||
t.Cleanup(watcher.Stop)
|
||||
t.Cleanup(routinesPool.Stop)
|
||||
|
||||
// give some time so that the configuration can be processed.
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
@@ -740,7 +749,6 @@ func TestPublishConfigUpdatedByProvider(t *testing.T) {
|
||||
|
||||
func TestPublishConfigUpdatedByConfigWatcherListener(t *testing.T) {
|
||||
routinesPool := safe.NewPool(context.Background())
|
||||
defer routinesPool.Stop()
|
||||
|
||||
pvd := &mockProvider{
|
||||
wait: 10 * time.Millisecond,
|
||||
@@ -774,13 +782,15 @@ func TestPublishConfigUpdatedByConfigWatcherListener(t *testing.T) {
|
||||
watcher.AddListener(func(configuration dynamic.Configuration) {
|
||||
publishedConfigCount++
|
||||
|
||||
// Modify the provided configuration. This should not modify the configuration stored in the configuration
|
||||
// watcher and cause a new publish.
|
||||
// Modify the provided configuration.
|
||||
// This should not modify the configuration stored in the configuration watcher and therefore there will be no new publishing.
|
||||
configuration.TCP.Routers["foo@mock"].Rule = "bar"
|
||||
})
|
||||
|
||||
watcher.Start()
|
||||
defer watcher.Stop()
|
||||
|
||||
t.Cleanup(watcher.Stop)
|
||||
t.Cleanup(routinesPool.Stop)
|
||||
|
||||
// give some time so that the configuration can be processed.
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
@@ -18,16 +18,14 @@ type ChainBuilder struct {
|
||||
metricsRegistry metrics.Registry
|
||||
accessLoggerMiddleware *accesslog.Handler
|
||||
tracer *tracing.Tracing
|
||||
captureMiddleware *capture.Handler
|
||||
}
|
||||
|
||||
// NewChainBuilder Creates a new ChainBuilder.
|
||||
func NewChainBuilder(metricsRegistry metrics.Registry, accessLoggerMiddleware *accesslog.Handler, tracer *tracing.Tracing, captureMiddleware *capture.Handler) *ChainBuilder {
|
||||
func NewChainBuilder(metricsRegistry metrics.Registry, accessLoggerMiddleware *accesslog.Handler, tracer *tracing.Tracing) *ChainBuilder {
|
||||
return &ChainBuilder{
|
||||
metricsRegistry: metricsRegistry,
|
||||
accessLoggerMiddleware: accessLoggerMiddleware,
|
||||
tracer: tracer,
|
||||
captureMiddleware: captureMiddleware,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,8 +33,8 @@ func NewChainBuilder(metricsRegistry metrics.Registry, accessLoggerMiddleware *a
|
||||
func (c *ChainBuilder) Build(ctx context.Context, entryPointName string) alice.Chain {
|
||||
chain := alice.New()
|
||||
|
||||
if c.captureMiddleware != nil {
|
||||
chain = chain.Append(capture.WrapHandler(c.captureMiddleware))
|
||||
if c.accessLoggerMiddleware != nil || c.metricsRegistry != nil && (c.metricsRegistry.IsEpEnabled() || c.metricsRegistry.IsRouterEnabled() || c.metricsRegistry.IsSvcEnabled()) {
|
||||
chain = chain.Append(capture.Wrap)
|
||||
}
|
||||
|
||||
if c.accessLoggerMiddleware != nil {
|
||||
|
||||
@@ -3,6 +3,7 @@ package router
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/containous/alice"
|
||||
@@ -16,6 +17,7 @@ import (
|
||||
httpmuxer "github.com/traefik/traefik/v2/pkg/muxer/http"
|
||||
"github.com/traefik/traefik/v2/pkg/server/middleware"
|
||||
"github.com/traefik/traefik/v2/pkg/server/provider"
|
||||
"github.com/traefik/traefik/v2/pkg/tls"
|
||||
)
|
||||
|
||||
type middlewareBuilder interface {
|
||||
@@ -35,10 +37,11 @@ type Manager struct {
|
||||
middlewaresBuilder middlewareBuilder
|
||||
chainBuilder *middleware.ChainBuilder
|
||||
conf *runtime.Configuration
|
||||
tlsManager *tls.Manager
|
||||
}
|
||||
|
||||
// NewManager Creates a new Manager.
|
||||
func NewManager(conf *runtime.Configuration, serviceManager serviceManager, middlewaresBuilder middlewareBuilder, chainBuilder *middleware.ChainBuilder, metricsRegistry metrics.Registry) *Manager {
|
||||
// NewManager creates a new Manager.
|
||||
func NewManager(conf *runtime.Configuration, serviceManager serviceManager, middlewaresBuilder middlewareBuilder, chainBuilder *middleware.ChainBuilder, metricsRegistry metrics.Registry, tlsManager *tls.Manager) *Manager {
|
||||
return &Manager{
|
||||
routerHandlers: make(map[string]http.Handler),
|
||||
serviceManager: serviceManager,
|
||||
@@ -46,6 +49,7 @@ func NewManager(conf *runtime.Configuration, serviceManager serviceManager, midd
|
||||
middlewaresBuilder: middlewaresBuilder,
|
||||
chainBuilder: chainBuilder,
|
||||
conf: conf,
|
||||
tlsManager: tlsManager,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,6 +145,17 @@ func (m *Manager) buildRouterHandler(ctx context.Context, routerName string, rou
|
||||
return handler, nil
|
||||
}
|
||||
|
||||
if routerConfig.TLS != nil {
|
||||
// Don't build the router if the TLSOptions configuration is invalid.
|
||||
tlsOptionsName := tls.DefaultTLSConfigName
|
||||
if len(routerConfig.TLS.Options) > 0 && routerConfig.TLS.Options != tls.DefaultTLSConfigName {
|
||||
tlsOptionsName = provider.GetQualifiedName(ctx, routerConfig.TLS.Options)
|
||||
}
|
||||
if _, err := m.tlsManager.Get(tls.DefaultTLSStoreName, tlsOptionsName); err != nil {
|
||||
return nil, fmt.Errorf("building router handler: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
handler, err := m.buildHTTPHandler(ctx, routerConfig, routerName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/traefik/traefik/v2/pkg/server/middleware"
|
||||
"github.com/traefik/traefik/v2/pkg/server/service"
|
||||
"github.com/traefik/traefik/v2/pkg/testhelpers"
|
||||
"github.com/traefik/traefik/v2/pkg/tls"
|
||||
"github.com/traefik/traefik/v2/pkg/types"
|
||||
)
|
||||
|
||||
@@ -316,9 +317,10 @@ func TestRouterManager_Get(t *testing.T) {
|
||||
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
||||
serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
|
||||
chainBuilder := middleware.NewChainBuilder(nil, nil, nil, nil)
|
||||
chainBuilder := middleware.NewChainBuilder(nil, nil, nil)
|
||||
tlsManager := tls.NewManager()
|
||||
|
||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry())
|
||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry(), tlsManager)
|
||||
|
||||
handlers := routerManager.BuildHandlers(context.Background(), test.entryPoints, false)
|
||||
|
||||
@@ -422,9 +424,10 @@ func TestAccessLog(t *testing.T) {
|
||||
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
||||
serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
|
||||
chainBuilder := middleware.NewChainBuilder(nil, nil, nil, nil)
|
||||
chainBuilder := middleware.NewChainBuilder(nil, nil, nil)
|
||||
tlsManager := tls.NewManager()
|
||||
|
||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry())
|
||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry(), tlsManager)
|
||||
|
||||
handlers := routerManager.BuildHandlers(context.Background(), test.entryPoints, false)
|
||||
|
||||
@@ -439,7 +442,7 @@ func TestAccessLog(t *testing.T) {
|
||||
reqHost := requestdecorator.New(nil)
|
||||
|
||||
chain := alice.New()
|
||||
chain = chain.Append(capture.WrapHandler(&capture.Handler{}))
|
||||
chain = chain.Append(capture.Wrap)
|
||||
chain = chain.Append(accesslog.WrapHandler(accesslogger))
|
||||
handler, err := chain.Then(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
reqHost.ServeHTTP(w, req, handlers["web"].ServeHTTP)
|
||||
@@ -462,6 +465,7 @@ func TestRuntimeConfiguration(t *testing.T) {
|
||||
serviceConfig map[string]*dynamic.Service
|
||||
routerConfig map[string]*dynamic.Router
|
||||
middlewareConfig map[string]*dynamic.Middleware
|
||||
tlsOptions map[string]tls.Options
|
||||
expectedError int
|
||||
}{
|
||||
{
|
||||
@@ -665,7 +669,6 @@ func TestRuntimeConfiguration(t *testing.T) {
|
||||
},
|
||||
expectedError: 1,
|
||||
},
|
||||
|
||||
{
|
||||
desc: "Router with broken middleware",
|
||||
serviceConfig: map[string]*dynamic.Service{
|
||||
@@ -696,8 +699,71 @@ func TestRuntimeConfiguration(t *testing.T) {
|
||||
},
|
||||
expectedError: 2,
|
||||
},
|
||||
{
|
||||
desc: "Router with broken tlsOption",
|
||||
serviceConfig: map[string]*dynamic.Service{
|
||||
"foo-service": {
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
middlewareConfig: map[string]*dynamic.Middleware{},
|
||||
routerConfig: map[string]*dynamic.Router{
|
||||
"bar": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
TLS: &dynamic.RouterTLSConfig{
|
||||
Options: "broken-tlsOption",
|
||||
},
|
||||
},
|
||||
},
|
||||
tlsOptions: map[string]tls.Options{
|
||||
"broken-tlsOption": {
|
||||
ClientAuth: tls.ClientAuth{
|
||||
ClientAuthType: "foobar",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: 1,
|
||||
},
|
||||
{
|
||||
desc: "Router with broken default tlsOption",
|
||||
serviceConfig: map[string]*dynamic.Service{
|
||||
"foo-service": {
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
middlewareConfig: map[string]*dynamic.Middleware{},
|
||||
routerConfig: map[string]*dynamic.Router{
|
||||
"bar": {
|
||||
EntryPoints: []string{"web"},
|
||||
Service: "foo-service",
|
||||
Rule: "Host(`foo.bar`)",
|
||||
TLS: &dynamic.RouterTLSConfig{},
|
||||
},
|
||||
},
|
||||
tlsOptions: map[string]tls.Options{
|
||||
"default": {
|
||||
ClientAuth: tls.ClientAuth{
|
||||
ClientAuthType: "foobar",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
@@ -711,17 +777,23 @@ func TestRuntimeConfiguration(t *testing.T) {
|
||||
Routers: test.routerConfig,
|
||||
Middlewares: test.middlewareConfig,
|
||||
},
|
||||
TLS: &dynamic.TLSConfiguration{
|
||||
Options: test.tlsOptions,
|
||||
},
|
||||
})
|
||||
|
||||
roundTripperManager := service.NewRoundTripperManager()
|
||||
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
||||
serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
|
||||
chainBuilder := middleware.NewChainBuilder(nil, nil, nil, nil)
|
||||
chainBuilder := middleware.NewChainBuilder(nil, nil, nil)
|
||||
tlsManager := tls.NewManager()
|
||||
tlsManager.UpdateConfigs(context.Background(), nil, test.tlsOptions, nil)
|
||||
|
||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry())
|
||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry(), tlsManager)
|
||||
|
||||
_ = routerManager.BuildHandlers(context.Background(), entryPoints, false)
|
||||
_ = routerManager.BuildHandlers(context.Background(), entryPoints, true)
|
||||
|
||||
// even though rtConf was passed by argument to the manager builders above,
|
||||
// it's ok to use it as the result we check, because everything worth checking
|
||||
@@ -792,9 +864,10 @@ func TestProviderOnMiddlewares(t *testing.T) {
|
||||
roundTripperManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}})
|
||||
serviceManager := service.NewManager(rtConf.Services, nil, nil, roundTripperManager)
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
|
||||
chainBuilder := middleware.NewChainBuilder(nil, nil, nil, nil)
|
||||
chainBuilder := middleware.NewChainBuilder(nil, nil, nil)
|
||||
tlsManager := tls.NewManager()
|
||||
|
||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry())
|
||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry(), tlsManager)
|
||||
|
||||
_ = routerManager.BuildHandlers(context.Background(), entryPoints, false)
|
||||
|
||||
@@ -860,9 +933,10 @@ func BenchmarkRouterServe(b *testing.B) {
|
||||
|
||||
serviceManager := service.NewManager(rtConf.Services, nil, nil, staticRoundTripperGetter{res})
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil)
|
||||
chainBuilder := middleware.NewChainBuilder(nil, nil, nil, nil)
|
||||
chainBuilder := middleware.NewChainBuilder(nil, nil, nil)
|
||||
tlsManager := tls.NewManager()
|
||||
|
||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry())
|
||||
routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, chainBuilder, metrics.NewVoidRegistry(), tlsManager)
|
||||
|
||||
handlers := routerManager.BuildHandlers(context.Background(), entryPoints, false)
|
||||
|
||||
|
||||
@@ -103,18 +103,21 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
||||
|
||||
router.SetHTTPHandler(handlerHTTP)
|
||||
|
||||
// Even though the error is seemingly ignored (aside from logging it),
|
||||
// we actually rely later on the fact that a tls config is nil (which happens when an error is returned) to take special steps
|
||||
// when assigning a handler to a route.
|
||||
defaultTLSConf, err := m.tlsManager.Get(traefiktls.DefaultTLSStoreName, traefiktls.DefaultTLSConfigName)
|
||||
if err != nil {
|
||||
log.FromContext(ctx).Errorf("Error during the build of the default TLS configuration: %v", err)
|
||||
}
|
||||
|
||||
// Keyed by domain. The source of truth for doing SNI checking, and for what TLS
|
||||
// options will actually be used for the connection.
|
||||
// Keyed by domain. The source of truth for doing SNI checking (domain fronting).
|
||||
// As soon as there's (at least) two different tlsOptions found for the same domain,
|
||||
// we set the value to the default TLS conf.
|
||||
tlsOptionsForHost := map[string]string{}
|
||||
|
||||
// Keyed by domain, then by options reference.
|
||||
// The actual source of truth for what TLS options will actually be used for the connection.
|
||||
// As opposed to tlsOptionsForHost, it keeps track of all the (different) TLS
|
||||
// options that occur for a given host name, so that later on we can set relevant
|
||||
// errors and logging for all the routers concerned (i.e. wrongly configured).
|
||||
@@ -142,21 +145,20 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
||||
}
|
||||
|
||||
if len(domains) == 0 {
|
||||
// Extra Host(*) rule, for HTTPS routers with no Host rule, and for requests for
|
||||
// which the SNI does not match _any_ of the other existing routers Host. This is
|
||||
// only about choosing the TLS configuration. The actual routing will be done
|
||||
// further on by the HTTPS handler. See examples below.
|
||||
// Extra Host(*) rule, for HTTPS routers with no Host rule,
|
||||
// and for requests for which the SNI does not match _any_ of the other existing routers Host.
|
||||
// This is only about choosing the TLS configuration.
|
||||
// The actual routing will be done further on by the HTTPS handler.
|
||||
// See examples below.
|
||||
router.AddHTTPTLSConfig("*", defaultTLSConf)
|
||||
|
||||
// The server name (from a Host(SNI) rule) is the only parameter (available in
|
||||
// HTTP routing rules) on which we can map a TLS config, because it is the only one
|
||||
// accessible before decryption (we obtain it during the ClientHello). Therefore,
|
||||
// when a router has no Host rule, it does not make any sense to specify some TLS
|
||||
// options. Consequently, when it comes to deciding what TLS config will be used,
|
||||
// for a request that will match an HTTPS router with no Host rule, the result will
|
||||
// depend on the _others_ existing routers (their Host rule, to be precise), and
|
||||
// the TLS options associated with them, even though they don't match the incoming
|
||||
// request. Consider the following examples:
|
||||
// The server name (from a Host(SNI) rule) is the only parameter (available in HTTP routing rules) on which we can map a TLS config,
|
||||
// because it is the only one accessible before decryption (we obtain it during the ClientHello).
|
||||
// Therefore, when a router has no Host rule, it does not make any sense to specify some TLS options.
|
||||
// Consequently, when it comes to deciding what TLS config will be used,
|
||||
// for a request that will match an HTTPS router with no Host rule,
|
||||
// the result will depend on the _others_ existing routers (their Host rule, to be precise), and the TLS options associated with them,
|
||||
// even though they don't match the incoming request. Consider the following examples:
|
||||
|
||||
// # conf1
|
||||
// httpRouter1:
|
||||
@@ -170,17 +172,19 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
||||
// httpRouter2:
|
||||
// rule: Host("foo.com") && PathPrefix("/bar")
|
||||
// tlsoptions: myTLSOptions
|
||||
// # When a request for "/foo" comes, even though it won't be routed by
|
||||
// httpRouter2, if its SNI is set to foo.com, myTLSOptions will be used for the TLS
|
||||
// connection. Otherwise, it will fallback to the default TLS config.
|
||||
// # When a request for "/foo" comes, even though it won't be routed by httpRouter2,
|
||||
// # if its SNI is set to foo.com, myTLSOptions will be used for the TLS connection.
|
||||
// # Otherwise, it will fallback to the default TLS config.
|
||||
logger.Warnf("No domain found in rule %v, the TLS options applied for this router will depend on the SNI of each request", routerHTTPConfig.Rule)
|
||||
}
|
||||
|
||||
tlsConf, err := m.tlsManager.Get(traefiktls.DefaultTLSStoreName, tlsOptionsName)
|
||||
if err != nil {
|
||||
routerHTTPConfig.AddError(err, true)
|
||||
logger.Error(err)
|
||||
continue
|
||||
// Even though the error is seemingly ignored (aside from logging it),
|
||||
// we actually rely later on the fact that a tls config is nil (which happens when an error is returned) to take special steps
|
||||
// when assigning a handler to a route.
|
||||
tlsConf, tlsConfErr := m.tlsManager.Get(traefiktls.DefaultTLSStoreName, tlsOptionsName)
|
||||
if tlsConfErr != nil {
|
||||
// Note: we do not call AddError here because we already did so when buildRouterHandler errored for the same reason.
|
||||
logger.Error(tlsConfErr)
|
||||
}
|
||||
|
||||
for _, domain := range domains {
|
||||
@@ -204,6 +208,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
||||
|
||||
sniCheck := snicheck.New(tlsOptionsForHost, handlerHTTPS)
|
||||
|
||||
// Keep in mind that defaultTLSConf might be nil here.
|
||||
router.SetHTTPSHandler(sniCheck, defaultTLSConf)
|
||||
|
||||
logger := log.FromContext(ctx)
|
||||
@@ -217,22 +222,42 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
||||
break
|
||||
}
|
||||
|
||||
logger.Debugf("Adding route for %s with TLS options %s", hostSNI, optionsName)
|
||||
|
||||
router.AddHTTPTLSConfig(hostSNI, config)
|
||||
} else {
|
||||
routers := make([]string, 0, len(tlsConfigs))
|
||||
for _, v := range tlsConfigs {
|
||||
configsHTTP[v.routerName].AddError(fmt.Errorf("found different TLS options for routers on the same host %v, so using the default TLS options instead", hostSNI), false)
|
||||
routers = append(routers, v.routerName)
|
||||
if config == nil {
|
||||
// we use nil config as a signal to insert a handler
|
||||
// that enforces that TLS connection attempts to the corresponding (broken) router should fail.
|
||||
logger.Debugf("Adding special closing route for %s because broken TLS options %s", hostSNI, optionsName)
|
||||
router.AddHTTPTLSConfig(hostSNI, nil)
|
||||
continue
|
||||
}
|
||||
|
||||
logger.Warnf("Found different TLS options for routers on the same host %v, so using the default TLS options instead for these routers: %#v", hostSNI, routers)
|
||||
|
||||
router.AddHTTPTLSConfig(hostSNI, defaultTLSConf)
|
||||
logger.Debugf("Adding route for %s with TLS options %s", hostSNI, optionsName)
|
||||
router.AddHTTPTLSConfig(hostSNI, config)
|
||||
continue
|
||||
}
|
||||
|
||||
// multiple tlsConfigs
|
||||
|
||||
routers := make([]string, 0, len(tlsConfigs))
|
||||
for _, v := range tlsConfigs {
|
||||
configsHTTP[v.routerName].AddError(fmt.Errorf("found different TLS options for routers on the same host %v, so using the default TLS options instead", hostSNI), false)
|
||||
routers = append(routers, v.routerName)
|
||||
}
|
||||
|
||||
logger.Warnf("Found different TLS options for routers on the same host %v, so using the default TLS options instead for these routers: %#v", hostSNI, routers)
|
||||
if defaultTLSConf == nil {
|
||||
logger.Debugf("Adding special closing route for %s because broken default TLS options", hostSNI)
|
||||
}
|
||||
|
||||
router.AddHTTPTLSConfig(hostSNI, defaultTLSConf)
|
||||
}
|
||||
|
||||
m.addTCPHandlers(ctx, configs, router)
|
||||
|
||||
return router, nil
|
||||
}
|
||||
|
||||
// addTCPHandlers creates the TCP handlers defined in configs, and adds them to router.
|
||||
func (m *Manager) addTCPHandlers(ctx context.Context, configs map[string]*runtime.TCPRouterInfo, router *Router) {
|
||||
for routerName, routerConfig := range configs {
|
||||
ctxRouter := log.With(provider.AddInContext(ctx, routerName), log.Str(log.RouterName, routerName))
|
||||
logger := log.FromContext(ctxRouter)
|
||||
@@ -251,13 +276,6 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
||||
continue
|
||||
}
|
||||
|
||||
handler, err := m.buildTCPHandler(ctxRouter, routerConfig)
|
||||
if err != nil {
|
||||
routerConfig.AddError(err, true)
|
||||
logger.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
domains, err := tcpmuxer.ParseHostSNI(routerConfig.Rule)
|
||||
if err != nil {
|
||||
routerErr := fmt.Errorf("invalid rule: %q , %w", routerConfig.Rule, err)
|
||||
@@ -274,6 +292,16 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
||||
logger.Error(routerErr)
|
||||
}
|
||||
|
||||
var handler tcp.Handler
|
||||
if routerConfig.TLS == nil || routerConfig.TLS.Passthrough {
|
||||
handler, err = m.buildTCPHandler(ctxRouter, routerConfig)
|
||||
if err != nil {
|
||||
routerConfig.AddError(err, true)
|
||||
logger.Error(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if routerConfig.TLS == nil {
|
||||
logger.Debugf("Adding route for %q", routerConfig.Rule)
|
||||
if err := router.AddRoute(routerConfig.Rule, routerConfig.Priority, handler); err != nil {
|
||||
@@ -285,7 +313,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
||||
|
||||
if routerConfig.TLS.Passthrough {
|
||||
logger.Debugf("Adding Passthrough route for %q", routerConfig.Rule)
|
||||
if err := router.AddRouteTLS(routerConfig.Rule, routerConfig.Priority, handler, nil); err != nil {
|
||||
if err := router.muxerTCPTLS.AddRoute(routerConfig.Rule, routerConfig.Priority, handler); err != nil {
|
||||
routerConfig.AddError(err, true)
|
||||
logger.Error(err)
|
||||
}
|
||||
@@ -315,7 +343,15 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
||||
tlsConf, err := m.tlsManager.Get(traefiktls.DefaultTLSStoreName, tlsOptionsName)
|
||||
if err != nil {
|
||||
routerConfig.AddError(err, true)
|
||||
|
||||
logger.Error(err)
|
||||
logger.Debugf("Adding special TLS closing route for %q because broken TLS options %s", routerConfig.Rule, tlsOptionsName)
|
||||
|
||||
err = router.muxerTCPTLS.AddRoute(routerConfig.Rule, routerConfig.Priority, &brokenTLSRouter{})
|
||||
if err != nil {
|
||||
routerConfig.AddError(err, true)
|
||||
logger.Error(err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -327,20 +363,30 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
||||
// rule: HostSNI(foo.com) && ClientIP(IP2)
|
||||
// tlsOption: tlsTwo
|
||||
// i.e. same HostSNI but different tlsOptions
|
||||
// This is only applicable if the muxer can decide about the routing _before_
|
||||
// telling the client about the tlsConf (i.e. before the TLS HandShake). This seems
|
||||
// to be the case so far with the existing matchers (HostSNI, and ClientIP), so
|
||||
// it's all good. Otherwise, we would have to do as for HTTPS, i.e. disallow
|
||||
// different TLS configs for the same HostSNIs.
|
||||
// This is only applicable if the muxer can decide about the routing _before_ telling the client about the tlsConf (i.e. before the TLS HandShake).
|
||||
// This seems to be the case so far with the existing matchers (HostSNI, and ClientIP), so it's all good.
|
||||
// Otherwise, we would have to do as for HTTPS, i.e. disallow different TLS configs for the same HostSNIs.
|
||||
|
||||
handler, err = m.buildTCPHandler(ctxRouter, routerConfig)
|
||||
if err != nil {
|
||||
routerConfig.AddError(err, true)
|
||||
logger.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
handler = &tcp.TLSHandler{
|
||||
Next: handler,
|
||||
Config: tlsConf,
|
||||
}
|
||||
|
||||
logger.Debugf("Adding TLS route for %q", routerConfig.Rule)
|
||||
if err := router.AddRouteTLS(routerConfig.Rule, routerConfig.Priority, handler, tlsConf); err != nil {
|
||||
|
||||
err = router.muxerTCPTLS.AddRoute(routerConfig.Rule, routerConfig.Priority, handler)
|
||||
if err != nil {
|
||||
routerConfig.AddError(err, true)
|
||||
logger.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
return router, nil
|
||||
}
|
||||
|
||||
func (m *Manager) buildTCPHandler(ctx context.Context, router *runtime.TCPRouterInfo) (tcp.Handler, error) {
|
||||
|
||||
@@ -27,19 +27,20 @@ type Router struct {
|
||||
muxerHTTPS tcpmuxer.Muxer
|
||||
|
||||
// Forwarder handlers.
|
||||
// Handles all HTTP requests.
|
||||
// httpForwarder handles all HTTP requests.
|
||||
httpForwarder tcp.Handler
|
||||
// Handles (indirectly through muxerHTTPS, or directly) all HTTPS requests.
|
||||
// httpsForwarder handles (indirectly through muxerHTTPS, or directly) all HTTPS requests.
|
||||
httpsForwarder tcp.Handler
|
||||
|
||||
// Neither is used directly, but they are held here, and recreated on config
|
||||
// reload, so that they can be passed to the Switcher at the end of the config
|
||||
// reload phase.
|
||||
// Neither is used directly, but they are held here, and recreated on config reload,
|
||||
// so that they can be passed to the Switcher at the end of the config reload phase.
|
||||
httpHandler http.Handler
|
||||
httpsHandler http.Handler
|
||||
|
||||
// TLS configs.
|
||||
httpsTLSConfig *tls.Config // default TLS config
|
||||
httpsTLSConfig *tls.Config // default TLS config
|
||||
// hostHTTPTLSConfig contains TLS configs keyed by SNI.
|
||||
// A nil config is the hint to set up a brokenTLSRouter.
|
||||
hostHTTPTLSConfig map[string]*tls.Config // TLS configs keyed by SNI
|
||||
}
|
||||
|
||||
@@ -80,11 +81,11 @@ func (r *Router) GetTLSGetClientInfo() func(info *tls.ClientHelloInfo) (*tls.Con
|
||||
|
||||
// ServeTCP forwards the connection to the right TCP/HTTP handler.
|
||||
func (r *Router) ServeTCP(conn tcp.WriteCloser) {
|
||||
// Handling Non-TLS TCP connection early if there is neither HTTP(S) nor TLS
|
||||
// routers on the entryPoint, and if there is at least one non-TLS TCP router.
|
||||
// In the case of a non-TLS TCP client (that does not "send" first), we would
|
||||
// block forever on clientHelloInfo, which is why we want to detect and
|
||||
// handle that case first and foremost.
|
||||
// Handling Non-TLS TCP connection early if there is neither HTTP(S) nor TLS routers on the entryPoint,
|
||||
// and if there is at least one non-TLS TCP router.
|
||||
// In the case of a non-TLS TCP client (that does not "send" first),
|
||||
// we would block forever on clientHelloInfo,
|
||||
// which is why we want to detect and handle that case first and foremost.
|
||||
if r.muxerTCP.HasRoutes() && !r.muxerTCPTLS.HasRoutes() && !r.muxerHTTPS.HasRoutes() {
|
||||
connData, err := tcpmuxer.NewConnData("", conn, nil)
|
||||
if err != nil {
|
||||
@@ -152,9 +153,9 @@ func (r *Router) ServeTCP(conn tcp.WriteCloser) {
|
||||
// (wrapped inside the returned handler) requested for the given HostSNI.
|
||||
handlerHTTPS, catchAllHTTPS := r.muxerHTTPS.Match(connData)
|
||||
if handlerHTTPS != nil && !catchAllHTTPS {
|
||||
// In order not to depart from the behavior in 2.6, we only allow an HTTPS router
|
||||
// to take precedence over a TCP-TLS router if it is _not_ an HostSNI(*) router (so
|
||||
// basically any router that has a specific HostSNI based rule).
|
||||
// In order not to depart from the behavior in 2.6,
|
||||
// we only allow an HTTPS router to take precedence over a TCP-TLS router if it is _not_ an HostSNI(*) router
|
||||
// (so basically any router that has a specific HostSNI based rule).
|
||||
handlerHTTPS.ServeTCP(r.GetConn(conn, hello.peeked))
|
||||
return
|
||||
}
|
||||
@@ -180,7 +181,7 @@ func (r *Router) ServeTCP(conn tcp.WriteCloser) {
|
||||
return
|
||||
}
|
||||
|
||||
// needed to handle 404s for HTTPS, as well as all non-Host (e.g. PathPrefix) matches.
|
||||
// To handle 404s for HTTPS.
|
||||
if r.httpsForwarder != nil {
|
||||
r.httpsForwarder.ServeTCP(r.GetConn(conn, hello.peeked))
|
||||
return
|
||||
@@ -194,19 +195,6 @@ func (r *Router) AddRoute(rule string, priority int, target tcp.Handler) error {
|
||||
return r.muxerTCP.AddRoute(rule, priority, target)
|
||||
}
|
||||
|
||||
// AddRouteTLS defines a handler for a given rule and sets the matching tlsConfig.
|
||||
func (r *Router) AddRouteTLS(rule string, priority int, target tcp.Handler, config *tls.Config) error {
|
||||
// TLS PassThrough
|
||||
if config == nil {
|
||||
return r.muxerTCPTLS.AddRoute(rule, priority, target)
|
||||
}
|
||||
|
||||
return r.muxerTCPTLS.AddRoute(rule, priority, &tcp.TLSHandler{
|
||||
Next: target,
|
||||
Config: config,
|
||||
})
|
||||
}
|
||||
|
||||
// AddHTTPTLSConfig defines a handler for a given sniHost and sets the matching tlsConfig.
|
||||
func (r *Router) AddHTTPTLSConfig(sniHost string, config *tls.Config) {
|
||||
if r.hostHTTPTLSConfig == nil {
|
||||
@@ -242,20 +230,44 @@ func (r *Router) SetHTTPForwarder(handler tcp.Handler) {
|
||||
r.httpForwarder = handler
|
||||
}
|
||||
|
||||
// SetHTTPSForwarder sets the tcp handler that will forward the TLS connections to an http handler.
|
||||
// brokenTLSRouter is associated to a Host(SNI) rule for which we know the TLS conf is broken.
|
||||
// It is used to make sure any attempt to connect to that hostname is closed,
|
||||
// since we cannot proceed with the intended TLS conf.
|
||||
type brokenTLSRouter struct{}
|
||||
|
||||
// ServeTCP instantly closes the connection.
|
||||
func (t *brokenTLSRouter) ServeTCP(conn tcp.WriteCloser) {
|
||||
_ = conn.Close()
|
||||
}
|
||||
|
||||
// SetHTTPSForwarder sets the tcp handler that will forward the TLS connections to an HTTP handler.
|
||||
// It also sets up each TLS handler (with its TLS config) for each Host(SNI) rule we previously kept track of.
|
||||
// It sets up a special handler that closes the connection if a TLS config is nil.
|
||||
func (r *Router) SetHTTPSForwarder(handler tcp.Handler) {
|
||||
for sniHost, tlsConf := range r.hostHTTPTLSConfig {
|
||||
var tcpHandler tcp.Handler
|
||||
if tlsConf == nil {
|
||||
tcpHandler = &brokenTLSRouter{}
|
||||
} else {
|
||||
tcpHandler = &tcp.TLSHandler{
|
||||
Next: handler,
|
||||
Config: tlsConf,
|
||||
}
|
||||
}
|
||||
|
||||
// muxerHTTPS only contains single HostSNI rules (and no other kind of rules),
|
||||
// so there's no need for specifying a priority for them.
|
||||
err := r.muxerHTTPS.AddRoute("HostSNI(`"+sniHost+"`)", 0, &tcp.TLSHandler{
|
||||
Next: handler,
|
||||
Config: tlsConf,
|
||||
})
|
||||
err := r.muxerHTTPS.AddRoute("HostSNI(`"+sniHost+"`)", 0, tcpHandler)
|
||||
if err != nil {
|
||||
log.WithoutContext().Errorf("Error while adding route for host: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if r.httpsTLSConfig == nil {
|
||||
r.httpsForwarder = &brokenTLSRouter{}
|
||||
return
|
||||
}
|
||||
|
||||
r.httpsForwarder = &tcp.TLSHandler{
|
||||
Next: handler,
|
||||
Config: r.httpsTLSConfig,
|
||||
@@ -275,15 +287,14 @@ func (r *Router) SetHTTPSHandler(handler http.Handler, config *tls.Config) {
|
||||
|
||||
// Conn is a connection proxy that handles Peeked bytes.
|
||||
type Conn struct {
|
||||
// Peeked are the bytes that have been read from Conn for the
|
||||
// purposes of route matching, but have not yet been consumed
|
||||
// by Read calls. It set to nil by Read when fully consumed.
|
||||
// Peeked are the bytes that have been read from Conn for the purposes of route matching,
|
||||
// but have not yet been consumed by Read calls.
|
||||
// It set to nil by Read when fully consumed.
|
||||
Peeked []byte
|
||||
|
||||
// Conn is the underlying connection.
|
||||
// It can be type asserted against *net.TCPConn or other types
|
||||
// as needed. It should not be read from directly unless
|
||||
// Peeked is nil.
|
||||
// It can be type asserted against *net.TCPConn or other types as needed.
|
||||
// It should not be read from directly unless Peeked is nil.
|
||||
tcp.WriteCloser
|
||||
}
|
||||
|
||||
@@ -320,15 +331,14 @@ func clientHelloInfo(br *bufio.Reader) (*clientHello, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// No valid TLS record has a type of 0x80, however SSLv2 handshakes
|
||||
// start with a uint16 length where the MSB is set and the first record
|
||||
// is always < 256 bytes long. Therefore typ == 0x80 strongly suggests
|
||||
// an SSLv2 client.
|
||||
// No valid TLS record has a type of 0x80, however SSLv2 handshakes start with an uint16 length
|
||||
// where the MSB is set and the first record is always < 256 bytes long.
|
||||
// Therefore, typ == 0x80 strongly suggests an SSLv2 client.
|
||||
const recordTypeSSLv2 = 0x80
|
||||
const recordTypeHandshake = 0x16
|
||||
if hdr[0] != recordTypeHandshake {
|
||||
if hdr[0] == recordTypeSSLv2 {
|
||||
// we consider SSLv2 as TLS and it will be refused by real TLS handshake.
|
||||
// we consider SSLv2 as TLS, and it will be refused by real TLS handshake.
|
||||
return &clientHello{
|
||||
isTLS: true,
|
||||
peeked: getPeeked(br),
|
||||
|
||||
@@ -72,7 +72,7 @@ func (f *RouterFactory) CreateRouters(rtConf *runtime.Configuration) (map[string
|
||||
|
||||
middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, f.pluginBuilder)
|
||||
|
||||
routerManager := router.NewManager(rtConf, serviceManager, middlewaresBuilder, f.chainBuilder, f.metricsRegistry)
|
||||
routerManager := router.NewManager(rtConf, serviceManager, middlewaresBuilder, f.chainBuilder, f.metricsRegistry, f.tlsManager)
|
||||
|
||||
handlersNonTLS := routerManager.BuildHandlers(ctx, f.entryPointsTCP, false)
|
||||
handlersTLS := routerManager.BuildHandlers(ctx, f.entryPointsTCP, true)
|
||||
|
||||
@@ -53,7 +53,7 @@ func TestReuseService(t *testing.T) {
|
||||
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry(), roundTripperManager, nil)
|
||||
tlsManager := tls.NewManager()
|
||||
|
||||
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, middleware.NewChainBuilder(nil, nil, nil, nil), nil, metrics.NewVoidRegistry())
|
||||
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, middleware.NewChainBuilder(nil, nil, nil), nil, metrics.NewVoidRegistry())
|
||||
|
||||
entryPointsHandlers, _ := factory.CreateRouters(runtime.NewConfig(dynamic.Configuration{HTTP: dynamicConfigs}))
|
||||
|
||||
@@ -189,7 +189,7 @@ func TestServerResponseEmptyBackend(t *testing.T) {
|
||||
managerFactory := service.NewManagerFactory(staticConfig, nil, metrics.NewVoidRegistry(), roundTripperManager, nil)
|
||||
tlsManager := tls.NewManager()
|
||||
|
||||
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, middleware.NewChainBuilder(nil, nil, nil, nil), nil, metrics.NewVoidRegistry())
|
||||
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, middleware.NewChainBuilder(nil, nil, nil), nil, metrics.NewVoidRegistry())
|
||||
|
||||
entryPointsHandlers, _ := factory.CreateRouters(runtime.NewConfig(dynamic.Configuration{HTTP: test.config(testServer.URL)}))
|
||||
|
||||
@@ -232,7 +232,7 @@ func TestInternalServices(t *testing.T) {
|
||||
|
||||
voidRegistry := metrics.NewVoidRegistry()
|
||||
|
||||
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, middleware.NewChainBuilder(voidRegistry, nil, nil, nil), nil, voidRegistry)
|
||||
factory := NewRouterFactory(staticConfig, managerFactory, tlsManager, middleware.NewChainBuilder(voidRegistry, nil, nil), nil, voidRegistry)
|
||||
|
||||
entryPointsHandlers, _ := factory.CreateRouters(runtime.NewConfig(dynamic.Configuration{HTTP: dynamicConfigs}))
|
||||
|
||||
|
||||
@@ -48,18 +48,13 @@ func TestShutdownTCP(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
err = router.AddRoute("HostSNI(`*`)", 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {
|
||||
for {
|
||||
_, err := http.ReadRequest(bufio.NewReader(conn))
|
||||
|
||||
if errors.Is(err, io.EOF) || (err != nil && errors.Is(err, net.ErrClosed)) {
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
resp := http.Response{StatusCode: http.StatusOK}
|
||||
err = resp.Write(conn)
|
||||
require.NoError(t, err)
|
||||
_, err := http.ReadRequest(bufio.NewReader(conn))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
resp := http.Response{StatusCode: http.StatusOK}
|
||||
_ = resp.Write(conn)
|
||||
}))
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -89,6 +84,7 @@ func testShutdown(t *testing.T, router *tcprouter.Router) {
|
||||
|
||||
conn, err := startEntrypoint(entryPoint, router)
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { _ = conn.Close() })
|
||||
|
||||
epAddr := entryPoint.listener.Addr().String()
|
||||
|
||||
@@ -97,14 +93,14 @@ func testShutdown(t *testing.T, router *tcprouter.Router) {
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// We need to do a write on the conn before the shutdown to make it "exist".
|
||||
// We need to do a write on conn before the shutdown to make it "exist".
|
||||
// Because the connection indeed exists as far as TCP is concerned,
|
||||
// but since we only pass it along to the HTTP server after at least one byte is peeked,
|
||||
// the HTTP server (and hence its shutdown) does not know about the connection until that first byte peeked.
|
||||
err = request.Write(conn)
|
||||
require.NoError(t, err)
|
||||
|
||||
reader := bufio.NewReader(conn)
|
||||
reader := bufio.NewReaderSize(conn, 1)
|
||||
// Wait for first byte in response.
|
||||
_, err = reader.Peek(1)
|
||||
require.NoError(t, err)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user