Compare commits

...

30 commits

Author SHA1 Message Date
baalajimaestro ca45770bbd
Merge branch 'v3.0' of github.com:traefik/traefik
Signed-off-by: baalajimaestro <me@baalajimaestro.me>
2024-01-29 10:43:41 +05:30
Julien Salleyron 3174c69c66
Adds weight on ServersLoadBalancer 2024-01-26 01:44:05 +01:00
Matthieu W f4f3dbe1f5
Update version comment in quick-start.md 2024-01-25 15:12:05 +01:00
Michael aece9a1051
fix: opentelemetry unit tests 2024-01-24 16:58:05 +01:00
mmatur 190b9b1afa
Merge current v2.11 into v3.0 2024-01-24 11:19:52 +01:00
Julien Salleyron 9befe0dd51
Fix flaky test 2024-01-23 16:46:05 +01:00
Romain 683e2ee5c6
Bring back v2 rule matchers 2024-01-23 11:34:05 +01:00
Michael 21da705ec9
fix: gateway api conformance tests 2024-01-23 11:04:05 +01:00
mmatur a3ac456199
fix: OpenTelemetry unit tests 2024-01-23 10:13:20 +01:00
mmatur 9843757834
Merge current v2.11 into v3.0 2024-01-23 09:32:28 +01:00
Romain f9831f5b1b
Introduce static config hints
Co-authored-by: Baptiste Mayelle <baptiste.mayelle@traefik.io>
2024-01-23 09:22:05 +01:00
Michael 177c4b0ed1
fix: flakiness test on configuration watcher 2024-01-22 16:52:05 +01:00
Michael bab48bed22
fix: OpenTelemetry metrics flaky test 2024-01-22 16:38:06 +01:00
Michael 6cb2ff2af9
fix: gateway api conformance tests 2024-01-22 16:04:05 +01:00
Michael 5e0855ecc7
feat: adds conformance test gateway api 2024-01-22 15:30:05 +01:00
mmatur f57cee578f
Merge back v2.11 into v3.0 2024-01-19 16:03:50 +01:00
Michael 8da38ec0a5
fix: tailscale is required for Docker Desktop users 2024-01-19 15:44:05 +01:00
Michael a6d462f6e8
feat: upgrade gh-action os 2024-01-19 15:12:05 +01:00
baalajimaestro d69e3d80af
Merge branch 'v3.0' of github.com:traefik/traefik
Signed-off-by: baalajimaestro <me@baalajimaestro.me>
2024-01-19 12:30:00 +05:30
白泽 0eeb85d01d
Support setting sticky cookie max age 2024-01-18 09:30:06 +01:00
Baptiste Mayelle 64ff214ff8
remove marathon labels from gendoc 2024-01-17 14:54:05 +01:00
mmatur 111f3716fa
Merge current v2.11 into v3.0 2024-01-17 11:37:50 +01:00
Robert Socha 4e0a05406b
Implements the includedContentTypes option for the compress middleware 2024-01-17 11:32:06 +01:00
Michael 39b0aa6650
Improve makefile 2024-01-17 11:12:05 +01:00
Baptiste Mayelle 319517adef
Merge branch v2.11 into v3.0 2024-01-16 15:42:38 +01:00
Robin Moser 7a315bb043
Disable br compression when no Accept-Encoding header is present 2024-01-16 15:30:06 +01:00
Michael 34d2a816c2
Enhance gendoc for Generating Static and Dynamic Reference Configuration Files 2024-01-16 10:32:05 +01:00
Thomas Gunsch 81ce45271d
Add forwardAuth.addAuthCookiesToResponse 2024-01-15 16:14:05 +01:00
James Rasell 3a461d2f23
deps: update the Nomad API dependency to v1.7.2 2024-01-12 14:22:05 +01:00
Oliver Dvorski e78374aa29
docs: slightly rewords the documentation 2024-01-10 15:12:07 +01:00
124 changed files with 7629 additions and 1302 deletions

View file

@ -1,5 +1,5 @@
dist/
!dist/traefik
!dist/**/traefik
site/
vendor/
.idea/

1
.gitignore vendored
View file

@ -19,3 +19,4 @@ plugins-storage/
plugins-local/
traefik_changelog.md
integration/tailscale.secret
integration/conformance-reports/

View file

@ -280,3 +280,9 @@ issues:
text: 'unusedwrite: unused write to field'
linters:
- govet
- path: pkg/cli/deprecation.go
linters:
- goconst
- path: pkg/cli/loader_file.go
linters:
- goconst

View file

@ -1,6 +1,13 @@
FROM scratch
COPY script/ca-certificates.crt /etc/ssl/certs/
COPY dist/traefik /
# syntax=docker/dockerfile:1.2
FROM alpine:3.19
RUN apk --no-cache --no-progress add ca-certificates tzdata \
&& rm -rf /var/cache/apk/*
ARG TARGETPLATFORM
COPY ./dist/$TARGETPLATFORM/traefik /
EXPOSE 80
VOLUME ["/tmp"]
ENTRYPOINT ["/traefik"]

149
Makefile
View file

@ -7,8 +7,22 @@ VERSION := $(if $(VERSION),$(VERSION),$(VERSION_GIT))
GIT_BRANCH := $(subst heads/,,$(shell git rev-parse --abbrev-ref HEAD 2>/dev/null))
REPONAME := $(shell echo $(REPO) | tr '[:upper:]' '[:lower:]')
BIN_NAME := traefik
CODENAME := cheddar
DATE := $(shell date -u '+%Y-%m-%d_%I:%M:%S%p')
# Default build target
GOOS := $(shell go env GOOS)
GOARCH := $(shell go env GOARCH)
LINT_EXECUTABLES = misspell shellcheck
DOCKER_BUILD_PLATFORMS ?= linux/amd64,linux/arm64
.PHONY: default
default: binary
default: generate binary
## Create the "dist" directory
dist:
@ -35,43 +49,68 @@ webui/static/index.html:
.PHONY: generate-webui
generate-webui: webui/static/index.html
## Generate code
.PHONY: generate
generate:
go generate
## Build the binary
.PHONY: binary
binary: generate-webui
./script/make.sh generate binary
binary: generate-webui dist
@echo SHA: $(VERSION) $(CODENAME) $(DATE)
CGO_ENABLED=0 GOGC=off GOOS=${GOOS} GOARCH=${GOARCH} go build ${FLAGS[*]} -ldflags "-s -w \
-X github.com/traefik/traefik/v3/pkg/version.Version=$(VERSION) \
-X github.com/traefik/traefik/v3/pkg/version.Codename=$(CODENAME) \
-X github.com/traefik/traefik/v3/pkg/version.BuildDate=$(DATE)" \
-installsuffix nocgo -o "./dist/${GOOS}/${GOARCH}/$(BIN_NAME)" ./cmd/traefik
## Build the linux binary locally
.PHONY: binary-debug
binary-debug: generate-webui
GOOS=linux ./script/make.sh binary
binary-linux-arm64: export GOOS := linux
binary-linux-arm64: export GOARCH := arm64
binary-linux-arm64:
@$(MAKE) binary
binary-linux-amd64: export GOOS := linux
binary-linux-amd64: export GOARCH := amd64
binary-linux-amd64:
@$(MAKE) binary
binary-windows-amd64: export GOOS := windows
binary-windows-amd64: export GOARCH := amd64
binary-windows-amd64: export BIN_NAME := traefik.exe
binary-windows-amd64:
@$(MAKE) binary
## Build the binary for the standard platforms (linux, darwin, windows)
.PHONY: crossbinary-default
crossbinary-default: generate-webui
./script/make.sh generate crossbinary-default
## Build the binary for the standard platforms (linux, darwin, windows) in parallel
.PHONY: crossbinary-default-parallel
crossbinary-default-parallel:
$(MAKE) generate-webui
$(MAKE) crossbinary-default
crossbinary-default: generate generate-webui
$(CURDIR)/script/crossbinary-default.sh
## Run the unit and integration tests
.PHONY: test
test:
./script/make.sh generate test-unit binary test-integration
test: test-unit test-integration
## Run the unit tests
.PHONY: test-unit
test-unit:
./script/make.sh generate test-unit
GOOS=$(GOOS) GOARCH=$(GOARCH) go test -cover "-coverprofile=cover.out" -v $(TESTFLAGS) ./pkg/... ./cmd/...
## Run the integration tests
.PHONY: test-integration
test-integration:
./script/make.sh generate binary test-integration
test-integration: binary
GOOS=$(GOOS) GOARCH=$(GOARCH) go test ./integration -test.timeout=20m -failfast -v $(TESTFLAGS)
## Pull all images for integration tests
## Run the conformance tests
.PHONY: test-gateway-api-conformance
test-gateway-api-conformance: binary
GOOS=$(GOOS) GOARCH=$(GOARCH) go test ./integration -v -test.run K8sConformanceSuite -k8sConformance=true $(TESTFLAGS)
## TODO: Need to be fixed to work in all situations.
## Run the conformance tests
.PHONY: test-gateway-api-conformance-ci
test-gateway-api-conformance-ci:
GOOS=$(GOOS) GOARCH=$(GOARCH) go test ./integration -v -test.run K8sConformanceSuite -k8sConformance=true $(TESTFLAGS)
## Pull all Docker images to avoid timeout during integration tests
.PHONY: pull-images
pull-images:
grep --no-filename -E '^\s+image:' ./integration/resources/compose/*.yml \
@ -80,38 +119,47 @@ pull-images:
| uniq \
| xargs -P 6 -n 1 docker pull
EXECUTABLES = misspell shellcheck
## Lint run golangci-lint
.PHONY: lint
lint:
golangci-lint run
## Validate code and docs
.PHONY: validate-files
validate-files:
$(foreach exec,$(EXECUTABLES),\
validate-files: lint
$(foreach exec,$(LINT_EXECUTABLES),\
$(if $(shell which $(exec)),,$(error "No $(exec) in PATH")))
./script/make.sh generate validate-lint validate-misspell
bash $(CURDIR)/script/validate-shell-script.sh
$(CURDIR)/script/validate-misspell.sh
$(CURDIR)/script/validate-shell-script.sh
## Validate code, docs, and vendor
.PHONY: validate
validate:
validate: lint
$(foreach exec,$(EXECUTABLES),\
$(if $(shell which $(exec)),,$(error "No $(exec) in PATH")))
./script/make.sh generate validate-lint validate-misspell validate-vendor
bash $(CURDIR)/script/validate-shell-script.sh
$(CURDIR)/script/validate-vendor.sh
$(CURDIR)/script/validate-misspell.sh
$(CURDIR)/script/validate-shell-script.sh
# Target for building images for multiple architectures.
.PHONY: multi-arch-image-%
multi-arch-image-%: binary-linux-amd64 binary-linux-arm64
docker buildx build $(DOCKER_BUILDX_ARGS) -t traefik/traefik:$* --platform=$(DOCKER_BUILD_PLATFORMS) -f Dockerfile .
## Clean up static directory and build a Docker Traefik image
.PHONY: build-image
build-image: clean-webui binary
docker build -t $(TRAEFIK_IMAGE) .
build-image: export DOCKER_BUILDX_ARGS := --load
build-image: export DOCKER_BUILD_PLATFORMS := linux/$(GOARCH)
build-image: clean-webui
@$(MAKE) multi-arch-image-latest
## Build a Docker Traefik image without re-building the webui
## Build a Docker Traefik image without re-building the webui when it's already built
.PHONY: build-image-dirty
build-image-dirty: binary
docker build -t $(TRAEFIK_IMAGE) .
## Locally build traefik for linux, then shove it an alpine image, with basic tools.
.PHONY: build-image-debug
build-image-debug: binary-debug
docker build -t $(TRAEFIK_IMAGE) -f debug.Dockerfile .
build-image-dirty: export DOCKER_BUILDX_ARGS := --load
build-image-dirty: export DOCKER_BUILD_PLATFORMS := linux/$(GOARCH)
build-image-dirty:
@$(MAKE) multi-arch-image-latest
## Build documentation site
.PHONY: docs
@ -141,30 +189,9 @@ generate-genconf:
## Create packages for the release
.PHONY: release-packages
release-packages: generate-webui
rm -rf dist
@- $(foreach os, linux darwin windows freebsd openbsd, \
goreleaser release --skip-publish -p 2 --timeout="90m" --config $(shell go run ./internal/release $(os)); \
go clean -cache; \
)
cat dist/**/*_checksums.txt >> dist/traefik_${VERSION}_checksums.txt
rm dist/**/*_checksums.txt
tar cfz dist/traefik-${VERSION}.src.tar.gz \
--exclude-vcs \
--exclude .idea \
--exclude .travis \
--exclude .semaphoreci \
--exclude .github \
--exclude dist .
chown -R $(shell id -u):$(shell id -g) dist/
$(CURDIR)/script/release-packages.sh
## Format the Code
.PHONY: fmt
fmt:
gofmt -s -l -w $(SRCS)
.PHONY: run-dev
run-dev:
go generate
GO111MODULE=on go build ./cmd/traefik
./traefik

View file

@ -1,37 +0,0 @@
FROM golang:1.21-alpine
RUN apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar ca-certificates tzdata \
&& update-ca-certificates \
&& rm -rf /var/cache/apk/*
# Which docker version to test on
ARG DOCKER_VERSION=18.09.7
# Download docker
RUN mkdir -p /usr/local/bin \
&& curl -fL https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}.tgz \
| 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.55.2
# Download misspell binary to bin folder in $GOPATH
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
WORKDIR /go/src/github.com/traefik/traefik
# Because of CVE-2022-24765 (https://github.blog/2022-04-12-git-security-vulnerability-announced/),
# we configure git to allow the Traefik codebase path on the Host for docker in docker usages.
ARG HOST_PWD=""
RUN git config --global --add safe.directory "${HOST_PWD}"
# Download go modules
COPY go.mod .
COPY go.sum .
RUN GO111MODULE=on GOPROXY=https://proxy.golang.org go mod download
COPY . /go/src/github.com/traefik/traefik

View file

@ -53,7 +53,7 @@ func main() {
// traefik config inits
tConfig := cmd.NewTraefikConfiguration()
loaders := []cli.ResourceLoader{&tcli.FileLoader{}, &tcli.FlagLoader{}, &tcli.EnvLoader{}}
loaders := []cli.ResourceLoader{&tcli.DeprecationLoader{}, &tcli.FileLoader{}, &tcli.FlagLoader{}, &tcli.EnvLoader{}}
cmdTraefik := &cli.Command{
Name: "traefik",

View file

@ -1,10 +0,0 @@
FROM alpine:3.14
# Feel free to add below any helpful dependency for debugging.
# iproute2 is for ss.
RUN apk --no-cache --no-progress add bash curl ca-certificates tzdata lsof iproute2 \
&& update-ca-certificates \
&& rm -rf /var/cache/apk/*
COPY dist/traefik /
EXPOSE 80
VOLUME ["/tmp"]
ENTRYPOINT ["/traefik"]

View file

@ -58,10 +58,12 @@ Once you've set up your go environment and cloned the source repository, you can
```bash
$ make binary
./script/make.sh generate binary
---> Making bundle: generate (in .)
---> Making bundle: binary (in .)
SHA: 8fddfe118288bb5280eb5e77fa952f52def360b4 cheddar 2024-01-11_03:14:57PM
CGO_ENABLED=0 GOGC=off GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w \
-X github.com/traefik/traefik/v2/pkg/version.Version=8fddfe118288bb5280eb5e77fa952f52def360b4 \
-X github.com/traefik/traefik/v2/pkg/version.Codename=cheddar \
-X github.com/traefik/traefik/v2/pkg/version.BuildDate=2024-01-11_03:14:57PM" \
-installsuffix nocgo -o "./dist/darwin/arm64/traefik" ./cmd/traefik
$ ls dist/
traefik*
@ -77,10 +79,7 @@ Run all tests (unit and integration) using the `test` target.
```bash
$ make test-unit
./script/make.sh generate test-unit
---> Making bundle: generate (in .)
---> Making bundle: test-unit (in .)
GOOS=darwin GOARCH=arm64 go test -cover "-coverprofile=cover.out" -v ./pkg/... ./cmd/...
+ go test -cover -coverprofile=cover.out .
ok github.com/traefik/traefik 0.005s coverage: 4.1% of statements

View file

@ -25,7 +25,7 @@ The main features include dynamic configuration, automatic service discovery, an
## Edge Router
Traefik is an *Edge Router*, it means that it's the door to your platform, and that it intercepts and routes every incoming request:
Traefik is an *Edge Router*; this means that it's the door to your platform, and that it intercepts and routes every incoming request:
it knows all the logic and every [rule](../routing/routers/index.md#rule "Link to docs about routing rules") that determine which services handle which requests (based on the *path*, the *host*, *headers*, etc.).
![The Door to Your Infrastructure](../assets/img/traefik-concepts-1.png "Picture explaining the infrastructure")
@ -38,7 +38,7 @@ Deploying your services, you attach information that tells Traefik the character
![Decentralized Configuration](../assets/img/traefik-concepts-2.png "Picture about Decentralized Configuration")
It means that when a service is deployed, Traefik detects it immediately and updates the routing rules in real time.
This means that when a service is deployed, Traefik detects it immediately and updates the routing rules in real time.
Similarly, when a service is removed from the infrastructure, the corresponding route is deleted accordingly.
You no longer need to create and synchronize configuration files cluttered with IP addresses or other rules.

View file

@ -19,7 +19,7 @@ version: '3'
services:
reverse-proxy:
# The official v2 Traefik docker image
# The official v3 Traefik docker image
image: traefik:v3.0
# Enables the web UI and tells Traefik to listen to docker
command: --api.insecure=true --providers.docker

View file

@ -55,10 +55,10 @@ http:
Responses are compressed when the following criteria are all met:
* The `Accept-Encoding` request header contains `gzip`, `*`, and/or `br` with or without [quality values](https://developer.mozilla.org/en-US/docs/Glossary/Quality_values).
If the `Accept-Encoding` request header is absent, it is meant as br compression is requested.
If the `Accept-Encoding` request header is absent, the response won't be encoded.
If it is present, but its value is the empty string, then compression is disabled.
* The response is not already compressed, i.e. the `Content-Encoding` response header is not already set.
* The response`Content-Type` header is not one among the [excludedContentTypes options](#excludedcontenttypes).
* The response`Content-Type` header is not one among the [excludedContentTypes options](#excludedcontenttypes), or is one among the [includedContentTypes options](#includedcontenttypes).
* The response body is larger than the [configured minimum amount of bytes](#minresponsebodybytes) (default is `1024`).
## Configuration Options
@ -73,6 +73,10 @@ The responses with content types defined in `excludedContentTypes` are not compr
Content types are compared in a case-insensitive, whitespace-ignored manner.
!!! info
The `excludedContentTypes` and `includedContentTypes` options are mutually exclusive.
!!! info "In the case of gzip"
If the `Content-Type` header is not defined, or empty, the compress middleware will automatically [detect](https://mimesniff.spec.whatwg.org/) a content type.
@ -117,6 +121,59 @@ http:
excludedContentTypes = ["text/event-stream"]
```
### `includedContentTypes`
_Optional, Default=""_
`includedContentTypes` specifies a list of content types to compare the `Content-Type` header of the responses before compressing.
The responses with content types defined in `includedContentTypes` are compressed.
Content types are compared in a case-insensitive, whitespace-ignored manner.
!!! info
The `excludedContentTypes` and `includedContentTypes` options are mutually exclusive.
```yaml tab="Docker & Swarm"
labels:
- "traefik.http.middlewares.test-compress.compress.includedcontenttypes=application/json,text/html,text/plain"
```
```yaml tab="Kubernetes"
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: test-compress
spec:
compress:
includedContentTypes:
- application/json
- text/html
- text/plain
```
```yaml tab="Consul Catalog"
- "traefik.http.middlewares.test-compress.compress.includedcontenttypes=application/json,text/html,text/plain"
```
```yaml tab="File (YAML)"
http:
middlewares:
test-compress:
compress:
includedContentTypes:
- application/json
- text/html
- text/plain
```
```toml tab="File (TOML)"
[http.middlewares]
[http.middlewares.test-compress.compress]
includedContentTypes = ["application/json","text/html","text/plain"]
```
### `minResponseBodyBytes`
_Optional, Default=1024_

View file

@ -285,6 +285,55 @@ http:
authRequestHeaders = "Accept,X-CustomHeader"
```
### `addAuthCookiesToResponse`
The `addAuthCookiesToResponse` option is the list of cookies to copy from the authentication server to the response,
replacing any existing conflicting cookie from the forwarded response.
!!! info
Please note that all backend cookies matching the configured list will not be added to the response.
```yaml tab="Docker"
labels:
- "traefik.http.middlewares.test-auth.forwardauth.addAuthCookiesToResponse=Session-Cookie,State-Cookie"
```
```yaml tab="Kubernetes"
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: test-auth
spec:
forwardAuth:
address: https://example.com/auth
addAuthCookiesToResponse:
- Session-Cookie
- State-Cookie
```
```yaml tab="Consul Catalog"
- "traefik.http.middlewares.test-auth.forwardauth.addAuthCookiesToResponse=Session-Cookie,State-Cookie"
```
```toml tab="File (TOML)"
[http.middlewares]
[http.middlewares.test-auth.forwardAuth]
address = "https://example.com/auth"
addAuthCookiesToResponse = ["Session-Cookie", "State-Cookie"]
```
```yaml tab="File (YAML)"
http:
middlewares:
test-auth:
forwardAuth:
address: "https://example.com/auth"
addAuthCookiesToResponse:
- "Session-Cookie"
- "State-Cookie"
```
### `tls`
_Optional_

View file

@ -19,6 +19,8 @@ and how it now looks like in v3.
### Docker & Docker Swarm
#### SwarmMode
In v3, the provider Docker has been split into 2 providers:
- Docker provider (without Swarm support)
@ -43,7 +45,7 @@ In v3, the provider Docker has been split into 2 providers:
This configuration is now unsupported and would prevent Traefik to start.
#### Remediation
##### Remediation
In v3, the `swarmMode` should not be used with the Docker provider, and, to use Swarm, the Swarm provider should be used instead.
@ -64,7 +66,35 @@ In v3, the `swarmMode` should not be used with the Docker provider, and, to use
--providers.swarm.endpoint=tcp://127.0.0.1:2377
```
### HTTP3 Experimental Configuration
#### TLS.CAOptional
Docker provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://pkg.go.dev/crypto/tls#ClientAuthType).
??? example "An example usage of the TLS.CAOptional option"
```yaml tab="File (YAML)"
providers:
docker:
tls:
caOptional: true
```
```toml tab="File (TOML)"
[providers.docker.tls]
caOptional=true
```
```bash tab="CLI"
--providers.docker.tls.caOptional=true
```
##### Remediation
The `tls.caOptional` option should be removed from the Docker provider static configuration.
### Experimental Configuration
#### HTTP3
In v3, HTTP/3 is no longer an experimental feature.
It can be enabled on entry points without the associated `experimental.http3` option, which is now removed.
@ -86,12 +116,14 @@ It is now unsupported and would prevent Traefik to start.
--experimental.http3=true
```
#### Remediation
##### Remediation
The `http3` option should be removed from the static configuration experimental section.
### Consul provider
#### namespace
The Consul provider `namespace` option was deprecated in v2 and is now removed in v3.
It is now unsupported and would prevent Traefik to start.
@ -111,7 +143,7 @@ It is now unsupported and would prevent Traefik to start.
--consul.namespace=foobar
```
#### Remediation
##### Remediation
In v3, the `namespaces` option should be used instead of the `namespace` option.
@ -132,8 +164,36 @@ In v3, the `namespaces` option should be used instead of the `namespace` option.
--consul.namespaces=foobar
```
#### TLS.CAOptional
Consul provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://pkg.go.dev/crypto/tls#ClientAuthType).
??? example "An example usage of the TLS.CAOptional option"
```yaml tab="File (YAML)"
providers:
consul:
tls:
caOptional: true
```
```toml tab="File (TOML)"
[providers.consul.tls]
caOptional=true
```
```bash tab="CLI"
--providers.consul.tls.caOptional=true
```
##### Remediation
The `tls.caOptional` option should be removed from the Consul provider static configuration.
### ConsulCatalog provider
#### namespace
The ConsulCatalog provider `namespace` option was deprecated in v2 and is now removed in v3.
It is now unsupported and would prevent Traefik to start.
@ -153,7 +213,7 @@ It is now unsupported and would prevent Traefik to start.
--consulCatalog.namespace=foobar
```
#### Remediation
##### Remediation
In v3, the `namespaces` option should be used instead of the `namespace` option.
@ -174,8 +234,37 @@ In v3, the `namespaces` option should be used instead of the `namespace` option.
--consulCatalog.namespaces=foobar
```
#### Endpoint.TLS.CAOptional
ConsulCatalog provider `endpoint.tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://pkg.go.dev/crypto/tls#ClientAuthType).
??? example "An example usage of the Endpoint.TLS.CAOptional option"
```yaml tab="File (YAML)"
providers:
consulCatalog:
endpoint:
tls:
caOptional: true
```
```toml tab="File (TOML)"
[providers.consulCatalog.endpoint.tls]
caOptional=true
```
```bash tab="CLI"
--providers.consulCatalog.endpoint.tls.caOptional=true
```
##### Remediation
The `endpoint.tls.caOptional` option should be removed from the ConsulCatalog provider static configuration.
### Nomad provider
#### namespace
The Nomad provider `namespace` option was deprecated in v2 and is now removed in v3.
It is now unsupported and would prevent Traefik to start.
@ -195,7 +284,7 @@ It is now unsupported and would prevent Traefik to start.
--nomad.namespace=foobar
```
#### Remediation
##### Remediation
In v3, the `namespaces` option should be used instead of the `namespace` option.
@ -216,6 +305,33 @@ In v3, the `namespaces` option should be used instead of the `namespace` option.
--nomad.namespaces=foobar
```
#### Endpoint.TLS.CAOptional
Nomad provider `endpoint.tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://pkg.go.dev/crypto/tls#ClientAuthType).
??? example "An example usage of the Endpoint.TLS.CAOptional option"
```yaml tab="File (YAML)"
providers:
nomad:
endpoint:
tls:
caOptional: true
```
```toml tab="File (TOML)"
[providers.nomad.endpoint.tls]
caOptional=true
```
```bash tab="CLI"
--providers.nomad.endpoint.tls.caOptional=true
```
##### Remediation
The `endpoint.tls.caOptional` option should be removed from the Nomad provider static configuration.
### Rancher v1 Provider
In v3, the Rancher v1 provider has been removed because Rancher v1 is [no longer actively maintaned](https://rancher.com/docs/os/v1.x/en/support/),
@ -271,6 +387,90 @@ This configuration is now unsupported and would prevent Traefik to start.
All Marathon provider related configuration should be removed from the static configuration.
### HTTP Provider
#### TLS.CAOptional
HTTP provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://pkg.go.dev/crypto/tls#ClientAuthType).
??? example "An example usage of the TLS.CAOptional option"
```yaml tab="File (YAML)"
providers:
http:
tls:
caOptional: true
```
```toml tab="File (TOML)"
[providers.http.tls]
caOptional=true
```
```bash tab="CLI"
--providers.http.tls.caOptional=true
```
##### Remediation
The `tls.caOptional` option should be removed from the HTTP provider static configuration.
### ETCD Provider
#### TLS.CAOptional
ETCD provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://pkg.go.dev/crypto/tls#ClientAuthType).
??? example "An example usage of the TLS.CAOptional option"
```yaml tab="File (YAML)"
providers:
etcd:
tls:
caOptional: true
```
```toml tab="File (TOML)"
[providers.etcd.tls]
caOptional=true
```
```bash tab="CLI"
--providers.etcd.tls.caOptional=true
```
##### Remediation
The `tls.caOptional` option should be removed from the ETCD provider static configuration.
### Redis Provider
#### TLS.CAOptional
Redis provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://pkg.go.dev/crypto/tls#ClientAuthType).
??? example "An example usage of the TLS.CAOptional option"
```yaml tab="File (YAML)"
providers:
redis:
tls:
caOptional: true
```
```toml tab="File (TOML)"
[providers.redis.tls]
caOptional=true
```
```bash tab="CLI"
--providers.redis.tls.caOptional=true
```
##### Remediation
The `tls.caOptional` option should be removed from the Redis provider static configuration.
### InfluxDB v1
InfluxDB v1.x maintenance [ended in 2021](https://www.influxdata.com/blog/influxdb-oss-and-enterprise-roadmap-update-from-influxdays-emea/).
@ -326,21 +526,16 @@ All Pilot related configuration should be removed from the static configuration.
## Dynamic configuration
### IPWhiteList
### Router Rule Matchers
In v3, we renamed the `IPWhiteList` middleware to `IPAllowList` without changing anything to the configuration.
In v3, a new rule matchers syntax has been introduced for HTTP and TCP routers.
The default rule matchers syntax is now the v3 one, but for backward compatibility this can be configured.
The v2 rule matchers syntax is deprecated and its support will be removed in the next major version.
For this reason, we encourage migrating to the new syntax.
### Deprecated Options Removal
#### New V3 Syntax Notable Changes
- The `tracing.datadog.globaltag` option has been removed.
- The `tls.caOptional` option has been removed from the ForwardAuth middleware, as well as from the HTTP, Consul, Etcd, Redis, ZooKeeper, Consul Catalog, and Docker providers.
- `sslRedirect`, `sslTemporaryRedirect`, `sslHost`, `sslForceHost` and `featurePolicy` options of the Headers middleware have been removed.
- The `forceSlash` option of the StripPrefix middleware has been removed.
- The `preferServerCipherSuites` option has been removed.
### Matchers
In v3, the `Headers` and `HeadersRegexp` matchers have been renamed to `Header` and `HeaderRegexp` respectively.
The `Headers` and `HeadersRegexp` matchers have been renamed to `Header` and `HeaderRegexp` respectively.
`PathPrefix` no longer uses regular expressions to match path prefixes.
@ -355,6 +550,87 @@ and should be explicitly combined using logical operators to mimic previous beha
`HostHeader` has been removed, use `Host` instead.
#### Remediation
##### Configure the Default Syntax In Static Configuration
The default rule matchers syntax is the expected syntax for any router that is not self opt-out from this default value.
It can be configured in the static configuration.
??? example "An example configuration for the default rule matchers syntax"
```yaml tab="File (YAML)"
# static configuration
core:
defaultRuleSyntax: v2
```
```toml tab="File (TOML)"
# static configuration
[core]
defaultRuleSyntax="v2"
```
```bash tab="CLI"
# static configuration
--core.defaultRuleSyntax=v2
```
##### Configure the Syntax Per Router
The rule syntax can also be configured on a per-router basis.
This allows to have heterogeneous router configurations and ease migration.
??? example "An example router with syntax configuration"
```yaml tab="Docker & Swarm"
labels:
- "traefik.http.routers.test.ruleSyntax=v2"
```
```yaml tab="Kubernetes"
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: test.route
namespace: default
spec:
routes:
- match: PathPrefix(`/foo`, `/bar`)
syntax: v2
kind: Rule
```
```yaml tab="Consul Catalog"
- "traefik.http.routers.test.ruleSyntax=v2"
```
```yaml tab="File (YAML)"
http:
routers:
test:
ruleSyntax: v2
```
```toml tab="File (TOML)"
[http.routers]
[http.routers.test]
ruleSyntax = "v2"
```
### IPWhiteList
In v3, we renamed the `IPWhiteList` middleware to `IPAllowList` without changing anything to the configuration.
### Deprecated Options Removal
- The `tracing.datadog.globaltag` option has been removed.
- The `tls.caOptional` option has been removed from the ForwardAuth middleware, as well as from the HTTP, Consul, Etcd, Redis, ZooKeeper, Consul Catalog, and Docker providers.
- `sslRedirect`, `sslTemporaryRedirect`, `sslHost`, `sslForceHost` and `featurePolicy` options of the Headers middleware have been removed.
- The `forceSlash` option of the StripPrefix middleware has been removed.
- The `preferServerCipherSuites` option has been removed.
### TCP LoadBalancer `terminationDelay` option
The TCP LoadBalancer `terminationDelay` option has been removed.
@ -415,3 +691,5 @@ Here are two possible transition strategies:
For legacy stacks that cannot immediately upgrade to the latest vendor agents supporting OTLP ingestion,
using OpenTelemetry (OTel) collectors with appropriate exporters configuration is a viable solution.
This allows continued compatibility with the existing infrastructure.
Please check the [OpenTelemetry Tracing provider documention](../observability/tracing/opentelemetry.md) for more information.

View file

@ -1,128 +1,138 @@
- "traefik.http.middlewares.middleware00.addprefix.prefix=foobar"
- "traefik.http.middlewares.middleware01.basicauth.headerfield=foobar"
- "traefik.http.middlewares.middleware01.basicauth.realm=foobar"
- "traefik.http.middlewares.middleware01.basicauth.removeheader=true"
- "traefik.http.middlewares.middleware01.basicauth.users=foobar, foobar"
- "traefik.http.middlewares.middleware01.basicauth.usersfile=foobar"
- "traefik.http.middlewares.middleware02.buffering.maxrequestbodybytes=42"
- "traefik.http.middlewares.middleware02.buffering.maxresponsebodybytes=42"
- "traefik.http.middlewares.middleware02.buffering.memrequestbodybytes=42"
- "traefik.http.middlewares.middleware02.buffering.memresponsebodybytes=42"
- "traefik.http.middlewares.middleware02.buffering.retryexpression=foobar"
- "traefik.http.middlewares.middleware03.chain.middlewares=foobar, foobar"
- "traefik.http.middlewares.middleware04.circuitbreaker.expression=foobar"
- "traefik.http.middlewares.middleware04.circuitbreaker.checkperiod=42s"
- "traefik.http.middlewares.middleware04.circuitbreaker.fallbackduration=42s"
- "traefik.http.middlewares.middleware04.circuitbreaker.recoveryduration=42s"
- "traefik.http.middlewares.middleware05.compress=true"
- "traefik.http.middlewares.middleware05.compress.excludedcontenttypes=foobar, foobar"
- "traefik.http.middlewares.middleware05.compress.minresponsebodybytes=42"
- "traefik.http.middlewares.middleware06.contenttype=true"
- "traefik.http.middlewares.middleware07.digestauth.headerfield=foobar"
- "traefik.http.middlewares.middleware07.digestauth.realm=foobar"
- "traefik.http.middlewares.middleware07.digestauth.removeheader=true"
- "traefik.http.middlewares.middleware07.digestauth.users=foobar, foobar"
- "traefik.http.middlewares.middleware07.digestauth.usersfile=foobar"
- "traefik.http.middlewares.middleware08.errors.query=foobar"
- "traefik.http.middlewares.middleware08.errors.service=foobar"
- "traefik.http.middlewares.middleware08.errors.status=foobar, foobar"
- "traefik.http.middlewares.middleware09.forwardauth.address=foobar"
- "traefik.http.middlewares.middleware09.forwardauth.authresponseheaders=foobar, foobar"
- "traefik.http.middlewares.middleware09.forwardauth.authresponseheadersregex=foobar"
- "traefik.http.middlewares.middleware09.forwardauth.authrequestheaders=foobar, foobar"
- "traefik.http.middlewares.middleware09.forwardauth.tls.ca=foobar"
- "traefik.http.middlewares.middleware09.forwardauth.tls.cert=foobar"
- "traefik.http.middlewares.middleware09.forwardauth.tls.insecureskipverify=true"
- "traefik.http.middlewares.middleware09.forwardauth.tls.key=foobar"
- "traefik.http.middlewares.middleware09.forwardauth.trustforwardheader=true"
- "traefik.http.middlewares.middleware10.headers.accesscontrolallowcredentials=true"
- "traefik.http.middlewares.middleware10.headers.accesscontrolallowheaders=foobar, foobar"
- "traefik.http.middlewares.middleware10.headers.accesscontrolallowmethods=foobar, foobar"
- "traefik.http.middlewares.middleware10.headers.accesscontrolalloworiginlist=foobar, foobar"
- "traefik.http.middlewares.middleware10.headers.accesscontrolalloworiginlistregex=foobar, foobar"
- "traefik.http.middlewares.middleware10.headers.accesscontrolexposeheaders=foobar, foobar"
- "traefik.http.middlewares.middleware10.headers.accesscontrolmaxage=42"
- "traefik.http.middlewares.middleware10.headers.addvaryheader=true"
- "traefik.http.middlewares.middleware10.headers.allowedhosts=foobar, foobar"
- "traefik.http.middlewares.middleware10.headers.browserxssfilter=true"
- "traefik.http.middlewares.middleware10.headers.contentsecuritypolicy=foobar"
- "traefik.http.middlewares.middleware10.headers.contenttypenosniff=true"
- "traefik.http.middlewares.middleware10.headers.custombrowserxssvalue=foobar"
- "traefik.http.middlewares.middleware10.headers.customframeoptionsvalue=foobar"
- "traefik.http.middlewares.middleware10.headers.customrequestheaders.name0=foobar"
- "traefik.http.middlewares.middleware10.headers.customrequestheaders.name1=foobar"
- "traefik.http.middlewares.middleware10.headers.customresponseheaders.name0=foobar"
- "traefik.http.middlewares.middleware10.headers.customresponseheaders.name1=foobar"
- "traefik.http.middlewares.middleware10.headers.forcestsheader=true"
- "traefik.http.middlewares.middleware10.headers.framedeny=true"
- "traefik.http.middlewares.middleware10.headers.hostsproxyheaders=foobar, foobar"
- "traefik.http.middlewares.middleware10.headers.isdevelopment=true"
- "traefik.http.middlewares.middleware10.headers.permissionspolicy=foobar"
- "traefik.http.middlewares.middleware10.headers.publickey=foobar"
- "traefik.http.middlewares.middleware10.headers.referrerpolicy=foobar"
- "traefik.http.middlewares.middleware10.headers.sslproxyheaders.name0=foobar"
- "traefik.http.middlewares.middleware10.headers.sslproxyheaders.name1=foobar"
- "traefik.http.middlewares.middleware10.headers.stsincludesubdomains=true"
- "traefik.http.middlewares.middleware10.headers.stspreload=true"
- "traefik.http.middlewares.middleware10.headers.stsseconds=42"
- "traefik.http.middlewares.middleware11.ipwhitelist.ipstrategy.depth=42"
- "traefik.http.middlewares.middleware11.ipwhitelist.ipstrategy.excludedips=foobar, foobar"
- "traefik.http.middlewares.middleware12.ipwhitelist.sourcerange=foobar, foobar"
- "traefik.http.middlewares.middleware12.ipallowlist.ipstrategy.depth=42"
- "traefik.http.middlewares.middleware12.ipallowlist.ipstrategy.excludedips=foobar, foobar"
- "traefik.http.middlewares.middleware12.ipallowlist.sourcerange=foobar, foobar"
- "traefik.http.middlewares.middleware12.ipallowlist.rejectstatuscode=404"
- "traefik.http.middlewares.middleware13.inflightreq.amount=42"
- "traefik.http.middlewares.middleware13.inflightreq.sourcecriterion.ipstrategy.depth=42"
- "traefik.http.middlewares.middleware13.inflightreq.sourcecriterion.ipstrategy.excludedips=foobar, foobar"
- "traefik.http.middlewares.middleware13.inflightreq.sourcecriterion.requestheadername=foobar"
- "traefik.http.middlewares.middleware13.inflightreq.sourcecriterion.requesthost=true"
- "traefik.http.middlewares.middleware14.passtlsclientcert.info.issuer.commonname=true"
- "traefik.http.middlewares.middleware14.passtlsclientcert.info.issuer.country=true"
- "traefik.http.middlewares.middleware14.passtlsclientcert.info.issuer.domaincomponent=true"
- "traefik.http.middlewares.middleware14.passtlsclientcert.info.issuer.locality=true"
- "traefik.http.middlewares.middleware14.passtlsclientcert.info.issuer.organization=true"
- "traefik.http.middlewares.middleware14.passtlsclientcert.info.issuer.province=true"
- "traefik.http.middlewares.middleware14.passtlsclientcert.info.issuer.serialnumber=true"
- "traefik.http.middlewares.middleware14.passtlsclientcert.info.notafter=true"
- "traefik.http.middlewares.middleware14.passtlsclientcert.info.notbefore=true"
- "traefik.http.middlewares.middleware14.passtlsclientcert.info.sans=true"
- "traefik.http.middlewares.middleware14.passtlsclientcert.info.serialnumber=true"
- "traefik.http.middlewares.middleware14.passtlsclientcert.info.subject.commonname=true"
- "traefik.http.middlewares.middleware14.passtlsclientcert.info.subject.country=true"
- "traefik.http.middlewares.middleware14.passtlsclientcert.info.subject.domaincomponent=true"
- "traefik.http.middlewares.middleware14.passtlsclientcert.info.subject.locality=true"
- "traefik.http.middlewares.middleware14.passtlsclientcert.info.subject.organization=true"
- "traefik.http.middlewares.middleware14.passtlsclientcert.info.subject.organizationalunit=true"
- "traefik.http.middlewares.middleware14.passtlsclientcert.info.subject.province=true"
- "traefik.http.middlewares.middleware14.passtlsclientcert.info.subject.serialnumber=true"
- "traefik.http.middlewares.middleware14.passtlsclientcert.pem=true"
- "traefik.http.middlewares.middleware15.plugin.foobar.foo=bar"
- "traefik.http.middlewares.middleware16.ratelimit.average=42"
- "traefik.http.middlewares.middleware16.ratelimit.burst=42"
- "traefik.http.middlewares.middleware16.ratelimit.period=42"
- "traefik.http.middlewares.middleware16.ratelimit.sourcecriterion.ipstrategy.depth=42"
- "traefik.http.middlewares.middleware16.ratelimit.sourcecriterion.ipstrategy.excludedips=foobar, foobar"
- "traefik.http.middlewares.middleware16.ratelimit.sourcecriterion.requestheadername=foobar"
- "traefik.http.middlewares.middleware16.ratelimit.sourcecriterion.requesthost=true"
- "traefik.http.middlewares.middleware17.redirectregex.permanent=true"
- "traefik.http.middlewares.middleware17.redirectregex.regex=foobar"
- "traefik.http.middlewares.middleware17.redirectregex.replacement=foobar"
- "traefik.http.middlewares.middleware18.redirectscheme.permanent=true"
- "traefik.http.middlewares.middleware18.redirectscheme.port=foobar"
- "traefik.http.middlewares.middleware18.redirectscheme.scheme=foobar"
- "traefik.http.middlewares.middleware19.replacepath.path=foobar"
- "traefik.http.middlewares.middleware20.replacepathregex.regex=foobar"
- "traefik.http.middlewares.middleware20.replacepathregex.replacement=foobar"
- "traefik.http.middlewares.middleware21.retry.attempts=42"
- "traefik.http.middlewares.middleware21.retry.initialinterval=42"
- "traefik.http.middlewares.middleware22.stripprefix.prefixes=foobar, foobar"
- "traefik.http.middlewares.middleware23.stripprefixregex.regex=foobar, foobar"
- "traefik.http.middlewares.middleware24.grpcweb.alloworigins=foobar, foobar"
## CODE GENERATED AUTOMATICALLY
## THIS FILE MUST NOT BE EDITED BY HAND
- "traefik.http.middlewares.middleware01.addprefix.prefix=foobar"
- "traefik.http.middlewares.middleware02.basicauth.headerfield=foobar"
- "traefik.http.middlewares.middleware02.basicauth.realm=foobar"
- "traefik.http.middlewares.middleware02.basicauth.removeheader=true"
- "traefik.http.middlewares.middleware02.basicauth.users=foobar, foobar"
- "traefik.http.middlewares.middleware02.basicauth.usersfile=foobar"
- "traefik.http.middlewares.middleware03.buffering.maxrequestbodybytes=42"
- "traefik.http.middlewares.middleware03.buffering.maxresponsebodybytes=42"
- "traefik.http.middlewares.middleware03.buffering.memrequestbodybytes=42"
- "traefik.http.middlewares.middleware03.buffering.memresponsebodybytes=42"
- "traefik.http.middlewares.middleware03.buffering.retryexpression=foobar"
- "traefik.http.middlewares.middleware04.chain.middlewares=foobar, foobar"
- "traefik.http.middlewares.middleware05.circuitbreaker.checkperiod=42s"
- "traefik.http.middlewares.middleware05.circuitbreaker.expression=foobar"
- "traefik.http.middlewares.middleware05.circuitbreaker.fallbackduration=42s"
- "traefik.http.middlewares.middleware05.circuitbreaker.recoveryduration=42s"
- "traefik.http.middlewares.middleware06.compress=true"
- "traefik.http.middlewares.middleware06.compress.excludedcontenttypes=foobar, foobar"
- "traefik.http.middlewares.middleware06.compress.includedcontenttypes=foobar, foobar"
- "traefik.http.middlewares.middleware06.compress.minresponsebodybytes=42"
- "traefik.http.middlewares.middleware07.contenttype=true"
- "traefik.http.middlewares.middleware08.digestauth.headerfield=foobar"
- "traefik.http.middlewares.middleware08.digestauth.realm=foobar"
- "traefik.http.middlewares.middleware08.digestauth.removeheader=true"
- "traefik.http.middlewares.middleware08.digestauth.users=foobar, foobar"
- "traefik.http.middlewares.middleware08.digestauth.usersfile=foobar"
- "traefik.http.middlewares.middleware09.errors.query=foobar"
- "traefik.http.middlewares.middleware09.errors.service=foobar"
- "traefik.http.middlewares.middleware09.errors.status=foobar, foobar"
- "traefik.http.middlewares.middleware10.forwardauth.addauthcookiestoresponse=foobar, foobar"
- "traefik.http.middlewares.middleware10.forwardauth.address=foobar"
- "traefik.http.middlewares.middleware10.forwardauth.authrequestheaders=foobar, foobar"
- "traefik.http.middlewares.middleware10.forwardauth.authresponseheaders=foobar, foobar"
- "traefik.http.middlewares.middleware10.forwardauth.authresponseheadersregex=foobar"
- "traefik.http.middlewares.middleware10.forwardauth.tls.ca=foobar"
- "traefik.http.middlewares.middleware10.forwardauth.tls.cert=foobar"
- "traefik.http.middlewares.middleware10.forwardauth.tls.insecureskipverify=true"
- "traefik.http.middlewares.middleware10.forwardauth.tls.key=foobar"
- "traefik.http.middlewares.middleware10.forwardauth.trustforwardheader=true"
- "traefik.http.middlewares.middleware11.grpcweb.alloworigins=foobar, foobar"
- "traefik.http.middlewares.middleware12.headers.accesscontrolallowcredentials=true"
- "traefik.http.middlewares.middleware12.headers.accesscontrolallowheaders=foobar, foobar"
- "traefik.http.middlewares.middleware12.headers.accesscontrolallowmethods=foobar, foobar"
- "traefik.http.middlewares.middleware12.headers.accesscontrolalloworiginlist=foobar, foobar"
- "traefik.http.middlewares.middleware12.headers.accesscontrolalloworiginlistregex=foobar, foobar"
- "traefik.http.middlewares.middleware12.headers.accesscontrolexposeheaders=foobar, foobar"
- "traefik.http.middlewares.middleware12.headers.accesscontrolmaxage=42"
- "traefik.http.middlewares.middleware12.headers.addvaryheader=true"
- "traefik.http.middlewares.middleware12.headers.allowedhosts=foobar, foobar"
- "traefik.http.middlewares.middleware12.headers.browserxssfilter=true"
- "traefik.http.middlewares.middleware12.headers.contentsecuritypolicy=foobar"
- "traefik.http.middlewares.middleware12.headers.contenttypenosniff=true"
- "traefik.http.middlewares.middleware12.headers.custombrowserxssvalue=foobar"
- "traefik.http.middlewares.middleware12.headers.customframeoptionsvalue=foobar"
- "traefik.http.middlewares.middleware12.headers.customrequestheaders.name0=foobar"
- "traefik.http.middlewares.middleware12.headers.customrequestheaders.name1=foobar"
- "traefik.http.middlewares.middleware12.headers.customresponseheaders.name0=foobar"
- "traefik.http.middlewares.middleware12.headers.customresponseheaders.name1=foobar"
- "traefik.http.middlewares.middleware12.headers.forcestsheader=true"
- "traefik.http.middlewares.middleware12.headers.framedeny=true"
- "traefik.http.middlewares.middleware12.headers.hostsproxyheaders=foobar, foobar"
- "traefik.http.middlewares.middleware12.headers.isdevelopment=true"
- "traefik.http.middlewares.middleware12.headers.permissionspolicy=foobar"
- "traefik.http.middlewares.middleware12.headers.publickey=foobar"
- "traefik.http.middlewares.middleware12.headers.referrerpolicy=foobar"
- "traefik.http.middlewares.middleware12.headers.sslproxyheaders.name0=foobar"
- "traefik.http.middlewares.middleware12.headers.sslproxyheaders.name1=foobar"
- "traefik.http.middlewares.middleware12.headers.stsincludesubdomains=true"
- "traefik.http.middlewares.middleware12.headers.stspreload=true"
- "traefik.http.middlewares.middleware12.headers.stsseconds=42"
- "traefik.http.middlewares.middleware13.ipallowlist.ipstrategy=true"
- "traefik.http.middlewares.middleware13.ipallowlist.ipstrategy.depth=42"
- "traefik.http.middlewares.middleware13.ipallowlist.ipstrategy.excludedips=foobar, foobar"
- "traefik.http.middlewares.middleware13.ipallowlist.rejectstatuscode=42"
- "traefik.http.middlewares.middleware13.ipallowlist.sourcerange=foobar, foobar"
- "traefik.http.middlewares.middleware14.ipwhitelist.ipstrategy=true"
- "traefik.http.middlewares.middleware14.ipwhitelist.ipstrategy.depth=42"
- "traefik.http.middlewares.middleware14.ipwhitelist.ipstrategy.excludedips=foobar, foobar"
- "traefik.http.middlewares.middleware14.ipwhitelist.sourcerange=foobar, foobar"
- "traefik.http.middlewares.middleware15.inflightreq.amount=42"
- "traefik.http.middlewares.middleware15.inflightreq.sourcecriterion.ipstrategy.depth=42"
- "traefik.http.middlewares.middleware15.inflightreq.sourcecriterion.ipstrategy.excludedips=foobar, foobar"
- "traefik.http.middlewares.middleware15.inflightreq.sourcecriterion.requestheadername=foobar"
- "traefik.http.middlewares.middleware15.inflightreq.sourcecriterion.requesthost=true"
- "traefik.http.middlewares.middleware16.passtlsclientcert.info.issuer.commonname=true"
- "traefik.http.middlewares.middleware16.passtlsclientcert.info.issuer.country=true"
- "traefik.http.middlewares.middleware16.passtlsclientcert.info.issuer.domaincomponent=true"
- "traefik.http.middlewares.middleware16.passtlsclientcert.info.issuer.locality=true"
- "traefik.http.middlewares.middleware16.passtlsclientcert.info.issuer.organization=true"
- "traefik.http.middlewares.middleware16.passtlsclientcert.info.issuer.province=true"
- "traefik.http.middlewares.middleware16.passtlsclientcert.info.issuer.serialnumber=true"
- "traefik.http.middlewares.middleware16.passtlsclientcert.info.notafter=true"
- "traefik.http.middlewares.middleware16.passtlsclientcert.info.notbefore=true"
- "traefik.http.middlewares.middleware16.passtlsclientcert.info.sans=true"
- "traefik.http.middlewares.middleware16.passtlsclientcert.info.serialnumber=true"
- "traefik.http.middlewares.middleware16.passtlsclientcert.info.subject.commonname=true"
- "traefik.http.middlewares.middleware16.passtlsclientcert.info.subject.country=true"
- "traefik.http.middlewares.middleware16.passtlsclientcert.info.subject.domaincomponent=true"
- "traefik.http.middlewares.middleware16.passtlsclientcert.info.subject.locality=true"
- "traefik.http.middlewares.middleware16.passtlsclientcert.info.subject.organization=true"
- "traefik.http.middlewares.middleware16.passtlsclientcert.info.subject.organizationalunit=true"
- "traefik.http.middlewares.middleware16.passtlsclientcert.info.subject.province=true"
- "traefik.http.middlewares.middleware16.passtlsclientcert.info.subject.serialnumber=true"
- "traefik.http.middlewares.middleware16.passtlsclientcert.pem=true"
- "traefik.http.middlewares.middleware17.plugin.pluginconf0.name0=foobar"
- "traefik.http.middlewares.middleware17.plugin.pluginconf0.name1=foobar"
- "traefik.http.middlewares.middleware17.plugin.pluginconf1.name0=foobar"
- "traefik.http.middlewares.middleware17.plugin.pluginconf1.name1=foobar"
- "traefik.http.middlewares.middleware18.ratelimit.average=42"
- "traefik.http.middlewares.middleware18.ratelimit.burst=42"
- "traefik.http.middlewares.middleware18.ratelimit.period=42s"
- "traefik.http.middlewares.middleware18.ratelimit.sourcecriterion.ipstrategy.depth=42"
- "traefik.http.middlewares.middleware18.ratelimit.sourcecriterion.ipstrategy.excludedips=foobar, foobar"
- "traefik.http.middlewares.middleware18.ratelimit.sourcecriterion.requestheadername=foobar"
- "traefik.http.middlewares.middleware18.ratelimit.sourcecriterion.requesthost=true"
- "traefik.http.middlewares.middleware19.redirectregex.permanent=true"
- "traefik.http.middlewares.middleware19.redirectregex.regex=foobar"
- "traefik.http.middlewares.middleware19.redirectregex.replacement=foobar"
- "traefik.http.middlewares.middleware20.redirectscheme.permanent=true"
- "traefik.http.middlewares.middleware20.redirectscheme.port=foobar"
- "traefik.http.middlewares.middleware20.redirectscheme.scheme=foobar"
- "traefik.http.middlewares.middleware21.replacepath.path=foobar"
- "traefik.http.middlewares.middleware22.replacepathregex.regex=foobar"
- "traefik.http.middlewares.middleware22.replacepathregex.replacement=foobar"
- "traefik.http.middlewares.middleware23.retry.attempts=42"
- "traefik.http.middlewares.middleware23.retry.initialinterval=42s"
- "traefik.http.middlewares.middleware24.stripprefix.prefixes=foobar, foobar"
- "traefik.http.middlewares.middleware25.stripprefixregex.regex=foobar, foobar"
- "traefik.http.routers.router0.entrypoints=foobar, foobar"
- "traefik.http.routers.router0.middlewares=foobar, foobar"
- "traefik.http.routers.router0.priority=42"
- "traefik.http.routers.router0.rule=foobar"
- "traefik.http.routers.router0.rulesyntax=foobar"
- "traefik.http.routers.router0.service=foobar"
- "traefik.http.routers.router0.tls=true"
- "traefik.http.routers.router0.tls.certresolver=foobar"
@ -135,6 +145,7 @@
- "traefik.http.routers.router1.middlewares=foobar, foobar"
- "traefik.http.routers.router1.priority=42"
- "traefik.http.routers.router1.rule=foobar"
- "traefik.http.routers.router1.rulesyntax=foobar"
- "traefik.http.routers.router1.service=foobar"
- "traefik.http.routers.router1.tls=true"
- "traefik.http.routers.router1.tls.certresolver=foobar"
@ -143,35 +154,39 @@
- "traefik.http.routers.router1.tls.domains[1].main=foobar"
- "traefik.http.routers.router1.tls.domains[1].sans=foobar, foobar"
- "traefik.http.routers.router1.tls.options=foobar"
- "traefik.http.services.service01.loadbalancer.healthcheck.followredirects=true"
- "traefik.http.services.service01.loadbalancer.healthcheck.headers.name0=foobar"
- "traefik.http.services.service01.loadbalancer.healthcheck.headers.name1=foobar"
- "traefik.http.services.service01.loadbalancer.healthcheck.hostname=foobar"
- "traefik.http.services.service01.loadbalancer.healthcheck.interval=foobar"
- "traefik.http.services.service01.loadbalancer.healthcheck.path=foobar"
- "traefik.http.services.service01.loadbalancer.healthcheck.method=foobar"
- "traefik.http.services.service01.loadbalancer.healthcheck.status=42"
- "traefik.http.services.service01.loadbalancer.healthcheck.port=42"
- "traefik.http.services.service01.loadbalancer.healthcheck.scheme=foobar"
- "traefik.http.services.service01.loadbalancer.healthcheck.mode=foobar"
- "traefik.http.services.service01.loadbalancer.healthcheck.timeout=foobar"
- "traefik.http.services.service01.loadbalancer.passhostheader=true"
- "traefik.http.services.service01.loadbalancer.responseforwarding.flushinterval=foobar"
- "traefik.http.services.service01.loadbalancer.serverstransport=foobar"
- "traefik.http.services.service01.loadbalancer.sticky.cookie=true"
- "traefik.http.services.service01.loadbalancer.sticky.cookie.httponly=true"
- "traefik.http.services.service01.loadbalancer.sticky.cookie.name=foobar"
- "traefik.http.services.service01.loadbalancer.sticky.cookie.samesite=foobar"
- "traefik.http.services.service01.loadbalancer.sticky.cookie.secure=true"
- "traefik.http.services.service01.loadbalancer.server.port=foobar"
- "traefik.http.services.service01.loadbalancer.server.scheme=foobar"
- "traefik.tcp.middlewares.tcpmiddleware00.inflightconn.amount=42"
- "traefik.tcp.middlewares.tcpmiddleware01.ipwhitelist.sourcerange=foobar, foobar"
- "traefik.tcp.middlewares.tcpmiddleware02.ipallowlist.sourcerange=foobar, foobar"
- "traefik.http.services.service02.loadbalancer.healthcheck.followredirects=true"
- "traefik.http.services.service02.loadbalancer.healthcheck.headers.name0=foobar"
- "traefik.http.services.service02.loadbalancer.healthcheck.headers.name1=foobar"
- "traefik.http.services.service02.loadbalancer.healthcheck.hostname=foobar"
- "traefik.http.services.service02.loadbalancer.healthcheck.interval=42s"
- "traefik.http.services.service02.loadbalancer.healthcheck.method=foobar"
- "traefik.http.services.service02.loadbalancer.healthcheck.mode=foobar"
- "traefik.http.services.service02.loadbalancer.healthcheck.path=foobar"
- "traefik.http.services.service02.loadbalancer.healthcheck.port=42"
- "traefik.http.services.service02.loadbalancer.healthcheck.scheme=foobar"
- "traefik.http.services.service02.loadbalancer.healthcheck.status=42"
- "traefik.http.services.service02.loadbalancer.healthcheck.timeout=42s"
- "traefik.http.services.service02.loadbalancer.passhostheader=true"
- "traefik.http.services.service02.loadbalancer.responseforwarding.flushinterval=42s"
- "traefik.http.services.service02.loadbalancer.serverstransport=foobar"
- "traefik.http.services.service02.loadbalancer.sticky=true"
- "traefik.http.services.service02.loadbalancer.sticky.cookie=true"
- "traefik.http.services.service02.loadbalancer.sticky.cookie.httponly=true"
- "traefik.http.services.service02.loadbalancer.sticky.cookie.maxage=42"
- "traefik.http.services.service02.loadbalancer.sticky.cookie.name=foobar"
- "traefik.http.services.service02.loadbalancer.sticky.cookie.samesite=foobar"
- "traefik.http.services.service02.loadbalancer.sticky.cookie.secure=true"
- "traefik.http.services.service02.loadbalancer.server.port=foobar"
- "traefik.http.services.service02.loadbalancer.server.scheme=foobar"
- "traefik.http.services.service02.loadbalancer.server.weight=42"
- "traefik.tcp.middlewares.tcpmiddleware01.ipallowlist.sourcerange=foobar, foobar"
- "traefik.tcp.middlewares.tcpmiddleware02.ipwhitelist.sourcerange=foobar, foobar"
- "traefik.tcp.middlewares.tcpmiddleware03.inflightconn.amount=42"
- "traefik.tcp.routers.tcprouter0.entrypoints=foobar, foobar"
- "traefik.tcp.routers.tcprouter0.middlewares=foobar, foobar"
- "traefik.tcp.routers.tcprouter0.rule=foobar"
- "traefik.tcp.routers.tcprouter0.priority=42"
- "traefik.tcp.routers.tcprouter0.rule=foobar"
- "traefik.tcp.routers.tcprouter0.rulesyntax=foobar"
- "traefik.tcp.routers.tcprouter0.service=foobar"
- "traefik.tcp.routers.tcprouter0.tls=true"
- "traefik.tcp.routers.tcprouter0.tls.certresolver=foobar"
@ -183,8 +198,9 @@
- "traefik.tcp.routers.tcprouter0.tls.passthrough=true"
- "traefik.tcp.routers.tcprouter1.entrypoints=foobar, foobar"
- "traefik.tcp.routers.tcprouter1.middlewares=foobar, foobar"
- "traefik.tcp.routers.tcprouter1.rule=foobar"
- "traefik.tcp.routers.tcprouter1.priority=42"
- "traefik.tcp.routers.tcprouter1.rule=foobar"
- "traefik.tcp.routers.tcprouter1.rulesyntax=foobar"
- "traefik.tcp.routers.tcprouter1.service=foobar"
- "traefik.tcp.routers.tcprouter1.tls=true"
- "traefik.tcp.routers.tcprouter1.tls.certresolver=foobar"
@ -194,22 +210,13 @@
- "traefik.tcp.routers.tcprouter1.tls.domains[1].sans=foobar, foobar"
- "traefik.tcp.routers.tcprouter1.tls.options=foobar"
- "traefik.tcp.routers.tcprouter1.tls.passthrough=true"
- "traefik.tcp.services.tcpservice01.loadbalancer.proxyprotocol=true"
- "traefik.tcp.services.tcpservice01.loadbalancer.proxyprotocol.version=42"
- "traefik.tcp.services.tcpservice01.loadbalancer.serverstransport=foobar"
- "traefik.tcp.services.tcpservice01.loadbalancer.server.port=foobar"
- "traefik.tcp.services.tcpservice01.loadbalancer.server.tls=true"
- "traefik.tcp.services.tcpservice01.loadbalancer.serverstransport=foobar"
- "traefik.udp.routers.udprouter0.entrypoints=foobar, foobar"
- "traefik.udp.routers.udprouter0.service=foobar"
- "traefik.udp.routers.udprouter1.entrypoints=foobar, foobar"
- "traefik.udp.routers.udprouter1.service=foobar"
- "traefik.udp.services.udpservice01.loadbalancer.server.port=foobar"
- "traefik.tls.stores.Store0.defaultcertificate.certfile=foobar"
- "traefik.tls.stores.Store0.defaultcertificate.keyfile=foobar"
- "traefik.tls.stores.Store0.defaultgeneratedcert.domain.main=foobar"
- "traefik.tls.stores.Store0.defaultgeneratedcert.domain.sans=foobar, foobar"
- "traefik.tls.stores.Store0.defaultgeneratedcert.resolver=foobar"
- "traefik.tls.stores.Store1.defaultcertificate.certfile=foobar"
- "traefik.tls.stores.Store1.defaultcertificate.keyfile=foobar"
- "traefik.tls.stores.Store1.defaultgeneratedcert.domain.main=foobar"
- "traefik.tls.stores.Store1.defaultgeneratedcert.domain.sans=foobar, foobar"
- "traefik.tls.stores.Store1.defaultgeneratedcert.resolver=foobar"

View file

@ -1,3 +1,5 @@
## CODE GENERATED AUTOMATICALLY
## THIS FILE MUST NOT BE EDITED BY HAND
[http]
[http.routers]
[http.routers.Router0]
@ -5,6 +7,7 @@
middlewares = ["foobar", "foobar"]
service = "foobar"
rule = "foobar"
ruleSyntax = "foobar"
priority = 42
[http.routers.Router0.tls]
options = "foobar"
@ -22,6 +25,7 @@
middlewares = ["foobar", "foobar"]
service = "foobar"
rule = "foobar"
ruleSyntax = "foobar"
priority = 42
[http.routers.Router1.tls]
options = "foobar"
@ -36,22 +40,30 @@
sans = ["foobar", "foobar"]
[http.services]
[http.services.Service01]
[http.services.Service01.loadBalancer]
[http.services.Service01.failover]
service = "foobar"
fallback = "foobar"
[http.services.Service01.failover.healthCheck]
[http.services.Service02]
[http.services.Service02.loadBalancer]
passHostHeader = true
serversTransport = "foobar"
[http.services.Service01.loadBalancer.sticky]
[http.services.Service01.loadBalancer.sticky.cookie]
[http.services.Service02.loadBalancer.sticky]
[http.services.Service02.loadBalancer.sticky.cookie]
name = "foobar"
secure = true
httpOnly = true
sameSite = "foobar"
maxAge = 42
[[http.services.Service01.loadBalancer.servers]]
[[http.services.Service02.loadBalancer.servers]]
url = "foobar"
weight = 42
[[http.services.Service01.loadBalancer.servers]]
[[http.services.Service02.loadBalancer.servers]]
url = "foobar"
[http.services.Service01.loadBalancer.healthCheck]
weight = 42
[http.services.Service02.loadBalancer.healthCheck]
scheme = "foobar"
mode = "foobar"
path = "foobar"
@ -62,107 +74,106 @@
timeout = "42s"
hostname = "foobar"
followRedirects = true
[http.services.Service01.loadBalancer.healthCheck.headers]
[http.services.Service02.loadBalancer.healthCheck.headers]
name0 = "foobar"
name1 = "foobar"
[http.services.Service01.loadBalancer.responseForwarding]
[http.services.Service02.loadBalancer.responseForwarding]
flushInterval = "42s"
[http.services.Service02]
[http.services.Service02.mirroring]
[http.services.Service03]
[http.services.Service03.mirroring]
service = "foobar"
maxBodySize = 42
[http.services.Service02.mirroring.healthCheck]
[[http.services.Service02.mirroring.mirrors]]
[[http.services.Service03.mirroring.mirrors]]
name = "foobar"
percent = 42
[[http.services.Service02.mirroring.mirrors]]
[[http.services.Service03.mirroring.mirrors]]
name = "foobar"
percent = 42
[http.services.Service03]
[http.services.Service03.weighted]
[http.services.Service03.weighted.healthCheck]
[http.services.Service03.mirroring.healthCheck]
[http.services.Service04]
[http.services.Service04.weighted]
[[http.services.Service03.weighted.services]]
[[http.services.Service04.weighted.services]]
name = "foobar"
weight = 42
[[http.services.Service03.weighted.services]]
[[http.services.Service04.weighted.services]]
name = "foobar"
weight = 42
[http.services.Service03.weighted.sticky]
[http.services.Service03.weighted.sticky.cookie]
[http.services.Service04.weighted.sticky]
[http.services.Service04.weighted.sticky.cookie]
name = "foobar"
secure = true
httpOnly = true
sameSite = "foobar"
[http.services.Service04]
[http.services.Service04.failover]
service = "foobar"
fallback = "foobar"
[http.services.Service04.failover.healthCheck]
maxAge = 42
[http.services.Service04.weighted.healthCheck]
[http.middlewares]
[http.middlewares.Middleware00]
[http.middlewares.Middleware00.addPrefix]
prefix = "foobar"
[http.middlewares.Middleware01]
[http.middlewares.Middleware01.basicAuth]
[http.middlewares.Middleware01.addPrefix]
prefix = "foobar"
[http.middlewares.Middleware02]
[http.middlewares.Middleware02.basicAuth]
users = ["foobar", "foobar"]
usersFile = "foobar"
realm = "foobar"
removeHeader = true
headerField = "foobar"
[http.middlewares.Middleware02]
[http.middlewares.Middleware02.buffering]
[http.middlewares.Middleware03]
[http.middlewares.Middleware03.buffering]
maxRequestBodyBytes = 42
memRequestBodyBytes = 42
maxResponseBodyBytes = 42
memResponseBodyBytes = 42
retryExpression = "foobar"
[http.middlewares.Middleware03]
[http.middlewares.Middleware03.chain]
middlewares = ["foobar", "foobar"]
[http.middlewares.Middleware04]
[http.middlewares.Middleware04.circuitBreaker]
[http.middlewares.Middleware04.chain]
middlewares = ["foobar", "foobar"]
[http.middlewares.Middleware05]
[http.middlewares.Middleware05.circuitBreaker]
expression = "foobar"
checkPeriod = "42s"
fallbackDuration = "42s"
recoveryDuration = "42s"
[http.middlewares.Middleware05]
[http.middlewares.Middleware05.compress]
excludedContentTypes = ["foobar", "foobar"]
minResponseBodyBytes = 42
[http.middlewares.Middleware06]
[http.middlewares.Middleware06.contentType]
[http.middlewares.Middleware06.compress]
excludedContentTypes = ["foobar", "foobar"]
includedContentTypes = ["foobar", "foobar"]
minResponseBodyBytes = 42
[http.middlewares.Middleware07]
[http.middlewares.Middleware07.digestAuth]
[http.middlewares.Middleware07.contentType]
[http.middlewares.Middleware08]
[http.middlewares.Middleware08.digestAuth]
users = ["foobar", "foobar"]
usersFile = "foobar"
removeHeader = true
realm = "foobar"
headerField = "foobar"
[http.middlewares.Middleware08]
[http.middlewares.Middleware08.errors]
[http.middlewares.Middleware09]
[http.middlewares.Middleware09.errors]
status = ["foobar", "foobar"]
service = "foobar"
query = "foobar"
[http.middlewares.Middleware09]
[http.middlewares.Middleware09.forwardAuth]
[http.middlewares.Middleware10]
[http.middlewares.Middleware10.forwardAuth]
address = "foobar"
trustForwardHeader = true
authResponseHeaders = ["foobar", "foobar"]
authResponseHeadersRegex = "foobar"
authRequestHeaders = ["foobar", "foobar"]
[http.middlewares.Middleware09.forwardAuth.tls]
addAuthCookiesToResponse = ["foobar", "foobar"]
[http.middlewares.Middleware10.forwardAuth.tls]
ca = "foobar"
cert = "foobar"
key = "foobar"
insecureSkipVerify = true
[http.middlewares.Middleware10]
[http.middlewares.Middleware10.headers]
[http.middlewares.Middleware11]
[http.middlewares.Middleware11.grpcWeb]
allowOrigins = ["foobar", "foobar"]
[http.middlewares.Middleware12]
[http.middlewares.Middleware12.headers]
accessControlAllowCredentials = true
accessControlAllowHeaders = ["foobar", "foobar"]
accessControlAllowMethods = ["foobar", "foobar"]
@ -187,46 +198,46 @@
referrerPolicy = "foobar"
permissionsPolicy = "foobar"
isDevelopment = true
[http.middlewares.Middleware10.headers.customRequestHeaders]
[http.middlewares.Middleware12.headers.customRequestHeaders]
name0 = "foobar"
name1 = "foobar"
[http.middlewares.Middleware10.headers.customResponseHeaders]
[http.middlewares.Middleware12.headers.customResponseHeaders]
name0 = "foobar"
name1 = "foobar"
[http.middlewares.Middleware10.headers.sslProxyHeaders]
[http.middlewares.Middleware12.headers.sslProxyHeaders]
name0 = "foobar"
name1 = "foobar"
[http.middlewares.Middleware11]
[http.middlewares.Middleware11.ipWhiteList]
sourceRange = ["foobar", "foobar"]
[http.middlewares.Middleware11.ipWhiteList.ipStrategy]
depth = 42
excludedIPs = ["foobar", "foobar"]
[http.middlewares.Middleware12]
[http.middlewares.Middleware12.ipAllowList]
sourceRange = ["foobar", "foobar"]
rejectStatusCode = 404
[http.middlewares.Middleware12.ipAllowList.ipStrategy]
depth = 42
excludedIPs = ["foobar", "foobar"]
[http.middlewares.Middleware13]
[http.middlewares.Middleware13.inFlightReq]
[http.middlewares.Middleware13.ipAllowList]
sourceRange = ["foobar", "foobar"]
rejectStatusCode = 42
[http.middlewares.Middleware13.ipAllowList.ipStrategy]
depth = 42
excludedIPs = ["foobar", "foobar"]
[http.middlewares.Middleware14]
[http.middlewares.Middleware14.ipWhiteList]
sourceRange = ["foobar", "foobar"]
[http.middlewares.Middleware14.ipWhiteList.ipStrategy]
depth = 42
excludedIPs = ["foobar", "foobar"]
[http.middlewares.Middleware15]
[http.middlewares.Middleware15.inFlightReq]
amount = 42
[http.middlewares.Middleware13.inFlightReq.sourceCriterion]
[http.middlewares.Middleware15.inFlightReq.sourceCriterion]
requestHeaderName = "foobar"
requestHost = true
[http.middlewares.Middleware13.inFlightReq.sourceCriterion.ipStrategy]
[http.middlewares.Middleware15.inFlightReq.sourceCriterion.ipStrategy]
depth = 42
excludedIPs = ["foobar", "foobar"]
[http.middlewares.Middleware14]
[http.middlewares.Middleware14.passTLSClientCert]
[http.middlewares.Middleware16]
[http.middlewares.Middleware16.passTLSClientCert]
pem = true
[http.middlewares.Middleware14.passTLSClientCert.info]
[http.middlewares.Middleware16.passTLSClientCert.info]
notAfter = true
notBefore = true
sans = true
serialNumber = true
[http.middlewares.Middleware14.passTLSClientCert.info.subject]
[http.middlewares.Middleware16.passTLSClientCert.info.subject]
country = true
province = true
locality = true
@ -235,7 +246,7 @@
commonName = true
serialNumber = true
domainComponent = true
[http.middlewares.Middleware14.passTLSClientCert.info.issuer]
[http.middlewares.Middleware16.passTLSClientCert.info.issuer]
country = true
province = true
locality = true
@ -243,51 +254,52 @@
commonName = true
serialNumber = true
domainComponent = true
[http.middlewares.Middleware15]
[http.middlewares.Middleware15.plugin]
[http.middlewares.Middleware15.plugin.PluginConf]
foo = "bar"
[http.middlewares.Middleware16]
[http.middlewares.Middleware16.rateLimit]
[http.middlewares.Middleware17]
[http.middlewares.Middleware17.plugin]
[http.middlewares.Middleware17.plugin.PluginConf0]
name0 = "foobar"
name1 = "foobar"
[http.middlewares.Middleware17.plugin.PluginConf1]
name0 = "foobar"
name1 = "foobar"
[http.middlewares.Middleware18]
[http.middlewares.Middleware18.rateLimit]
average = 42
period = "42s"
burst = 42
[http.middlewares.Middleware16.rateLimit.sourceCriterion]
[http.middlewares.Middleware18.rateLimit.sourceCriterion]
requestHeaderName = "foobar"
requestHost = true
[http.middlewares.Middleware16.rateLimit.sourceCriterion.ipStrategy]
[http.middlewares.Middleware18.rateLimit.sourceCriterion.ipStrategy]
depth = 42
excludedIPs = ["foobar", "foobar"]
[http.middlewares.Middleware17]
[http.middlewares.Middleware17.redirectRegex]
[http.middlewares.Middleware19]
[http.middlewares.Middleware19.redirectRegex]
regex = "foobar"
replacement = "foobar"
permanent = true
[http.middlewares.Middleware18]
[http.middlewares.Middleware18.redirectScheme]
[http.middlewares.Middleware20]
[http.middlewares.Middleware20.redirectScheme]
scheme = "foobar"
port = "foobar"
permanent = true
[http.middlewares.Middleware19]
[http.middlewares.Middleware19.replacePath]
[http.middlewares.Middleware21]
[http.middlewares.Middleware21.replacePath]
path = "foobar"
[http.middlewares.Middleware20]
[http.middlewares.Middleware20.replacePathRegex]
[http.middlewares.Middleware22]
[http.middlewares.Middleware22.replacePathRegex]
regex = "foobar"
replacement = "foobar"
[http.middlewares.Middleware21]
[http.middlewares.Middleware21.retry]
[http.middlewares.Middleware23]
[http.middlewares.Middleware23.retry]
attempts = 42
initialInterval = "42s"
[http.middlewares.Middleware22]
[http.middlewares.Middleware22.stripPrefix]
prefixes = ["foobar", "foobar"]
[http.middlewares.Middleware23]
[http.middlewares.Middleware23.stripPrefixRegex]
regex = ["foobar", "foobar"]
[http.middlewares.Middleware24]
[http.middlewares.Middleware24.grpcWeb]
allowOrigins = ["foobar", "foobar"]
[http.middlewares.Middleware24.stripPrefix]
prefixes = ["foobar", "foobar"]
[http.middlewares.Middleware25]
[http.middlewares.Middleware25.stripPrefixRegex]
regex = ["foobar", "foobar"]
[http.serversTransports]
[http.serversTransports.ServersTransport0]
serverName = "foobar"
@ -304,18 +316,15 @@
[[http.serversTransports.ServersTransport0.certificates]]
certFile = "foobar"
keyFile = "foobar"
[http.serversTransports.ServersTransport0.forwardingTimeouts]
dialTimeout = "42s"
responseHeaderTimeout = "42s"
idleConnTimeout = "42s"
readIdleTimeout = "42s"
pingTimeout = "42s"
[http.serversTransports.ServersTransport0.spiffe]
ids = ["foobar", "foobar"]
trustDomain = "foobar"
[http.serversTransports.ServersTransport1]
serverName = "foobar"
insecureSkipVerify = true
@ -331,14 +340,12 @@
[[http.serversTransports.ServersTransport1.certificates]]
certFile = "foobar"
keyFile = "foobar"
[http.serversTransports.ServersTransport1.forwardingTimeouts]
dialTimeout = "42s"
responseHeaderTimeout = "42s"
idleConnTimeout = "42s"
readIdleTimeout = "42s"
pingTimeout = "42s"
[http.serversTransports.ServersTransport1.spiffe]
ids = ["foobar", "foobar"]
trustDomain = "foobar"
@ -350,6 +357,7 @@
middlewares = ["foobar", "foobar"]
service = "foobar"
rule = "foobar"
ruleSyntax = "foobar"
priority = 42
[tcp.routers.TCPRouter0.tls]
passthrough = true
@ -368,6 +376,7 @@
middlewares = ["foobar", "foobar"]
service = "foobar"
rule = "foobar"
ruleSyntax = "foobar"
priority = 42
[tcp.routers.TCPRouter1.tls]
passthrough = true
@ -405,21 +414,21 @@
[[tcp.services.TCPService02.weighted.services]]
name = "foobar"
weight = 42
[tcp.middlewares]
[tcp.middlewares.TCPMiddleware00]
[tcp.middlewares.TCPMiddleware00.ipAllowList]
sourceRange = ["foobar", "foobar"]
[tcp.middlewares.TCPMiddleware01]
[tcp.middlewares.TCPMiddleware01.inFlightConn]
[tcp.middlewares.TCPMiddleware01.ipAllowList]
sourceRange = ["foobar", "foobar"]
[tcp.middlewares.TCPMiddleware02]
[tcp.middlewares.TCPMiddleware02.ipWhiteList]
sourceRange = ["foobar", "foobar"]
[tcp.middlewares.TCPMiddleware03]
[tcp.middlewares.TCPMiddleware03.inFlightConn]
amount = 42
[tcp.serversTransports]
[tcp.serversTransports.TCPServersTransport0]
dialTimeout = "42s"
dialKeepAlive = "42s"
dialTimeout = "42s"
terminationDelay = "42s"
[tcp.serversTransports.TCPServersTransport0.tls]
serverName = "foobar"
insecureSkipVerify = true
@ -433,16 +442,13 @@
[[tcp.serversTransports.TCPServersTransport0.tls.certificates]]
certFile = "foobar"
keyFile = "foobar"
[tcp.serversTransports.TCPServersTransport0.spiffe]
ids = ["foobar", "foobar"]
trustDomain = "foobar"
[tcp.serversTransports.TCPServersTransport0.tls.spiffe]
ids = ["foobar", "foobar"]
trustDomain = "foobar"
[tcp.serversTransports.TCPServersTransport1]
dialTimeout = "42s"
dialKeepAlive = "42s"
dialTimeout = "42s"
terminationDelay = "42s"
[tcp.serversTransports.TCPServersTransport1.tls]
serverName = "foobar"
insecureSkipVerify = true
@ -456,10 +462,9 @@
[[tcp.serversTransports.TCPServersTransport1.tls.certificates]]
certFile = "foobar"
keyFile = "foobar"
[tcp.serversTransports.TCPServersTransport1.spiffe]
ids = ["foobar", "foobar"]
trustDomain = "foobar"
[tcp.serversTransports.TCPServersTransport1.tls.spiffe]
ids = ["foobar", "foobar"]
trustDomain = "foobar"
[udp]
[udp.routers]

View file

@ -1,3 +1,5 @@
## CODE GENERATED AUTOMATICALLY
## THIS FILE MUST NOT BE EDITED BY HAND
http:
routers:
Router0:
@ -9,6 +11,7 @@ http:
- foobar
service: foobar
rule: foobar
ruleSyntax: foobar
priority: 42
tls:
options: foobar
@ -31,6 +34,7 @@ http:
- foobar
service: foobar
rule: foobar
ruleSyntax: foobar
priority: 42
tls:
options: foobar
@ -46,6 +50,11 @@ http:
- foobar
services:
Service01:
failover:
service: foobar
fallback: foobar
healthCheck: {}
Service02:
loadBalancer:
sticky:
cookie:
@ -53,9 +62,12 @@ http:
secure: true
httpOnly: true
sameSite: foobar
maxAge: 42
servers:
- url: foobar
weight: 42
- url: foobar
weight: 42
healthCheck:
scheme: foobar
mode: foobar
@ -74,19 +86,18 @@ http:
responseForwarding:
flushInterval: 42s
serversTransport: foobar
Service02:
Service03:
mirroring:
service: foobar
maxBodySize: 42
healthCheck: {}
mirrors:
- name: foobar
percent: 42
- name: foobar
percent: 42
Service03:
weighted:
healthCheck: {}
Service04:
weighted:
services:
- name: foobar
weight: 42
@ -98,16 +109,13 @@ http:
secure: true
httpOnly: true
sameSite: foobar
Service04:
failover:
service: foobar
fallback: foobar
maxAge: 42
healthCheck: {}
middlewares:
Middleware00:
Middleware01:
addPrefix:
prefix: foobar
Middleware01:
Middleware02:
basicAuth:
users:
- foobar
@ -116,33 +124,36 @@ http:
realm: foobar
removeHeader: true
headerField: foobar
Middleware02:
Middleware03:
buffering:
maxRequestBodyBytes: 42
memRequestBodyBytes: 42
maxResponseBodyBytes: 42
memResponseBodyBytes: 42
retryExpression: foobar
Middleware03:
Middleware04:
chain:
middlewares:
- foobar
- foobar
Middleware04:
Middleware05:
circuitBreaker:
expression: foobar
checkPeriod: 42s
fallbackDuration: 42s
recoveryDuration: 42s
Middleware05:
Middleware06:
compress:
excludedContentTypes:
- foobar
- foobar
includedContentTypes:
- foobar
- foobar
minResponseBodyBytes: 42
Middleware06:
contentType: {}
Middleware07:
contentType: {}
Middleware08:
digestAuth:
users:
- foobar
@ -151,14 +162,14 @@ http:
removeHeader: true
realm: foobar
headerField: foobar
Middleware08:
Middleware09:
errors:
status:
- foobar
- foobar
service: foobar
query: foobar
Middleware09:
Middleware10:
forwardAuth:
address: foobar
tls:
@ -174,7 +185,15 @@ http:
authRequestHeaders:
- foobar
- foobar
Middleware10:
addAuthCookiesToResponse:
- foobar
- foobar
Middleware11:
grpcWeb:
allowOrigins:
- foobar
- foobar
Middleware12:
headers:
customRequestHeaders:
name0: foobar
@ -223,7 +242,18 @@ http:
referrerPolicy: foobar
permissionsPolicy: foobar
isDevelopment: true
Middleware11:
Middleware13:
ipAllowList:
sourceRange:
- foobar
- foobar
ipStrategy:
depth: 42
excludedIPs:
- foobar
- foobar
rejectStatusCode: 42
Middleware14:
ipWhiteList:
sourceRange:
- foobar
@ -233,18 +263,7 @@ http:
excludedIPs:
- foobar
- foobar
Middleware12:
ipAllowList:
rejectStatusCode: 404
sourceRange:
- foobar
- foobar
ipStrategy:
depth: 42
excludedIPs:
- foobar
- foobar
Middleware13:
Middleware15:
inFlightReq:
amount: 42
sourceCriterion:
@ -255,13 +274,14 @@ http:
- foobar
requestHeaderName: foobar
requestHost: true
Middleware14:
Middleware16:
passTLSClientCert:
pem: true
info:
notAfter: true
notBefore: true
sans: true
serialNumber: true
subject:
country: true
province: true
@ -279,12 +299,15 @@ http:
commonName: true
serialNumber: true
domainComponent: true
serialNumber: true
Middleware15:
Middleware17:
plugin:
PluginConf:
foo: bar
Middleware16:
PluginConf0:
name0: foobar
name1: foobar
PluginConf1:
name0: foobar
name1: foobar
Middleware18:
rateLimit:
average: 42
period: 42s
@ -297,42 +320,37 @@ http:
- foobar
requestHeaderName: foobar
requestHost: true
Middleware17:
Middleware19:
redirectRegex:
regex: foobar
replacement: foobar
permanent: true
Middleware18:
Middleware20:
redirectScheme:
scheme: foobar
port: foobar
permanent: true
Middleware19:
Middleware21:
replacePath:
path: foobar
Middleware20:
Middleware22:
replacePathRegex:
regex: foobar
replacement: foobar
Middleware21:
Middleware23:
retry:
attempts: 42
initialInterval: 42s
Middleware22:
Middleware24:
stripPrefix:
prefixes:
- foobar
- foobar
Middleware23:
Middleware25:
stripPrefixRegex:
regex:
- foobar
- foobar
Middleware24:
grpcWeb:
allowOrigins:
- foobar
- foobar
serversTransports:
ServersTransport0:
serverName: foobar
@ -359,7 +377,6 @@ http:
- foobar
- foobar
trustDomain: foobar
ServersTransport1:
serverName: foobar
insecureSkipVerify: true
@ -385,7 +402,6 @@ http:
- foobar
- foobar
trustDomain: foobar
tcp:
routers:
TCPRouter0:
@ -397,6 +413,7 @@ tcp:
- foobar
service: foobar
rule: foobar
ruleSyntax: foobar
priority: 42
tls:
passthrough: true
@ -420,6 +437,7 @@ tcp:
- foobar
service: foobar
rule: foobar
ruleSyntax: foobar
priority: 42
tls:
passthrough: true
@ -437,7 +455,6 @@ tcp:
services:
TCPService01:
loadBalancer:
serversTransport: foobar
proxyProtocol:
version: 42
servers:
@ -445,6 +462,7 @@ tcp:
tls: true
- address: foobar
tls: true
serversTransport: foobar
TCPService02:
weighted:
services:
@ -453,23 +471,23 @@ tcp:
- name: foobar
weight: 42
middlewares:
TCPMiddleware00:
TCPMiddleware01:
ipAllowList:
sourceRange:
- foobar
- foobar
TCPMiddleware01:
TCPMiddleware02:
ipWhiteList:
sourceRange:
- foobar
- foobar
TCPMiddleware03:
inFlightConn:
amount: 42
TCPMiddleware02:
ipAllowList:
sourceRange:
- foobar
- foobar
serversTransports:
TCPServersTransport0:
dialTimeout: 42s
dialKeepAlive: 42s
dialTimeout: 42s
terminationDelay: 42s
tls:
serverName: foobar
@ -483,14 +501,14 @@ tcp:
- certFile: foobar
keyFile: foobar
peerCertURI: foobar
spiffe:
ids:
- foobar
- foobar
trustDomain: foobar
spiffe:
ids:
- foobar
- foobar
trustDomain: foobar
TCPServersTransport1:
dialTimeout: 42s
dialKeepAlive: 42s
dialTimeout: 42s
terminationDelay: 42s
tls:
serverName: foobar
@ -504,11 +522,11 @@ tcp:
- certFile: foobar
keyFile: foobar
peerCertURI: foobar
spiffe:
ids:
- foobar
- foobar
trustDomain: foobar
spiffe:
ids:
- foobar
- foobar
trustDomain: foobar
udp:
routers:
UDPRouter0:

View file

@ -160,6 +160,12 @@ spec:
can be accessed by client-side APIs, such as
JavaScript.
type: boolean
maxAge:
description: MaxAge indicates the number of seconds
until the cookie expires. When set to a negative
number, the cookie expires immediately. When
set to zero, the cookie never expires.
type: integer
name:
description: Name defines the Cookie name.
type: string
@ -189,6 +195,10 @@ spec:
- name
type: object
type: array
syntax:
description: 'Syntax defines the router''s rule syntax. More
info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rulesyntax'
type: string
required:
- kind
- match
@ -396,6 +406,10 @@ spec:
- port
type: object
type: array
syntax:
description: 'Syntax defines the router''s rule syntax. More
info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rulesyntax_1'
type: string
required:
- match
type: object
@ -750,6 +764,13 @@ spec:
items:
type: string
type: array
includedContentTypes:
description: IncludedContentTypes defines the list of content
types to compare the Content-Type header of the responses before
compressing.
items:
type: string
type: array
minResponseBodyBytes:
description: 'MinResponseBodyBytes defines the minimum amount
of bytes a response body must have to be compressed. Default:
@ -870,6 +891,12 @@ spec:
description: HTTPOnly defines whether the cookie can
be accessed by client-side APIs, such as JavaScript.
type: boolean
maxAge:
description: MaxAge indicates the number of seconds
until the cookie expires. When set to a negative
number, the cookie expires immediately. When set
to zero, the cookie never expires.
type: integer
name:
description: Name defines the Cookie name.
type: string
@ -912,6 +939,12 @@ spec:
This middleware delegates the request authentication to a Service.
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/forwardauth/'
properties:
addAuthCookiesToResponse:
description: AddAuthCookiesToResponse defines the list of cookies
to copy from the authentication server response to the response.
items:
type: string
type: array
address:
description: Address defines the authentication server address.
type: string
@ -2145,6 +2178,12 @@ spec:
description: HTTPOnly defines whether the cookie
can be accessed by client-side APIs, such as JavaScript.
type: boolean
maxAge:
description: MaxAge indicates the number of seconds
until the cookie expires. When set to a negative
number, the cookie expires immediately. When set
to zero, the cookie never expires.
type: integer
name:
description: Name defines the Cookie name.
type: string
@ -2236,6 +2275,12 @@ spec:
description: HTTPOnly defines whether the cookie can be
accessed by client-side APIs, such as JavaScript.
type: boolean
maxAge:
description: MaxAge indicates the number of seconds until
the cookie expires. When set to a negative number, the
cookie expires immediately. When set to zero, the cookie
never expires.
type: integer
name:
description: Name defines the Cookie name.
type: string
@ -2343,6 +2388,12 @@ spec:
description: HTTPOnly defines whether the cookie
can be accessed by client-side APIs, such as JavaScript.
type: boolean
maxAge:
description: MaxAge indicates the number of seconds
until the cookie expires. When set to a negative
number, the cookie expires immediately. When set
to zero, the cookie never expires.
type: integer
name:
description: Name defines the Cookie name.
type: string
@ -2382,6 +2433,12 @@ spec:
description: HTTPOnly defines whether the cookie can be
accessed by client-side APIs, such as JavaScript.
type: boolean
maxAge:
description: MaxAge indicates the number of seconds until
the cookie expires. When set to a negative number, the
cookie expires immediately. When set to zero, the cookie
never expires.
type: integer
name:
description: Name defines the Cookie name.
type: string

View file

@ -1,152 +1,164 @@
| `traefik/http/middlewares/Middleware00/addPrefix/prefix` | `foobar` |
| `traefik/http/middlewares/Middleware01/basicAuth/headerField` | `foobar` |
| `traefik/http/middlewares/Middleware01/basicAuth/realm` | `foobar` |
| `traefik/http/middlewares/Middleware01/basicAuth/removeHeader` | `true` |
| `traefik/http/middlewares/Middleware01/basicAuth/users/0` | `foobar` |
| `traefik/http/middlewares/Middleware01/basicAuth/users/1` | `foobar` |
| `traefik/http/middlewares/Middleware01/basicAuth/usersFile` | `foobar` |
| `traefik/http/middlewares/Middleware02/buffering/maxRequestBodyBytes` | `42` |
| `traefik/http/middlewares/Middleware02/buffering/maxResponseBodyBytes` | `42` |
| `traefik/http/middlewares/Middleware02/buffering/memRequestBodyBytes` | `42` |
| `traefik/http/middlewares/Middleware02/buffering/memResponseBodyBytes` | `42` |
| `traefik/http/middlewares/Middleware02/buffering/retryExpression` | `foobar` |
| `traefik/http/middlewares/Middleware03/chain/middlewares/0` | `foobar` |
| `traefik/http/middlewares/Middleware03/chain/middlewares/1` | `foobar` |
| `traefik/http/middlewares/Middleware04/circuitBreaker/checkPeriod` | `42s` |
| `traefik/http/middlewares/Middleware04/circuitBreaker/expression` | `foobar` |
| `traefik/http/middlewares/Middleware04/circuitBreaker/fallbackDuration` | `42s` |
| `traefik/http/middlewares/Middleware04/circuitBreaker/recoveryDuration` | `42s` |
| `traefik/http/middlewares/Middleware05/compress/excludedContentTypes/0` | `foobar` |
| `traefik/http/middlewares/Middleware05/compress/excludedContentTypes/1` | `foobar` |
| `traefik/http/middlewares/Middleware05/compress/minResponseBodyBytes` | `42` |
| `traefik/http/middlewares/Middleware06/contentType` | `` |
| `traefik/http/middlewares/Middleware07/digestAuth/headerField` | `foobar` |
| `traefik/http/middlewares/Middleware07/digestAuth/realm` | `foobar` |
| `traefik/http/middlewares/Middleware07/digestAuth/removeHeader` | `true` |
| `traefik/http/middlewares/Middleware07/digestAuth/users/0` | `foobar` |
| `traefik/http/middlewares/Middleware07/digestAuth/users/1` | `foobar` |
| `traefik/http/middlewares/Middleware07/digestAuth/usersFile` | `foobar` |
| `traefik/http/middlewares/Middleware08/errors/query` | `foobar` |
| `traefik/http/middlewares/Middleware08/errors/service` | `foobar` |
| `traefik/http/middlewares/Middleware08/errors/status/0` | `foobar` |
| `traefik/http/middlewares/Middleware08/errors/status/1` | `foobar` |
| `traefik/http/middlewares/Middleware09/forwardAuth/address` | `foobar` |
| `traefik/http/middlewares/Middleware09/forwardAuth/authRequestHeaders/0` | `foobar` |
| `traefik/http/middlewares/Middleware09/forwardAuth/authRequestHeaders/1` | `foobar` |
| `traefik/http/middlewares/Middleware09/forwardAuth/authResponseHeaders/0` | `foobar` |
| `traefik/http/middlewares/Middleware09/forwardAuth/authResponseHeaders/1` | `foobar` |
| `traefik/http/middlewares/Middleware09/forwardAuth/authResponseHeadersRegex` | `foobar` |
| `traefik/http/middlewares/Middleware09/forwardAuth/tls/ca` | `foobar` |
| `traefik/http/middlewares/Middleware09/forwardAuth/tls/cert` | `foobar` |
| `traefik/http/middlewares/Middleware09/forwardAuth/tls/insecureSkipVerify` | `true` |
| `traefik/http/middlewares/Middleware09/forwardAuth/tls/key` | `foobar` |
| `traefik/http/middlewares/Middleware09/forwardAuth/trustForwardHeader` | `true` |
| `traefik/http/middlewares/Middleware10/headers/accessControlAllowCredentials` | `true` |
| `traefik/http/middlewares/Middleware10/headers/accessControlAllowHeaders/0` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/accessControlAllowHeaders/1` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/accessControlAllowMethods/0` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/accessControlAllowMethods/1` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/accessControlAllowOriginList/0` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/accessControlAllowOriginList/1` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/accessControlAllowOriginListRegex/0` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/accessControlAllowOriginListRegex/1` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/accessControlExposeHeaders/0` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/accessControlExposeHeaders/1` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/accessControlMaxAge` | `42` |
| `traefik/http/middlewares/Middleware10/headers/addVaryHeader` | `true` |
| `traefik/http/middlewares/Middleware10/headers/allowedHosts/0` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/allowedHosts/1` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/browserXssFilter` | `true` |
| `traefik/http/middlewares/Middleware10/headers/contentSecurityPolicy` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/contentTypeNosniff` | `true` |
| `traefik/http/middlewares/Middleware10/headers/customBrowserXSSValue` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/customFrameOptionsValue` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/customRequestHeaders/name0` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/customRequestHeaders/name1` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/customResponseHeaders/name0` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/customResponseHeaders/name1` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/forceSTSHeader` | `true` |
| `traefik/http/middlewares/Middleware10/headers/frameDeny` | `true` |
| `traefik/http/middlewares/Middleware10/headers/hostsProxyHeaders/0` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/hostsProxyHeaders/1` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/isDevelopment` | `true` |
| `traefik/http/middlewares/Middleware10/headers/permissionsPolicy` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/publicKey` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/referrerPolicy` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/sslProxyHeaders/name0` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/sslProxyHeaders/name1` | `foobar` |
| `traefik/http/middlewares/Middleware10/headers/stsIncludeSubdomains` | `true` |
| `traefik/http/middlewares/Middleware10/headers/stsPreload` | `true` |
| `traefik/http/middlewares/Middleware10/headers/stsSeconds` | `42` |
| `traefik/http/middlewares/Middleware11/ipWhiteList/ipStrategy/depth` | `42` |
| `traefik/http/middlewares/Middleware11/ipWhiteList/ipStrategy/excludedIPs/0` | `foobar` |
| `traefik/http/middlewares/Middleware11/ipWhiteList/ipStrategy/excludedIPs/1` | `foobar` |
| `traefik/http/middlewares/Middleware11/ipWhiteList/sourceRange/0` | `foobar` |
| `traefik/http/middlewares/Middleware11/ipWhiteList/sourceRange/1` | `foobar` |
| `traefik/http/middlewares/Middleware12/ipAllowList/ipStrategy/depth` | `42` |
| `traefik/http/middlewares/Middleware12/ipAllowList/ipStrategy/excludedIPs/0` | `foobar` |
| `traefik/http/middlewares/Middleware12/ipAllowList/ipStrategy/excludedIPs/1` | `foobar` |
| `traefik/http/middlewares/Middleware12/ipAllowList/rejectStatusCode` | `404` |
| `traefik/http/middlewares/Middleware12/ipAllowList/sourceRange/0` | `foobar` |
| `traefik/http/middlewares/Middleware12/ipAllowList/sourceRange/1` | `foobar` |
| `traefik/http/middlewares/Middleware13/inFlightReq/amount` | `42` |
| `traefik/http/middlewares/Middleware13/inFlightReq/sourceCriterion/ipStrategy/depth` | `42` |
| `traefik/http/middlewares/Middleware13/inFlightReq/sourceCriterion/ipStrategy/excludedIPs/0` | `foobar` |
| `traefik/http/middlewares/Middleware13/inFlightReq/sourceCriterion/ipStrategy/excludedIPs/1` | `foobar` |
| `traefik/http/middlewares/Middleware13/inFlightReq/sourceCriterion/requestHeaderName` | `foobar` |
| `traefik/http/middlewares/Middleware13/inFlightReq/sourceCriterion/requestHost` | `true` |
| `traefik/http/middlewares/Middleware14/passTLSClientCert/info/issuer/commonName` | `true` |
| `traefik/http/middlewares/Middleware14/passTLSClientCert/info/issuer/country` | `true` |
| `traefik/http/middlewares/Middleware14/passTLSClientCert/info/issuer/domainComponent` | `true` |
| `traefik/http/middlewares/Middleware14/passTLSClientCert/info/issuer/locality` | `true` |
| `traefik/http/middlewares/Middleware14/passTLSClientCert/info/issuer/organization` | `true` |
| `traefik/http/middlewares/Middleware14/passTLSClientCert/info/issuer/province` | `true` |
| `traefik/http/middlewares/Middleware14/passTLSClientCert/info/issuer/serialNumber` | `true` |
| `traefik/http/middlewares/Middleware14/passTLSClientCert/info/notAfter` | `true` |
| `traefik/http/middlewares/Middleware14/passTLSClientCert/info/notBefore` | `true` |
| `traefik/http/middlewares/Middleware14/passTLSClientCert/info/sans` | `true` |
| `traefik/http/middlewares/Middleware14/passTLSClientCert/info/serialNumber` | `true` |
| `traefik/http/middlewares/Middleware14/passTLSClientCert/info/subject/commonName` | `true` |
| `traefik/http/middlewares/Middleware14/passTLSClientCert/info/subject/country` | `true` |
| `traefik/http/middlewares/Middleware14/passTLSClientCert/info/subject/domainComponent` | `true` |
| `traefik/http/middlewares/Middleware14/passTLSClientCert/info/subject/locality` | `true` |
| `traefik/http/middlewares/Middleware14/passTLSClientCert/info/subject/organization` | `true` |
| `traefik/http/middlewares/Middleware14/passTLSClientCert/info/subject/organizationalUnit` | `true` |
| `traefik/http/middlewares/Middleware14/passTLSClientCert/info/subject/province` | `true` |
| `traefik/http/middlewares/Middleware14/passTLSClientCert/info/subject/serialNumber` | `true` |
| `traefik/http/middlewares/Middleware14/passTLSClientCert/pem` | `true` |
| `traefik/http/middlewares/Middleware15/plugin/PluginConf/foo` | `bar` |
| `traefik/http/middlewares/Middleware16/rateLimit/average` | `42` |
| `traefik/http/middlewares/Middleware16/rateLimit/burst` | `42` |
| `traefik/http/middlewares/Middleware16/rateLimit/period` | `42s` |
| `traefik/http/middlewares/Middleware16/rateLimit/sourceCriterion/ipStrategy/depth` | `42` |
| `traefik/http/middlewares/Middleware16/rateLimit/sourceCriterion/ipStrategy/excludedIPs/0` | `foobar` |
| `traefik/http/middlewares/Middleware16/rateLimit/sourceCriterion/ipStrategy/excludedIPs/1` | `foobar` |
| `traefik/http/middlewares/Middleware16/rateLimit/sourceCriterion/requestHeaderName` | `foobar` |
| `traefik/http/middlewares/Middleware16/rateLimit/sourceCriterion/requestHost` | `true` |
| `traefik/http/middlewares/Middleware17/redirectRegex/permanent` | `true` |
| `traefik/http/middlewares/Middleware17/redirectRegex/regex` | `foobar` |
| `traefik/http/middlewares/Middleware17/redirectRegex/replacement` | `foobar` |
| `traefik/http/middlewares/Middleware18/redirectScheme/permanent` | `true` |
| `traefik/http/middlewares/Middleware18/redirectScheme/port` | `foobar` |
| `traefik/http/middlewares/Middleware18/redirectScheme/scheme` | `foobar` |
| `traefik/http/middlewares/Middleware19/replacePath/path` | `foobar` |
| `traefik/http/middlewares/Middleware20/replacePathRegex/regex` | `foobar` |
| `traefik/http/middlewares/Middleware20/replacePathRegex/replacement` | `foobar` |
| `traefik/http/middlewares/Middleware21/retry/attempts` | `42` |
| `traefik/http/middlewares/Middleware21/retry/initialInterval` | `42s` |
| `traefik/http/middlewares/Middleware22/stripPrefix/prefixes/0` | `foobar` |
| `traefik/http/middlewares/Middleware22/stripPrefix/prefixes/1` | `foobar` |
| `traefik/http/middlewares/Middleware23/stripPrefixRegex/regex/0` | `foobar` |
| `traefik/http/middlewares/Middleware23/stripPrefixRegex/regex/1` | `foobar` |
| `traefik/http/middlewares/Middleware24/grpcWeb/allowOrigins/0` | `foobar` |
| `traefik/http/middlewares/Middleware24/grpcWeb/allowOrigins/1` | `foobar` |
<!--
CODE GENERATED AUTOMATICALLY
THIS FILE MUST NOT BE EDITED BY HAND
-->
| `traefik/http/middlewares/Middleware01/addPrefix/prefix` | `foobar` |
| `traefik/http/middlewares/Middleware02/basicAuth/headerField` | `foobar` |
| `traefik/http/middlewares/Middleware02/basicAuth/realm` | `foobar` |
| `traefik/http/middlewares/Middleware02/basicAuth/removeHeader` | `true` |
| `traefik/http/middlewares/Middleware02/basicAuth/users/0` | `foobar` |
| `traefik/http/middlewares/Middleware02/basicAuth/users/1` | `foobar` |
| `traefik/http/middlewares/Middleware02/basicAuth/usersFile` | `foobar` |
| `traefik/http/middlewares/Middleware03/buffering/maxRequestBodyBytes` | `42` |
| `traefik/http/middlewares/Middleware03/buffering/maxResponseBodyBytes` | `42` |
| `traefik/http/middlewares/Middleware03/buffering/memRequestBodyBytes` | `42` |
| `traefik/http/middlewares/Middleware03/buffering/memResponseBodyBytes` | `42` |
| `traefik/http/middlewares/Middleware03/buffering/retryExpression` | `foobar` |
| `traefik/http/middlewares/Middleware04/chain/middlewares/0` | `foobar` |
| `traefik/http/middlewares/Middleware04/chain/middlewares/1` | `foobar` |
| `traefik/http/middlewares/Middleware05/circuitBreaker/checkPeriod` | `42s` |
| `traefik/http/middlewares/Middleware05/circuitBreaker/expression` | `foobar` |
| `traefik/http/middlewares/Middleware05/circuitBreaker/fallbackDuration` | `42s` |
| `traefik/http/middlewares/Middleware05/circuitBreaker/recoveryDuration` | `42s` |
| `traefik/http/middlewares/Middleware06/compress/excludedContentTypes/0` | `foobar` |
| `traefik/http/middlewares/Middleware06/compress/excludedContentTypes/1` | `foobar` |
| `traefik/http/middlewares/Middleware06/compress/includedContentTypes/0` | `foobar` |
| `traefik/http/middlewares/Middleware06/compress/includedContentTypes/1` | `foobar` |
| `traefik/http/middlewares/Middleware06/compress/minResponseBodyBytes` | `42` |
| `traefik/http/middlewares/Middleware07/contentType` | `` |
| `traefik/http/middlewares/Middleware08/digestAuth/headerField` | `foobar` |
| `traefik/http/middlewares/Middleware08/digestAuth/realm` | `foobar` |
| `traefik/http/middlewares/Middleware08/digestAuth/removeHeader` | `true` |
| `traefik/http/middlewares/Middleware08/digestAuth/users/0` | `foobar` |
| `traefik/http/middlewares/Middleware08/digestAuth/users/1` | `foobar` |
| `traefik/http/middlewares/Middleware08/digestAuth/usersFile` | `foobar` |
| `traefik/http/middlewares/Middleware09/errors/query` | `foobar` |
| `traefik/http/middlewares/Middleware09/errors/service` | `foobar` |
| `traefik/http/middlewares/Middleware09/errors/status/0` | `foobar` |
| `traefik/http/middlewares/Middleware09/errors/status/1` | `foobar` |
| `traefik/http/middlewares/Middleware10/forwardAuth/addAuthCookiesToResponse/0` | `foobar` |
| `traefik/http/middlewares/Middleware10/forwardAuth/addAuthCookiesToResponse/1` | `foobar` |
| `traefik/http/middlewares/Middleware10/forwardAuth/address` | `foobar` |
| `traefik/http/middlewares/Middleware10/forwardAuth/authRequestHeaders/0` | `foobar` |
| `traefik/http/middlewares/Middleware10/forwardAuth/authRequestHeaders/1` | `foobar` |
| `traefik/http/middlewares/Middleware10/forwardAuth/authResponseHeaders/0` | `foobar` |
| `traefik/http/middlewares/Middleware10/forwardAuth/authResponseHeaders/1` | `foobar` |
| `traefik/http/middlewares/Middleware10/forwardAuth/authResponseHeadersRegex` | `foobar` |
| `traefik/http/middlewares/Middleware10/forwardAuth/tls/ca` | `foobar` |
| `traefik/http/middlewares/Middleware10/forwardAuth/tls/cert` | `foobar` |
| `traefik/http/middlewares/Middleware10/forwardAuth/tls/insecureSkipVerify` | `true` |
| `traefik/http/middlewares/Middleware10/forwardAuth/tls/key` | `foobar` |
| `traefik/http/middlewares/Middleware10/forwardAuth/trustForwardHeader` | `true` |
| `traefik/http/middlewares/Middleware11/grpcWeb/allowOrigins/0` | `foobar` |
| `traefik/http/middlewares/Middleware11/grpcWeb/allowOrigins/1` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/accessControlAllowCredentials` | `true` |
| `traefik/http/middlewares/Middleware12/headers/accessControlAllowHeaders/0` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/accessControlAllowHeaders/1` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/accessControlAllowMethods/0` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/accessControlAllowMethods/1` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/accessControlAllowOriginList/0` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/accessControlAllowOriginList/1` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/accessControlAllowOriginListRegex/0` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/accessControlAllowOriginListRegex/1` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/accessControlExposeHeaders/0` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/accessControlExposeHeaders/1` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/accessControlMaxAge` | `42` |
| `traefik/http/middlewares/Middleware12/headers/addVaryHeader` | `true` |
| `traefik/http/middlewares/Middleware12/headers/allowedHosts/0` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/allowedHosts/1` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/browserXssFilter` | `true` |
| `traefik/http/middlewares/Middleware12/headers/contentSecurityPolicy` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/contentTypeNosniff` | `true` |
| `traefik/http/middlewares/Middleware12/headers/customBrowserXSSValue` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/customFrameOptionsValue` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/customRequestHeaders/name0` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/customRequestHeaders/name1` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/customResponseHeaders/name0` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/customResponseHeaders/name1` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/forceSTSHeader` | `true` |
| `traefik/http/middlewares/Middleware12/headers/frameDeny` | `true` |
| `traefik/http/middlewares/Middleware12/headers/hostsProxyHeaders/0` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/hostsProxyHeaders/1` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/isDevelopment` | `true` |
| `traefik/http/middlewares/Middleware12/headers/permissionsPolicy` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/publicKey` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/referrerPolicy` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/sslProxyHeaders/name0` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/sslProxyHeaders/name1` | `foobar` |
| `traefik/http/middlewares/Middleware12/headers/stsIncludeSubdomains` | `true` |
| `traefik/http/middlewares/Middleware12/headers/stsPreload` | `true` |
| `traefik/http/middlewares/Middleware12/headers/stsSeconds` | `42` |
| `traefik/http/middlewares/Middleware13/ipAllowList/ipStrategy/depth` | `42` |
| `traefik/http/middlewares/Middleware13/ipAllowList/ipStrategy/excludedIPs/0` | `foobar` |
| `traefik/http/middlewares/Middleware13/ipAllowList/ipStrategy/excludedIPs/1` | `foobar` |
| `traefik/http/middlewares/Middleware13/ipAllowList/rejectStatusCode` | `42` |
| `traefik/http/middlewares/Middleware13/ipAllowList/sourceRange/0` | `foobar` |
| `traefik/http/middlewares/Middleware13/ipAllowList/sourceRange/1` | `foobar` |
| `traefik/http/middlewares/Middleware14/ipWhiteList/ipStrategy/depth` | `42` |
| `traefik/http/middlewares/Middleware14/ipWhiteList/ipStrategy/excludedIPs/0` | `foobar` |
| `traefik/http/middlewares/Middleware14/ipWhiteList/ipStrategy/excludedIPs/1` | `foobar` |
| `traefik/http/middlewares/Middleware14/ipWhiteList/sourceRange/0` | `foobar` |
| `traefik/http/middlewares/Middleware14/ipWhiteList/sourceRange/1` | `foobar` |
| `traefik/http/middlewares/Middleware15/inFlightReq/amount` | `42` |
| `traefik/http/middlewares/Middleware15/inFlightReq/sourceCriterion/ipStrategy/depth` | `42` |
| `traefik/http/middlewares/Middleware15/inFlightReq/sourceCriterion/ipStrategy/excludedIPs/0` | `foobar` |
| `traefik/http/middlewares/Middleware15/inFlightReq/sourceCriterion/ipStrategy/excludedIPs/1` | `foobar` |
| `traefik/http/middlewares/Middleware15/inFlightReq/sourceCriterion/requestHeaderName` | `foobar` |
| `traefik/http/middlewares/Middleware15/inFlightReq/sourceCriterion/requestHost` | `true` |
| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/issuer/commonName` | `true` |
| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/issuer/country` | `true` |
| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/issuer/domainComponent` | `true` |
| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/issuer/locality` | `true` |
| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/issuer/organization` | `true` |
| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/issuer/province` | `true` |
| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/issuer/serialNumber` | `true` |
| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/notAfter` | `true` |
| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/notBefore` | `true` |
| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/sans` | `true` |
| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/serialNumber` | `true` |
| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/subject/commonName` | `true` |
| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/subject/country` | `true` |
| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/subject/domainComponent` | `true` |
| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/subject/locality` | `true` |
| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/subject/organization` | `true` |
| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/subject/organizationalUnit` | `true` |
| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/subject/province` | `true` |
| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/subject/serialNumber` | `true` |
| `traefik/http/middlewares/Middleware16/passTLSClientCert/pem` | `true` |
| `traefik/http/middlewares/Middleware17/plugin/PluginConf0/name0` | `foobar` |
| `traefik/http/middlewares/Middleware17/plugin/PluginConf0/name1` | `foobar` |
| `traefik/http/middlewares/Middleware17/plugin/PluginConf1/name0` | `foobar` |
| `traefik/http/middlewares/Middleware17/plugin/PluginConf1/name1` | `foobar` |
| `traefik/http/middlewares/Middleware18/rateLimit/average` | `42` |
| `traefik/http/middlewares/Middleware18/rateLimit/burst` | `42` |
| `traefik/http/middlewares/Middleware18/rateLimit/period` | `42s` |
| `traefik/http/middlewares/Middleware18/rateLimit/sourceCriterion/ipStrategy/depth` | `42` |
| `traefik/http/middlewares/Middleware18/rateLimit/sourceCriterion/ipStrategy/excludedIPs/0` | `foobar` |
| `traefik/http/middlewares/Middleware18/rateLimit/sourceCriterion/ipStrategy/excludedIPs/1` | `foobar` |
| `traefik/http/middlewares/Middleware18/rateLimit/sourceCriterion/requestHeaderName` | `foobar` |
| `traefik/http/middlewares/Middleware18/rateLimit/sourceCriterion/requestHost` | `true` |
| `traefik/http/middlewares/Middleware19/redirectRegex/permanent` | `true` |
| `traefik/http/middlewares/Middleware19/redirectRegex/regex` | `foobar` |
| `traefik/http/middlewares/Middleware19/redirectRegex/replacement` | `foobar` |
| `traefik/http/middlewares/Middleware20/redirectScheme/permanent` | `true` |
| `traefik/http/middlewares/Middleware20/redirectScheme/port` | `foobar` |
| `traefik/http/middlewares/Middleware20/redirectScheme/scheme` | `foobar` |
| `traefik/http/middlewares/Middleware21/replacePath/path` | `foobar` |
| `traefik/http/middlewares/Middleware22/replacePathRegex/regex` | `foobar` |
| `traefik/http/middlewares/Middleware22/replacePathRegex/replacement` | `foobar` |
| `traefik/http/middlewares/Middleware23/retry/attempts` | `42` |
| `traefik/http/middlewares/Middleware23/retry/initialInterval` | `42s` |
| `traefik/http/middlewares/Middleware24/stripPrefix/prefixes/0` | `foobar` |
| `traefik/http/middlewares/Middleware24/stripPrefix/prefixes/1` | `foobar` |
| `traefik/http/middlewares/Middleware25/stripPrefixRegex/regex/0` | `foobar` |
| `traefik/http/middlewares/Middleware25/stripPrefixRegex/regex/1` | `foobar` |
| `traefik/http/routers/Router0/entryPoints/0` | `foobar` |
| `traefik/http/routers/Router0/entryPoints/1` | `foobar` |
| `traefik/http/routers/Router0/middlewares/0` | `foobar` |
| `traefik/http/routers/Router0/middlewares/1` | `foobar` |
| `traefik/http/routers/Router0/priority` | `42` |
| `traefik/http/routers/Router0/rule` | `foobar` |
| `traefik/http/routers/Router0/ruleSyntax` | `foobar` |
| `traefik/http/routers/Router0/service` | `foobar` |
| `traefik/http/routers/Router0/tls/certResolver` | `foobar` |
| `traefik/http/routers/Router0/tls/domains/0/main` | `foobar` |
@ -162,6 +174,7 @@
| `traefik/http/routers/Router1/middlewares/1` | `foobar` |
| `traefik/http/routers/Router1/priority` | `42` |
| `traefik/http/routers/Router1/rule` | `foobar` |
| `traefik/http/routers/Router1/ruleSyntax` | `foobar` |
| `traefik/http/routers/Router1/service` | `foobar` |
| `traefik/http/routers/Router1/tls/certResolver` | `foobar` |
| `traefik/http/routers/Router1/tls/domains/0/main` | `foobar` |
@ -209,55 +222,62 @@
| `traefik/http/serversTransports/ServersTransport1/spiffe/ids/0` | `foobar` |
| `traefik/http/serversTransports/ServersTransport1/spiffe/ids/1` | `foobar` |
| `traefik/http/serversTransports/ServersTransport1/spiffe/trustDomain` | `foobar` |
| `traefik/http/services/Service01/loadBalancer/healthCheck/followRedirects` | `true` |
| `traefik/http/services/Service01/loadBalancer/healthCheck/headers/name0` | `foobar` |
| `traefik/http/services/Service01/loadBalancer/healthCheck/headers/name1` | `foobar` |
| `traefik/http/services/Service01/loadBalancer/healthCheck/hostname` | `foobar` |
| `traefik/http/services/Service01/loadBalancer/healthCheck/interval` | `42s` |
| `traefik/http/services/Service01/loadBalancer/healthCheck/method` | `foobar` |
| `traefik/http/services/Service01/loadBalancer/healthCheck/mode` | `foobar` |
| `traefik/http/services/Service01/loadBalancer/healthCheck/path` | `foobar` |
| `traefik/http/services/Service01/loadBalancer/healthCheck/port` | `42` |
| `traefik/http/services/Service01/loadBalancer/healthCheck/scheme` | `foobar` |
| `traefik/http/services/Service01/loadBalancer/healthCheck/status` | `42` |
| `traefik/http/services/Service01/loadBalancer/healthCheck/timeout` | `42s` |
| `traefik/http/services/Service01/loadBalancer/passHostHeader` | `true` |
| `traefik/http/services/Service01/loadBalancer/responseForwarding/flushInterval` | `42s` |
| `traefik/http/services/Service01/loadBalancer/servers/0/url` | `foobar` |
| `traefik/http/services/Service01/loadBalancer/servers/1/url` | `foobar` |
| `traefik/http/services/Service01/loadBalancer/serversTransport` | `foobar` |
| `traefik/http/services/Service01/loadBalancer/sticky/cookie/httpOnly` | `true` |
| `traefik/http/services/Service01/loadBalancer/sticky/cookie/name` | `foobar` |
| `traefik/http/services/Service01/loadBalancer/sticky/cookie/sameSite` | `foobar` |
| `traefik/http/services/Service01/loadBalancer/sticky/cookie/secure` | `true` |
| `traefik/http/services/Service02/mirroring/healthCheck` | `` |
| `traefik/http/services/Service02/mirroring/maxBodySize` | `42` |
| `traefik/http/services/Service02/mirroring/mirrors/0/name` | `foobar` |
| `traefik/http/services/Service02/mirroring/mirrors/0/percent` | `42` |
| `traefik/http/services/Service02/mirroring/mirrors/1/name` | `foobar` |
| `traefik/http/services/Service02/mirroring/mirrors/1/percent` | `42` |
| `traefik/http/services/Service02/mirroring/service` | `foobar` |
| `traefik/http/services/Service03/weighted/healthCheck` | `` |
| `traefik/http/services/Service03/weighted/services/0/name` | `foobar` |
| `traefik/http/services/Service03/weighted/services/0/weight` | `42` |
| `traefik/http/services/Service03/weighted/services/1/name` | `foobar` |
| `traefik/http/services/Service03/weighted/services/1/weight` | `42` |
| `traefik/http/services/Service03/weighted/sticky/cookie/httpOnly` | `true` |
| `traefik/http/services/Service03/weighted/sticky/cookie/name` | `foobar` |
| `traefik/http/services/Service03/weighted/sticky/cookie/sameSite` | `foobar` |
| `traefik/http/services/Service03/weighted/sticky/cookie/secure` | `true` |
| `traefik/http/services/Service04/failover/fallback` | `foobar` |
| `traefik/http/services/Service04/failover/healthCheck` | `` |
| `traefik/http/services/Service04/failover/service` | `foobar` |
| `traefik/tcp/middlewares/TCPMiddleware00/ipAllowList/sourceRange/0` | `foobar` |
| `traefik/tcp/middlewares/TCPMiddleware00/ipAllowList/sourceRange/1` | `foobar` |
| `traefik/tcp/middlewares/TCPMiddleware01/inFlightConn/amount` | `42` |
| `traefik/http/services/Service01/failover/fallback` | `foobar` |
| `traefik/http/services/Service01/failover/healthCheck` | `` |
| `traefik/http/services/Service01/failover/service` | `foobar` |
| `traefik/http/services/Service02/loadBalancer/healthCheck/followRedirects` | `true` |
| `traefik/http/services/Service02/loadBalancer/healthCheck/headers/name0` | `foobar` |
| `traefik/http/services/Service02/loadBalancer/healthCheck/headers/name1` | `foobar` |
| `traefik/http/services/Service02/loadBalancer/healthCheck/hostname` | `foobar` |
| `traefik/http/services/Service02/loadBalancer/healthCheck/interval` | `42s` |
| `traefik/http/services/Service02/loadBalancer/healthCheck/method` | `foobar` |
| `traefik/http/services/Service02/loadBalancer/healthCheck/mode` | `foobar` |
| `traefik/http/services/Service02/loadBalancer/healthCheck/path` | `foobar` |
| `traefik/http/services/Service02/loadBalancer/healthCheck/port` | `42` |
| `traefik/http/services/Service02/loadBalancer/healthCheck/scheme` | `foobar` |
| `traefik/http/services/Service02/loadBalancer/healthCheck/status` | `42` |
| `traefik/http/services/Service02/loadBalancer/healthCheck/timeout` | `42s` |
| `traefik/http/services/Service02/loadBalancer/passHostHeader` | `true` |
| `traefik/http/services/Service02/loadBalancer/responseForwarding/flushInterval` | `42s` |
| `traefik/http/services/Service02/loadBalancer/servers/0/url` | `foobar` |
| `traefik/http/services/Service02/loadBalancer/servers/0/weight` | `42` |
| `traefik/http/services/Service02/loadBalancer/servers/1/url` | `foobar` |
| `traefik/http/services/Service02/loadBalancer/servers/1/weight` | `42` |
| `traefik/http/services/Service02/loadBalancer/serversTransport` | `foobar` |
| `traefik/http/services/Service02/loadBalancer/sticky/cookie/httpOnly` | `true` |
| `traefik/http/services/Service02/loadBalancer/sticky/cookie/maxAge` | `42` |
| `traefik/http/services/Service02/loadBalancer/sticky/cookie/name` | `foobar` |
| `traefik/http/services/Service02/loadBalancer/sticky/cookie/sameSite` | `foobar` |
| `traefik/http/services/Service02/loadBalancer/sticky/cookie/secure` | `true` |
| `traefik/http/services/Service03/mirroring/healthCheck` | `` |
| `traefik/http/services/Service03/mirroring/maxBodySize` | `42` |
| `traefik/http/services/Service03/mirroring/mirrors/0/name` | `foobar` |
| `traefik/http/services/Service03/mirroring/mirrors/0/percent` | `42` |
| `traefik/http/services/Service03/mirroring/mirrors/1/name` | `foobar` |
| `traefik/http/services/Service03/mirroring/mirrors/1/percent` | `42` |
| `traefik/http/services/Service03/mirroring/service` | `foobar` |
| `traefik/http/services/Service04/weighted/healthCheck` | `` |
| `traefik/http/services/Service04/weighted/services/0/name` | `foobar` |
| `traefik/http/services/Service04/weighted/services/0/weight` | `42` |
| `traefik/http/services/Service04/weighted/services/1/name` | `foobar` |
| `traefik/http/services/Service04/weighted/services/1/weight` | `42` |
| `traefik/http/services/Service04/weighted/sticky/cookie/httpOnly` | `true` |
| `traefik/http/services/Service04/weighted/sticky/cookie/maxAge` | `42` |
| `traefik/http/services/Service04/weighted/sticky/cookie/name` | `foobar` |
| `traefik/http/services/Service04/weighted/sticky/cookie/sameSite` | `foobar` |
| `traefik/http/services/Service04/weighted/sticky/cookie/secure` | `true` |
| `traefik/tcp/middlewares/TCPMiddleware01/ipAllowList/sourceRange/0` | `foobar` |
| `traefik/tcp/middlewares/TCPMiddleware01/ipAllowList/sourceRange/1` | `foobar` |
| `traefik/tcp/middlewares/TCPMiddleware02/ipWhiteList/sourceRange/0` | `foobar` |
| `traefik/tcp/middlewares/TCPMiddleware02/ipWhiteList/sourceRange/1` | `foobar` |
| `traefik/tcp/middlewares/TCPMiddleware03/inFlightConn/amount` | `42` |
| `traefik/tcp/routers/TCPRouter0/entryPoints/0` | `foobar` |
| `traefik/tcp/routers/TCPRouter0/entryPoints/1` | `foobar` |
| `traefik/tcp/routers/TCPRouter0/middlewares/0` | `foobar` |
| `traefik/tcp/routers/TCPRouter0/middlewares/1` | `foobar` |
| `traefik/tcp/routers/TCPRouter0/priority` | `42` |
| `traefik/tcp/routers/TCPRouter0/rule` | `foobar` |
| `traefik/tcp/routers/TCPRouter0/ruleSyntax` | `foobar` |
| `traefik/tcp/routers/TCPRouter0/service` | `foobar` |
| `traefik/tcp/routers/TCPRouter0/tls/certResolver` | `foobar` |
| `traefik/tcp/routers/TCPRouter0/tls/domains/0/main` | `foobar` |
@ -274,6 +294,7 @@
| `traefik/tcp/routers/TCPRouter1/middlewares/1` | `foobar` |
| `traefik/tcp/routers/TCPRouter1/priority` | `42` |
| `traefik/tcp/routers/TCPRouter1/rule` | `foobar` |
| `traefik/tcp/routers/TCPRouter1/ruleSyntax` | `foobar` |
| `traefik/tcp/routers/TCPRouter1/service` | `foobar` |
| `traefik/tcp/routers/TCPRouter1/tls/certResolver` | `foobar` |
| `traefik/tcp/routers/TCPRouter1/tls/domains/0/main` | `foobar` |
@ -286,9 +307,6 @@
| `traefik/tcp/routers/TCPRouter1/tls/passthrough` | `true` |
| `traefik/tcp/serversTransports/TCPServersTransport0/dialKeepAlive` | `42s` |
| `traefik/tcp/serversTransports/TCPServersTransport0/dialTimeout` | `42s` |
| `traefik/tcp/serversTransports/TCPServersTransport0/spiffe/ids/0` | `foobar` |
| `traefik/tcp/serversTransports/TCPServersTransport0/spiffe/ids/1` | `foobar` |
| `traefik/tcp/serversTransports/TCPServersTransport0/spiffe/trustDomain` | `foobar` |
| `traefik/tcp/serversTransports/TCPServersTransport0/terminationDelay` | `42s` |
| `traefik/tcp/serversTransports/TCPServersTransport0/tls/certificates/0/certFile` | `foobar` |
| `traefik/tcp/serversTransports/TCPServersTransport0/tls/certificates/0/keyFile` | `foobar` |
@ -299,11 +317,11 @@
| `traefik/tcp/serversTransports/TCPServersTransport0/tls/rootCAs/0` | `foobar` |
| `traefik/tcp/serversTransports/TCPServersTransport0/tls/rootCAs/1` | `foobar` |
| `traefik/tcp/serversTransports/TCPServersTransport0/tls/serverName` | `foobar` |
| `traefik/tcp/serversTransports/TCPServersTransport0/tls/spiffe/ids/0` | `foobar` |
| `traefik/tcp/serversTransports/TCPServersTransport0/tls/spiffe/ids/1` | `foobar` |
| `traefik/tcp/serversTransports/TCPServersTransport0/tls/spiffe/trustDomain` | `foobar` |
| `traefik/tcp/serversTransports/TCPServersTransport1/dialKeepAlive` | `42s` |
| `traefik/tcp/serversTransports/TCPServersTransport1/dialTimeout` | `42s` |
| `traefik/tcp/serversTransports/TCPServersTransport1/spiffe/ids/0` | `foobar` |
| `traefik/tcp/serversTransports/TCPServersTransport1/spiffe/ids/1` | `foobar` |
| `traefik/tcp/serversTransports/TCPServersTransport1/spiffe/trustDomain` | `foobar` |
| `traefik/tcp/serversTransports/TCPServersTransport1/terminationDelay` | `42s` |
| `traefik/tcp/serversTransports/TCPServersTransport1/tls/certificates/0/certFile` | `foobar` |
| `traefik/tcp/serversTransports/TCPServersTransport1/tls/certificates/0/keyFile` | `foobar` |
@ -314,6 +332,9 @@
| `traefik/tcp/serversTransports/TCPServersTransport1/tls/rootCAs/0` | `foobar` |
| `traefik/tcp/serversTransports/TCPServersTransport1/tls/rootCAs/1` | `foobar` |
| `traefik/tcp/serversTransports/TCPServersTransport1/tls/serverName` | `foobar` |
| `traefik/tcp/serversTransports/TCPServersTransport1/tls/spiffe/ids/0` | `foobar` |
| `traefik/tcp/serversTransports/TCPServersTransport1/tls/spiffe/ids/1` | `foobar` |
| `traefik/tcp/serversTransports/TCPServersTransport1/tls/spiffe/trustDomain` | `foobar` |
| `traefik/tcp/services/TCPService01/loadBalancer/proxyProtocol/version` | `42` |
| `traefik/tcp/services/TCPService01/loadBalancer/servers/0/address` | `foobar` |
| `traefik/tcp/services/TCPService01/loadBalancer/servers/0/tls` | `true` |

View file

@ -160,6 +160,12 @@ spec:
can be accessed by client-side APIs, such as
JavaScript.
type: boolean
maxAge:
description: MaxAge indicates the number of seconds
until the cookie expires. When set to a negative
number, the cookie expires immediately. When
set to zero, the cookie never expires.
type: integer
name:
description: Name defines the Cookie name.
type: string
@ -189,6 +195,10 @@ spec:
- name
type: object
type: array
syntax:
description: 'Syntax defines the router''s rule syntax. More
info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rulesyntax'
type: string
required:
- kind
- match

View file

@ -129,6 +129,10 @@ spec:
- port
type: object
type: array
syntax:
description: 'Syntax defines the router''s rule syntax. More
info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rulesyntax_1'
type: string
required:
- match
type: object

View file

@ -175,6 +175,13 @@ spec:
items:
type: string
type: array
includedContentTypes:
description: IncludedContentTypes defines the list of content
types to compare the Content-Type header of the responses before
compressing.
items:
type: string
type: array
minResponseBodyBytes:
description: 'MinResponseBodyBytes defines the minimum amount
of bytes a response body must have to be compressed. Default:
@ -295,6 +302,12 @@ spec:
description: HTTPOnly defines whether the cookie can
be accessed by client-side APIs, such as JavaScript.
type: boolean
maxAge:
description: MaxAge indicates the number of seconds
until the cookie expires. When set to a negative
number, the cookie expires immediately. When set
to zero, the cookie never expires.
type: integer
name:
description: Name defines the Cookie name.
type: string
@ -337,6 +350,12 @@ spec:
This middleware delegates the request authentication to a Service.
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/forwardauth/'
properties:
addAuthCookiesToResponse:
description: AddAuthCookiesToResponse defines the list of cookies
to copy from the authentication server response to the response.
items:
type: string
type: array
address:
description: Address defines the authentication server address.
type: string

View file

@ -134,6 +134,12 @@ spec:
description: HTTPOnly defines whether the cookie
can be accessed by client-side APIs, such as JavaScript.
type: boolean
maxAge:
description: MaxAge indicates the number of seconds
until the cookie expires. When set to a negative
number, the cookie expires immediately. When set
to zero, the cookie never expires.
type: integer
name:
description: Name defines the Cookie name.
type: string
@ -225,6 +231,12 @@ spec:
description: HTTPOnly defines whether the cookie can be
accessed by client-side APIs, such as JavaScript.
type: boolean
maxAge:
description: MaxAge indicates the number of seconds until
the cookie expires. When set to a negative number, the
cookie expires immediately. When set to zero, the cookie
never expires.
type: integer
name:
description: Name defines the Cookie name.
type: string
@ -332,6 +344,12 @@ spec:
description: HTTPOnly defines whether the cookie
can be accessed by client-side APIs, such as JavaScript.
type: boolean
maxAge:
description: MaxAge indicates the number of seconds
until the cookie expires. When set to a negative
number, the cookie expires immediately. When set
to zero, the cookie never expires.
type: integer
name:
description: Name defines the Cookie name.
type: string
@ -371,6 +389,12 @@ spec:
description: HTTPOnly defines whether the cookie can be
accessed by client-side APIs, such as JavaScript.
type: boolean
maxAge:
description: MaxAge indicates the number of seconds until
the cookie expires. When set to a negative number, the
cookie expires immediately. When set to zero, the cookie
never expires.
type: integer
name:
description: Name defines the Cookie name.
type: string

View file

@ -105,6 +105,9 @@ Activate TLS-ALPN-01 Challenge. (Default: ```true```)
`--certificatesresolvers.<name>.tailscale`:
Enables Tailscale certificate resolution. (Default: ```true```)
`--core.defaultrulesyntax`:
Defines the rule parser default syntax (v2 or v3) (Default: ```v3```)
`--entrypoints.<name>`:
Entry points definition. (Default: ```false```)

View file

@ -105,6 +105,9 @@ Activate TLS-ALPN-01 Challenge. (Default: ```true```)
`TRAEFIK_CERTIFICATESRESOLVERS_<NAME>_TAILSCALE`:
Enables Tailscale certificate resolution. (Default: ```true```)
`TRAEFIK_CORE_DEFAULTRULESYNTAX`:
Defines the rule parser default syntax (v2 or v3) (Default: ```v3```)
`TRAEFIK_ENTRYPOINTS_<NAME>`:
Entry points definition. (Default: ```false```)

View file

@ -1,3 +1,5 @@
## CODE GENERATED AUTOMATICALLY
## THIS FILE MUST NOT BE EDITED BY HAND
[global]
checkNewVersion = true
sendAnonymousUsage = true
@ -6,26 +8,21 @@
insecureSkipVerify = true
rootCAs = ["foobar", "foobar"]
maxIdleConnsPerHost = 42
[serversTransport.forwardingTimeouts]
dialTimeout = "42s"
responseHeaderTimeout = "42s"
idleConnTimeout = "42s"
readIdleTimeout = "42s"
pingTimeout = "42s"
[serversTransport.spiffe]
ids = ["foobar", "foobar"]
trustDomain = "foobar"
[tcpServersTransport]
dialTimeout = "42s"
dialKeepAlive = "42s"
dialTimeout = "42s"
terminationDelay = "42s"
[tcpServersTransport.tls]
insecureSkipVerify = true
rootCAs = ["foobar", "foobar"]
[tcpServersTransport.tls.spiffe]
ids = ["foobar", "foobar"]
trustDomain = "foobar"
@ -35,8 +32,8 @@
address = "foobar"
asDefault = true
[entryPoints.EntryPoint0.transport]
keepAliveMaxRequests = 42
keepAliveMaxTime = "42s"
keepAliveMaxRequests = 42
[entryPoints.EntryPoint0.transport.lifeCycle]
requestAcceptGraceTimeout = "42s"
graceTimeOut = "42s"
@ -80,31 +77,31 @@
[providers]
providersThrottleDuration = "42s"
[providers.docker]
constraints = "foobar"
watch = true
endpoint = "foobar"
defaultRule = "foobar"
exposedByDefault = true
useBindPortIP = true
network = "foobar"
httpClientTimeout = "42s"
constraints = "foobar"
allowEmptyServices = true
network = "foobar"
useBindPortIP = true
watch = true
defaultRule = "foobar"
endpoint = "foobar"
httpClientTimeout = "42s"
[providers.docker.tls]
ca = "foobar"
cert = "foobar"
key = "foobar"
insecureSkipVerify = true
[providers.swarm]
constraints = "foobar"
watch = true
endpoint = "foobar"
defaultRule = "foobar"
exposedByDefault = true
useBindPortIP = true
network = "foobar"
refreshSeconds = "42s"
httpClientTimeout = "42s"
constraints = "foobar"
allowEmptyServices = true
network = "foobar"
useBindPortIP = true
watch = true
defaultRule = "foobar"
endpoint = "foobar"
httpClientTimeout = "42s"
refreshSeconds = "42s"
[providers.swarm.tls]
ca = "foobar"
cert = "foobar"
@ -183,9 +180,9 @@
constraints = "foobar"
prefix = "foobar"
stale = true
namespaces = ["foobar", "foobar"]
exposedByDefault = true
refreshInterval = "42s"
namespaces = ["foobar", "foobar"]
[providers.nomad.endpoint]
address = "foobar"
region = "foobar"
@ -203,11 +200,11 @@
defaultRule = "foobar"
clusters = ["foobar", "foobar"]
autoDiscoverClusters = true
healthyTasksOnly = true
ecsAnywhere = true
region = "foobar"
accessKeyID = "foobar"
secretAccessKey = "foobar"
ecsAnywhere = true
healthyTasksOnly = true
[providers.consul]
rootKey = "foobar"
endpoints = ["foobar", "foobar"]
@ -265,14 +262,18 @@
key = "foobar"
insecureSkipVerify = true
[providers.plugin]
[providers.plugin.Descriptor0]
[providers.plugin.Descriptor1]
[providers.plugin.PluginConf0]
name0 = "foobar"
name1 = "foobar"
[providers.plugin.PluginConf1]
name0 = "foobar"
name1 = "foobar"
[api]
insecure = true
dashboard = true
debug = true
disabledashboardad = false
disableDashboardAd = true
[metrics]
[metrics.prometheus]
@ -283,8 +284,8 @@
entryPoint = "foobar"
manualRouting = true
[metrics.prometheus.headerLabels]
label1 = "foobar"
label2 = "foobar"
name0 = "foobar"
name1 = "foobar"
[metrics.datadog]
address = "foobar"
pushInterval = "42s"
@ -316,20 +317,19 @@
addEntryPointsLabels = true
addRoutersLabels = true
addServicesLabels = true
pushInterval = "42s"
path = "foobar"
explicitBoundaries = [42.0, 42.0]
explicitBoundaries = [42.0, 42.0]
insecure = true
path = "foobar"
pushInterval = "42s"
[metrics.openTelemetry.grpc]
[metrics.openTelemetry.headers]
name0 = "foobar"
name1 = "foobar"
[metrics.openTelemetry.tls]
ca = "foobar"
caOptional = true
cert = "foobar"
insecureSkipVerify = true
key = "foobar"
[metrics.openTelemetry.grpc]
insecureSkipVerify = true
[ping]
entryPoint = "foobar"
@ -342,8 +342,8 @@
noColor = true
filePath = "foobar"
maxSize = 42
maxBackups = 42
maxAge = 42
maxBackups = 42
compress = true
[accessLog]
@ -367,28 +367,29 @@
[tracing]
serviceName = "foobar"
sampleRate = 42
sampleRate = 42.0
[tracing.headers]
header1 = "foobar"
header2 = "foobar"
name0 = "foobar"
name1 = "foobar"
[tracing.globalAttributes]
attr1 = "foobar"
attr2 = "foobar"
[tracing.otlp.grpc]
endpoint = "foobar"
insecure = true
[tracing.otlp.grpc.tls]
ca = "foobar"
cert = "foobar"
key = "foobar"
insecureSkipVerify = true
[tracing.otlp.http]
endpoint = "foobar"
[tracing.otlp.http.tls]
ca = "foobar"
cert = "foobar"
key = "foobar"
insecureSkipVerify = true
name0 = "foobar"
name1 = "foobar"
[tracing.otlp]
[tracing.otlp.grpc]
endpoint = "foobar"
insecure = true
[tracing.otlp.grpc.tls]
ca = "foobar"
cert = "foobar"
key = "foobar"
insecureSkipVerify = true
[tracing.otlp.http]
endpoint = "foobar"
[tracing.otlp.http.tls]
ca = "foobar"
cert = "foobar"
key = "foobar"
insecureSkipVerify = true
[hostResolver]
cnameFlattening = true
@ -415,7 +416,27 @@
[certificatesResolvers.CertificateResolver0.acme.httpChallenge]
entryPoint = "foobar"
[certificatesResolvers.CertificateResolver0.acme.tlsChallenge]
[certificatesResolvers.CertificateResolver1.tailscale]
[certificatesResolvers.CertificateResolver0.tailscale]
[certificatesResolvers.CertificateResolver1]
[certificatesResolvers.CertificateResolver1.acme]
email = "foobar"
caServer = "foobar"
preferredChain = "foobar"
storage = "foobar"
keyType = "foobar"
certificatesDuration = 42
[certificatesResolvers.CertificateResolver1.acme.eab]
kid = "foobar"
hmacEncoded = "foobar"
[certificatesResolvers.CertificateResolver1.acme.dnsChallenge]
provider = "foobar"
delayBeforeCheck = "42s"
resolvers = ["foobar", "foobar"]
disablePropagationCheck = true
[certificatesResolvers.CertificateResolver1.acme.httpChallenge]
entryPoint = "foobar"
[certificatesResolvers.CertificateResolver1.acme.tlsChallenge]
[certificatesResolvers.CertificateResolver1.tailscale]
[experimental]
kubernetesGateway = true
@ -427,7 +448,13 @@
moduleName = "foobar"
version = "foobar"
[experimental.localPlugins]
[experimental.localPlugins.Descriptor0]
[experimental.localPlugins.LocalDescriptor0]
moduleName = "foobar"
[experimental.localPlugins.Descriptor1]
[experimental.localPlugins.LocalDescriptor1]
moduleName = "foobar"
[core]
defaultRuleSyntax = "foobar"
[spiffe]
workloadAPIAddr = "foobar"

View file

@ -1,7 +1,9 @@
## CODE GENERATED AUTOMATICALLY
## THIS FILE MUST NOT BE EDITED BY HAND
global:
checkNewVersion: true
sendAnonymousUsage: true
serversTransports:
serversTransport:
insecureSkipVerify: true
rootCAs:
- foobar
@ -11,16 +13,15 @@ serversTransports:
dialTimeout: 42s
responseHeaderTimeout: 42s
idleConnTimeout: 42s
readIdleTimeout: 42s
pingTimeout: 42s
spiffe:
ids:
- foobar
- foobar
trustDomain: foobar
tcpServersTransport:
dialTimeout: 42s
dialKeepAlive: 42s
dialTimeout: 42s
terminationDelay: 42s
tls:
insecureSkipVerify: true
rootCAs:
@ -36,8 +37,6 @@ entryPoints:
address: foobar
asDefault: true
transport:
keepAliveMaxRequests: 42
keepAliveMaxTime: 42s
lifeCycle:
requestAcceptGraceTimeout: 42s
graceTimeOut: 42s
@ -45,6 +44,8 @@ entryPoints:
readTimeout: 42s
writeTimeout: 42s
idleTimeout: 42s
keepAliveMaxTime: 42s
keepAliveMaxRequests: 42
proxyProtocol:
insecure: true
trustedIPs:
@ -56,7 +57,6 @@ entryPoints:
- foobar
- foobar
http:
encodeQuerySemicolons: true
redirections:
entryPoint:
to: foobar
@ -78,6 +78,7 @@ entryPoints:
sans:
- foobar
- foobar
encodeQuerySemicolons: true
http2:
maxConcurrentStreams: 42
http3:
@ -87,36 +88,36 @@ entryPoints:
providers:
providersThrottleDuration: 42s
docker:
exposedByDefault: true
constraints: foobar
allowEmptyServices: true
network: foobar
useBindPortIP: true
watch: true
endpoint: foobar
defaultRule: foobar
endpoint: foobar
tls:
ca: foobar
cert: foobar
key: foobar
insecureSkipVerify: true
exposedByDefault: true
useBindPortIP: true
network: foobar
httpClientTimeout: 42s
allowEmptyServices: true
swarm:
exposedByDefault: true
constraints: foobar
allowEmptyServices: true
network: foobar
useBindPortIP: true
watch: true
endpoint: foobar
defaultRule: foobar
endpoint: foobar
tls:
ca: foobar
cert: foobar
key: foobar
insecureSkipVerify: true
exposedByDefault: true
useBindPortIP: true
network: foobar
refreshSeconds: 42s
httpClientTimeout: 42s
allowEmptyServices: true
refreshSeconds: 42s
file:
directory: foobar
watch: true
@ -131,14 +132,14 @@ providers:
- foobar
labelSelector: foobar
ingressClass: foobar
throttleDuration: 42s
allowEmptyServices: true
allowExternalNameServices: true
disableIngressClassLookup: true
ingressEndpoint:
ip: foobar
hostname: foobar
publishedService: foobar
throttleDuration: 42s
allowEmptyServices: true
allowExternalNameServices: true
disableIngressClassLookup: true
kubernetesCRD:
endpoint: foobar
token: foobar
@ -165,6 +166,20 @@ providers:
insecure: true
consulCatalog:
constraints: foobar
endpoint:
address: foobar
scheme: foobar
datacenter: foobar
token: foobar
tls:
ca: foobar
cert: foobar
key: foobar
insecureSkipVerify: true
httpAuth:
username: foobar
password: foobar
endpointWaitTime: 42s
prefix: foobar
refreshInterval: 42s
requireConsistent: true
@ -179,40 +194,26 @@ providers:
namespaces:
- foobar
- foobar
endpoint:
address: foobar
scheme: foobar
datacenter: foobar
token: foobar
endpointWaitTime: 42s
tls:
ca: foobar
cert: foobar
key: foobar
insecureSkipVerify: true
httpAuth:
username: foobar
password: foobar
nomad:
defaultRule: foobar
constraints: foobar
prefix: foobar
stale: true
namespaces:
- foobar
- foobar
exposedByDefault: true
refreshInterval: 42s
endpoint:
address: foobar
region: foobar
token: foobar
endpointWaitTime: 42s
tls:
ca: foobar
cert: foobar
key: foobar
insecureSkipVerify: true
endpointWaitTime: 42s
prefix: foobar
stale: true
exposedByDefault: true
refreshInterval: 42s
namespaces:
- foobar
- foobar
ecs:
constraints: foobar
exposedByDefault: true
@ -222,37 +223,37 @@ providers:
- foobar
- foobar
autoDiscoverClusters: true
healthyTasksOnly: true
ecsAnywhere: true
region: foobar
accessKeyID: foobar
secretAccessKey: foobar
ecsAnywhere: true
healthyTasksOnly: true
consul:
rootKey: foobar
endpoints:
- foobar
- foobar
token: foobar
namespaces:
- foobar
- foobar
tls:
ca: foobar
cert: foobar
key: foobar
insecureSkipVerify: true
namespaces:
- foobar
- foobar
etcd:
rootKey: foobar
endpoints:
- foobar
- foobar
username: foobar
password: foobar
tls:
ca: foobar
cert: foobar
key: foobar
insecureSkipVerify: true
username: foobar
password: foobar
zooKeeper:
rootKey: foobar
endpoints:
@ -265,14 +266,14 @@ providers:
endpoints:
- foobar
- foobar
username: foobar
password: foobar
db: 42
tls:
ca: foobar
cert: foobar
key: foobar
insecureSkipVerify: true
username: foobar
password: foobar
db: 42
sentinel:
masterName: foobar
username: foobar
@ -294,13 +295,17 @@ providers:
key: foobar
insecureSkipVerify: true
plugin:
Descriptor0: {}
Descriptor1: {}
PluginConf0:
name0: foobar
name1: foobar
PluginConf1:
name0: foobar
name1: foobar
api:
insecure: true
dashboard: true
debug: true
disabledashboardad: false
disableDashboardAd: true
metrics:
prometheus:
buckets:
@ -312,8 +317,8 @@ metrics:
entryPoint: foobar
manualRouting: true
headerLabels:
label1: foobar
label2: foobar
name0: foobar
name1: foobar
datadog:
address: foobar
pushInterval: 42s
@ -341,6 +346,7 @@ metrics:
name0: foobar
name1: foobar
openTelemetry:
grpc: {}
address: foobar
addEntryPointsLabels: true
addRoutersLabels: true
@ -356,12 +362,9 @@ metrics:
pushInterval: 42s
tls:
ca: foobar
caOptional: true
cert: foobar
insecureSkipVerify: true
key: foobar
grpc: {}
insecureSkipVerify: true
ping:
entryPoint: foobar
manualRouting: true
@ -372,8 +375,8 @@ log:
noColor: true
filePath: foobar
maxSize: 42
maxBackups: 42
maxAge: 42
maxBackups: 42
compress: true
accessLog:
filePath: foobar
@ -397,13 +400,13 @@ accessLog:
bufferingSize: 42
tracing:
serviceName: foobar
sampleRate: 42
headers:
header1: foobar
header2: foobar
name0: foobar
name1: foobar
globalAttributes:
attr1: foobar
attr2: foobar
name0: foobar
name1: foobar
sampleRate: 42
otlp:
grpc:
endpoint: foobar
@ -429,13 +432,13 @@ certificatesResolvers:
acme:
email: foobar
caServer: foobar
certificatesDuration: 42
preferredChain: foobar
storage: foobar
keyType: foobar
eab:
kid: foobar
hmacEncoded: foobar
certificatesDuration: 42
dnsChallenge:
provider: foobar
delayBeforeCheck: 42s
@ -446,10 +449,30 @@ certificatesResolvers:
httpChallenge:
entryPoint: foobar
tlsChallenge: {}
tailscale: {}
CertificateResolver1:
acme:
email: foobar
caServer: foobar
preferredChain: foobar
storage: foobar
keyType: foobar
eab:
kid: foobar
hmacEncoded: foobar
certificatesDuration: 42
dnsChallenge:
provider: foobar
delayBeforeCheck: 42s
resolvers:
- foobar
- foobar
disablePropagationCheck: true
httpChallenge:
entryPoint: foobar
tlsChallenge: {}
tailscale: {}
experimental:
kubernetesGateway: true
plugins:
Descriptor0:
moduleName: foobar
@ -458,7 +481,12 @@ experimental:
moduleName: foobar
version: foobar
localPlugins:
Descriptor0:
LocalDescriptor0:
moduleName: foobar
Descriptor1:
LocalDescriptor1:
moduleName: foobar
kubernetesGateway: true
core:
defaultRuleSyntax: foobar
spiffe:
workloadAPIAddr: foobar

View file

@ -273,6 +273,14 @@ you'd add the tag `traefik.http.services.{name-of-your-choice}.loadbalancer.pass
traefik.http.services.myservice.loadbalancer.sticky.cookie.samesite=none
```
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.maxage`"
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
```yaml
traefik.http.services.myservice.loadbalancer.sticky.cookie.maxage=42
```
??? info "`traefik.http.services.<service_name>.loadbalancer.responseforwarding.flushinterval`"
See [response forwarding](../services/index.md#response-forwarding) for more information.

View file

@ -376,6 +376,14 @@ you'd add the label `traefik.http.services.<name-of-your-choice>.loadbalancer.pa
- "traefik.http.services.myservice.loadbalancer.sticky.cookie.samesite=none"
```
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.maxage`"
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
```yaml
- "traefik.http.services.myservice.loadbalancer.sticky.cookie.maxage=42"
```
??? info "`traefik.http.services.<service_name>.loadbalancer.responseforwarding.flushinterval`"
See [response forwarding](../services/index.md#response-forwarding) for more information.

View file

@ -275,6 +275,14 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa
traefik.http.services.myservice.loadbalancer.sticky.cookie.samesite=none
```
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.maxage`"
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
```yaml
traefik.http.services.myservice.loadbalancer.sticky.cookie.maxage=42
```
??? info "`traefik.http.services.<service_name>.loadbalancer.responseforwarding.flushinterval`"
See [response forwarding](../services/index.md#response-forwarding) for more information.

View file

@ -348,6 +348,7 @@ Register the `IngressRoute` [kind](../../reference/dynamic-configuration/kuberne
name: cookie
secure: true
sameSite: none
maxAge: 42
strategy: RoundRobin
weight: 10
nativeLB: true # [11]

View file

@ -351,6 +351,14 @@ which in turn will create the resulting routers, services, handlers, etc.
traefik.ingress.kubernetes.io/service.sticky.cookie.httponly: "true"
```
??? info "`traefik.ingress.kubernetes.io/service.sticky.cookie.maxage`"
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
```yaml
traefik.ingress.kubernetes.io/service.sticky.cookie.maxage: 42
```
## Path Types on Kubernetes 1.18+
If the Kubernetes cluster version is 1.18+,

View file

@ -244,6 +244,14 @@ A Story of key & values
|-----------------------------------------------------------------------|--------|
| `traefik/http/services/myservice/loadbalancer/sticky/cookie/samesite` | `none` |
??? info "`traefik/http/services/<service_name>/loadbalancer/sticky/cookie/maxage`"
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
| Key (Path) | Value |
|---------------------------------------------------------------------|-------|
| `traefik/http/services/myservice/loadbalancer/sticky/cookie/maxage` | `42` |
??? info "`traefik/http/services/<service_name>/loadbalancer/responseforwarding/flushinterval`"
See [response forwarding](../services/index.md#response-forwarding) for more information.
@ -306,6 +314,12 @@ A Story of key & values
|------------------------------------------------------------------------|--------|
| `traefik/http/services/<service_name>/weighted/sticky/cookie/httpOnly` | `true` |
??? info "`traefik/http/services/<service_name>/weighted/sticky/cookie/maxage`"
| Key (Path) | Value |
|----------------------------------------------------------------------|-------|
| `traefik/http/services/<service_name>/weighted/sticky/cookie/maxage` | `42` |
### Middleware
More information about available middlewares in the dedicated [middlewares section](../../middlewares/overview.md).

View file

@ -265,6 +265,14 @@ you'd add the tag `traefik.http.services.{name-of-your-choice}.loadbalancer.pass
traefik.http.services.myservice.loadbalancer.sticky.cookie.samesite=none
```
??? info "`traefik.http.services.<service_name>.loadbalancer.sticky.cookie.maxage`"
See [sticky sessions](../services/index.md#sticky-sessions) for more information.
```yaml
traefik.http.services.myservice.loadbalancer.sticky.cookie.maxage=42
```
??? info "`traefik.http.services.<service_name>.loadbalancer.responseforwarding.flushinterval`"
See [response forwarding](../services/index.md#response-forwarding) for more information.

View file

@ -515,6 +515,60 @@ A value of `0` for the priority is ignored: `priority = 0` means that the defaul
In this configuration, the priority is configured to allow `Router-2` to handle requests with the `foobar.traefik.com` host.
### RuleSyntax
In Traefik v3 a new rule syntax has been introduced ([migration guide](../../migration/v2-to-v3.md#router-rule-matchers)).
`ruleSyntax` option allows to configure the rule syntax to be used for parsing the rule on a per-router basis.
This allows to have heterogeneous router configurations and ease migration.
??? example "Set rule syntax -- using the [File Provider](../../providers/file.md)"
```yaml tab="File (YAML)"
## Dynamic configuration
http:
routers:
Router-v3:
rule: HostRegexp(`[a-z]+\\.traefik\\.com`)
ruleSyntax: v3
Router-v2:
rule: HostRegexp(`{subdomain:[a-z]+}.traefik.com`)
ruleSyntax: v2
```
```toml tab="File (TOML)"
## Dynamic configuration
[http.routers]
[http.routers.Router-v3]
rule = "HostRegexp(`[a-z]+\\.traefik\\.com`)"
ruleSyntax = v3
[http.routers.Router-v2]
rule = "HostRegexp(`{subdomain:[a-z]+}.traefik.com`)"
ruleSyntax = v2
```
```yaml tab="Kubernetes traefik.io/v1alpha1"
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: test.route
namespace: default
spec:
routes:
# route v3
- match: HostRegexp(`[a-z]+\\.traefik\\.com`)
syntax: v3
kind: Rule
# route v2
- match: HostRegexp(`{subdomain:[a-z]+}.traefik.com`)
syntax: v2
kind: Rule
```
In this configuration, the ruleSyntax is configured to allow `Router-v2` to use v2 syntax,
while for `Router-v3` it is configured to use v3 syntax.
### Middlewares
You can attach a list of [middlewares](../../middlewares/overview.md) to each HTTP router.
@ -1161,6 +1215,60 @@ A value of `0` for the priority is ignored: `priority = 0` means that the defaul
In this configuration, the priority is configured so that `Router-1` will handle requests from `192.168.0.12`.
### RuleSyntax
In Traefik v3 a new rule syntax has been introduced ([migration guide](../../migration/v2-to-v3.md#router-rule-matchers)).
`ruleSyntax` option allows to configure the rule syntax to be used for parsing the rule on a per-router basis.
This allows to have heterogeneous router configurations and ease migration.
??? example "Set rule syntax -- using the [File Provider](../../providers/file.md)"
```yaml tab="File (YAML)"
## Dynamic configuration
tcp:
routers:
Router-v3:
rule: ClientIP(`192.168.0.11`) || ClientIP(`192.168.0.12`)
ruleSyntax: v3
Router-v2:
rule: ClientIP(`192.168.0.11`, `192.168.0.12`)
ruleSyntax: v2
```
```toml tab="File (TOML)"
## Dynamic configuration
[tcp.routers]
[tcp.routers.Router-v3]
rule = "ClientIP(`192.168.0.11`) || ClientIP(`192.168.0.12`)"
ruleSyntax = v3
[tcp.routers.Router-v2]
rule = "ClientIP(`192.168.0.11`, `192.168.0.12`)"
ruleSyntax = v2
```
```yaml tab="Kubernetes traefik.io/v1alpha1"
apiVersion: traefik.io/v1alpha1
kind: IngressRouteTCP
metadata:
name: test.route
namespace: default
spec:
routes:
# route v3
- match: ClientIP(`192.168.0.11`) || ClientIP(`192.168.0.12`)
syntax: v3
kind: Rule
# route v2
- match: ClientIP(`192.168.0.11`, `192.168.0.12`)
syntax: v2
kind: Rule
```
In this configuration, the ruleSyntax is configured to allow `Router-v2` to use v2 syntax,
while for `Router-v3` it is configured to use v3 syntax.
### Middlewares
You can attach a list of [middlewares](../../middlewares/overview.md) to each TCP router.

View file

@ -143,6 +143,36 @@ The `url` option point to a specific instance.
url = "http://private-ip-server-1/"
```
The `weight` option allows for weighted load balancing on the servers.
??? example "A Service with Two Servers with Weight -- Using the [File Provider](../../providers/file.md)"
```yaml tab="YAML"
## Dynamic configuration
http:
services:
my-service:
loadBalancer:
servers:
- url: "http://private-ip-server-1/"
weight: 2
- url: "http://private-ip-server-2/"
weight: 1
```
```toml tab="TOML"
## Dynamic configuration
[http.services]
[http.services.my-service.loadBalancer]
[[http.services.my-service.loadBalancer.servers]]
url = "http://private-ip-server-1/"
weight = 2
[[http.services.my-service.loadBalancer.servers]]
url = "http://private-ip-server-2/"
weight = 1
```
#### Load-balancing
For now, only round robin load balancing is supported:
@ -187,6 +217,13 @@ On subsequent requests, to keep the session alive with the same server, the clie
The default cookie name is an abbreviation of a sha1 (ex: `_1d52e`).
!!! info "MaxAge"
By default, the affinity cookie will never expire as the `MaxAge` option is set to zero.
This option indicates the number of seconds until the cookie expires.
When set to a negative number, the cookie expires immediately.
!!! info "Secure & HTTPOnly & SameSite flags"
By default, the affinity cookie is created without those flags.

View file

@ -1,47 +0,0 @@
# WEBUI
FROM node:12.11 as webui
ENV WEBUI_DIR /src/webui
RUN mkdir -p $WEBUI_DIR
COPY ./webui/ $WEBUI_DIR/
WORKDIR $WEBUI_DIR
RUN yarn install
RUN yarn build
# BUILD
FROM golang:1.21-alpine as gobuild
RUN apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar ca-certificates tzdata \
&& update-ca-certificates \
&& rm -rf /var/cache/apk/*
WORKDIR /go/src/github.com/traefik/traefik
# Download go modules
COPY go.mod .
COPY go.sum .
RUN GO111MODULE=on GOPROXY=https://proxy.golang.org go mod download
COPY . /go/src/github.com/traefik/traefik
RUN rm -rf /go/src/github.com/traefik/traefik/webui/static/
COPY --from=webui /src/webui/static/ /go/src/github.com/traefik/traefik/webui/static/
RUN ./script/make.sh generate binary
## IMAGE
FROM alpine:3.14
RUN apk --no-cache --no-progress add bash curl ca-certificates tzdata \
&& update-ca-certificates \
&& rm -rf /var/cache/apk/*
COPY --from=gobuild /go/src/github.com/traefik/traefik/dist/traefik /
EXPOSE 80
VOLUME ["/tmp"]
ENTRYPOINT ["/traefik"]

14
go.mod
View file

@ -28,7 +28,7 @@ require (
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/go-retryablehttp v0.7.4
github.com/hashicorp/go-version v1.6.0
github.com/hashicorp/nomad/api v0.0.0-20220506174431-b5665129cd1f
github.com/hashicorp/nomad/api v0.0.0-20240122103822-8a4bd61caf74
github.com/http-wasm/http-wasm-host-go v0.5.2
github.com/influxdata/influxdb-client-go/v2 v2.7.0
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d
@ -85,12 +85,13 @@ require (
golang.org/x/tools v0.14.0
google.golang.org/grpc v1.59.0
gopkg.in/yaml.v3 v3.0.1
k8s.io/api v0.28.3
k8s.io/api v0.28.4
k8s.io/apiextensions-apiserver v0.28.3
k8s.io/apimachinery v0.28.3
k8s.io/client-go v0.28.3
k8s.io/apimachinery v0.28.4
k8s.io/client-go v0.28.4
k8s.io/utils v0.0.0-20230726121419-3b25d923346b
mvdan.cc/xurls/v2 v2.5.0
sigs.k8s.io/controller-runtime v0.16.3
sigs.k8s.io/gateway-api v1.0.0
)
@ -162,6 +163,7 @@ require (
github.com/docker/go-units v0.5.0 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/evanphx/json-patch v5.7.0+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.7.0 // indirect
github.com/exoscale/egoscale v0.100.1 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
@ -196,7 +198,7 @@ require (
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/grpc-gateway/v2 v2.16.0 // indirect
github.com/hashicorp/cronexpr v1.1.1 // indirect
github.com/hashicorp/cronexpr v1.1.2 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
@ -236,6 +238,7 @@ require (
github.com/mitchellh/go-ps v1.0.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/spdystream v0.2.0 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
@ -254,7 +257,6 @@ require (
github.com/nzdjb/go-metaname v1.0.0 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/onsi/ginkgo/v2 v2.11.0 // indirect
github.com/onsi/gomega v1.27.10 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc5 // indirect
github.com/opencontainers/runc v1.1.5 // indirect

37
go.sum
View file

@ -114,6 +114,8 @@ github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJ
github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
@ -302,6 +304,8 @@ github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI=
github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch/v5 v5.7.0 h1:nJqP7uwL84RJInrohHfW0Fx3awjbm8qZeFv0nW9SYGc=
github.com/evanphx/json-patch/v5 v5.7.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
github.com/exoscale/egoscale v0.100.1 h1:iXsV1Ei7daqe/6FYSCSDyrFs1iUG1l1X9qNh2uMw6z0=
github.com/exoscale/egoscale v0.100.1/go.mod h1:BAb9p4rmyU+Wl400CJZO5270H2sXtdsZjLcm5xMKkz4=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
@ -311,6 +315,8 @@ github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
@ -356,6 +362,8 @@ github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo=
github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
@ -538,8 +546,8 @@ github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyN
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/consul/sdk v0.15.0 h1:2qK9nDrr4tiJKRoxPGhm6B7xJjLVIQqkjiab2M4aKjU=
github.com/hashicorp/consul/sdk v0.15.0/go.mod h1:r/OmRRPbHOe0yxNahLw7G9x5WG17E1BIECMtCjcPSNo=
github.com/hashicorp/cronexpr v1.1.1 h1:NJZDd87hGXjoZBdvyCF9mX4DCq5Wy7+A/w+A7q0wn6c=
github.com/hashicorp/cronexpr v1.1.1/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4=
github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A=
github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@ -590,8 +598,8 @@ github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM=
github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0=
github.com/hashicorp/nomad/api v0.0.0-20220506174431-b5665129cd1f h1:jSBbBJcPca465gK6XfwdXRQnFCd63e0oJmqllZTsawI=
github.com/hashicorp/nomad/api v0.0.0-20220506174431-b5665129cd1f/go.mod h1:b/AoT79m3PEpb6tKCFKva/M+q1rKJNUk5mdu1S8DymM=
github.com/hashicorp/nomad/api v0.0.0-20240122103822-8a4bd61caf74 h1:Q+WuGTnZkL2cJ7yNsg4Go4GNnRkcahGLiQP/WD41TTA=
github.com/hashicorp/nomad/api v0.0.0-20240122103822-8a4bd61caf74/go.mod h1:ijDwa6o1uG1jFSq6kERiX2PamKGpZzTmo0XOFNeFZgw=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY=
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
@ -794,6 +802,8 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
@ -1004,8 +1014,9 @@ github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9
github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/shoenig/test v1.7.0 h1:eWcHtTXa6QLnBvm0jgEabMRN/uJ4DMV3M8xUGgRkZmk=
github.com/shoenig/test v1.7.0/go.mod h1:UxJ6u/x2v/TNs/LoLxBNJRV9DiwBBKYxXSyczsBHFoI=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
@ -1482,6 +1493,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM=
gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
@ -1616,14 +1629,14 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM=
k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc=
k8s.io/api v0.28.4 h1:8ZBrLjwosLl/NYgv1P7EQLqoO8MGQApnbgH8tu3BMzY=
k8s.io/api v0.28.4/go.mod h1:axWTGrY88s/5YE+JSt4uUi6NMM+gur1en2REMR7IRj0=
k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08=
k8s.io/apiextensions-apiserver v0.28.3/go.mod h1:NE1XJZ4On0hS11aWWJUTNkmVB03j9LM7gJSisbRt8Lc=
k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A=
k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8=
k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4=
k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo=
k8s.io/apimachinery v0.28.4 h1:zOSJe1mc+GxuMnFzD4Z/U1wst50X28ZNsn5bhgIIao8=
k8s.io/apimachinery v0.28.4/go.mod h1:wI37ncBvfAoswfq626yPTe6Bz1c22L7uaJ8dho83mgg=
k8s.io/client-go v0.28.4 h1:Np5ocjlZcTrkyRJ3+T3PkXDpe4UpatQxj85+xjaD2wY=
k8s.io/client-go v0.28.4/go.mod h1:0VDZFpgoZfelyP5Wqu0/r/TRYcLYuJ2U1KEeoaPa1N4=
k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780=
@ -1637,6 +1650,8 @@ nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4=
sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0=
sigs.k8s.io/gateway-api v1.0.0 h1:iPTStSv41+d9p0xFydll6d7f7MOBGuqXM6p2/zVYMAs=
sigs.k8s.io/gateway-api v1.0.0/go.mod h1:4cUgr0Lnp5FZ0Cdq8FdRwCvpiWws7LVhLHGIudLlf4c=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=

View file

@ -4,6 +4,7 @@ import (
"encoding/json"
"io"
"net/http"
"strings"
"testing"
"time"
@ -55,6 +56,56 @@ func (s *DockerSuite) TestSimpleConfiguration() {
require.NoError(s.T(), err)
}
func (s *DockerSuite) TestWRRServer() {
tempObjects := struct {
DockerHost string
DefaultRule string
}{
DockerHost: s.getDockerHost(),
DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)",
}
file := s.adaptFile("fixtures/docker/simple.toml", tempObjects)
s.composeUp()
s.traefikCmd(withConfigFile(file))
whoami1IP := s.getComposeServiceIP("wrr-server")
whoami2IP := s.getComposeServiceIP("wrr-server2")
// Expected a 404 as we did not configure anything
err := try.GetRequest("http://127.0.0.1:8000/", 500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound))
require.NoError(s.T(), err)
err = try.GetRequest("http://127.0.0.1:8080/api/http/services", 1000*time.Millisecond, try.BodyContains("wrr-server"))
require.NoError(s.T(), err)
repartition := map[string]int{}
for i := 0; i < 4; i++ {
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil)
req.Host = "my.wrr.host"
require.NoError(s.T(), err)
response, err := http.DefaultClient.Do(req)
require.NoError(s.T(), err)
assert.Equal(s.T(), http.StatusOK, response.StatusCode)
body, err := io.ReadAll(response.Body)
require.NoError(s.T(), err)
if strings.Contains(string(body), whoami1IP) {
repartition[whoami1IP]++
}
if strings.Contains(string(body), whoami2IP) {
repartition[whoami2IP]++
}
}
assert.Equal(s.T(), 3, repartition[whoami1IP])
assert.Equal(s.T(), 1, repartition[whoami2IP])
}
func (s *DockerSuite) TestDefaultDockerContainers() {
tempObjects := struct {
DockerHost string

View file

@ -160,6 +160,12 @@ spec:
can be accessed by client-side APIs, such as
JavaScript.
type: boolean
maxAge:
description: MaxAge indicates the number of seconds
until the cookie expires. When set to a negative
number, the cookie expires immediately. When
set to zero, the cookie never expires.
type: integer
name:
description: Name defines the Cookie name.
type: string
@ -189,6 +195,10 @@ spec:
- name
type: object
type: array
syntax:
description: 'Syntax defines the router''s rule syntax. More
info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rulesyntax'
type: string
required:
- kind
- match
@ -396,6 +406,10 @@ spec:
- port
type: object
type: array
syntax:
description: 'Syntax defines the router''s rule syntax. More
info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rulesyntax_1'
type: string
required:
- match
type: object
@ -750,6 +764,13 @@ spec:
items:
type: string
type: array
includedContentTypes:
description: IncludedContentTypes defines the list of content
types to compare the Content-Type header of the responses before
compressing.
items:
type: string
type: array
minResponseBodyBytes:
description: 'MinResponseBodyBytes defines the minimum amount
of bytes a response body must have to be compressed. Default:
@ -870,6 +891,12 @@ spec:
description: HTTPOnly defines whether the cookie can
be accessed by client-side APIs, such as JavaScript.
type: boolean
maxAge:
description: MaxAge indicates the number of seconds
until the cookie expires. When set to a negative
number, the cookie expires immediately. When set
to zero, the cookie never expires.
type: integer
name:
description: Name defines the Cookie name.
type: string
@ -912,6 +939,12 @@ spec:
This middleware delegates the request authentication to a Service.
More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/forwardauth/'
properties:
addAuthCookiesToResponse:
description: AddAuthCookiesToResponse defines the list of cookies
to copy from the authentication server response to the response.
items:
type: string
type: array
address:
description: Address defines the authentication server address.
type: string
@ -2145,6 +2178,12 @@ spec:
description: HTTPOnly defines whether the cookie
can be accessed by client-side APIs, such as JavaScript.
type: boolean
maxAge:
description: MaxAge indicates the number of seconds
until the cookie expires. When set to a negative
number, the cookie expires immediately. When set
to zero, the cookie never expires.
type: integer
name:
description: Name defines the Cookie name.
type: string
@ -2236,6 +2275,12 @@ spec:
description: HTTPOnly defines whether the cookie can be
accessed by client-side APIs, such as JavaScript.
type: boolean
maxAge:
description: MaxAge indicates the number of seconds until
the cookie expires. When set to a negative number, the
cookie expires immediately. When set to zero, the cookie
never expires.
type: integer
name:
description: Name defines the Cookie name.
type: string
@ -2343,6 +2388,12 @@ spec:
description: HTTPOnly defines whether the cookie
can be accessed by client-side APIs, such as JavaScript.
type: boolean
maxAge:
description: MaxAge indicates the number of seconds
until the cookie expires. When set to a negative
number, the cookie expires immediately. When set
to zero, the cookie never expires.
type: integer
name:
description: Name defines the Cookie name.
type: string
@ -2382,6 +2433,12 @@ spec:
description: HTTPOnly defines whether the cookie can be
accessed by client-side APIs, such as JavaScript.
type: boolean
maxAge:
description: MaxAge indicates the number of seconds until
the cookie expires. When set to a negative number, the
cookie expires immediately. When set to zero, the cookie
never expires.
type: integer
name:
description: Name defines the Cookie name.
type: string

View file

@ -0,0 +1,21 @@
[global]
checkNewVersion = false
sendAnonymousUsage = false
[log]
level = "DEBUG"
noColor = true
[api]
insecure = true
[experimental]
kubernetesGateway = true
[entryPoints]
[entryPoints.web]
address = ":80"
[entryPoints.websecure]
address = ":443"
[providers.kubernetesGateway]

View file

@ -0,0 +1,41 @@
[global]
checkNewVersion = false
sendAnonymousUsage = false
[core]
defaultRuleSyntax = "v2"
[log]
level = "DEBUG"
noColor = true
[entryPoints]
[entryPoints.web]
address = ":8000"
[api]
insecure = true
[providers.file]
filename = "{{ .SelfFilename }}"
## dynamic configuration ##
[http.routers]
[http.routers.router1]
service = "service1"
rule = "PathPrefix(`/foo`, `/bar`)"
[http.routers.router2]
service = "service1"
rule = "QueryRegexp(`foo`, `bar`)"
[http.routers.router3]
service = "service1"
rule = "PathPrefix(`/foo`, `/bar`)"
ruleSyntax = "v3"
[http.services]
[http.services.service1]
[http.services.service1.loadBalancer]
[http.services.service1.loadBalancer.servers]

View file

@ -0,0 +1,38 @@
[global]
checkNewVersion = false
sendAnonymousUsage = false
[log]
level = "DEBUG"
noColor = true
[entryPoints]
[entryPoints.web]
address = ":8000"
[api]
insecure = true
[providers.file]
filename = "{{ .SelfFilename }}"
## dynamic configuration ##
[http.routers]
[http.routers.router1]
service = "service1"
rule = "PathPrefix(`/foo`) || PathPrefix(`/bar`)"
[http.routers.router2]
service = "service1"
rule = "PathPrefix(`/foo`, `/bar`)"
[http.routers.router3]
service = "service1"
rule = "QueryRegexp(`foo`, `bar`)"
ruleSyntax = "v2"
[http.services]
[http.services.service1]
[http.services.service1.loadBalancer]
[http.services.service1.loadBalancer.servers]

View file

@ -0,0 +1,35 @@
[global]
checkNewVersion = false
sendAnonymousUsage = false
[api]
insecure = true
[log]
level = "DEBUG"
noColor = true
[entryPoints]
[entryPoints.web]
address = ":8000"
[providers.file]
filename = "{{ .SelfFilename }}"
## dynamic configuration ##
[http.routers]
[http.routers.router]
service = "service1"
rule = "Path(`/whoami`)"
[http.services]
[http.services.service1.loadBalancer]
[[http.services.service1.loadBalancer.servers]]
url = "{{ .Server1 }}"
weight = 3
[[http.services.service1.loadBalancer.servers]]
url = "{{ .Server2 }}"

View file

@ -15,6 +15,7 @@ import (
"os/exec"
"path/filepath"
"regexp"
"runtime"
"slices"
"strings"
"testing"
@ -34,7 +35,13 @@ import (
"gopkg.in/yaml.v3"
)
var showLog = flag.Bool("tlog", false, "always show Traefik logs")
var (
showLog = flag.Bool("tlog", false, "always show Traefik logs")
k8sConformance = flag.Bool("k8sConformance", false, "run K8s Gateway API conformance test")
k8sConformanceRunTest = flag.String("k8sConformanceRunTest", "", "run a specific K8s Gateway API conformance test")
)
const tailscaleSecretFilePath = "tailscale.secret"
type composeConfig struct {
Services map[string]composeService `yaml:"services"`
@ -56,8 +63,6 @@ type composeDeploy struct {
Replicas int `yaml:"replicas"`
}
var traefikBinary = "../dist/traefik"
type BaseSuite struct {
suite.Suite
containers map[string]testcontainers.Container
@ -100,6 +105,11 @@ func (s *BaseSuite) displayTraefikLogFile(path string) {
}
func (s *BaseSuite) SetupSuite() {
if isDockerDesktop(context.Background(), s.T()) {
_, err := os.Stat(tailscaleSecretFilePath)
require.NoError(s.T(), err, "Tailscale need to be configured when running integration tests with Docker Desktop: (https://doc.traefik.io/traefik/v2.11/contributing/building-testing/#testing)")
}
// configure default standard log.
stdlog.SetFlags(stdlog.Lshortfile | stdlog.LstdFlags)
// TODO
@ -125,7 +135,7 @@ func (s *BaseSuite) SetupSuite() {
s.hostIP = "172.31.42.1"
if isDockerDesktop(ctx, s.T()) {
s.hostIP = getDockerDesktopHostIP(ctx, s.T())
s.setupVPN("tailscale.secret")
s.setupVPN(tailscaleSecretFilePath)
}
}
@ -308,7 +318,13 @@ func (s *BaseSuite) composeDown() {
}
func (s *BaseSuite) cmdTraefik(args ...string) (*exec.Cmd, *bytes.Buffer) {
cmd := exec.Command(traefikBinary, args...)
binName := "traefik"
if runtime.GOOS == "windows" {
binName += ".exe"
}
traefikBinPath := filepath.Join("..", "dist", runtime.GOOS, runtime.GOARCH, binName)
cmd := exec.Command(traefikBinPath, args...)
s.T().Cleanup(func() {
s.killCmd(cmd)

View file

@ -0,0 +1,229 @@
package integration
import (
"context"
"fmt"
"net"
"os"
"path/filepath"
"strings"
"testing"
"time"
"github.com/rs/zerolog/log"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/traefik/traefik/v3/integration/try"
"github.com/traefik/traefik/v3/pkg/version"
"gopkg.in/yaml.v3"
ktypes "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
kclientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
"sigs.k8s.io/controller-runtime/pkg/client"
gatev1 "sigs.k8s.io/gateway-api/apis/v1"
gatev1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
gatev1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1"
conformanceV1alpha1 "sigs.k8s.io/gateway-api/conformance/apis/v1alpha1"
"sigs.k8s.io/gateway-api/conformance/tests"
"sigs.k8s.io/gateway-api/conformance/utils/config"
ksuite "sigs.k8s.io/gateway-api/conformance/utils/suite"
)
// K8sConformanceSuite tests suite.
type K8sConformanceSuite struct{ BaseSuite }
func TestK8sConformanceSuite(t *testing.T) {
suite.Run(t, new(K8sConformanceSuite))
}
func (s *K8sConformanceSuite) SetupSuite() {
s.BaseSuite.SetupSuite()
s.createComposeProject("k8s")
s.composeUp()
abs, err := filepath.Abs("./fixtures/k8s/config.skip/kubeconfig.yaml")
require.NoError(s.T(), err)
err = try.Do(60*time.Second, func() error {
_, err := os.Stat(abs)
return err
})
require.NoError(s.T(), err)
data, err := os.ReadFile(abs)
require.NoError(s.T(), err)
content := strings.ReplaceAll(string(data), "https://server:6443", fmt.Sprintf("https://%s", net.JoinHostPort(s.getComposeServiceIP("server"), "6443")))
err = os.WriteFile(abs, []byte(content), 0o644)
require.NoError(s.T(), err)
err = os.Setenv("KUBECONFIG", abs)
require.NoError(s.T(), err)
}
func (s *K8sConformanceSuite) TearDownSuite() {
s.BaseSuite.TearDownSuite()
generatedFiles := []string{
"./fixtures/k8s/config.skip/kubeconfig.yaml",
"./fixtures/k8s/config.skip/k3s.log",
"./fixtures/k8s/rolebindings.yaml",
"./fixtures/k8s/ccm.yaml",
}
for _, filename := range generatedFiles {
if err := os.Remove(filename); err != nil {
log.Warn().Err(err).Send()
}
}
}
func (s *K8sConformanceSuite) TestK8sGatewayAPIConformance() {
if !*k8sConformance {
s.T().Skip("Skip because it can take a long time to execute. To enable pass the `k8sConformance` flag.")
}
configFromFlags, err := clientcmd.BuildConfigFromFlags("", os.Getenv("KUBECONFIG"))
if err != nil {
s.T().Fatal(err)
}
kClient, err := client.New(configFromFlags, client.Options{})
if err != nil {
s.T().Fatalf("Error initializing Kubernetes client: %v", err)
}
kClientSet, err := kclientset.NewForConfig(configFromFlags)
if err != nil {
s.T().Fatal(err)
}
err = gatev1alpha2.AddToScheme(kClient.Scheme())
require.NoError(s.T(), err)
err = gatev1beta1.AddToScheme(kClient.Scheme())
require.NoError(s.T(), err)
err = gatev1.AddToScheme(kClient.Scheme())
require.NoError(s.T(), err)
s.traefikCmd(withConfigFile("fixtures/k8s_gateway_conformance.toml"))
// Wait for traefik to start
err = try.GetRequest("http://127.0.0.1:8080/api/entrypoints", 10*time.Second, try.BodyContains(`"name":"web"`))
require.NoError(s.T(), err)
err = try.Do(10*time.Second, func() error {
gwc := &gatev1.GatewayClass{}
err := kClient.Get(context.Background(), ktypes.NamespacedName{Name: "my-gateway-class"}, gwc)
if err != nil {
return fmt.Errorf("error fetching GatewayClass: %w", err)
}
return nil
})
require.NoError(s.T(), err)
opts := ksuite.Options{
Client: kClient,
RestConfig: configFromFlags,
Clientset: kClientSet,
GatewayClassName: "my-gateway-class",
Debug: true,
CleanupBaseResources: true,
TimeoutConfig: config.TimeoutConfig{
CreateTimeout: 5 * time.Second,
DeleteTimeout: 5 * time.Second,
GetTimeout: 5 * time.Second,
GatewayMustHaveAddress: 5 * time.Second,
GatewayMustHaveCondition: 5 * time.Second,
GatewayStatusMustHaveListeners: 10 * time.Second,
GatewayListenersMustHaveCondition: 5 * time.Second,
GWCMustBeAccepted: 60 * time.Second, // Pod creation in k3s cluster can be long.
HTTPRouteMustNotHaveParents: 5 * time.Second,
HTTPRouteMustHaveCondition: 5 * time.Second,
TLSRouteMustHaveCondition: 5 * time.Second,
RouteMustHaveParents: 5 * time.Second,
ManifestFetchTimeout: 5 * time.Second,
MaxTimeToConsistency: 5 * time.Second,
NamespacesMustBeReady: 60 * time.Second, // Pod creation in k3s cluster can be long.
RequestTimeout: 5 * time.Second,
LatestObservedGenerationSet: 5 * time.Second,
RequiredConsecutiveSuccesses: 0,
},
SupportedFeatures: sets.New[ksuite.SupportedFeature]().
Insert(ksuite.GatewayCoreFeatures.UnsortedList()...),
EnableAllSupportedFeatures: false,
RunTest: *k8sConformanceRunTest,
// Until the feature are all supported, following tests are skipped.
SkipTests: []string{
"HTTPExactPathMatching",
"HTTPRouteHostnameIntersection",
"GatewaySecretReferenceGrantAllInNamespace",
"HTTPRouteListenerHostnameMatching",
"HTTPRouteRequestHeaderModifier",
"GatewaySecretInvalidReferenceGrant",
"GatewayClassObservedGenerationBump",
"HTTPRouteInvalidNonExistentBackendRef",
"GatewayWithAttachedRoutes",
"HTTPRouteCrossNamespace",
"HTTPRouteDisallowedKind",
"HTTPRouteInvalidReferenceGrant",
"HTTPRouteObservedGenerationBump",
"GatewayInvalidRouteKind",
"TLSRouteSimpleSameNamespace",
"TLSRouteInvalidReferenceGrant",
"HTTPRouteInvalidCrossNamespaceParentRef",
"HTTPRouteInvalidParentRefNotMatchingSectionName",
"GatewaySecretReferenceGrantSpecific",
"GatewayModifyListeners",
"GatewaySecretMissingReferenceGrant",
"GatewayInvalidTLSConfiguration",
"HTTPRouteInvalidCrossNamespaceBackendRef",
"HTTPRouteMatchingAcrossRoutes",
"HTTPRoutePartiallyInvalidViaInvalidReferenceGrant",
"HTTPRouteRedirectHostAndStatus",
"HTTPRouteInvalidBackendRefUnknownKind",
"HTTPRoutePathMatchOrder",
"HTTPRouteSimpleSameNamespace",
"HTTPRouteMatching",
"HTTPRouteHeaderMatching",
"HTTPRouteReferenceGrant",
},
}
cSuite, err := ksuite.NewExperimentalConformanceTestSuite(ksuite.ExperimentalConformanceOptions{
Options: opts,
Implementation: conformanceV1alpha1.Implementation{
Organization: "traefik",
Project: "traefik",
URL: "https://traefik.io/",
Version: version.Version,
Contact: []string{"@traefik/maintainers"},
},
ConformanceProfiles: sets.New[ksuite.ConformanceProfileName](
ksuite.HTTPConformanceProfileName,
ksuite.TLSConformanceProfileName,
),
})
require.NoError(s.T(), err)
cSuite.Setup(s.T())
err = cSuite.Run(s.T(), tests.ConformanceTests)
require.NoError(s.T(), err)
report, err := cSuite.Report()
require.NoError(s.T(), err, "failed generating conformance report")
report.GatewayAPIVersion = "1.0.0"
rawReport, err := yaml.Marshal(report)
require.NoError(s.T(), err)
s.T().Logf("Conformance report:\n%s", string(rawReport))
require.NoError(s.T(), os.MkdirAll("./conformance-reports", 0o755))
outFile := filepath.Join("conformance-reports", fmt.Sprintf("traefik-traefik-%d.yaml", time.Now().UnixNano()))
require.NoError(s.T(), os.WriteFile(outFile, rawReport, 0o600))
s.T().Logf("Report written to: %s", outFile)
}

View file

@ -36,3 +36,14 @@ services:
labels:
traefik.http.Routers.Super.Rule: Host(`my.super.host`)
traefik.http.Services.powpow.LoadBalancer.server.Port: 2375
wrr-server:
image: traefik/whoami
labels:
traefik.http.Routers.wrr-server.Rule: Host(`my.wrr.host`)
traefik.http.Services.wrr-server.LoadBalancer.server.Weight: 4
wrr-server2:
image: traefik/whoami
labels:
traefik.http.Routers.wrr-server.Rule: Host(`my.wrr.host`)
traefik.http.Services.wrr-server.LoadBalancer.server.Weight: 1

View file

@ -659,6 +659,66 @@ func (s *SimpleSuite) TestSimpleConfigurationHostRequestTrailingPeriod() {
}
}
func (s *SimpleSuite) TestWithDefaultRuleSyntax() {
file := s.adaptFile("fixtures/with_default_rule_syntax.toml", struct{}{})
s.traefikCmd(withConfigFile(file))
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("PathPrefix"))
require.NoError(s.T(), err)
// router1 has no error
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router1@file", 1*time.Second, try.BodyContains(`"status":"enabled"`))
require.NoError(s.T(), err)
err = try.GetRequest("http://127.0.0.1:8000/notfound", 1*time.Second, try.StatusCodeIs(http.StatusNotFound))
require.NoError(s.T(), err)
err = try.GetRequest("http://127.0.0.1:8000/foo", 1*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
require.NoError(s.T(), err)
err = try.GetRequest("http://127.0.0.1:8000/bar", 1*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
require.NoError(s.T(), err)
// router2 has an error because it uses the wrong rule syntax (v3 instead of v2)
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router2@file", 1*time.Second, try.BodyContains("error while parsing rule QueryRegexp(`foo`, `bar`): unsupported function: QueryRegexp"))
require.NoError(s.T(), err)
// router3 has an error because it uses the wrong rule syntax (v2 instead of v3)
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router3@file", 1*time.Second, try.BodyContains("error while adding rule PathPrefix: unexpected number of parameters; got 2, expected one of [1]"))
require.NoError(s.T(), err)
}
func (s *SimpleSuite) TestWithoutDefaultRuleSyntax() {
file := s.adaptFile("fixtures/without_default_rule_syntax.toml", struct{}{})
s.traefikCmd(withConfigFile(file))
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("PathPrefix"))
require.NoError(s.T(), err)
// router1 has no error
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router1@file", 1*time.Second, try.BodyContains(`"status":"enabled"`))
require.NoError(s.T(), err)
err = try.GetRequest("http://127.0.0.1:8000/notfound", 1*time.Second, try.StatusCodeIs(http.StatusNotFound))
require.NoError(s.T(), err)
err = try.GetRequest("http://127.0.0.1:8000/foo", 1*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
require.NoError(s.T(), err)
err = try.GetRequest("http://127.0.0.1:8000/bar", 1*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
require.NoError(s.T(), err)
// router2 has an error because it uses the wrong rule syntax (v3 instead of v2)
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router2@file", 1*time.Second, try.BodyContains("error while adding rule PathPrefix: unexpected number of parameters; got 2, expected one of [1]"))
require.NoError(s.T(), err)
// router2 has an error because it uses the wrong rule syntax (v2 instead of v3)
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router3@file", 1*time.Second, try.BodyContains("error while parsing rule QueryRegexp(`foo`, `bar`): unsupported function: QueryRegexp"))
require.NoError(s.T(), err)
}
func (s *SimpleSuite) TestRouterConfigErrors() {
file := s.adaptFile("fixtures/router_errors.toml", struct{}{})
@ -749,6 +809,49 @@ func (s *SimpleSuite) TestUDPServiceConfigErrors() {
require.NoError(s.T(), err)
}
func (s *SimpleSuite) TestWRRServer() {
s.createComposeProject("base")
s.composeUp()
defer s.composeDown()
whoami1IP := s.getComposeServiceIP("whoami1")
whoami2IP := s.getComposeServiceIP("whoami2")
file := s.adaptFile("fixtures/wrr_server.toml", struct {
Server1 string
Server2 string
}{Server1: "http://" + whoami1IP, Server2: "http://" + whoami2IP})
s.traefikCmd(withConfigFile(file))
err := try.GetRequest("http://127.0.0.1:8080/api/http/services", 1000*time.Millisecond, try.BodyContains("service1"))
require.NoError(s.T(), err)
repartition := map[string]int{}
for i := 0; i < 4; i++ {
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil)
require.NoError(s.T(), err)
response, err := http.DefaultClient.Do(req)
require.NoError(s.T(), err)
assert.Equal(s.T(), http.StatusOK, response.StatusCode)
body, err := io.ReadAll(response.Body)
require.NoError(s.T(), err)
if strings.Contains(string(body), whoami1IP) {
repartition[whoami1IP]++
}
if strings.Contains(string(body), whoami2IP) {
repartition[whoami2IP]++
}
}
assert.Equal(s.T(), 3, repartition[whoami1IP])
assert.Equal(s.T(), 1, repartition[whoami2IP])
}
func (s *SimpleSuite) TestWRR() {
s.createComposeProject("base")

View file

@ -34,6 +34,7 @@
],
"service": "default-http-app-1-my-gateway-web-1c0cf64bde37d9d0df06-wrr",
"rule": "Host(`foo.com`) \u0026\u0026 Path(`/bar`)",
"ruleSyntax": "v3",
"priority": 31,
"status": "enabled",
"using": [
@ -46,6 +47,7 @@
],
"service": "default-http-app-1-my-https-gateway-websecure-1c0cf64bde37d9d0df06-wrr",
"rule": "Host(`foo.com`) \u0026\u0026 Path(`/bar`)",
"ruleSyntax": "v3",
"priority": 31,
"tls": {},
"status": "enabled",
@ -152,6 +154,7 @@
],
"service": "default-tcp-app-1-my-tcp-gateway-footcp-e3b0c44298fc1c149afb-wrr-0",
"rule": "HostSNI(`*`)",
"ruleSyntax": "v3",
"priority": -1,
"status": "enabled",
"using": [
@ -164,6 +167,7 @@
],
"service": "default-tcp-app-1-my-tls-gateway-footlsterminate-e3b0c44298fc1c149afb-wrr-0",
"rule": "HostSNI(`*`)",
"ruleSyntax": "v3",
"priority": -1,
"tls": {
"passthrough": false
@ -179,6 +183,7 @@
],
"service": "default-tls-app-1-my-tls-gateway-footlspassthrough-2279fe75c5156dc5eb26-wrr-0",
"rule": "HostSNI(`foo.bar`)",
"ruleSyntax": "v3",
"priority": 18,
"tls": {
"passthrough": true

View file

@ -1,10 +1,13 @@
package main
import (
"bytes"
"fmt"
"io"
"os"
"path"
"path/filepath"
"reflect"
"sort"
"strconv"
"strings"
@ -16,9 +19,65 @@ import (
"github.com/traefik/paerser/generator"
"github.com/traefik/paerser/parser"
"github.com/traefik/traefik/v3/cmd"
"github.com/traefik/traefik/v3/pkg/collector/hydratation"
"github.com/traefik/traefik/v3/pkg/config/dynamic"
"github.com/traefik/traefik/v3/pkg/config/static"
"gopkg.in/yaml.v3"
)
var commentGenerated = `## CODE GENERATED AUTOMATICALLY
## THIS FILE MUST NOT BE EDITED BY HAND
`
func main() {
logger := log.With().Logger()
dynConf := &dynamic.Configuration{}
err := hydratation.Hydrate(dynConf)
if err != nil {
logger.Fatal().Err(err).Send()
}
dynConf.HTTP.Models = map[string]*dynamic.Model{}
clean(dynConf.HTTP.Middlewares)
clean(dynConf.TCP.Middlewares)
clean(dynConf.HTTP.Services)
clean(dynConf.TCP.Services)
clean(dynConf.UDP.Services)
err = tomlWrite("./docs/content/reference/dynamic-configuration/file.toml", dynConf)
if err != nil {
logger.Fatal().Err(err).Send()
}
err = yamlWrite("./docs/content/reference/dynamic-configuration/file.yaml", dynConf)
if err != nil {
logger.Fatal().Err(err).Send()
}
err = labelsWrite("./docs/content/reference/dynamic-configuration", dynConf)
if err != nil {
logger.Fatal().Err(err).Send()
}
staticConf := &static.Configuration{}
err = hydratation.Hydrate(staticConf)
if err != nil {
logger.Fatal().Err(err).Send()
}
delete(staticConf.EntryPoints, "EntryPoint1")
err = tomlWrite("./docs/content/reference/static-configuration/file.toml", staticConf)
if err != nil {
logger.Fatal().Err(err).Send()
}
err = yamlWrite("./docs/content/reference/static-configuration/file.yaml", staticConf)
if err != nil {
logger.Fatal().Err(err).Send()
}
genStaticConfDoc("./docs/content/reference/static-configuration/env-ref.md", "", func(i interface{}) ([]parser.Flat, error) {
return env.Encode(env.DefaultNamePrefix, i)
})
@ -26,6 +85,144 @@ func main() {
genKVDynConfDoc("./docs/content/reference/dynamic-configuration/kv-ref.md")
}
func labelsWrite(outputDir string, element *dynamic.Configuration) error {
cleanServers(element)
etnOpts := parser.EncoderToNodeOpts{OmitEmpty: true, TagName: parser.TagLabel, AllowSliceAsStruct: true}
node, err := parser.EncodeToNode(element, parser.DefaultRootName, etnOpts)
if err != nil {
return err
}
metaOpts := parser.MetadataOpts{TagName: parser.TagLabel, AllowSliceAsStruct: true}
err = parser.AddMetadata(element, node, metaOpts)
if err != nil {
return err
}
labels := make(map[string]string)
encodeNode(labels, node.Name, node)
var keys []string
for k := range labels {
keys = append(keys, k)
}
sort.Strings(keys)
dockerLabels, err := os.Create(filepath.Join(outputDir, "docker-labels.yml"))
if err != nil {
return err
}
defer dockerLabels.Close()
// Write the comment at the beginning of the file
if _, err := dockerLabels.WriteString(commentGenerated); err != nil {
return err
}
for _, k := range keys {
v := labels[k]
if v != "" {
if v == "42000000000" {
v = "42s"
}
fmt.Fprintln(dockerLabels, `- "`+strings.ToLower(k)+`=`+v+`"`)
}
}
return nil
}
func cleanServers(element *dynamic.Configuration) {
for _, svc := range element.HTTP.Services {
if svc.LoadBalancer != nil {
server := svc.LoadBalancer.Servers[0]
svc.LoadBalancer.Servers = nil
svc.LoadBalancer.Servers = append(svc.LoadBalancer.Servers, server)
}
}
for _, svc := range element.TCP.Services {
if svc.LoadBalancer != nil {
server := svc.LoadBalancer.Servers[0]
svc.LoadBalancer.Servers = nil
svc.LoadBalancer.Servers = append(svc.LoadBalancer.Servers, server)
}
}
for _, svc := range element.UDP.Services {
if svc.LoadBalancer != nil {
server := svc.LoadBalancer.Servers[0]
svc.LoadBalancer.Servers = nil
svc.LoadBalancer.Servers = append(svc.LoadBalancer.Servers, server)
}
}
}
func yamlWrite(outputFile string, element any) error {
file, err := os.OpenFile(outputFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o666)
if err != nil {
return err
}
defer file.Close()
// Write the comment at the beginning of the file
if _, err := file.WriteString(commentGenerated); err != nil {
return err
}
buf := new(bytes.Buffer)
encoder := yaml.NewEncoder(buf)
encoder.SetIndent(2)
err = encoder.Encode(element)
if err != nil {
return err
}
_, err = file.Write(buf.Bytes())
return err
}
func tomlWrite(outputFile string, element any) error {
file, err := os.OpenFile(outputFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o666)
if err != nil {
return err
}
defer file.Close()
// Write the comment at the beginning of the file
if _, err := file.WriteString(commentGenerated); err != nil {
return err
}
return toml.NewEncoder(file).Encode(element)
}
func clean(element any) {
valSvcs := reflect.ValueOf(element)
key := valSvcs.MapKeys()[0]
valueSvcRoot := valSvcs.MapIndex(key).Elem()
var svcFieldNames []string
for i := 0; i < valueSvcRoot.NumField(); i++ {
svcFieldNames = append(svcFieldNames, valueSvcRoot.Type().Field(i).Name)
}
sort.Strings(svcFieldNames)
for i, fieldName := range svcFieldNames {
v := reflect.New(valueSvcRoot.Type())
v.Elem().FieldByName(fieldName).Set(valueSvcRoot.FieldByName(fieldName))
valSvcs.SetMapIndex(reflect.ValueOf(fmt.Sprintf("%s%.2d", valueSvcRoot.Type().Name(), i+1)), v)
}
valSvcs.SetMapIndex(reflect.ValueOf(fmt.Sprintf("%s0", valueSvcRoot.Type().Name())), reflect.Value{})
valSvcs.SetMapIndex(reflect.ValueOf(fmt.Sprintf("%s1", valueSvcRoot.Type().Name())), reflect.Value{})
}
func genStaticConfDoc(outputFile, prefix string, encodeFn func(interface{}) ([]parser.Flat, error)) {
logger := log.With().Str("file", outputFile).Logger()
@ -117,6 +314,7 @@ func genKVDynConfDoc(outputFile string) {
}
store := storeWriter{data: map[string]string{}}
c := client{store: store}
err = c.load("traefik", conf)
if err != nil {
@ -130,6 +328,12 @@ func genKVDynConfDoc(outputFile string) {
sort.Strings(keys)
_, _ = fmt.Fprintf(file, `<!--
CODE GENERATED AUTOMATICALLY
THIS FILE MUST NOT BE EDITED BY HAND
-->
`)
for _, k := range keys {
_, _ = fmt.Fprintf(file, "| `%s` | `%s` |\n", k, store.data[k])
}

66
internal/parser.go Normal file
View file

@ -0,0 +1,66 @@
package main
import (
"fmt"
"reflect"
"strings"
"github.com/traefik/paerser/parser"
)
func encodeNode(labels map[string]string, root string, node *parser.Node) {
for _, child := range node.Children {
if child.Disabled {
continue
}
var sep string
if child.Name[0] != '[' {
sep = "."
}
childName := root + sep + child.Name
if child.RawValue != nil {
encodeRawValue(labels, childName, child.RawValue)
continue
}
if strings.Contains(child.Tag.Get(parser.TagLabel), parser.TagLabelAllowEmpty) {
labels[childName] = "true"
}
if len(child.Children) > 0 {
encodeNode(labels, childName, child)
} else if len(child.Name) > 0 {
labels[childName] = child.Value
}
}
}
func encodeRawValue(labels map[string]string, root string, rawValue interface{}) {
if rawValue == nil {
return
}
tValue := reflect.TypeOf(rawValue)
if tValue.Kind() == reflect.Map && tValue.Elem().Kind() == reflect.Interface {
r := reflect.ValueOf(rawValue).
Convert(reflect.TypeOf((map[string]interface{})(nil))).
Interface().(map[string]interface{})
for k, v := range r {
switch tv := v.(type) {
case string:
labels[root+"."+k] = tv
case []interface{}:
for i, e := range tv {
encodeRawValue(labels, fmt.Sprintf("%s.%s[%d]", root, k, i), e)
}
default:
encodeRawValue(labels, root+"."+k, v)
}
}
}
}

541
pkg/cli/deprecation.go Normal file
View file

@ -0,0 +1,541 @@
package cli
import (
"errors"
"os"
"reflect"
"strings"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/traefik/paerser/cli"
"github.com/traefik/paerser/flag"
"github.com/traefik/paerser/parser"
)
type DeprecationLoader struct{}
func (d DeprecationLoader) Load(args []string, cmd *cli.Command) (bool, error) {
if logDeprecation(cmd.Configuration, args) {
return true, errors.New("incompatible deprecated static option found")
}
return false, nil
}
// logDeprecation prints deprecation hints and returns whether incompatible deprecated options need to be removed.
func logDeprecation(traefikConfiguration interface{}, args []string) bool {
// This part doesn't handle properly a flag defined like this:
// --accesslog true
// where `true` could be considered as a new argument.
// This is not really an issue with the deprecation loader since it will filter the unknown nodes later in this
// function.
for i, arg := range args {
if !strings.Contains(arg, "=") {
args[i] = arg + "=true"
}
}
labels, err := flag.Parse(args, nil)
if err != nil {
log.Error().Err(err).Msg("deprecated static options analysis failed")
return false
}
node, err := parser.DecodeToNode(labels, "traefik")
if err != nil {
log.Error().Err(err).Msg("deprecated static options analysis failed")
return false
}
if node != nil && len(node.Children) > 0 {
config := &configuration{}
filterUnknownNodes(reflect.TypeOf(config), node)
if len(node.Children) > 0 {
// Telling parser to look for the label struct tag to allow empty values.
err = parser.AddMetadata(config, node, parser.MetadataOpts{TagName: "label"})
if err != nil {
log.Error().Err(err).Msg("deprecated static options analysis failed")
return false
}
err = parser.Fill(config, node, parser.FillerOpts{})
if err != nil {
log.Error().Err(err).Msg("deprecated static options analysis failed")
return false
}
if config.deprecationNotice(log.With().Str("loader", "FLAG").Logger()) {
return true
}
// No further deprecation parsing and logging,
// as args configuration contains at least one deprecated option.
return false
}
}
// FILE
ref, err := flag.Parse(args, traefikConfiguration)
if err != nil {
log.Error().Err(err).Msg("deprecated static options analysis failed")
return false
}
configFileFlag := "traefik.configfile"
if _, ok := ref["traefik.configFile"]; ok {
configFileFlag = "traefik.configFile"
}
config := &configuration{}
_, err = loadConfigFiles(ref[configFileFlag], config)
if err == nil {
if config.deprecationNotice(log.With().Str("loader", "FILE").Logger()) {
return true
}
}
config = &configuration{}
l := EnvLoader{}
_, err = l.Load(os.Args, &cli.Command{
Configuration: config,
})
if err == nil {
if config.deprecationNotice(log.With().Str("loader", "ENV").Logger()) {
return true
}
}
return false
}
func filterUnknownNodes(fType reflect.Type, node *parser.Node) bool {
var children []*parser.Node
for _, child := range node.Children {
if hasKnownNodes(fType, child) {
children = append(children, child)
}
}
node.Children = children
return len(node.Children) > 0
}
func hasKnownNodes(rootType reflect.Type, node *parser.Node) bool {
rType := rootType
if rootType.Kind() == reflect.Pointer {
rType = rootType.Elem()
}
// unstructured type fitting anything, considering the current node as known.
if rType.Kind() == reflect.Map && rType.Elem().Kind() == reflect.Interface {
return true
}
// unstructured type fitting anything, considering the current node as known.
if rType.Kind() == reflect.Interface {
return true
}
// find matching field in struct type.
field, b := findTypedField(rType, node)
if !b {
return b
}
if len(node.Children) > 0 {
return filterUnknownNodes(field.Type, node)
}
return true
}
func findTypedField(rType reflect.Type, node *parser.Node) (reflect.StructField, bool) {
// avoid panicking.
if rType.Kind() != reflect.Struct {
return reflect.StructField{}, false
}
for i := 0; i < rType.NumField(); i++ {
cField := rType.Field(i)
// ignore unexported fields.
if cField.PkgPath == "" {
if strings.EqualFold(cField.Name, node.Name) {
node.FieldName = cField.Name
return cField, true
}
}
}
return reflect.StructField{}, false
}
// configuration holds the static configuration removed/deprecated options.
type configuration struct {
Experimental *experimental `json:"experimental,omitempty" toml:"experimental,omitempty" yaml:"experimental,omitempty" label:"allowEmpty" file:"allowEmpty"`
Pilot map[string]any `json:"pilot,omitempty" toml:"pilot,omitempty" yaml:"pilot,omitempty" label:"allowEmpty" file:"allowEmpty"`
Providers *providers `json:"providers,omitempty" toml:"providers,omitempty" yaml:"providers,omitempty" label:"allowEmpty" file:"allowEmpty"`
Tracing *tracing `json:"tracing,omitempty" toml:"tracing,omitempty" yaml:"tracing,omitempty" label:"allowEmpty" file:"allowEmpty"`
}
func (c *configuration) deprecationNotice(logger zerolog.Logger) bool {
if c == nil {
return false
}
var incompatible bool
if c.Pilot != nil {
incompatible = true
logger.Error().Msg("Pilot configuration has been removed in v3, please remove all Pilot-related static configuration for Traefik to start." +
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#pilot")
}
incompatibleExperimental := c.Experimental.deprecationNotice(logger)
incompatibleProviders := c.Providers.deprecationNotice(logger)
incompatibleTracing := c.Tracing.deprecationNotice(logger)
return incompatible || incompatibleExperimental || incompatibleProviders || incompatibleTracing
}
type providers struct {
Docker *docker `json:"docker,omitempty" toml:"docker,omitempty" yaml:"docker,omitempty" label:"allowEmpty" file:"allowEmpty"`
Swarm *swarm `json:"swarm,omitempty" toml:"swarm,omitempty" yaml:"swarm,omitempty" label:"allowEmpty" file:"allowEmpty"`
Consul *consul `json:"consul,omitempty" toml:"consul,omitempty" yaml:"consul,omitempty" label:"allowEmpty" file:"allowEmpty"`
ConsulCatalog *consulCatalog `json:"consulCatalog,omitempty" toml:"consulCatalog,omitempty" yaml:"consulCatalog,omitempty" label:"allowEmpty" file:"allowEmpty"`
Nomad *nomad `json:"nomad,omitempty" toml:"nomad,omitempty" yaml:"nomad,omitempty" label:"allowEmpty" file:"allowEmpty"`
Marathon map[string]any `json:"marathon,omitempty" toml:"marathon,omitempty" yaml:"marathon,omitempty" label:"allowEmpty" file:"allowEmpty"`
Rancher map[string]any `json:"rancher,omitempty" toml:"rancher,omitempty" yaml:"rancher,omitempty" label:"allowEmpty" file:"allowEmpty"`
ETCD *etcd `json:"etcd,omitempty" toml:"etcd,omitempty" yaml:"etcd,omitempty" label:"allowEmpty" file:"allowEmpty"`
Redis *redis `json:"redis,omitempty" toml:"redis,omitempty" yaml:"redis,omitempty" label:"allowEmpty" file:"allowEmpty"`
HTTP *http `json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" label:"allowEmpty" file:"allowEmpty"`
}
func (p *providers) deprecationNotice(logger zerolog.Logger) bool {
if p == nil {
return false
}
var incompatible bool
if p.Marathon != nil {
incompatible = true
logger.Error().Msg("Marathon provider has been removed in v3, please remove all Marathon-related static configuration for Traefik to start." +
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#marathon-provider")
}
if p.Rancher != nil {
incompatible = true
logger.Error().Msg("Rancher provider has been removed in v3, please remove all Rancher-related static configuration for Traefik to start." +
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#rancher-v1-provider")
}
dockerIncompatible := p.Docker.deprecationNotice(logger)
consulIncompatible := p.Consul.deprecationNotice(logger)
consulCatalogIncompatible := p.ConsulCatalog.deprecationNotice(logger)
nomadIncompatible := p.Nomad.deprecationNotice(logger)
swarmIncompatible := p.Swarm.deprecationNotice(logger)
etcdIncompatible := p.ETCD.deprecationNotice(logger)
redisIncompatible := p.Redis.deprecationNotice(logger)
httpIncompatible := p.HTTP.deprecationNotice(logger)
return incompatible ||
dockerIncompatible ||
consulIncompatible ||
consulCatalogIncompatible ||
nomadIncompatible ||
swarmIncompatible ||
etcdIncompatible ||
redisIncompatible ||
httpIncompatible
}
type tls struct {
CAOptional *bool `json:"caOptional,omitempty" toml:"caOptional,omitempty" yaml:"caOptional,omitempty"`
}
type docker struct {
SwarmMode *bool `json:"swarmMode,omitempty" toml:"swarmMode,omitempty" yaml:"swarmMode,omitempty"`
TLS *tls `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty"`
}
func (d *docker) deprecationNotice(logger zerolog.Logger) bool {
if d == nil {
return false
}
var incompatible bool
if d.SwarmMode != nil {
incompatible = true
logger.Error().Msg("Docker provider `swarmMode` option has been removed in v3, please use the Swarm Provider instead." +
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#docker-docker-swarm")
}
if d.TLS != nil && d.TLS.CAOptional != nil {
incompatible = true
logger.Error().Msg("Docker provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." +
"Please remove all occurrences from the static configuration for Traefik to start." +
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#tlscaoptional")
}
return incompatible
}
type swarm struct {
TLS *tls `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty"`
}
func (s *swarm) deprecationNotice(logger zerolog.Logger) bool {
if s == nil {
return false
}
var incompatible bool
if s.TLS != nil && s.TLS.CAOptional != nil {
incompatible = true
logger.Error().Msg("Swarm provider `tls.CAOptional` option does not exist, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." +
"Please remove all occurrences from the static configuration for Traefik to start.")
}
return incompatible
}
type etcd struct {
TLS *tls `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty"`
}
func (e *etcd) deprecationNotice(logger zerolog.Logger) bool {
if e == nil {
return false
}
var incompatible bool
if e.TLS != nil && e.TLS.CAOptional != nil {
incompatible = true
logger.Error().Msg("ETCD provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." +
"Please remove all occurrences from the static configuration for Traefik to start." +
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#tlscaoptional_3")
}
return incompatible
}
type redis struct {
TLS *tls `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty"`
}
func (r *redis) deprecationNotice(logger zerolog.Logger) bool {
if r == nil {
return false
}
var incompatible bool
if r.TLS != nil && r.TLS.CAOptional != nil {
incompatible = true
logger.Error().Msg("Redis provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." +
"Please remove all occurrences from the static configuration for Traefik to start." +
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#tlscaoptional_4")
}
return incompatible
}
type consul struct {
Namespace *string `json:"namespace,omitempty" toml:"namespace,omitempty" yaml:"namespace,omitempty"`
TLS *tls `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty"`
}
func (c *consul) deprecationNotice(logger zerolog.Logger) bool {
if c == nil {
return false
}
var incompatible bool
if c.Namespace != nil {
incompatible = true
logger.Error().Msg("Consul provider `namespace` option has been removed, please use the `namespaces` option instead." +
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#consul-provider")
}
if c.TLS != nil && c.TLS.CAOptional != nil {
incompatible = true
logger.Error().Msg("Consul provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." +
"Please remove all occurrences from the static configuration for Traefik to start." +
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#tlscaoptional_1")
}
return incompatible
}
type consulCatalog struct {
Namespace *string `json:"namespace,omitempty" toml:"namespace,omitempty" yaml:"namespace,omitempty"`
Endpoint *endpointConfig `json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty" label:"allowEmpty" file:"allowEmpty"`
}
type endpointConfig struct {
TLS *tls `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty"`
}
func (c *consulCatalog) deprecationNotice(logger zerolog.Logger) bool {
if c == nil {
return false
}
var incompatible bool
if c.Namespace != nil {
incompatible = true
logger.Error().Msg("ConsulCatalog provider `namespace` option has been removed, please use the `namespaces` option instead." +
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#consulcatalog-provider")
}
if c.Endpoint != nil && c.Endpoint.TLS != nil && c.Endpoint.TLS.CAOptional != nil {
incompatible = true
logger.Error().Msg("ConsulCatalog provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." +
"Please remove all occurrences from the static configuration for Traefik to start." +
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#endpointtlscaoptional")
}
return incompatible
}
type nomad struct {
Namespace *string `json:"namespace,omitempty" toml:"namespace,omitempty" yaml:"namespace,omitempty"`
Endpoint *endpointConfig `json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty" label:"allowEmpty" file:"allowEmpty"`
}
func (n *nomad) deprecationNotice(logger zerolog.Logger) bool {
if n == nil {
return false
}
var incompatible bool
if n.Namespace != nil {
incompatible = true
logger.Error().Msg("Nomad provider `namespace` option has been removed, please use the `namespaces` option instead." +
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#nomad-provider")
}
if n.Endpoint != nil && n.Endpoint.TLS != nil && n.Endpoint.TLS.CAOptional != nil {
incompatible = true
logger.Error().Msg("Nomad provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." +
"Please remove all occurrences from the static configuration for Traefik to start." +
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#endpointtlscaoptional_1")
}
return incompatible
}
type http struct {
TLS *tls `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty"`
}
func (h *http) deprecationNotice(logger zerolog.Logger) bool {
if h == nil {
return false
}
var incompatible bool
if h.TLS != nil && h.TLS.CAOptional != nil {
incompatible = true
logger.Error().Msg("HTTP provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." +
"Please remove all occurrences from the static configuration for Traefik to start." +
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#tlscaoptional_2")
}
return incompatible
}
type experimental struct {
HTTP3 *bool `json:"http3,omitempty" toml:"http3,omitempty" yaml:"http3,omitempty"`
}
func (e *experimental) deprecationNotice(logger zerolog.Logger) bool {
if e == nil {
return false
}
if e.HTTP3 != nil {
logger.Error().Msg("HTTP3 is not an experimental feature in v3 and the associated enablement has been removed." +
"Please remove its usage from the static configuration for Traefik to start." +
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#http3-experimental-configuration")
return true
}
return false
}
type tracing struct {
SpanNameLimit *int `json:"spanNameLimit,omitempty" toml:"spanNameLimit,omitempty" yaml:"spanNameLimit,omitempty"`
Jaeger map[string]any `json:"jaeger,omitempty" toml:"jaeger,omitempty" yaml:"jaeger,omitempty" label:"allowEmpty" file:"allowEmpty"`
Zipkin map[string]any `json:"zipkin,omitempty" toml:"zipkin,omitempty" yaml:"zipkin,omitempty" label:"allowEmpty" file:"allowEmpty"`
Datadog map[string]any `json:"datadog,omitempty" toml:"datadog,omitempty" yaml:"datadog,omitempty" label:"allowEmpty" file:"allowEmpty"`
Instana map[string]any `json:"instana,omitempty" toml:"instana,omitempty" yaml:"instana,omitempty" label:"allowEmpty" file:"allowEmpty"`
Haystack map[string]any `json:"haystack,omitempty" toml:"haystack,omitempty" yaml:"haystack,omitempty" label:"allowEmpty" file:"allowEmpty"`
Elastic map[string]any `json:"elastic,omitempty" toml:"elastic,omitempty" yaml:"elastic,omitempty" label:"allowEmpty" file:"allowEmpty"`
}
func (t *tracing) deprecationNotice(logger zerolog.Logger) bool {
if t == nil {
return false
}
var incompatible bool
if t.SpanNameLimit != nil {
incompatible = true
logger.Error().Msg("SpanNameLimit option for Tracing has been removed in v3, as Span names are now of a fixed length." +
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#tracing")
}
if t.Jaeger != nil {
incompatible = true
logger.Error().Msg("Jaeger Tracing backend has been removed in v3, please remove all Jaeger-related Tracing static configuration for Traefik to start." +
"In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." +
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#tracing")
}
if t.Zipkin != nil {
incompatible = true
logger.Error().Msg("Zipkin Tracing backend has been removed in v3, please remove all Zipkin-related Tracing static configuration for Traefik to start." +
"In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." +
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#tracing")
}
if t.Datadog != nil {
incompatible = true
logger.Error().Msg("Datadog Tracing backend has been removed in v3, please remove all Datadog-related Tracing static configuration for Traefik to start." +
"In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." +
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#tracing")
}
if t.Instana != nil {
incompatible = true
logger.Error().Msg("Instana Tracing backend has been removed in v3, please remove all Instana-related Tracing static configuration for Traefik to start." +
"In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." +
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#tracing")
}
if t.Haystack != nil {
incompatible = true
logger.Error().Msg("Haystack Tracing backend has been removed in v3, please remove all Haystack-related Tracing static configuration for Traefik to start." +
"In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." +
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#tracing")
}
if t.Elastic != nil {
incompatible = true
logger.Error().Msg("Elastic Tracing backend has been removed in v3, please remove all Elastic-related Tracing static configuration for Traefik to start." +
"In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." +
"For more information please read the migration guide: https://doc.traefik.io/traefik/v3.0/migration/v2-to-v3/#tracing")
}
return incompatible
}

404
pkg/cli/deprecation_test.go Normal file
View file

@ -0,0 +1,404 @@
package cli
import (
"testing"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/traefik/paerser/cli"
"github.com/traefik/traefik/v3/cmd"
)
func ptr[T any](t T) *T {
return &t
}
func TestDeprecationNotice(t *testing.T) {
tests := []struct {
desc string
config configuration
}{
{
desc: "Docker provider swarmMode option is incompatible",
config: configuration{
Providers: &providers{
Docker: &docker{
SwarmMode: ptr(true),
},
},
},
},
{
desc: "Docker provider tls.CAOptional option is incompatible",
config: configuration{
Providers: &providers{
Docker: &docker{
TLS: &tls{
CAOptional: ptr(true),
},
},
},
},
},
{
desc: "Swarm provider tls.CAOptional option is incompatible",
config: configuration{
Providers: &providers{
Swarm: &swarm{
TLS: &tls{
CAOptional: ptr(true),
},
},
},
},
},
{
desc: "Consul provider namespace option is incompatible",
config: configuration{
Providers: &providers{
Consul: &consul{
Namespace: ptr("foobar"),
},
},
},
},
{
desc: "Consul provider tls.CAOptional option is incompatible",
config: configuration{
Providers: &providers{
Consul: &consul{
TLS: &tls{
CAOptional: ptr(true),
},
},
},
},
},
{
desc: "ConsulCatalog provider namespace option is incompatible",
config: configuration{
Providers: &providers{
ConsulCatalog: &consulCatalog{
Namespace: ptr("foobar"),
},
},
},
},
{
desc: "ConsulCatalog provider tls.CAOptional option is incompatible",
config: configuration{
Providers: &providers{
ConsulCatalog: &consulCatalog{
Endpoint: &endpointConfig{
TLS: &tls{
CAOptional: ptr(true),
},
},
},
},
},
},
{
desc: "Nomad provider namespace option is incompatible",
config: configuration{
Providers: &providers{
Nomad: &nomad{
Namespace: ptr("foobar"),
},
},
},
},
{
desc: "Nomad provider tls.CAOptional option is incompatible",
config: configuration{
Providers: &providers{
Nomad: &nomad{
Endpoint: &endpointConfig{
TLS: &tls{
CAOptional: ptr(true),
},
},
},
},
},
},
{
desc: "Marathon configuration is incompatible",
config: configuration{
Providers: &providers{
Marathon: map[string]any{
"foo": "bar",
},
},
},
},
{
desc: "Rancher configuration is incompatible",
config: configuration{
Providers: &providers{
Rancher: map[string]any{
"foo": "bar",
},
},
},
},
{
desc: "ETCD provider tls.CAOptional option is incompatible",
config: configuration{
Providers: &providers{
ETCD: &etcd{
TLS: &tls{
CAOptional: ptr(true),
},
},
},
},
},
{
desc: "Redis provider tls.CAOptional option is incompatible",
config: configuration{
Providers: &providers{
Redis: &redis{
TLS: &tls{
CAOptional: ptr(true),
},
},
},
},
},
{
desc: "HTTP provider tls.CAOptional option is incompatible",
config: configuration{
Providers: &providers{
HTTP: &http{
TLS: &tls{
CAOptional: ptr(true),
},
},
},
},
},
{
desc: "Pilot configuration is incompatible",
config: configuration{
Pilot: map[string]any{
"foo": "bar",
},
},
},
{
desc: "Experimental HTTP3 enablement configuration is incompatible",
config: configuration{
Experimental: &experimental{
HTTP3: ptr(true),
},
},
},
{
desc: "Tracing SpanNameLimit option is incompatible",
config: configuration{
Tracing: &tracing{
SpanNameLimit: ptr(42),
},
},
},
{
desc: "Tracing Jaeger configuration is incompatible",
config: configuration{
Tracing: &tracing{
Jaeger: map[string]any{
"foo": "bar",
},
},
},
},
{
desc: "Tracing Zipkin configuration is incompatible",
config: configuration{
Tracing: &tracing{
Zipkin: map[string]any{
"foo": "bar",
},
},
},
},
{
desc: "Tracing Datadog configuration is incompatible",
config: configuration{
Tracing: &tracing{
Datadog: map[string]any{
"foo": "bar",
},
},
},
},
{
desc: "Tracing Instana configuration is incompatible",
config: configuration{
Tracing: &tracing{
Instana: map[string]any{
"foo": "bar",
},
},
},
},
{
desc: "Tracing Haystack configuration is incompatible",
config: configuration{
Tracing: &tracing{
Haystack: map[string]any{
"foo": "bar",
},
},
},
},
{
desc: "Tracing Elastic configuration is incompatible",
config: configuration{
Tracing: &tracing{
Elastic: map[string]any{
"foo": "bar",
},
},
},
},
}
for _, test := range tests {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
var gotLog bool
var gotLevel zerolog.Level
testHook := zerolog.HookFunc(func(e *zerolog.Event, level zerolog.Level, message string) {
gotLog = true
gotLevel = level
})
logger := log.With().Logger().Hook(testHook)
assert.True(t, test.config.deprecationNotice(logger))
assert.True(t, gotLog)
assert.Equal(t, zerolog.ErrorLevel, gotLevel)
})
}
}
func TestLoad(t *testing.T) {
testCases := []struct {
desc string
args []string
env map[string]string
wantDeprecated bool
}{
{
desc: "Empty",
args: []string{},
wantDeprecated: false,
},
{
desc: "[FLAG] providers.marathon is deprecated",
args: []string{
"--access-log",
"--log.level=DEBUG",
"--entrypoints.test.http.tls",
"--providers.nomad.endpoint.tls.insecureskipverify=true",
"--providers.marathon",
},
wantDeprecated: true,
},
{
desc: "[FLAG] multiple deprecated",
args: []string{
"--access-log",
"--log.level=DEBUG",
"--entrypoints.test.http.tls",
"--providers.marathon",
"--pilot.token=XXX",
},
wantDeprecated: true,
},
{
desc: "[FLAG] no deprecated",
args: []string{
"--access-log",
"--log.level=DEBUG",
"--entrypoints.test.http.tls",
"--providers.docker",
},
wantDeprecated: false,
},
{
desc: "[ENV] providers.marathon is deprecated",
env: map[string]string{
"TRAEFIK_ACCESS_LOG": "",
"TRAEFIK_LOG_LEVEL": "DEBUG",
"TRAEFIK_ENTRYPOINT_TEST_HTTP_TLS": "true",
"TRAEFIK_PROVIDERS_MARATHON": "true",
},
wantDeprecated: true,
},
{
desc: "[ENV] multiple deprecated",
env: map[string]string{
"TRAEFIK_ACCESS_LOG": "true",
"TRAEFIK_LOG_LEVEL": "DEBUG",
"TRAEFIK_ENTRYPOINT_TEST_HTTP_TLS": "true",
"TRAEFIK_PROVIDERS_MARATHON": "true",
"TRAEFIK_PILOT_TOKEN": "xxx",
},
wantDeprecated: true,
},
{
desc: "[ENV] no deprecated",
env: map[string]string{
"TRAEFIK_ACCESS_LOG": "true",
"TRAEFIK_LOG_LEVEL": "DEBUG",
"TRAEFIK_ENTRYPOINT_TEST_HTTP_TLS": "true",
},
wantDeprecated: false,
},
{
desc: "[FILE] providers.marathon is deprecated",
args: []string{
"--configfile=./fixtures/traefik_deprecated.toml",
},
wantDeprecated: true,
},
{
desc: "[FILE] multiple deprecated",
args: []string{
"--configfile=./fixtures/traefik_multiple_deprecated.toml",
},
wantDeprecated: true,
},
{
desc: "[FILE] no deprecated",
args: []string{
"--configfile=./fixtures/traefik_no_deprecated.toml",
},
wantDeprecated: false,
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
tconfig := cmd.NewTraefikConfiguration()
c := &cli.Command{Configuration: tconfig}
l := DeprecationLoader{}
for name, val := range test.env {
t.Setenv(name, val)
}
deprecated, err := l.Load(test.args, c)
assert.Equal(t, test.wantDeprecated, deprecated)
if !test.wantDeprecated {
require.NoError(t, err)
}
})
}
}

View file

@ -0,0 +1,5 @@
[accesslog]
[entrypoints.test.http.tls]
[providers.marathon]

View file

@ -0,0 +1,8 @@
[accesslog]
[entrypoints.test.http.tls]
[providers.marathon]
[pilot]
token="xxx"

View file

@ -0,0 +1,3 @@
[accesslog]
[entrypoints.test.http.tls]

View file

@ -5,13 +5,14 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/traefik/traefik/v3/pkg/collector/hydratation"
"github.com/traefik/traefik/v3/pkg/config/static"
)
func Test_createBody(t *testing.T) {
var staticConfiguration static.Configuration
err := hydrate(&staticConfiguration)
err := hydratation.Hydrate(&staticConfiguration)
require.NoError(t, err)
buffer, err := createBody(&staticConfiguration)

View file

@ -1,4 +1,4 @@
package collector
package hydratation
import (
"fmt"
@ -17,7 +17,8 @@ const (
defaultMapKeyPrefix = "name"
)
func hydrate(element interface{}) error {
// Hydrate hydrates a configuration.
func Hydrate(element interface{}) error {
field := reflect.ValueOf(element)
return fill(field)
}
@ -41,9 +42,7 @@ func fill(field reflect.Value) error {
return err
}
case reflect.Interface:
if err := fill(field.Elem()); err != nil {
return err
}
setTyped(field, defaultString)
case reflect.String:
setTyped(field, defaultString)
case reflect.Int:
@ -118,7 +117,7 @@ func makeKeyName(typ reflect.Type) string {
case reflect.String,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Bool, reflect.Float32, reflect.Float64:
reflect.Bool, reflect.Float32, reflect.Float64, reflect.Interface:
return defaultMapKeyPrefix
default:
return typ.Name()

View file

@ -24,7 +24,7 @@ type Configuration struct {
HTTP *HTTPConfiguration `json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" export:"true"`
TCP *TCPConfiguration `json:"tcp,omitempty" toml:"tcp,omitempty" yaml:"tcp,omitempty" export:"true"`
UDP *UDPConfiguration `json:"udp,omitempty" toml:"udp,omitempty" yaml:"udp,omitempty" export:"true"`
TLS *TLSConfiguration `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"`
TLS *TLSConfiguration `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"-" export:"true"`
}
// +k8s:deepcopy-gen=true

View file

@ -37,8 +37,9 @@ type HTTPConfiguration struct {
// Model is a set of default router's values.
type Model struct {
Middlewares []string `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"`
TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
Middlewares []string `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"`
TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
DefaultRuleSyntax string `json:"-" toml:"-" yaml:"-" label:"-" file:"-" kv:"-" export:"true"`
}
// +k8s:deepcopy-gen=true
@ -59,6 +60,7 @@ type Router struct {
Middlewares []string `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"`
Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty" export:"true"`
Rule string `json:"rule,omitempty" toml:"rule,omitempty" yaml:"rule,omitempty"`
RuleSyntax string `json:"ruleSyntax,omitempty" toml:"ruleSyntax,omitempty" yaml:"ruleSyntax,omitempty" export:"true"`
Priority int `json:"priority,omitempty" toml:"priority,omitempty,omitzero" yaml:"priority,omitempty" export:"true"`
TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
DefaultRule bool `json:"-" toml:"-" yaml:"-" label:"-" file:"-"`
@ -155,6 +157,10 @@ type Cookie struct {
// SameSite defines the same site policy.
// More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
SameSite string `json:"sameSite,omitempty" toml:"sameSite,omitempty" yaml:"sameSite,omitempty" export:"true"`
// MaxAge indicates the number of seconds until the cookie expires.
// When set to a negative number, the cookie expires immediately.
// When set to zero, the cookie never expires.
MaxAge int `json:"maxAge,omitempty" toml:"maxAge,omitempty" yaml:"maxAge,omitempty" export:"true"`
}
// +k8s:deepcopy-gen=true
@ -221,6 +227,7 @@ func (r *ResponseForwarding) SetDefaults() {
// Server holds the server configuration.
type Server struct {
URL string `json:"url,omitempty" toml:"url,omitempty" yaml:"url,omitempty" label:"-"`
Weight *int `json:"weight,omitempty" toml:"weight,omitempty" yaml:"weight,omitempty" label:"weight"`
Scheme string `json:"-" toml:"-" yaml:"-" file:"-"`
Port string `json:"-" toml:"-" yaml:"-" file:"-"`
}

View file

@ -159,6 +159,8 @@ type Compress struct {
// ExcludedContentTypes defines the list of content types to compare the Content-Type header of the incoming requests and responses before compressing.
// `application/grpc` is always excluded.
ExcludedContentTypes []string `json:"excludedContentTypes,omitempty" toml:"excludedContentTypes,omitempty" yaml:"excludedContentTypes,omitempty" export:"true"`
// IncludedContentTypes defines the list of content types to compare the Content-Type header of the responses before compressing.
IncludedContentTypes []string `json:"includedContentTypes,omitempty" toml:"includedContentTypes,omitempty" yaml:"includedContentTypes,omitempty" export:"true"`
// MinResponseBodyBytes defines the minimum amount of bytes a response body must have to be compressed.
// Default: 1024.
MinResponseBodyBytes int `json:"minResponseBodyBytes,omitempty" toml:"minResponseBodyBytes,omitempty" yaml:"minResponseBodyBytes,omitempty" export:"true"`
@ -223,6 +225,8 @@ type ForwardAuth struct {
// AuthRequestHeaders defines the list of the headers to copy from the request to the authentication server.
// If not set or empty then all request headers are passed.
AuthRequestHeaders []string `json:"authRequestHeaders,omitempty" toml:"authRequestHeaders,omitempty" yaml:"authRequestHeaders,omitempty" export:"true"`
// AddAuthCookiesToResponse defines the list of cookies to copy from the authentication server response to the response.
AddAuthCookiesToResponse []string `json:"addAuthCookiesToResponse,omitempty" toml:"addAuthCookiesToResponse,omitempty" yaml:"addAuthCookiesToResponse,omitempty" export:"true"`
}
// +k8s:deepcopy-gen=true

View file

@ -16,11 +16,19 @@ type TCPConfiguration struct {
Routers map[string]*TCPRouter `json:"routers,omitempty" toml:"routers,omitempty" yaml:"routers,omitempty" export:"true"`
Services map[string]*TCPService `json:"services,omitempty" toml:"services,omitempty" yaml:"services,omitempty" export:"true"`
Middlewares map[string]*TCPMiddleware `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"`
Models map[string]*TCPModel `json:"-" toml:"-" yaml:"-" label:"-" file:"-" kv:"-" export:"true"`
ServersTransports map[string]*TCPServersTransport `json:"serversTransports,omitempty" toml:"serversTransports,omitempty" yaml:"serversTransports,omitempty" label:"-" export:"true"`
}
// +k8s:deepcopy-gen=true
// TCPModel is a set of default router's values.
type TCPModel struct {
DefaultRuleSyntax string `json:"-" toml:"-" yaml:"-" label:"-" file:"-" kv:"-" export:"true"`
}
// +k8s:deepcopy-gen=true
// TCPService holds a tcp service configuration (can only be of one type at the same time).
type TCPService struct {
LoadBalancer *TCPServersLoadBalancer `json:"loadBalancer,omitempty" toml:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty" export:"true"`
@ -56,6 +64,7 @@ type TCPRouter struct {
Middlewares []string `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"`
Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty" export:"true"`
Rule string `json:"rule,omitempty" toml:"rule,omitempty" yaml:"rule,omitempty"`
RuleSyntax string `json:"ruleSyntax,omitempty" toml:"ruleSyntax,omitempty" yaml:"ruleSyntax,omitempty" export:"true"`
Priority int `json:"priority,omitempty" toml:"priority,omitempty,omitzero" yaml:"priority,omitempty" export:"true"`
TLS *RouterTCPTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
}

View file

@ -132,6 +132,11 @@ func (in *Compress) DeepCopyInto(out *Compress) {
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.IncludedContentTypes != nil {
in, out := &in.IncludedContentTypes, &out.IncludedContentTypes
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
@ -324,6 +329,11 @@ func (in *ForwardAuth) DeepCopyInto(out *ForwardAuth) {
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.AddAuthCookiesToResponse != nil {
in, out := &in.AddAuthCookiesToResponse, &out.AddAuthCookiesToResponse
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
@ -1118,6 +1128,11 @@ func (in *RouterTLSConfig) DeepCopy() *RouterTLSConfig {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Server) DeepCopyInto(out *Server) {
*out = *in
if in.Weight != nil {
in, out := &in.Weight, &out.Weight
*out = new(int)
**out = **in
}
return
}
@ -1170,7 +1185,9 @@ func (in *ServersLoadBalancer) DeepCopyInto(out *ServersLoadBalancer) {
if in.Servers != nil {
in, out := &in.Servers, &out.Servers
*out = make([]Server, len(*in))
copy(*out, *in)
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
if in.HealthCheck != nil {
in, out := &in.HealthCheck, &out.HealthCheck
@ -1427,6 +1444,21 @@ func (in *TCPConfiguration) DeepCopyInto(out *TCPConfiguration) {
(*out)[key] = outVal
}
}
if in.Models != nil {
in, out := &in.Models, &out.Models
*out = make(map[string]*TCPModel, len(*in))
for key, val := range *in {
var outVal *TCPModel
if val == nil {
(*out)[key] = nil
} else {
in, out := &val, &outVal
*out = new(TCPModel)
**out = **in
}
(*out)[key] = outVal
}
}
if in.ServersTransports != nil {
in, out := &in.ServersTransports, &out.ServersTransports
*out = make(map[string]*TCPServersTransport, len(*in))
@ -1544,6 +1576,22 @@ func (in *TCPMiddleware) DeepCopy() *TCPMiddleware {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TCPModel) DeepCopyInto(out *TCPModel) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPModel.
func (in *TCPModel) DeepCopy() *TCPModel {
if in == nil {
return nil
}
out := new(TCPModel)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TCPRouter) DeepCopyInto(out *TCPRouter) {
*out = *in

View file

@ -1334,6 +1334,7 @@ func TestEncodeConfiguration(t *testing.T) {
"traefik.HTTP.Services.Service0.LoadBalancer.Sticky.Cookie.Name": "foobar",
"traefik.HTTP.Services.Service0.LoadBalancer.Sticky.Cookie.HTTPOnly": "true",
"traefik.HTTP.Services.Service0.LoadBalancer.Sticky.Cookie.Secure": "false",
"traefik.HTTP.Services.Service0.LoadBalancer.Sticky.Cookie.MaxAge": "0",
"traefik.HTTP.Services.Service0.LoadBalancer.ServersTransport": "foobar",
"traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Headers.name0": "foobar",
"traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Headers.name1": "foobar",

View file

@ -71,11 +71,23 @@ type Configuration struct {
CertificatesResolvers map[string]CertificateResolver `description:"Certificates resolvers configuration." json:"certificatesResolvers,omitempty" toml:"certificatesResolvers,omitempty" yaml:"certificatesResolvers,omitempty" export:"true"`
Experimental *Experimental `description:"experimental features." json:"experimental,omitempty" toml:"experimental,omitempty" yaml:"experimental,omitempty" export:"true"`
Experimental *Experimental `description:"Experimental features." json:"experimental,omitempty" toml:"experimental,omitempty" yaml:"experimental,omitempty" export:"true"`
Core *Core `description:"Core controls." json:"core,omitempty" toml:"core,omitempty" yaml:"core,omitempty" export:"true"`
Spiffe *SpiffeClientConfig `description:"SPIFFE integration configuration." json:"spiffe,omitempty" toml:"spiffe,omitempty" yaml:"spiffe,omitempty" export:"true"`
}
// Core configures Traefik core behavior.
type Core struct {
DefaultRuleSyntax string `description:"Defines the rule parser default syntax (v2 or v3)" json:"defaultRuleSyntax,omitempty" toml:"defaultRuleSyntax,omitempty" yaml:"defaultRuleSyntax,omitempty"`
}
// SetDefaults sets the default values.
func (c *Core) SetDefaults() {
c.DefaultRuleSyntax = "v3"
}
// SpiffeClientConfig defines the SPIFFE client configuration.
type SpiffeClientConfig struct {
WorkloadAPIAddr string `description:"Defines the workload API address." json:"workloadAPIAddr,omitempty" toml:"workloadAPIAddr,omitempty" yaml:"workloadAPIAddr,omitempty"`
@ -317,6 +329,17 @@ func (c *Configuration) ValidateConfiguration() error {
acmeEmail = resolver.ACME.Email
}
if c.Core != nil {
switch c.Core.DefaultRuleSyntax {
case "v3": // NOOP
case "v2":
// TODO: point to migration guide.
log.Warn().Msgf("v2 rules syntax is now deprecated, please use v3 instead...")
default:
return fmt.Errorf("unsupported default rule syntax configuration: %q", c.Core.DefaultRuleSyntax)
}
}
if c.Tracing != nil && c.Tracing.OTLP != nil {
if c.Tracing.OTLP.HTTP == nil && c.Tracing.OTLP.GRPC == nil {
return errors.New("tracing OTLP: at least one of HTTP and gRPC options should be defined")

View file

@ -13,12 +13,13 @@ import (
)
func TestDatadog(t *testing.T) {
t.Cleanup(StopDatadog)
udp.SetAddr(":18125")
// This is needed to make sure that UDP Listener listens for data a bit longer, otherwise it will quit after a millisecond
udp.Timeout = 5 * time.Second
datadogRegistry := RegisterDatadog(context.Background(), &types.Datadog{Address: ":18125", PushInterval: ptypes.Duration(time.Second), AddEntryPointsLabels: true, AddRoutersLabels: true, AddServicesLabels: true})
defer StopDatadog()
if !datadogRegistry.IsEpEnabled() || !datadogRegistry.IsRouterEnabled() || !datadogRegistry.IsSvcEnabled() {
t.Errorf("DatadogRegistry should return true for IsEnabled(), IsRouterEnabled() and IsSvcEnabled()")
@ -27,9 +28,7 @@ func TestDatadog(t *testing.T) {
}
func TestDatadogWithPrefix(t *testing.T) {
t.Cleanup(func() {
StopDatadog()
})
t.Cleanup(StopDatadog)
udp.SetAddr(":18125")
// This is needed to make sure that UDP Listener listens for data a bit longer, otherwise it will quit after a millisecond

View file

@ -6,7 +6,6 @@ import (
"io"
"net/http"
"net/http/httptest"
"regexp"
"strconv"
"testing"
"time"
@ -26,7 +25,6 @@ func TestInfluxDB2(t *testing.T) {
c <- &bodyStr
_, _ = fmt.Fprintln(w, "ok")
}))
defer ts.Close()
influxDB2Registry := RegisterInfluxDB2(context.Background(),
&types.InfluxDB2{
@ -39,7 +37,11 @@ func TestInfluxDB2(t *testing.T) {
AddRoutersLabels: true,
AddServicesLabels: true,
})
defer StopInfluxDB2()
t.Cleanup(func() {
StopInfluxDB2()
ts.Close()
})
if !influxDB2Registry.IsEpEnabled() || !influxDB2Registry.IsRouterEnabled() || !influxDB2Registry.IsSvcEnabled() {
t.Fatalf("InfluxDB2Registry should return true for IsEnabled(), IsRouterEnabled() and IsSvcEnabled()")
@ -137,14 +139,3 @@ func TestInfluxDB2(t *testing.T) {
assertMessage(t, *msgServiceRetries, expectedServiceRetries)
}
func assertMessage(t *testing.T, msg string, patterns []string) {
t.Helper()
for _, pattern := range patterns {
re := regexp.MustCompile(pattern)
match := re.FindStringSubmatch(msg)
if len(match) != 2 {
t.Errorf("Got %q %v, want %q", msg, match, pattern)
}
}
}

View file

@ -4,10 +4,12 @@ import (
"compress/gzip"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httptest"
"net/url"
"regexp"
"strconv"
"testing"
"time"
@ -287,10 +289,8 @@ func TestOpenTelemetry_GaugeCollectorSet(t *testing.T) {
}
func TestOpenTelemetry(t *testing.T) {
t.Parallel()
c := make(chan *string, 5)
c := make(chan *string)
defer close(c)
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gzr, err := gzip.NewReader(r.Body)
require.NoError(t, err)
@ -310,7 +310,11 @@ func TestOpenTelemetry(t *testing.T) {
w.WriteHeader(http.StatusOK)
}))
defer ts.Close()
t.Cleanup(func() {
StopOpenTelemetry()
ts.Close()
})
sURL, err := url.Parse(ts.URL)
require.NoError(t, err)
@ -333,57 +337,53 @@ func TestOpenTelemetry(t *testing.T) {
`({"key":"service.name","value":{"stringValue":"traefik"}})`,
`({"key":"service.version","value":{"stringValue":"` + version.Version + `"}})`,
}
msgMisc := <-c
assertMessage(t, *msgMisc, expected)
tryAssertMessage(t, c, expected)
// TODO: the len of startUnixNano is no supposed to be 20, it should be 19
expected = append(expected,
expectedConfig := []string{
`({"name":"traefik_config_reloads_total","description":"Config reloads","unit":"1","sum":{"dataPoints":\[{"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
`({"name":"traefik_config_last_reload_success","description":"Last config reload success","unit":"ms","gauge":{"dataPoints":\[{"timeUnixNano":"[\d]{19}","asDouble":1}\]}})`,
`({"name":"traefik_open_connections","description":"How many open connections exist, by entryPoint and protocol","unit":"1","gauge":{"dataPoints":\[{"attributes":\[{"key":"entrypoint","value":{"stringValue":"test"}},{"key":"protocol","value":{"stringValue":"TCP"}}\],"timeUnixNano":"[\d]{19}","asDouble":1}\]}})`,
)
}
registry.ConfigReloadsCounter().Add(1)
registry.LastConfigReloadSuccessGauge().Set(1)
registry.OpenConnectionsGauge().With("entrypoint", "test", "protocol", "TCP").Set(1)
msgServer := <-c
assertMessage(t, *msgServer, expected)
tryAssertMessage(t, c, expectedConfig)
expected = append(expected,
expectedTLSCerts := []string{
`({"name":"traefik_tls_certs_not_after","description":"Certificate expiration timestamp","unit":"ms","gauge":{"dataPoints":\[{"attributes":\[{"key":"key","value":{"stringValue":"value"}}\],"timeUnixNano":"[\d]{19}","asDouble":1}\]}})`,
)
}
registry.TLSCertsNotAfterTimestampGauge().With("key", "value").Set(1)
msgTLS := <-c
assertMessage(t, *msgTLS, expected)
tryAssertMessage(t, c, expectedTLSCerts)
expected = append(expected,
expectedEntryPoints := []string{
`({"name":"traefik_entrypoint_requests_total","description":"How many HTTP requests processed on an entrypoint, partitioned by status code, protocol, and method.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"200"}},{"key":"entrypoint","value":{"stringValue":"test1"}},{"key":"method","value":{"stringValue":"GET"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
`({"name":"traefik_entrypoint_requests_tls_total","description":"How many HTTP requests with TLS processed on an entrypoint, partitioned by TLS Version and TLS cipher Used.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"entrypoint","value":{"stringValue":"test2"}},{"key":"tls_cipher","value":{"stringValue":"bar"}},{"key":"tls_version","value":{"stringValue":"foo"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
`({"name":"traefik_entrypoint_request_duration_seconds","description":"How long it took to process the request on an entrypoint, partitioned by status code, protocol, and method.","unit":"ms","histogram":{"dataPoints":\[{"attributes":\[{"key":"entrypoint","value":{"stringValue":"test3"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","count":"1","sum":10000,"bucketCounts":\["0","0","0","0","0","0","0","0","0","0","0","1"\],"explicitBounds":\[0.005,0.01,0.025,0.05,0.1,0.25,0.5,1,2.5,5,10\],"min":10000,"max":10000}\],"aggregationTemporality":2}})`,
`({"name":"traefik_entrypoint_requests_bytes_total","description":"The total size of requests in bytes handled by an entrypoint, partitioned by status code, protocol, and method.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"200"}},{"key":"entrypoint","value":{"stringValue":"test1"}},{"key":"method","value":{"stringValue":"GET"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
`({"name":"traefik_entrypoint_responses_bytes_total","description":"The total size of responses in bytes handled by an entrypoint, partitioned by status code, protocol, and method.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"200"}},{"key":"entrypoint","value":{"stringValue":"test1"}},{"key":"method","value":{"stringValue":"GET"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
)
}
registry.EntryPointReqsCounter().With(nil, "entrypoint", "test1", "code", strconv.Itoa(http.StatusOK), "method", http.MethodGet).Add(1)
registry.EntryPointReqsTLSCounter().With("entrypoint", "test2", "tls_version", "foo", "tls_cipher", "bar").Add(1)
registry.EntryPointReqDurationHistogram().With("entrypoint", "test3").Observe(10000)
registry.EntryPointReqsBytesCounter().With("entrypoint", "test1", "code", strconv.Itoa(http.StatusOK), "method", http.MethodGet).Add(1)
registry.EntryPointRespsBytesCounter().With("entrypoint", "test1", "code", strconv.Itoa(http.StatusOK), "method", http.MethodGet).Add(1)
msgEntrypoint := <-c
assertMessage(t, *msgEntrypoint, expected)
tryAssertMessage(t, c, expectedEntryPoints)
expected = append(expected,
expectedRouters := []string{
`({"name":"traefik_router_requests_total","description":"How many HTTP requests are processed on a router, partitioned by service, status code, protocol, and method.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"(?:200|404)"}},{"key":"method","value":{"stringValue":"GET"}},{"key":"router","value":{"stringValue":"RouterReqsCounter"}},{"key":"service","value":{"stringValue":"test"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1},{"attributes":\[{"key":"code","value":{"stringValue":"(?:200|404)"}},{"key":"method","value":{"stringValue":"GET"}},{"key":"router","value":{"stringValue":"RouterReqsCounter"}},{"key":"service","value":{"stringValue":"test"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
`({"name":"traefik_router_requests_tls_total","description":"How many HTTP requests with TLS are processed on a router, partitioned by service, TLS Version, and TLS cipher Used.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"router","value":{"stringValue":"demo"}},{"key":"service","value":{"stringValue":"test"}},{"key":"tls_cipher","value":{"stringValue":"bar"}},{"key":"tls_version","value":{"stringValue":"foo"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
`({"name":"traefik_router_request_duration_seconds","description":"How long it took to process the request on a router, partitioned by service, status code, protocol, and method.","unit":"ms","histogram":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"200"}},{"key":"router","value":{"stringValue":"demo"}},{"key":"service","value":{"stringValue":"test"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","count":"1","sum":10000,"bucketCounts":\["0","0","0","0","0","0","0","0","0","0","0","1"\],"explicitBounds":\[0.005,0.01,0.025,0.05,0.1,0.25,0.5,1,2.5,5,10\],"min":10000,"max":10000}\],"aggregationTemporality":2}})`,
`({"name":"traefik_router_requests_bytes_total","description":"The total size of requests in bytes handled by a router, partitioned by status code, protocol, and method.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"404"}},{"key":"method","value":{"stringValue":"GET"}},{"key":"router","value":{"stringValue":"RouterReqsCounter"}},{"key":"service","value":{"stringValue":"test"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
`({"name":"traefik_router_responses_bytes_total","description":"The total size of responses in bytes handled by a router, partitioned by status code, protocol, and method.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"404"}},{"key":"method","value":{"stringValue":"GET"}},{"key":"router","value":{"stringValue":"RouterReqsCounter"}},{"key":"service","value":{"stringValue":"test"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
)
}
registry.RouterReqsCounter().With(nil, "router", "RouterReqsCounter", "service", "test", "code", strconv.Itoa(http.StatusNotFound), "method", http.MethodGet).Add(1)
registry.RouterReqsCounter().With(nil, "router", "RouterReqsCounter", "service", "test", "code", strconv.Itoa(http.StatusOK), "method", http.MethodGet).Add(1)
@ -391,18 +391,17 @@ func TestOpenTelemetry(t *testing.T) {
registry.RouterReqDurationHistogram().With("router", "demo", "service", "test", "code", strconv.Itoa(http.StatusOK)).Observe(10000)
registry.RouterReqsBytesCounter().With("router", "RouterReqsCounter", "service", "test", "code", strconv.Itoa(http.StatusNotFound), "method", http.MethodGet).Add(1)
registry.RouterRespsBytesCounter().With("router", "RouterReqsCounter", "service", "test", "code", strconv.Itoa(http.StatusNotFound), "method", http.MethodGet).Add(1)
msgRouter := <-c
assertMessage(t, *msgRouter, expected)
tryAssertMessage(t, c, expectedRouters)
expected = append(expected,
expectedServices := []string{
`({"name":"traefik_service_requests_total","description":"How many HTTP requests processed on a service, partitioned by status code, protocol, and method.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"(?:200|404)"}},{"key":"method","value":{"stringValue":"GET"}},{"key":"service","value":{"stringValue":"ServiceReqsCounter"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1},{"attributes":\[{"key":"code","value":{"stringValue":"(?:200|404)"}},{"key":"method","value":{"stringValue":"GET"}},{"key":"service","value":{"stringValue":"ServiceReqsCounter"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
`({"name":"traefik_service_requests_tls_total","description":"How many HTTP requests with TLS processed on a service, partitioned by TLS version and TLS cipher.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"service","value":{"stringValue":"test"}},{"key":"tls_cipher","value":{"stringValue":"bar"}},{"key":"tls_version","value":{"stringValue":"foo"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
`({"name":"traefik_service_request_duration_seconds","description":"How long it took to process the request on a service, partitioned by status code, protocol, and method.","unit":"ms","histogram":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"200"}},{"key":"service","value":{"stringValue":"test"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","count":"1","sum":10000,"bucketCounts":\["0","0","0","0","0","0","0","0","0","0","0","1"\],"explicitBounds":\[0.005,0.01,0.025,0.05,0.1,0.25,0.5,1,2.5,5,10\],"min":10000,"max":10000}\],"aggregationTemporality":2}})`,
`({"name":"traefik_service_server_up","description":"service server is up, described by gauge value of 0 or 1.","unit":"1","gauge":{"dataPoints":\[{"attributes":\[{"key":"service","value":{"stringValue":"test"}},{"key":"url","value":{"stringValue":"http://127.0.0.1"}}\],"timeUnixNano":"[\d]{19}","asDouble":1}\]}})`,
`({"name":"traefik_service_requests_bytes_total","description":"The total size of requests in bytes received by a service, partitioned by status code, protocol, and method.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"404"}},{"key":"method","value":{"stringValue":"GET"}},{"key":"service","value":{"stringValue":"ServiceReqsCounter"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
`({"name":"traefik_service_responses_bytes_total","description":"The total size of responses in bytes returned by a service, partitioned by status code, protocol, and method.","unit":"1","sum":{"dataPoints":\[{"attributes":\[{"key":"code","value":{"stringValue":"404"}},{"key":"method","value":{"stringValue":"GET"}},{"key":"service","value":{"stringValue":"ServiceReqsCounter"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\],"aggregationTemporality":2,"isMonotonic":true}})`,
)
}
registry.ServiceReqsCounter().With(nil, "service", "ServiceReqsCounter", "code", strconv.Itoa(http.StatusOK), "method", http.MethodGet).Add(1)
registry.ServiceReqsCounter().With(nil, "service", "ServiceReqsCounter", "code", strconv.Itoa(http.StatusNotFound), "method", http.MethodGet).Add(1)
@ -411,21 +410,19 @@ func TestOpenTelemetry(t *testing.T) {
registry.ServiceServerUpGauge().With("service", "test", "url", "http://127.0.0.1").Set(1)
registry.ServiceReqsBytesCounter().With("service", "ServiceReqsCounter", "code", strconv.Itoa(http.StatusNotFound), "method", http.MethodGet).Add(1)
registry.ServiceRespsBytesCounter().With("service", "ServiceReqsCounter", "code", strconv.Itoa(http.StatusNotFound), "method", http.MethodGet).Add(1)
msgService := <-c
assertMessage(t, *msgService, expected)
tryAssertMessage(t, c, expectedServices)
expected = append(expected,
expectedServicesRetries := []string{
`({"attributes":\[{"key":"service","value":{"stringValue":"foobar"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1})`,
`({"attributes":\[{"key":"service","value":{"stringValue":"test"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":2})`,
)
}
registry.ServiceRetriesCounter().With("service", "test").Add(1)
registry.ServiceRetriesCounter().With("service", "test").Add(1)
registry.ServiceRetriesCounter().With("service", "foobar").Add(1)
msgServiceRetries := <-c
assertMessage(t, *msgServiceRetries, expected)
tryAssertMessage(t, c, expectedServicesRetries)
// We cannot rely on the previous expected pattern,
// because this pattern was for matching only one dataPoint in the histogram,
@ -437,14 +434,46 @@ func TestOpenTelemetry(t *testing.T) {
registry.EntryPointReqDurationHistogram().With("entrypoint", "myEntrypoint").Observe(10000)
registry.EntryPointReqDurationHistogram().With("entrypoint", "myEntrypoint").Observe(20000)
msgEntryPointReqDurationHistogram := <-c
assertMessage(t, *msgEntryPointReqDurationHistogram, expectedEntryPointReqDuration)
// We need to unlock the HTTP Server for the last export call when stopping
// OpenTelemetry.
go func() {
<-c
}()
StopOpenTelemetry()
tryAssertMessage(t, c, expectedEntryPointReqDuration)
}
func assertMessage(t *testing.T, msg string, expected []string) {
t.Helper()
errs := verifyMessage(msg, expected)
for _, err := range errs {
t.Error(err)
}
}
func tryAssertMessage(t *testing.T, c chan *string, expected []string) {
t.Helper()
var errs []error
timeout := time.After(1 * time.Second)
for {
select {
case <-timeout:
for _, err := range errs {
t.Error(err)
}
case msg := <-c:
errs = verifyMessage(*msg, expected)
if len(errs) == 0 {
return
}
}
}
}
func verifyMessage(msg string, expected []string) []error {
var errs []error
for _, pattern := range expected {
re := regexp.MustCompile(pattern)
match := re.FindStringSubmatch(msg)
if len(match) != 2 {
errs = append(errs, fmt.Errorf("Got %q %v, want %q", msg, match, pattern))
}
}
return errs
}

View file

@ -48,19 +48,26 @@ type forwardAuth struct {
client http.Client
trustForwardHeader bool
authRequestHeaders []string
addAuthCookiesToResponse map[string]struct{}
}
// NewForward creates a forward auth middleware.
func NewForward(ctx context.Context, next http.Handler, config dynamic.ForwardAuth, name string) (http.Handler, error) {
middlewares.GetLogger(ctx, name, typeNameForward).Debug().Msg("Creating middleware")
addAuthCookiesToResponse := make(map[string]struct{})
for _, cookieName := range config.AddAuthCookiesToResponse {
addAuthCookiesToResponse[cookieName] = struct{}{}
}
fa := &forwardAuth{
address: config.Address,
authResponseHeaders: config.AuthResponseHeaders,
next: next,
name: name,
trustForwardHeader: config.TrustForwardHeader,
authRequestHeaders: config.AuthRequestHeaders,
address: config.Address,
authResponseHeaders: config.AuthResponseHeaders,
next: next,
name: name,
trustForwardHeader: config.TrustForwardHeader,
authRequestHeaders: config.AuthRequestHeaders,
addAuthCookiesToResponse: addAuthCookiesToResponse,
}
// Ensure our request client does not follow redirects
@ -211,7 +218,35 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
tracing.LogResponseCode(forwardSpan, forwardResponse.StatusCode, trace.SpanKindClient)
req.RequestURI = req.URL.RequestURI()
fa.next.ServeHTTP(rw, req)
authCookies := forwardResponse.Cookies()
if len(authCookies) == 0 {
fa.next.ServeHTTP(rw, req)
return
}
fa.next.ServeHTTP(middlewares.NewResponseModifier(rw, req, fa.buildModifier(authCookies)), req)
}
func (fa *forwardAuth) buildModifier(authCookies []*http.Cookie) func(res *http.Response) error {
return func(res *http.Response) error {
cookies := res.Cookies()
res.Header.Del("Set-Cookie")
for _, cookie := range cookies {
if _, found := fa.addAuthCookiesToResponse[cookie.Name]; !found {
res.Header.Add("Set-Cookie", cookie.String())
}
}
for _, cookie := range authCookies {
if _, found := fa.addAuthCookiesToResponse[cookie.Name]; found {
res.Header.Add("Set-Cookie", cookie.String())
}
}
return nil
}
}
func writeHeader(req, forwardReq *http.Request, trustForwardHeader bool, allowedHeaders []string) {

View file

@ -66,6 +66,8 @@ func TestForwardAuthSuccess(t *testing.T) {
w.Header().Add("X-Auth-Group", "group1")
w.Header().Add("X-Auth-Group", "group2")
w.Header().Add("Foo-Bar", "auth-value")
w.Header().Add("Set-Cookie", "authCookie=Auth")
w.Header().Add("Set-Cookie", "authCookieNotAdded=Auth")
fmt.Fprintln(w, "Success")
}))
t.Cleanup(server.Close)
@ -76,6 +78,9 @@ func TestForwardAuthSuccess(t *testing.T) {
assert.Equal(t, []string{"group1", "group2"}, r.Header["X-Auth-Group"])
assert.Equal(t, "auth-value", r.Header.Get("Foo-Bar"))
assert.Empty(t, r.Header.Get("Foo-Baz"))
w.Header().Add("Set-Cookie", "authCookie=Backend")
w.Header().Add("Set-Cookie", "backendCookie=Backend")
w.Header().Add("Other-Header", "BackendHeaderValue")
fmt.Fprintln(w, "traefik")
})
@ -83,6 +88,7 @@ func TestForwardAuthSuccess(t *testing.T) {
Address: server.URL,
AuthResponseHeaders: []string{"X-Auth-User", "X-Auth-Group"},
AuthResponseHeadersRegex: "^Foo-",
AddAuthCookiesToResponse: []string{"authCookie"},
}
middleware, err := NewForward(context.Background(), next, auth, "authTest")
require.NoError(t, err)
@ -97,6 +103,8 @@ func TestForwardAuthSuccess(t *testing.T) {
res, err := http.DefaultClient.Do(req)
require.NoError(t, err)
assert.Equal(t, http.StatusOK, res.StatusCode)
assert.Equal(t, []string{"backendCookie=Backend", "authCookie=Auth"}, res.Header["Set-Cookie"])
assert.Equal(t, []string{"BackendHeaderValue"}, res.Header["Other-Header"])
body, err := io.ReadAll(res.Body)
require.NoError(t, err)

View file

@ -22,7 +22,11 @@ const (
// Config is the Brotli handler configuration.
type Config struct {
// ExcludedContentTypes is the list of content types for which we should not compress.
// Mutually exclusive with the IncludedContentTypes option.
ExcludedContentTypes []string
// IncludedContentTypes is the list of content types for which compression should be exclusively enabled.
// Mutually exclusive with the ExcludedContentTypes option.
IncludedContentTypes []string
// MinSize is the minimum size (in bytes) required to enable compression.
MinSize int
}
@ -33,14 +37,28 @@ func NewWrapper(cfg Config) (func(http.Handler) http.HandlerFunc, error) {
return nil, fmt.Errorf("minimum size must be greater than or equal to zero")
}
var contentTypes []parsedContentType
if len(cfg.ExcludedContentTypes) > 0 && len(cfg.IncludedContentTypes) > 0 {
return nil, fmt.Errorf("excludedContentTypes and includedContentTypes options are mutually exclusive")
}
var excludedContentTypes []parsedContentType
for _, v := range cfg.ExcludedContentTypes {
mediaType, params, err := mime.ParseMediaType(v)
if err != nil {
return nil, fmt.Errorf("parsing media type: %w", err)
return nil, fmt.Errorf("parsing excluded media type: %w", err)
}
contentTypes = append(contentTypes, parsedContentType{mediaType, params})
excludedContentTypes = append(excludedContentTypes, parsedContentType{mediaType, params})
}
var includedContentTypes []parsedContentType
for _, v := range cfg.IncludedContentTypes {
mediaType, params, err := mime.ParseMediaType(v)
if err != nil {
return nil, fmt.Errorf("parsing included media type: %w", err)
}
includedContentTypes = append(includedContentTypes, parsedContentType{mediaType, params})
}
return func(h http.Handler) http.HandlerFunc {
@ -52,7 +70,8 @@ func NewWrapper(cfg Config) (func(http.Handler) http.HandlerFunc, error) {
bw: brotli.NewWriter(rw),
minSize: cfg.MinSize,
statusCode: http.StatusOK,
excludedContentTypes: contentTypes,
excludedContentTypes: excludedContentTypes,
includedContentTypes: includedContentTypes,
}
defer brw.close()
@ -69,6 +88,7 @@ type responseWriter struct {
minSize int
excludedContentTypes []parsedContentType
includedContentTypes []parsedContentType
buf []byte
hijacked bool
@ -121,11 +141,25 @@ func (r *responseWriter) Write(p []byte) (int, error) {
return r.rw.Write(p)
}
// Disable compression according to user wishes in excludedContentTypes.
// Disable compression according to user wishes in excludedContentTypes or includedContentTypes.
if ct := r.rw.Header().Get(contentType); ct != "" {
mediaType, params, err := mime.ParseMediaType(ct)
if err != nil {
return 0, fmt.Errorf("parsing media type: %w", err)
return 0, fmt.Errorf("parsing content-type media type: %w", err)
}
if len(r.includedContentTypes) > 0 {
var found bool
for _, includedContentType := range r.includedContentTypes {
if includedContentType.equals(mediaType, params) {
found = true
break
}
}
if !found {
r.compressionDisabled = true
return r.rw.Write(p)
}
}
for _, excludedContentType := range r.excludedContentTypes {

View file

@ -291,10 +291,9 @@ func Test_ExcludedContentTypes(t *testing.T) {
expCompression bool
}{
{
desc: "Always compress when content types are empty",
contentType: "",
excludedContentTypes: []string{},
expCompression: true,
desc: "Always compress when content types are empty",
contentType: "",
expCompression: true,
},
{
desc: "MIME match",
@ -389,6 +388,111 @@ func Test_ExcludedContentTypes(t *testing.T) {
}
}
func Test_IncludedContentTypes(t *testing.T) {
testCases := []struct {
desc string
contentType string
includedContentTypes []string
expCompression bool
}{
{
desc: "Always compress when content types are empty",
contentType: "",
expCompression: true,
},
{
desc: "MIME match",
contentType: "application/json",
includedContentTypes: []string{"application/json"},
expCompression: true,
},
{
desc: "MIME no match",
contentType: "text/xml",
includedContentTypes: []string{"application/json"},
expCompression: false,
},
{
desc: "MIME match with no other directive ignores non-MIME directives",
contentType: "application/json; charset=utf-8",
includedContentTypes: []string{"application/json"},
expCompression: true,
},
{
desc: "MIME match with other directives requires all directives be equal, different charset",
contentType: "application/json; charset=ascii",
includedContentTypes: []string{"application/json; charset=utf-8"},
expCompression: false,
},
{
desc: "MIME match with other directives requires all directives be equal, same charset",
contentType: "application/json; charset=utf-8",
includedContentTypes: []string{"application/json; charset=utf-8"},
expCompression: true,
},
{
desc: "MIME match with other directives requires all directives be equal, missing charset",
contentType: "application/json",
includedContentTypes: []string{"application/json; charset=ascii"},
expCompression: false,
},
{
desc: "MIME match case insensitive",
contentType: "Application/Json",
includedContentTypes: []string{"application/json"},
expCompression: true,
},
{
desc: "MIME match ignore whitespace",
contentType: "application/json;charset=utf-8",
includedContentTypes: []string{"application/json; charset=utf-8"},
expCompression: true,
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
cfg := Config{
MinSize: 1024,
IncludedContentTypes: test.includedContentTypes,
}
h := mustNewWrapper(t, cfg)(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
rw.Header().Set(contentType, test.contentType)
rw.WriteHeader(http.StatusOK)
_, err := rw.Write(bigTestBody)
require.NoError(t, err)
}))
req, _ := http.NewRequest(http.MethodGet, "/whatever", nil)
req.Header.Set(acceptEncoding, "br")
rw := httptest.NewRecorder()
h.ServeHTTP(rw, req)
assert.Equal(t, http.StatusOK, rw.Code)
if test.expCompression {
assert.Equal(t, "br", rw.Header().Get(contentEncoding))
got, err := io.ReadAll(brotli.NewReader(rw.Body))
assert.NoError(t, err)
assert.Equal(t, bigTestBody, got)
} else {
assert.NotEqual(t, "br", rw.Header().Get("Content-Encoding"))
got, err := io.ReadAll(rw.Body)
assert.NoError(t, err)
assert.Equal(t, bigTestBody, got)
}
})
}
}
func Test_FlushExcludedContentTypes(t *testing.T) {
testCases := []struct {
desc string
@ -397,10 +501,9 @@ func Test_FlushExcludedContentTypes(t *testing.T) {
expCompression bool
}{
{
desc: "Always compress when content types are empty",
contentType: "",
excludedContentTypes: []string{},
expCompression: true,
desc: "Always compress when content types are empty",
contentType: "",
expCompression: true,
},
{
desc: "MIME match",
@ -509,6 +612,125 @@ func Test_FlushExcludedContentTypes(t *testing.T) {
}
}
func Test_FlushIncludedContentTypes(t *testing.T) {
testCases := []struct {
desc string
contentType string
includedContentTypes []string
expCompression bool
}{
{
desc: "Always compress when content types are empty",
contentType: "",
expCompression: true,
},
{
desc: "MIME match",
contentType: "application/json",
includedContentTypes: []string{"application/json"},
expCompression: true,
},
{
desc: "MIME no match",
contentType: "text/xml",
includedContentTypes: []string{"application/json"},
expCompression: false,
},
{
desc: "MIME match with no other directive ignores non-MIME directives",
contentType: "application/json; charset=utf-8",
includedContentTypes: []string{"application/json"},
expCompression: true,
},
{
desc: "MIME match with other directives requires all directives be equal, different charset",
contentType: "application/json; charset=ascii",
includedContentTypes: []string{"application/json; charset=utf-8"},
expCompression: false,
},
{
desc: "MIME match with other directives requires all directives be equal, same charset",
contentType: "application/json; charset=utf-8",
includedContentTypes: []string{"application/json; charset=utf-8"},
expCompression: true,
},
{
desc: "MIME match with other directives requires all directives be equal, missing charset",
contentType: "application/json",
includedContentTypes: []string{"application/json; charset=ascii"},
expCompression: false,
},
{
desc: "MIME match case insensitive",
contentType: "Application/Json",
includedContentTypes: []string{"application/json"},
expCompression: true,
},
{
desc: "MIME match ignore whitespace",
contentType: "application/json;charset=utf-8",
includedContentTypes: []string{"application/json; charset=utf-8"},
expCompression: true,
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
cfg := Config{
MinSize: 1024,
IncludedContentTypes: test.includedContentTypes,
}
h := mustNewWrapper(t, cfg)(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
rw.Header().Set(contentType, test.contentType)
rw.WriteHeader(http.StatusOK)
tb := bigTestBody
for len(tb) > 0 {
// Write 100 bytes per run
// Detection should not be affected (we send 100 bytes)
toWrite := 100
if toWrite > len(tb) {
toWrite = len(tb)
}
_, err := rw.Write(tb[:toWrite])
require.NoError(t, err)
// Flush between each write
rw.(http.Flusher).Flush()
tb = tb[toWrite:]
}
}))
req, _ := http.NewRequest(http.MethodGet, "/whatever", nil)
req.Header.Set(acceptEncoding, "br")
// This doesn't allow checking flushes, but we validate if content is correct.
rw := httptest.NewRecorder()
h.ServeHTTP(rw, req)
assert.Equal(t, http.StatusOK, rw.Code)
if test.expCompression {
assert.Equal(t, "br", rw.Header().Get(contentEncoding))
got, err := io.ReadAll(brotli.NewReader(rw.Body))
assert.NoError(t, err)
assert.Equal(t, bigTestBody, got)
} else {
assert.NotEqual(t, "br", rw.Header().Get(contentEncoding))
got, err := io.ReadAll(rw.Body)
assert.NoError(t, err)
assert.Equal(t, bigTestBody, got)
}
})
}
}
func mustNewWrapper(t *testing.T, cfg Config) func(http.Handler) http.HandlerFunc {
t.Helper()

View file

@ -26,6 +26,7 @@ type compress struct {
next http.Handler
name string
excludes []string
includes []string
minSize int
brotliHandler http.Handler
@ -36,16 +37,30 @@ type compress struct {
func New(ctx context.Context, next http.Handler, conf dynamic.Compress, name string) (http.Handler, error) {
middlewares.GetLogger(ctx, name, typeName).Debug().Msg("Creating middleware")
if len(conf.ExcludedContentTypes) > 0 && len(conf.IncludedContentTypes) > 0 {
return nil, fmt.Errorf("excludedContentTypes and includedContentTypes options are mutually exclusive")
}
excludes := []string{"application/grpc"}
for _, v := range conf.ExcludedContentTypes {
mediaType, _, err := mime.ParseMediaType(v)
if err != nil {
return nil, err
return nil, fmt.Errorf("parsing excluded media type: %w", err)
}
excludes = append(excludes, mediaType)
}
var includes []string
for _, v := range conf.IncludedContentTypes {
mediaType, _, err := mime.ParseMediaType(v)
if err != nil {
return nil, fmt.Errorf("parsing included media type: %w", err)
}
includes = append(includes, mediaType)
}
minSize := DefaultMinSize
if conf.MinResponseBodyBytes > 0 {
minSize = conf.MinResponseBodyBytes
@ -55,6 +70,7 @@ func New(ctx context.Context, next http.Handler, conf dynamic.Compress, name str
next: next,
name: name,
excludes: excludes,
includes: includes,
minSize: minSize,
}
@ -92,11 +108,11 @@ func (c *compress) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
return
}
// Client allows us to do whatever we want, so we br compress.
// See https://www.rfc-editor.org/rfc/rfc9110.html#section-12.5.3
// Client doesn't specify a preferred encoding, for compatibility don't encode the request
// See https://github.com/traefik/traefik/issues/9734
acceptEncoding, ok := req.Header["Accept-Encoding"]
if !ok {
c.brotliHandler.ServeHTTP(rw, req)
c.next.ServeHTTP(rw, req)
return
}
@ -118,10 +134,21 @@ func (c *compress) GetTracingInformation() (string, string, trace.SpanKind) {
}
func (c *compress) newGzipHandler() (http.Handler, error) {
wrapper, err := gzhttp.NewWrapper(
gzhttp.ExceptContentTypes(c.excludes),
gzhttp.MinSize(c.minSize),
)
var wrapper func(http.Handler) http.HandlerFunc
var err error
if len(c.includes) > 0 {
wrapper, err = gzhttp.NewWrapper(
gzhttp.ContentTypes(c.includes),
gzhttp.MinSize(c.minSize),
)
} else {
wrapper, err = gzhttp.NewWrapper(
gzhttp.ExceptContentTypes(c.excludes),
gzhttp.MinSize(c.minSize),
)
}
if err != nil {
return nil, fmt.Errorf("new gzip wrapper: %w", err)
}
@ -130,9 +157,11 @@ func (c *compress) newGzipHandler() (http.Handler, error) {
}
func (c *compress) newBrotliHandler() (http.Handler, error) {
cfg := brotli.Config{
ExcludedContentTypes: c.excludes,
MinSize: c.minSize,
cfg := brotli.Config{MinSize: c.minSize}
if len(c.includes) > 0 {
cfg.IncludedContentTypes = c.includes
} else {
cfg.ExcludedContentTypes = c.excludes
}
wrapper, err := brotli.NewWrapper(cfg)

View file

@ -10,7 +10,6 @@ import (
"net/textproto"
"testing"
"github.com/andybalholm/brotli"
"github.com/klauspost/compress/gzhttp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -35,7 +34,7 @@ func TestNegotiation(t *testing.T) {
}{
{
desc: "no accept header",
expEncoding: "br",
expEncoding: "",
},
{
desc: "unsupported accept header",
@ -151,7 +150,7 @@ func TestShouldNotCompressWhenContentEncodingHeader(t *testing.T) {
assert.EqualValues(t, rw.Body.Bytes(), fakeCompressedBody)
}
func TestShouldCompressWhenNoAcceptEncodingHeader(t *testing.T) {
func TestShouldNotCompressWhenNoAcceptEncodingHeader(t *testing.T) {
req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost", nil)
fakeBody := generateBytes(gzhttp.DefaultMinSize)
@ -167,12 +166,9 @@ func TestShouldCompressWhenNoAcceptEncodingHeader(t *testing.T) {
rw := httptest.NewRecorder()
handler.ServeHTTP(rw, req)
assert.Equal(t, brotliValue, rw.Header().Get(contentEncodingHeader))
assert.Equal(t, acceptEncodingHeader, rw.Header().Get(varyHeader))
got, err := io.ReadAll(brotli.NewReader(rw.Body))
require.NoError(t, err)
assert.Equal(t, got, fakeBody)
assert.Empty(t, rw.Header().Get(contentEncodingHeader))
assert.Empty(t, rw.Header().Get(varyHeader))
assert.EqualValues(t, rw.Body.Bytes(), fakeBody)
}
func TestShouldNotCompressWhenIdentityAcceptEncodingHeader(t *testing.T) {
@ -275,7 +271,28 @@ func TestShouldNotCompressWhenSpecificContentType(t *testing.T) {
respContentType: "text/event-stream",
},
{
desc: "application/grpc",
desc: "Include Response Content-Type",
conf: dynamic.Compress{
IncludedContentTypes: []string{"text/plain"},
},
respContentType: "text/html",
},
{
desc: "Ignoring application/grpc with exclude option",
conf: dynamic.Compress{
ExcludedContentTypes: []string{"application/json"},
},
reqContentType: "application/grpc",
},
{
desc: "Ignoring application/grpc with include option",
conf: dynamic.Compress{
IncludedContentTypes: []string{"application/json"},
},
reqContentType: "application/grpc",
},
{
desc: "Ignoring application/grpc with no option",
conf: dynamic.Compress{},
reqContentType: "application/grpc",
},
@ -316,6 +333,52 @@ func TestShouldNotCompressWhenSpecificContentType(t *testing.T) {
}
}
func TestShouldCompressWhenSpecificContentType(t *testing.T) {
baseBody := generateBytes(gzhttp.DefaultMinSize)
testCases := []struct {
desc string
conf dynamic.Compress
respContentType string
}{
{
desc: "Include Response Content-Type",
conf: dynamic.Compress{
IncludedContentTypes: []string{"text/html"},
},
respContentType: "text/html",
},
}
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost", nil)
req.Header.Add(acceptEncodingHeader, gzipValue)
next := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
rw.Header().Set(contentTypeHeader, test.respContentType)
if _, err := rw.Write(baseBody); err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError)
}
})
handler, err := New(context.Background(), next, test.conf, "test")
require.NoError(t, err)
rw := httptest.NewRecorder()
handler.ServeHTTP(rw, req)
assert.Equal(t, gzipValue, rw.Header().Get(contentEncodingHeader))
assert.Equal(t, acceptEncodingHeader, rw.Header().Get(varyHeader))
assert.NotEqualValues(t, rw.Body.Bytes(), baseBody)
})
}
}
func TestIntegrationShouldNotCompress(t *testing.T) {
fakeCompressedBody := generateBytes(100000)

View file

@ -8,6 +8,7 @@ import (
"strings"
"github.com/traefik/traefik/v3/pkg/config/dynamic"
"github.com/traefik/traefik/v3/pkg/middlewares"
"github.com/vulcand/oxy/v2/forward"
)
@ -58,7 +59,7 @@ func (s *Header) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
// If there is a next, call it.
if s.next != nil {
s.next.ServeHTTP(newResponseModifier(rw, req, s.PostRequestModifyResponseHeaders), req)
s.next.ServeHTTP(middlewares.NewResponseModifier(rw, req, s.PostRequestModifyResponseHeaders), req)
}
}

View file

@ -4,6 +4,7 @@ import (
"net/http"
"github.com/traefik/traefik/v3/pkg/config/dynamic"
"github.com/traefik/traefik/v3/pkg/middlewares"
"github.com/unrolled/secure"
)
@ -45,6 +46,6 @@ func newSecure(next http.Handler, cfg dynamic.Headers, contextKey string) *secur
func (s secureHeader) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
s.secure.HandlerFuncWithNextForRequestOnly(rw, req, func(writer http.ResponseWriter, request *http.Request) {
s.next.ServeHTTP(newResponseModifier(writer, request, s.secure.ModifyResponseHeaders), request)
s.next.ServeHTTP(middlewares.NewResponseModifier(writer, request, s.secure.ModifyResponseHeaders), request)
})
}

View file

@ -1,4 +1,4 @@
package headers
package middlewares
import (
"bufio"
@ -9,7 +9,8 @@ import (
"github.com/rs/zerolog/log"
)
type responseModifier struct {
// ResponseModifier is a ResponseWriter to modify the response headers before sending them.
type ResponseModifier struct {
req *http.Request
rw http.ResponseWriter
@ -21,9 +22,10 @@ type responseModifier struct {
modifierErr error // returned by modifier call
}
// modifier can be nil.
func newResponseModifier(w http.ResponseWriter, r *http.Request, modifier func(*http.Response) error) http.ResponseWriter {
return &responseModifier{
// NewResponseModifier returns a new ResponseModifier instance.
// The given modifier can be nil.
func NewResponseModifier(w http.ResponseWriter, r *http.Request, modifier func(*http.Response) error) http.ResponseWriter {
return &ResponseModifier{
req: r,
rw: w,
modifier: modifier,
@ -33,7 +35,7 @@ func newResponseModifier(w http.ResponseWriter, r *http.Request, modifier func(*
// WriteHeader is, in the specific case of 1xx status codes, a direct call to the wrapped ResponseWriter, without marking headers as sent,
// allowing so further calls.
func (r *responseModifier) WriteHeader(code int) {
func (r *ResponseModifier) WriteHeader(code int) {
if r.headersSent {
return
}
@ -73,11 +75,11 @@ func (r *responseModifier) WriteHeader(code int) {
r.rw.WriteHeader(code)
}
func (r *responseModifier) Header() http.Header {
func (r *ResponseModifier) Header() http.Header {
return r.rw.Header()
}
func (r *responseModifier) Write(b []byte) (int, error) {
func (r *ResponseModifier) Write(b []byte) (int, error) {
r.WriteHeader(r.code)
if r.modifierErr != nil {
return 0, r.modifierErr
@ -87,7 +89,7 @@ func (r *responseModifier) Write(b []byte) (int, error) {
}
// Hijack hijacks the connection.
func (r *responseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) {
func (r *ResponseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) {
if h, ok := r.rw.(http.Hijacker); ok {
return h.Hijack()
}
@ -96,7 +98,7 @@ func (r *responseModifier) Hijack() (net.Conn, *bufio.ReadWriter, error) {
}
// Flush sends any buffered data to the client.
func (r *responseModifier) Flush() {
func (r *ResponseModifier) Flush() {
if flusher, ok := r.rw.(http.Flusher); ok {
flusher.Flush()
}

View file

@ -73,7 +73,7 @@ func TestClientIPMatcher(t *testing.T) {
muxer, err := NewMuxer()
require.NoError(t, err)
err = muxer.AddRoute(test.rule, 0, handler)
err = muxer.AddRoute(test.rule, "", 0, handler)
if test.expectedError {
require.Error(t, err)
return
@ -147,7 +147,7 @@ func TestMethodMatcher(t *testing.T) {
muxer, err := NewMuxer()
require.NoError(t, err)
err = muxer.AddRoute(test.rule, 0, handler)
err = muxer.AddRoute(test.rule, "", 0, handler)
if test.expectedError {
require.Error(t, err)
return
@ -265,7 +265,7 @@ func TestHostMatcher(t *testing.T) {
muxer, err := NewMuxer()
require.NoError(t, err)
err = muxer.AddRoute(test.rule, 0, handler)
err = muxer.AddRoute(test.rule, "", 0, handler)
if test.expectedError {
require.Error(t, err)
return
@ -365,7 +365,7 @@ func TestHostRegexpMatcher(t *testing.T) {
muxer, err := NewMuxer()
require.NoError(t, err)
err = muxer.AddRoute(test.rule, 0, handler)
err = muxer.AddRoute(test.rule, "", 0, handler)
if test.expectedError {
require.Error(t, err)
return
@ -439,7 +439,7 @@ func TestPathMatcher(t *testing.T) {
muxer, err := NewMuxer()
require.NoError(t, err)
err = muxer.AddRoute(test.rule, 0, handler)
err = muxer.AddRoute(test.rule, "", 0, handler)
if test.expectedError {
require.Error(t, err)
return
@ -532,7 +532,7 @@ func TestPathRegexpMatcher(t *testing.T) {
muxer, err := NewMuxer()
require.NoError(t, err)
err = muxer.AddRoute(test.rule, 0, handler)
err = muxer.AddRoute(test.rule, "", 0, handler)
if test.expectedError {
require.Error(t, err)
return
@ -604,7 +604,7 @@ func TestPathPrefixMatcher(t *testing.T) {
muxer, err := NewMuxer()
require.NoError(t, err)
err = muxer.AddRoute(test.rule, 0, handler)
err = muxer.AddRoute(test.rule, "", 0, handler)
if test.expectedError {
require.Error(t, err)
return
@ -692,7 +692,7 @@ func TestHeaderMatcher(t *testing.T) {
muxer, err := NewMuxer()
require.NoError(t, err)
err = muxer.AddRoute(test.rule, 0, handler)
err = muxer.AddRoute(test.rule, "", 0, handler)
if test.expectedError {
require.Error(t, err)
return
@ -800,7 +800,7 @@ func TestHeaderRegexpMatcher(t *testing.T) {
muxer, err := NewMuxer()
require.NoError(t, err)
err = muxer.AddRoute(test.rule, 0, handler)
err = muxer.AddRoute(test.rule, "", 0, handler)
if test.expectedError {
require.Error(t, err)
return
@ -889,7 +889,7 @@ func TestQueryMatcher(t *testing.T) {
muxer, err := NewMuxer()
require.NoError(t, err)
err = muxer.AddRoute(test.rule, 0, handler)
err = muxer.AddRoute(test.rule, "", 0, handler)
if test.expectedError {
require.Error(t, err)
return
@ -1003,7 +1003,7 @@ func TestQueryRegexpMatcher(t *testing.T) {
muxer, err := NewMuxer()
require.NoError(t, err)
err = muxer.AddRoute(test.rule, 0, handler)
err = muxer.AddRoute(test.rule, "", 0, handler)
if test.expectedError {
require.Error(t, err)
return

View file

@ -0,0 +1,226 @@
package http
import (
"fmt"
"net/http"
"strings"
"github.com/gorilla/mux"
"github.com/rs/zerolog/log"
"github.com/traefik/traefik/v3/pkg/ip"
"github.com/traefik/traefik/v3/pkg/middlewares/requestdecorator"
)
var httpFuncsV2 = map[string]func(*matchersTree, ...string) error{
"Host": hostV2,
"HostHeader": hostV2,
"HostRegexp": hostRegexpV2,
"ClientIP": clientIPV2,
"Path": pathV2,
"PathPrefix": pathPrefixV2,
"Method": methodsV2,
"Headers": headersV2,
"HeadersRegexp": headersRegexpV2,
"Query": queryV2,
}
func pathV2(tree *matchersTree, paths ...string) error {
for _, path := range paths {
if !strings.HasPrefix(path, "/") {
return fmt.Errorf("path %q does not start with a '/'", path)
}
}
tree.matcher = func(req *http.Request) bool {
for _, path := range paths {
if req.URL.Path == path {
return true
}
}
return false
}
return nil
}
func pathPrefixV2(tree *matchersTree, paths ...string) error {
for _, path := range paths {
if !strings.HasPrefix(path, "/") {
return fmt.Errorf("path %q does not start with a '/'", path)
}
}
tree.matcher = func(req *http.Request) bool {
for _, path := range paths {
if strings.HasPrefix(req.URL.Path, path) {
return true
}
}
return false
}
return nil
}
func hostV2(tree *matchersTree, hosts ...string) error {
for i, host := range hosts {
if !IsASCII(host) {
return fmt.Errorf("invalid value %q for \"Host\" matcher, non-ASCII characters are not allowed", host)
}
hosts[i] = strings.ToLower(host)
}
tree.matcher = func(req *http.Request) bool {
reqHost := requestdecorator.GetCanonizedHost(req.Context())
if len(reqHost) == 0 {
// If the request is an HTTP/1.0 request, then a Host may not be defined.
if req.ProtoAtLeast(1, 1) {
log.Ctx(req.Context()).Warn().Msgf("Could not retrieve CanonizedHost, rejecting %s", req.Host)
}
return false
}
flatH := requestdecorator.GetCNAMEFlatten(req.Context())
if len(flatH) > 0 {
for _, host := range hosts {
if strings.EqualFold(reqHost, host) || strings.EqualFold(flatH, host) {
return true
}
log.Ctx(req.Context()).Debug().Msgf("CNAMEFlattening: request %s which resolved to %s, is not matched to route %s", reqHost, flatH, host)
}
return false
}
for _, host := range hosts {
if reqHost == host {
return true
}
// Check for match on trailing period on host
if last := len(host) - 1; last >= 0 && host[last] == '.' {
h := host[:last]
if reqHost == h {
return true
}
}
// Check for match on trailing period on request
if last := len(reqHost) - 1; last >= 0 && reqHost[last] == '.' {
h := reqHost[:last]
if h == host {
return true
}
}
}
return false
}
return nil
}
func clientIPV2(tree *matchersTree, clientIPs ...string) error {
checker, err := ip.NewChecker(clientIPs)
if err != nil {
return fmt.Errorf("could not initialize IP Checker for \"ClientIP\" matcher: %w", err)
}
strategy := ip.RemoteAddrStrategy{}
tree.matcher = func(req *http.Request) bool {
ok, err := checker.Contains(strategy.GetIP(req))
if err != nil {
log.Ctx(req.Context()).Warn().Err(err).Msg("\"ClientIP\" matcher: could not match remote address")
return false
}
return ok
}
return nil
}
func methodsV2(tree *matchersTree, methods ...string) error {
route := mux.NewRouter().NewRoute()
route.Methods(methods...)
if err := route.GetError(); err != nil {
return err
}
tree.matcher = func(req *http.Request) bool {
return route.Match(req, &mux.RouteMatch{})
}
return nil
}
func headersV2(tree *matchersTree, headers ...string) error {
route := mux.NewRouter().NewRoute()
route.Headers(headers...)
if err := route.GetError(); err != nil {
return err
}
tree.matcher = func(req *http.Request) bool {
return route.Match(req, &mux.RouteMatch{})
}
return nil
}
func queryV2(tree *matchersTree, query ...string) error {
var queries []string
for _, elem := range query {
queries = append(queries, strings.SplitN(elem, "=", 2)...)
}
route := mux.NewRouter().NewRoute()
route.Queries(queries...)
if err := route.GetError(); err != nil {
return err
}
tree.matcher = func(req *http.Request) bool {
return route.Match(req, &mux.RouteMatch{})
}
return nil
}
func hostRegexpV2(tree *matchersTree, hosts ...string) error {
router := mux.NewRouter()
for _, host := range hosts {
if !IsASCII(host) {
return fmt.Errorf("invalid value %q for HostRegexp matcher, non-ASCII characters are not allowed", host)
}
tmpRt := router.Host(host)
if tmpRt.GetError() != nil {
return tmpRt.GetError()
}
}
tree.matcher = func(req *http.Request) bool {
return router.Match(req, &mux.RouteMatch{})
}
return nil
}
func headersRegexpV2(tree *matchersTree, headers ...string) error {
route := mux.NewRouter().NewRoute()
route.HeadersRegexp(headers...)
if err := route.GetError(); err != nil {
return err
}
tree.matcher = func(req *http.Request) bool {
return route.Match(req, &mux.RouteMatch{})
}
return nil
}

File diff suppressed because it is too large Load diff

View file

@ -12,8 +12,9 @@ import (
// Muxer handles routing with rules.
type Muxer struct {
routes routes
parser predicate.Parser
routes routes
parser predicate.Parser
parserV2 predicate.Parser
}
// NewMuxer returns a new muxer instance.
@ -28,8 +29,19 @@ func NewMuxer() (*Muxer, error) {
return nil, fmt.Errorf("error while creating parser: %w", err)
}
var matchersV2 []string
for matcher := range httpFuncsV2 {
matchersV2 = append(matchersV2, matcher)
}
parserV2, err := rules.NewParser(matchersV2)
if err != nil {
return nil, fmt.Errorf("error while creating v2 parser: %w", err)
}
return &Muxer{
parser: parser,
parser: parser,
parserV2: parserV2,
}, nil
}
@ -53,10 +65,26 @@ func GetRulePriority(rule string) int {
}
// AddRoute add a new route to the router.
func (m *Muxer) AddRoute(rule string, priority int, handler http.Handler) error {
parse, err := m.parser.Parse(rule)
if err != nil {
return fmt.Errorf("error while parsing rule %s: %w", rule, err)
func (m *Muxer) AddRoute(rule string, syntax string, priority int, handler http.Handler) error {
var parse interface{}
var err error
var matcherFuncs map[string]func(*matchersTree, ...string) error
switch syntax {
case "v2":
parse, err = m.parserV2.Parse(rule)
if err != nil {
return fmt.Errorf("error while parsing rule %s: %w", rule, err)
}
matcherFuncs = httpFuncsV2
default:
parse, err = m.parser.Parse(rule)
if err != nil {
return fmt.Errorf("error while parsing rule %s: %w", rule, err)
}
matcherFuncs = httpFuncs
}
buildTree, ok := parse.(rules.TreeBuilder)
@ -65,7 +93,7 @@ func (m *Muxer) AddRoute(rule string, priority int, handler http.Handler) error
}
var matchers matchersTree
err = matchers.addRule(buildTree())
err = matchers.addRule(buildTree(), matcherFuncs)
if err != nil {
return fmt.Errorf("error while adding rule %s: %w", rule, err)
}
@ -87,6 +115,9 @@ func ParseDomains(rule string) ([]string, error) {
for matcher := range httpFuncs {
matchers = append(matchers, matcher)
}
for matcher := range httpFuncsV2 {
matchers = append(matchers, matcher)
}
parser, err := rules.NewParser(matchers)
if err != nil {
@ -166,25 +197,27 @@ func (m *matchersTree) match(req *http.Request) bool {
}
}
func (m *matchersTree) addRule(rule *rules.Tree) error {
type matcherFuncs map[string]func(*matchersTree, ...string) error
func (m *matchersTree) addRule(rule *rules.Tree, funcs matcherFuncs) error {
switch rule.Matcher {
case "and", "or":
m.operator = rule.Matcher
m.left = &matchersTree{}
err := m.left.addRule(rule.RuleLeft)
err := m.left.addRule(rule.RuleLeft, funcs)
if err != nil {
return fmt.Errorf("error while adding rule %s: %w", rule.Matcher, err)
}
m.right = &matchersTree{}
return m.right.addRule(rule.RuleRight)
return m.right.addRule(rule.RuleRight, funcs)
default:
err := rules.CheckRule(rule)
if err != nil {
return fmt.Errorf("error while checking rule %s: %w", rule.Matcher, err)
}
err = httpFuncs[rule.Matcher](m, rule.Value...)
err = funcs[rule.Matcher](m, rule.Value...)
if err != nil {
return fmt.Errorf("error while adding rule %s: %w", rule.Matcher, err)
}

View file

@ -231,7 +231,7 @@ func TestMuxer(t *testing.T) {
require.NoError(t, err)
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
err = muxer.AddRoute(test.rule, 0, handler)
err = muxer.AddRoute(test.rule, "", 0, handler)
if test.expectedError {
require.Error(t, err)
return
@ -394,7 +394,7 @@ func Test_addRoutePriority(t *testing.T) {
route.priority = GetRulePriority(route.rule)
}
err := muxer.AddRoute(route.rule, route.priority, handler)
err := muxer.AddRoute(route.rule, "", route.priority, handler)
require.NoError(t, err, route.rule)
}
@ -519,7 +519,7 @@ func TestEmptyHost(t *testing.T) {
muxer, err := NewMuxer()
require.NoError(t, err)
err = muxer.AddRoute(test.rule, 0, handler)
err = muxer.AddRoute(test.rule, "", 0, handler)
require.NoError(t, err)
// RequestDecorator is necessary for the host rule

View file

@ -38,7 +38,7 @@ func Test_HostSNICatchAll(t *testing.T) {
muxer, err := NewMuxer()
require.NoError(t, err)
err = muxer.AddRoute(test.rule, 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {}))
err = muxer.AddRoute(test.rule, "", 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {}))
require.NoError(t, err)
handler, catchAll := muxer.Match(ConnData{
@ -144,7 +144,7 @@ func Test_HostSNI(t *testing.T) {
muxer, err := NewMuxer()
require.NoError(t, err)
err = muxer.AddRoute(test.rule, 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {}))
err = muxer.AddRoute(test.rule, "", 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {}))
if test.buildErr {
require.Error(t, err)
return
@ -227,7 +227,7 @@ func Test_HostSNIRegexp(t *testing.T) {
muxer, err := NewMuxer()
require.NoError(t, err)
err = muxer.AddRoute(test.rule, 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {}))
err = muxer.AddRoute(test.rule, "", 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {}))
if test.buildErr {
require.Error(t, err)
return
@ -299,7 +299,7 @@ func Test_ClientIP(t *testing.T) {
muxer, err := NewMuxer()
require.NoError(t, err)
err = muxer.AddRoute(test.rule, 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {}))
err = muxer.AddRoute(test.rule, "", 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {}))
if test.buildErr {
require.Error(t, err)
return
@ -363,7 +363,7 @@ func Test_ALPN(t *testing.T) {
muxer, err := NewMuxer()
require.NoError(t, err)
err = muxer.AddRoute(test.rule, 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {}))
err = muxer.AddRoute(test.rule, "", 0, tcp.HandlerFunc(func(conn tcp.WriteCloser) {}))
if test.buildErr {
require.Error(t, err)
return

240
pkg/muxer/tcp/matcher_v2.go Normal file
View file

@ -0,0 +1,240 @@
package tcp
import (
"bytes"
"errors"
"fmt"
"regexp"
"strconv"
"strings"
"github.com/go-acme/lego/v4/challenge/tlsalpn01"
"github.com/rs/zerolog/log"
"github.com/traefik/traefik/v3/pkg/ip"
)
var tcpFuncsV2 = map[string]func(*matchersTree, ...string) error{
"ALPN": alpnV2,
"ClientIP": clientIPV2,
"HostSNI": hostSNIV2,
"HostSNIRegexp": hostSNIRegexpV2,
}
func clientIPV2(tree *matchersTree, clientIPs ...string) error {
checker, err := ip.NewChecker(clientIPs)
if err != nil {
return fmt.Errorf("could not initialize IP Checker for \"ClientIP\" matcher: %w", err)
}
tree.matcher = func(meta ConnData) bool {
if meta.remoteIP == "" {
return false
}
ok, err := checker.Contains(meta.remoteIP)
if err != nil {
log.Warn().Err(err).Msg("ClientIP matcher: could not match remote address")
return false
}
return ok
}
return nil
}
// alpnV2 checks if any of the connection ALPN protocols matches one of the matcher protocols.
func alpnV2(tree *matchersTree, protos ...string) error {
if len(protos) == 0 {
return errors.New("empty value for \"ALPN\" matcher is not allowed")
}
for _, proto := range protos {
if proto == tlsalpn01.ACMETLS1Protocol {
return fmt.Errorf("invalid protocol value for \"ALPN\" matcher, %q is not allowed", proto)
}
}
tree.matcher = func(meta ConnData) bool {
for _, proto := range meta.alpnProtos {
for _, filter := range protos {
if proto == filter {
return true
}
}
}
return false
}
return nil
}
// hostSNIV2 checks if the SNI Host of the connection match the matcher host.
func hostSNIV2(tree *matchersTree, hosts ...string) error {
if len(hosts) == 0 {
return errors.New("empty value for \"HostSNI\" matcher is not allowed")
}
for i, host := range hosts {
// Special case to allow global wildcard
if host == "*" {
continue
}
if !hostOrIP.MatchString(host) {
return fmt.Errorf("invalid value for \"HostSNI\" matcher, %q is not a valid hostname or IP", host)
}
hosts[i] = strings.ToLower(host)
}
tree.matcher = func(meta ConnData) bool {
// Since a HostSNI(`*`) rule has been provided as catchAll for non-TLS TCP,
// it allows matching with an empty serverName.
// Which is why we make sure to take that case into account before
// checking meta.serverName.
if hosts[0] == "*" {
return true
}
if meta.serverName == "" {
return false
}
for _, host := range hosts {
if host == "*" {
return true
}
if host == meta.serverName {
return true
}
// trim trailing period in case of FQDN
host = strings.TrimSuffix(host, ".")
if host == meta.serverName {
return true
}
}
return false
}
return nil
}
// hostSNIRegexpV2 checks if the SNI Host of the connection matches the matcher host regexp.
func hostSNIRegexpV2(tree *matchersTree, templates ...string) error {
if len(templates) == 0 {
return fmt.Errorf("empty value for \"HostSNIRegexp\" matcher is not allowed")
}
var regexps []*regexp.Regexp
for _, template := range templates {
preparedPattern, err := preparePattern(template)
if err != nil {
return fmt.Errorf("invalid pattern value for \"HostSNIRegexp\" matcher, %q is not a valid pattern: %w", template, err)
}
regexp, err := regexp.Compile(preparedPattern)
if err != nil {
return err
}
regexps = append(regexps, regexp)
}
tree.matcher = func(meta ConnData) bool {
for _, regexp := range regexps {
if regexp.MatchString(meta.serverName) {
return true
}
}
return false
}
return nil
}
// preparePattern builds a regexp pattern from the initial user defined expression.
// This function reuses the code dedicated to host matching of the newRouteRegexp func from the gorilla/mux library.
// https://github.com/containous/mux/tree/8ffa4f6d063c1e2b834a73be6a1515cca3992618.
func preparePattern(template string) (string, error) {
// Check if it is well-formed.
idxs, errBraces := braceIndices(template)
if errBraces != nil {
return "", errBraces
}
defaultPattern := "[^.]+"
pattern := bytes.NewBufferString("")
// Host SNI matching is case-insensitive
_, _ = fmt.Fprint(pattern, "(?i)")
pattern.WriteByte('^')
var end int
for i := 0; i < len(idxs); i += 2 {
// Set all values we are interested in.
raw := template[end:idxs[i]]
end = idxs[i+1]
parts := strings.SplitN(template[idxs[i]+1:end-1], ":", 2)
name := parts[0]
patt := defaultPattern
if len(parts) == 2 {
patt = parts[1]
}
// Name or pattern can't be empty.
if name == "" || patt == "" {
return "", fmt.Errorf("mux: missing name or pattern in %q",
template[idxs[i]:end])
}
// Build the regexp pattern.
_, _ = fmt.Fprintf(pattern, "%s(?P<%s>%s)", regexp.QuoteMeta(raw), varGroupName(i/2), patt)
}
// Add the remaining.
raw := template[end:]
pattern.WriteString(regexp.QuoteMeta(raw))
pattern.WriteByte('$')
return pattern.String(), nil
}
// varGroupName builds a capturing group name for the indexed variable.
// This function is a copy of varGroupName func from the gorilla/mux library.
// https://github.com/containous/mux/tree/8ffa4f6d063c1e2b834a73be6a1515cca3992618.
func varGroupName(idx int) string {
return "v" + strconv.Itoa(idx)
}
// braceIndices returns the first level curly brace indices from a string.
// This function is a copy of braceIndices func from the gorilla/mux library.
// https://github.com/containous/mux/tree/8ffa4f6d063c1e2b834a73be6a1515cca3992618.
func braceIndices(s string) ([]int, error) {
var level, idx int
var idxs []int
for i := 0; i < len(s); i++ {
switch s[i] {
case '{':
if level++; level == 1 {
idx = i
}
case '}':
if level--; level == 0 {
idxs = append(idxs, idx, i+1)
} else if level < 0 {
return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
}
}
}
if level != 0 {
return nil, fmt.Errorf("mux: unbalanced braces in %q", s)
}
return idxs, nil
}

File diff suppressed because it is too large Load diff

View file

@ -41,8 +41,9 @@ func NewConnData(serverName string, conn tcp.WriteCloser, alpnProtos []string) (
// Muxer defines a muxer that handles TCP routing with rules.
type Muxer struct {
routes routes
parser predicate.Parser
routes routes
parser predicate.Parser
parserV2 predicate.Parser
}
// NewMuxer returns a TCP muxer.
@ -57,7 +58,20 @@ func NewMuxer() (*Muxer, error) {
return nil, fmt.Errorf("error while creating rules parser: %w", err)
}
return &Muxer{parser: parser}, nil
var matchersV2 []string
for matcher := range tcpFuncsV2 {
matchersV2 = append(matchersV2, matcher)
}
parserV2, err := rules.NewParser(matchersV2)
if err != nil {
return nil, fmt.Errorf("error while creating v2 rules parser: %w", err)
}
return &Muxer{
parser: parser,
parserV2: parserV2,
}, nil
}
// Match returns the handler of the first route matching the connection metadata,
@ -106,10 +120,26 @@ func GetRulePriority(rule string) int {
// AddRoute adds a new route, associated to the given handler, at the given
// priority, to the muxer.
func (m *Muxer) AddRoute(rule string, priority int, handler tcp.Handler) error {
parse, err := m.parser.Parse(rule)
if err != nil {
return fmt.Errorf("error while parsing rule %s: %w", rule, err)
func (m *Muxer) AddRoute(rule string, syntax string, priority int, handler tcp.Handler) error {
var parse interface{}
var err error
var matcherFuncs map[string]func(*matchersTree, ...string) error
switch syntax {
case "v2":
parse, err = m.parserV2.Parse(rule)
if err != nil {
return fmt.Errorf("error while parsing rule %s: %w", rule, err)
}
matcherFuncs = tcpFuncsV2
default:
parse, err = m.parser.Parse(rule)
if err != nil {
return fmt.Errorf("error while parsing rule %s: %w", rule, err)
}
matcherFuncs = tcpFuncs
}
buildTree, ok := parse.(rules.TreeBuilder)
@ -120,7 +150,7 @@ func (m *Muxer) AddRoute(rule string, priority int, handler tcp.Handler) error {
ruleTree := buildTree()
var matchers matchersTree
err = matchers.addRule(ruleTree)
err = matchers.addRule(ruleTree, matcherFuncs)
if err != nil {
return fmt.Errorf("error while adding rule %s: %w", rule, err)
}
@ -155,6 +185,9 @@ func ParseHostSNI(rule string) ([]string, error) {
for matcher := range tcpFuncs {
matchers = append(matchers, matcher)
}
for matcher := range tcpFuncsV2 {
matchers = append(matchers, matcher)
}
parser, err := rules.NewParser(matchers)
if err != nil {
@ -237,25 +270,27 @@ func (m *matchersTree) match(meta ConnData) bool {
}
}
func (m *matchersTree) addRule(rule *rules.Tree) error {
type matcherFuncs map[string]func(*matchersTree, ...string) error
func (m *matchersTree) addRule(rule *rules.Tree, funcs matcherFuncs) error {
switch rule.Matcher {
case "and", "or":
m.operator = rule.Matcher
m.left = &matchersTree{}
err := m.left.addRule(rule.RuleLeft)
err := m.left.addRule(rule.RuleLeft, funcs)
if err != nil {
return err
}
m.right = &matchersTree{}
return m.right.addRule(rule.RuleRight)
return m.right.addRule(rule.RuleRight, funcs)
default:
err := rules.CheckRule(rule)
if err != nil {
return err
}
err = tcpFuncs[rule.Matcher](m, rule.Value...)
err = funcs[rule.Matcher](m, rule.Value...)
if err != nil {
return err
}

View file

@ -277,7 +277,7 @@ func Test_addTCPRoute(t *testing.T) {
router, err := NewMuxer()
require.NoError(t, err)
err = router.AddRoute(test.rule, 0, handler)
err = router.AddRoute(test.rule, "", 0, handler)
if test.routeErr {
require.Error(t, err)
return
@ -447,7 +447,7 @@ func Test_Priority(t *testing.T) {
matchedRule := ""
for rule, priority := range test.rules {
rule := rule
err := muxer.AddRoute(rule, priority, tcp.HandlerFunc(func(conn tcp.WriteCloser) {
err := muxer.AddRoute(rule, "", priority, tcp.HandlerFunc(func(conn tcp.WriteCloser) {
matchedRule = rule
}))
require.NoError(t, err)

View file

@ -731,6 +731,7 @@ func createForwardAuthMiddleware(k8sClient Client, namespace string, auth *traef
AuthResponseHeaders: auth.AuthResponseHeaders,
AuthResponseHeadersRegex: auth.AuthResponseHeadersRegex,
AuthRequestHeaders: auth.AuthRequestHeaders,
AddAuthCookiesToResponse: auth.AddAuthCookiesToResponse,
}
if auth.TLS == nil {

View file

@ -112,6 +112,7 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
r := &dynamic.Router{
Middlewares: mds,
Priority: route.Priority,
RuleSyntax: route.Syntax,
EntryPoints: ingressRoute.Spec.EntryPoints,
Rule: route.Match,
Service: serviceName,

View file

@ -102,6 +102,7 @@ func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client
Middlewares: mds,
Rule: route.Match,
Priority: route.Priority,
RuleSyntax: route.Syntax,
Service: serviceName,
}

View file

@ -33,6 +33,9 @@ type Route struct {
// Priority defines the router's priority.
// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#priority
Priority int `json:"priority,omitempty"`
// Syntax defines the router's rule syntax.
// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rulesyntax
Syntax string `json:"syntax,omitempty"`
// Services defines the list of Service.
// It can contain any combination of TraefikService and/or reference to a Kubernetes Service.
Services []Service `json:"services,omitempty"`

View file

@ -29,6 +29,9 @@ type RouteTCP struct {
// Priority defines the router's priority.
// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#priority_1
Priority int `json:"priority,omitempty"`
// Syntax defines the router's rule syntax.
// More info: https://doc.traefik.io/traefik/v3.0/routing/routers/#rulesyntax_1
Syntax string `json:"syntax,omitempty"`
// Services defines the list of TCP services.
Services []ServiceTCP `json:"services,omitempty"`
// Middlewares defines the list of references to MiddlewareTCP resources.

View file

@ -157,6 +157,8 @@ type ForwardAuth struct {
AuthRequestHeaders []string `json:"authRequestHeaders,omitempty"`
// TLS defines the configuration used to secure the connection to the authentication server.
TLS *ClientTLS `json:"tls,omitempty"`
// AddAuthCookiesToResponse defines the list of cookies to copy from the authentication server response to the response.
AddAuthCookiesToResponse []string `json:"addAuthCookiesToResponse,omitempty"`
}
// ClientTLS holds the client TLS configuration.

View file

@ -215,6 +215,11 @@ func (in *ForwardAuth) DeepCopyInto(out *ForwardAuth) {
*out = new(ClientTLS)
**out = **in
}
if in.AddAuthCookiesToResponse != nil {
in, out := &in.AddAuthCookiesToResponse, &out.AddAuthCookiesToResponse
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}

View file

@ -230,6 +230,7 @@ func (p *Provider) loadConfigurationFromGateway(ctx context.Context, client Clie
err := client.UpdateGatewayClassStatus(gatewayClass, metav1.Condition{
Type: string(gatev1.GatewayClassConditionStatusAccepted),
Status: metav1.ConditionTrue,
ObservedGeneration: gatewayClass.Generation,
Reason: "Handled",
Message: "Handled by Traefik controller",
LastTransitionTime: metav1.Now(),
@ -587,7 +588,16 @@ func (p *Provider) makeGatewayStatus(gateway *gatev1.Gateway, listenerStatuses [
Type: string(gatev1.GatewayConditionAccepted),
Status: metav1.ConditionTrue,
ObservedGeneration: gateway.Generation,
Reason: string(gatev1.GatewayConditionAccepted),
Reason: string(gatev1.GatewayReasonAccepted),
Message: "Gateway successfully scheduled",
LastTransitionTime: metav1.Now(),
},
// update "Programmed" status with "Programmed" reason
metav1.Condition{
Type: string(gatev1.GatewayConditionProgrammed),
Status: metav1.ConditionTrue,
ObservedGeneration: gateway.Generation,
Reason: string(gatev1.GatewayReasonProgrammed),
Message: "Gateway successfully scheduled",
LastTransitionTime: metav1.Now(),
},
@ -760,6 +770,7 @@ func (p *Provider) gatewayHTTPRouteToHTTPConf(ctx context.Context, ep string, li
router := dynamic.Router{
Rule: rule,
RuleSyntax: "v3",
EntryPoints: []string{ep},
}
@ -898,6 +909,7 @@ func gatewayTCPRouteToTCPConf(ctx context.Context, ep string, listener gatev1.Li
router := dynamic.TCPRouter{
Rule: "HostSNI(`*`)",
EntryPoints: []string{ep},
RuleSyntax: "v3",
}
if listener.Protocol == gatev1.TLSProtocolType && listener.TLS != nil {
@ -1062,6 +1074,7 @@ func gatewayTLSRouteToTCPConf(ctx context.Context, ep string, listener gatev1.Li
router := dynamic.TCPRouter{
Rule: rule,
RuleSyntax: "v3",
EntryPoints: []string{ep},
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: listener.TLS.Mode != nil && *listener.TLS.Mode == gatev1.TLSModePassthrough,
@ -1385,7 +1398,7 @@ func extractHeaderRules(headers []gatev1.HTTPHeaderMatch) ([]string, error) {
switch *header.Type {
case gatev1.HeaderMatchExact:
headerRules = append(headerRules, fmt.Sprintf("Headers(`%s`,`%s`)", header.Name, header.Value))
headerRules = append(headerRules, fmt.Sprintf("Header(`%s`,`%s`)", header.Name, header.Value))
default:
return nil, fmt.Errorf("unsupported header match type %s", *header.Type)
}

View file

@ -550,6 +550,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
EntryPoints: []string{"web"},
Service: "default-http-app-1-my-gateway-web-1c0cf64bde37d9d0df06-wrr",
Rule: "Host(`foo.com`) && Path(`/bar`)",
RuleSyntax: "v3",
},
},
Middlewares: map[string]*dynamic.Middleware{},
@ -609,6 +610,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
EntryPoints: []string{"web"},
Service: "api@internal",
Rule: "Host(`foo.com`) && Path(`/bar`)",
RuleSyntax: "v3",
},
},
Middlewares: map[string]*dynamic.Middleware{},
@ -641,6 +643,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
EntryPoints: []string{"web"},
Service: "default-http-app-1-my-gateway-web-1c0cf64bde37d9d0df06-wrr",
Rule: "Host(`foo.com`) && Path(`/bar`)",
RuleSyntax: "v3",
},
},
Middlewares: map[string]*dynamic.Middleware{},
@ -704,6 +707,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
EntryPoints: []string{"websecure"},
Service: "default-http-app-1-my-gateway-websecure-1c0cf64bde37d9d0df06-wrr",
Rule: "Host(`foo.com`) && Path(`/bar`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTLSConfig{},
},
},
@ -773,6 +777,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
EntryPoints: []string{"web"},
Service: "default-http-app-1-my-gateway-web-66e726cd8903b49727ae-wrr",
Rule: "(Host(`foo.com`) || Host(`bar.com`)) && PathPrefix(`/`)",
RuleSyntax: "v3",
},
},
Middlewares: map[string]*dynamic.Middleware{},
@ -832,6 +837,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
EntryPoints: []string{"web"},
Service: "default-http-app-1-my-gateway-web-3b78e2feb3295ddd87f0-wrr",
Rule: "(Host(`foo.com`) || HostRegexp(`^[a-zA-Z0-9-]+\\.bar\\.com$`)) && PathPrefix(`/`)",
RuleSyntax: "v3",
},
},
Middlewares: map[string]*dynamic.Middleware{},
@ -891,6 +897,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
EntryPoints: []string{"web"},
Service: "default-http-app-1-my-gateway-web-b0521a61fb43068694b4-wrr",
Rule: "(Host(`foo.com`) || HostRegexp(`^[a-zA-Z0-9-]+\\.foo\\.com$`)) && PathPrefix(`/`)",
RuleSyntax: "v3",
},
},
Middlewares: map[string]*dynamic.Middleware{},
@ -949,11 +956,13 @@ func TestLoadHTTPRoutes(t *testing.T) {
"default-http-app-1-my-gateway-web-1c0cf64bde37d9d0df06": {
EntryPoints: []string{"web"},
Rule: "Host(`foo.com`) && Path(`/bar`)",
RuleSyntax: "v3",
Service: "default-http-app-1-my-gateway-web-1c0cf64bde37d9d0df06-wrr",
},
"default-http-app-1-my-gateway-web-d737b4933fa88e68ab8a": {
EntryPoints: []string{"web"},
Rule: "Host(`foo.com`) && Path(`/bir`)",
RuleSyntax: "v3",
Service: "default-http-app-1-my-gateway-web-d737b4933fa88e68ab8a-wrr",
},
},
@ -1039,6 +1048,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
"default-http-app-1-my-gateway-web-1c0cf64bde37d9d0df06": {
EntryPoints: []string{"web"},
Rule: "Host(`foo.com`) && Path(`/bar`)",
RuleSyntax: "v3",
Service: "default-http-app-1-my-gateway-web-1c0cf64bde37d9d0df06-wrr",
},
},
@ -1124,11 +1134,13 @@ func TestLoadHTTPRoutes(t *testing.T) {
EntryPoints: []string{"web"},
Service: "default-http-app-1-my-gateway-http-web-1c0cf64bde37d9d0df06-wrr",
Rule: "Host(`foo.com`) && Path(`/bar`)",
RuleSyntax: "v3",
},
"default-http-app-1-my-gateway-https-websecure-1c0cf64bde37d9d0df06": {
EntryPoints: []string{"websecure"},
Service: "default-http-app-1-my-gateway-https-websecure-1c0cf64bde37d9d0df06-wrr",
Rule: "Host(`foo.com`) && Path(`/bar`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTLSConfig{},
},
},
@ -1213,11 +1225,13 @@ func TestLoadHTTPRoutes(t *testing.T) {
EntryPoints: []string{"web"},
Service: "default-http-app-1-my-gateway-web-1c0cf64bde37d9d0df06-wrr",
Rule: "Host(`foo.com`) && Path(`/bar`)",
RuleSyntax: "v3",
},
"default-http-app-1-my-gateway-websecure-1c0cf64bde37d9d0df06": {
EntryPoints: []string{"websecure"},
Service: "default-http-app-1-my-gateway-websecure-1c0cf64bde37d9d0df06-wrr",
Rule: "Host(`foo.com`) && Path(`/bar`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTLSConfig{},
},
},
@ -1293,20 +1307,22 @@ func TestLoadHTTPRoutes(t *testing.T) {
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"default-http-app-1-my-gateway-web-330d644a7f2079e8f454": {
"default-http-app-1-my-gateway-web-4a1b73e6f83804949a37": {
EntryPoints: []string{"web"},
Service: "default-http-app-1-my-gateway-web-330d644a7f2079e8f454-wrr",
Rule: "Host(`foo.com`) && PathPrefix(`/bar`) && Headers(`my-header`,`foo`) && Headers(`my-header2`,`bar`)",
Service: "default-http-app-1-my-gateway-web-4a1b73e6f83804949a37-wrr",
Rule: "Host(`foo.com`) && PathPrefix(`/bar`) && Header(`my-header`,`foo`) && Header(`my-header2`,`bar`)",
RuleSyntax: "v3",
},
"default-http-app-1-my-gateway-web-fe80e69a38713941ea22": {
"default-http-app-1-my-gateway-web-aaba0f24fd26e1ca2276": {
EntryPoints: []string{"web"},
Service: "default-http-app-1-my-gateway-web-fe80e69a38713941ea22-wrr",
Rule: "Host(`foo.com`) && Path(`/bar`) && Headers(`my-header`,`bar`)",
Service: "default-http-app-1-my-gateway-web-aaba0f24fd26e1ca2276-wrr",
Rule: "Host(`foo.com`) && Path(`/bar`) && Header(`my-header`,`bar`)",
RuleSyntax: "v3",
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"default-http-app-1-my-gateway-web-330d644a7f2079e8f454-wrr": {
"default-http-app-1-my-gateway-web-4a1b73e6f83804949a37-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
@ -1316,7 +1332,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
},
},
},
"default-http-app-1-my-gateway-web-fe80e69a38713941ea22-wrr": {
"default-http-app-1-my-gateway-web-aaba0f24fd26e1ca2276-wrr": {
Weighted: &dynamic.WeightedRoundRobin{
Services: []dynamic.WRRService{
{
@ -1371,6 +1387,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
EntryPoints: []string{"web"},
Service: "default-http-app-default-my-gateway-web-efde1997778109a1f6eb-wrr",
Rule: "Host(`foo.com`) && Path(`/foo`)",
RuleSyntax: "v3",
},
},
Middlewares: map[string]*dynamic.Middleware{},
@ -1430,11 +1447,13 @@ func TestLoadHTTPRoutes(t *testing.T) {
EntryPoints: []string{"web"},
Service: "default-http-app-default-my-gateway-web-efde1997778109a1f6eb-wrr",
Rule: "Host(`foo.com`) && Path(`/foo`)",
RuleSyntax: "v3",
},
"bar-http-app-bar-my-gateway-web-66f5c78d03d948e36597": {
EntryPoints: []string{"web"},
Service: "bar-http-app-bar-my-gateway-web-66f5c78d03d948e36597-wrr",
Rule: "Host(`bar.com`) && Path(`/bar`)",
RuleSyntax: "v3",
},
},
Middlewares: map[string]*dynamic.Middleware{},
@ -1520,6 +1539,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
EntryPoints: []string{"web"},
Service: "bar-http-app-bar-my-gateway-web-66f5c78d03d948e36597-wrr",
Rule: "Host(`bar.com`) && Path(`/bar`)",
RuleSyntax: "v3",
},
},
Middlewares: map[string]*dynamic.Middleware{},
@ -1579,6 +1599,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
EntryPoints: []string{"web"},
Service: "default-http-app-1-my-gateway-web-364ce6ec04c3d49b19c4-wrr",
Rule: "Host(`example.org`) && PathPrefix(`/`)",
RuleSyntax: "v3",
Middlewares: []string{"default-http-app-1-my-gateway-web-364ce6ec04c3d49b19c4-requestredirect-0"},
},
},
@ -1647,6 +1668,7 @@ func TestLoadHTTPRoutes(t *testing.T) {
EntryPoints: []string{"web"},
Service: "default-http-app-1-my-gateway-web-364ce6ec04c3d49b19c4-wrr",
Rule: "Host(`example.org`) && PathPrefix(`/`)",
RuleSyntax: "v3",
Middlewares: []string{"default-http-app-1-my-gateway-web-364ce6ec04c3d49b19c4-requestredirect-0"},
},
},
@ -1912,6 +1934,7 @@ func TestLoadTCPRoutes(t *testing.T) {
EntryPoints: []string{"tcp"},
Service: "default-tcp-app-1-my-tcp-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
@ -1969,11 +1992,13 @@ func TestLoadTCPRoutes(t *testing.T) {
EntryPoints: []string{"tcp-1"},
Service: "default-tcp-app-1-my-tcp-gateway-tcp-1-e3b0c44298fc1c149afb-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
},
"default-tcp-app-2-my-tcp-gateway-tcp-2-e3b0c44298fc1c149afb": {
EntryPoints: []string{"tcp-2"},
Service: "default-tcp-app-2-my-tcp-gateway-tcp-2-e3b0c44298fc1c149afb-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
@ -2053,6 +2078,7 @@ func TestLoadTCPRoutes(t *testing.T) {
EntryPoints: []string{"tcp-1"},
Service: "default-tcp-app-my-tcp-gateway-tcp-1-e3b0c44298fc1c149afb-wrr",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
@ -2144,6 +2170,7 @@ func TestLoadTCPRoutes(t *testing.T) {
EntryPoints: []string{"tcp"},
Service: "default-tcp-app-1-my-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
@ -2203,6 +2230,7 @@ func TestLoadTCPRoutes(t *testing.T) {
EntryPoints: []string{"tls"},
Service: "default-tcp-app-1-my-gateway-tls-e3b0c44298fc1c149afb-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{},
},
},
@ -2266,6 +2294,7 @@ func TestLoadTCPRoutes(t *testing.T) {
EntryPoints: []string{"tcp"},
Service: "default-tcp-app-default-my-tcp-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
@ -2321,11 +2350,13 @@ func TestLoadTCPRoutes(t *testing.T) {
EntryPoints: []string{"tcp"},
Service: "default-tcp-app-default-my-tcp-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
},
"bar-tcp-app-bar-my-tcp-gateway-tcp-e3b0c44298fc1c149afb": {
EntryPoints: []string{"tcp"},
Service: "bar-tcp-app-bar-my-tcp-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
@ -2403,6 +2434,7 @@ func TestLoadTCPRoutes(t *testing.T) {
EntryPoints: []string{"tcp"},
Service: "bar-tcp-app-bar-my-tcp-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
@ -2696,6 +2728,7 @@ func TestLoadTLSRoutes(t *testing.T) {
EntryPoints: []string{"tcp"},
Service: "default-tcp-app-1-my-tls-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{},
},
},
@ -2761,6 +2794,7 @@ func TestLoadTLSRoutes(t *testing.T) {
EntryPoints: []string{"tcp"},
Service: "default-tcp-app-1-my-tls-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
@ -2819,6 +2853,7 @@ func TestLoadTLSRoutes(t *testing.T) {
EntryPoints: []string{"tcp"},
Service: "default-tls-app-1-my-tls-gateway-tcp-f0dd0dd89f82eae1c270-wrr-0",
Rule: "HostSNI(`foo.example.com`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
@ -2878,12 +2913,14 @@ func TestLoadTLSRoutes(t *testing.T) {
EntryPoints: []string{"tls"},
Service: "default-tcp-app-1-my-tls-gateway-tls-e3b0c44298fc1c149afb-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"default-tls-app-1-my-tls-gateway-tcp-673acf455cb2dab0b43a": {
EntryPoints: []string{"tcp"},
Service: "default-tls-app-1-my-tls-gateway-tcp-673acf455cb2dab0b43a-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
@ -2973,6 +3010,7 @@ func TestLoadTLSRoutes(t *testing.T) {
EntryPoints: []string{"tls"},
Service: "default-tcp-app-1-my-gateway-tls-e3b0c44298fc1c149afb-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{},
},
},
@ -3042,6 +3080,7 @@ func TestLoadTLSRoutes(t *testing.T) {
EntryPoints: []string{"tls"},
Service: "default-tls-app-1-my-gateway-tls-f0dd0dd89f82eae1c270-wrr-0",
Rule: "HostSNI(`foo.example.com`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
@ -3100,6 +3139,7 @@ func TestLoadTLSRoutes(t *testing.T) {
EntryPoints: []string{"tls"},
Service: "default-tls-app-1-my-gateway-tls-f0dd0dd89f82eae1c270-wrr-0",
Rule: "HostSNI(`foo.example.com`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
@ -3158,6 +3198,7 @@ func TestLoadTLSRoutes(t *testing.T) {
EntryPoints: []string{"tls"},
Service: "default-tls-app-1-my-gateway-tls-f0dd0dd89f82eae1c270-wrr-0",
Rule: "HostSNI(`foo.example.com`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
@ -3216,6 +3257,7 @@ func TestLoadTLSRoutes(t *testing.T) {
EntryPoints: []string{"tls"},
Service: "default-tls-app-1-my-gateway-tls-d5342d75658583f03593-wrr-0",
Rule: "HostSNI(`foo.example.com`) || HostSNI(`bar.example.com`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
@ -3274,6 +3316,7 @@ func TestLoadTLSRoutes(t *testing.T) {
EntryPoints: []string{"tls"},
Service: "default-tls-app-default-my-gateway-tls-06ae57dcf13ab4c60ee5-wrr-0",
Rule: "HostSNI(`foo.default`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
@ -3332,6 +3375,7 @@ func TestLoadTLSRoutes(t *testing.T) {
EntryPoints: []string{"tls"},
Service: "default-tls-app-default-my-gateway-tls-06ae57dcf13ab4c60ee5-wrr-0",
Rule: "HostSNI(`foo.default`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
@ -3340,6 +3384,7 @@ func TestLoadTLSRoutes(t *testing.T) {
EntryPoints: []string{"tls"},
Service: "bar-tls-app-bar-my-gateway-tls-2279fe75c5156dc5eb26-wrr-0",
Rule: "HostSNI(`foo.bar`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
@ -3420,6 +3465,7 @@ func TestLoadTLSRoutes(t *testing.T) {
EntryPoints: []string{"tls"},
Service: "bar-tls-app-bar-my-gateway-tls-2279fe75c5156dc5eb26-wrr-0",
Rule: "HostSNI(`foo.bar`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
@ -3478,6 +3524,7 @@ func TestLoadTLSRoutes(t *testing.T) {
EntryPoints: []string{"tcp-1"},
Service: "default-tls-app-my-gateway-tcp-1-673acf455cb2dab0b43a-wrr",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
@ -3702,17 +3749,20 @@ func TestLoadMixedRoutes(t *testing.T) {
EntryPoints: []string{"tcp"},
Service: "default-tcp-app-1-my-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
},
"default-tcp-app-1-my-gateway-tls-1-e3b0c44298fc1c149afb": {
EntryPoints: []string{"tls-1"},
Service: "default-tcp-app-1-my-gateway-tls-1-e3b0c44298fc1c149afb-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"default-tls-app-1-my-gateway-tls-2-59130f7db6718b7700c1": {
EntryPoints: []string{"tls-2"},
Service: "default-tls-app-1-my-gateway-tls-2-59130f7db6718b7700c1-wrr-0",
Rule: "HostSNI(`pass.tls.foo.example.com`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
@ -3771,11 +3821,13 @@ func TestLoadMixedRoutes(t *testing.T) {
EntryPoints: []string{"web"},
Service: "default-http-app-1-my-gateway-web-a431b128267aabc954fd-wrr",
Rule: "PathPrefix(`/`)",
RuleSyntax: "v3",
},
"default-http-app-1-my-gateway-websecure-a431b128267aabc954fd": {
EntryPoints: []string{"websecure"},
Service: "default-http-app-1-my-gateway-websecure-a431b128267aabc954fd-wrr",
Rule: "PathPrefix(`/`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTLSConfig{},
},
},
@ -3881,17 +3933,20 @@ func TestLoadMixedRoutes(t *testing.T) {
EntryPoints: []string{"tcp"},
Service: "default-tcp-app-default-my-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
},
"default-tcp-app-default-my-gateway-tls-1-e3b0c44298fc1c149afb": {
EntryPoints: []string{"tls-1"},
Service: "default-tcp-app-default-my-gateway-tls-1-e3b0c44298fc1c149afb-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"default-tls-app-default-my-gateway-tls-2-59130f7db6718b7700c1": {
EntryPoints: []string{"tls-2"},
Service: "default-tls-app-default-my-gateway-tls-2-59130f7db6718b7700c1-wrr-0",
Rule: "HostSNI(`pass.tls.foo.example.com`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
@ -3950,11 +4005,13 @@ func TestLoadMixedRoutes(t *testing.T) {
EntryPoints: []string{"web"},
Service: "default-http-app-default-my-gateway-web-a431b128267aabc954fd-wrr",
Rule: "PathPrefix(`/`)",
RuleSyntax: "v3",
},
"default-http-app-default-my-gateway-websecure-a431b128267aabc954fd": {
EntryPoints: []string{"websecure"},
Service: "default-http-app-default-my-gateway-websecure-a431b128267aabc954fd-wrr",
Rule: "PathPrefix(`/`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTLSConfig{},
},
},
@ -4032,17 +4089,20 @@ func TestLoadMixedRoutes(t *testing.T) {
EntryPoints: []string{"tcp"},
Service: "default-tcp-app-default-my-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
},
"default-tcp-app-default-my-gateway-tls-1-e3b0c44298fc1c149afb": {
EntryPoints: []string{"tls-1"},
Service: "default-tcp-app-default-my-gateway-tls-1-e3b0c44298fc1c149afb-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"default-tls-app-default-my-gateway-tls-2-59130f7db6718b7700c1": {
EntryPoints: []string{"tls-2"},
Service: "default-tls-app-default-my-gateway-tls-2-59130f7db6718b7700c1-wrr-0",
Rule: "HostSNI(`pass.tls.foo.example.com`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
@ -4051,11 +4111,13 @@ func TestLoadMixedRoutes(t *testing.T) {
EntryPoints: []string{"tcp"},
Service: "bar-tcp-app-bar-my-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
},
"bar-tcp-app-bar-my-gateway-tls-1-e3b0c44298fc1c149afb": {
EntryPoints: []string{"tls-1"},
Service: "bar-tcp-app-bar-my-gateway-tls-1-e3b0c44298fc1c149afb-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{},
},
},
@ -4144,22 +4206,26 @@ func TestLoadMixedRoutes(t *testing.T) {
EntryPoints: []string{"web"},
Service: "default-http-app-default-my-gateway-web-a431b128267aabc954fd-wrr",
Rule: "PathPrefix(`/`)",
RuleSyntax: "v3",
},
"default-http-app-default-my-gateway-websecure-a431b128267aabc954fd": {
EntryPoints: []string{"websecure"},
Service: "default-http-app-default-my-gateway-websecure-a431b128267aabc954fd-wrr",
Rule: "PathPrefix(`/`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTLSConfig{},
},
"bar-http-app-bar-my-gateway-web-a431b128267aabc954fd": {
EntryPoints: []string{"web"},
Service: "bar-http-app-bar-my-gateway-web-a431b128267aabc954fd-wrr",
Rule: "PathPrefix(`/`)",
RuleSyntax: "v3",
},
"bar-http-app-bar-my-gateway-websecure-a431b128267aabc954fd": {
EntryPoints: []string{"websecure"},
Service: "bar-http-app-bar-my-gateway-websecure-a431b128267aabc954fd-wrr",
Rule: "PathPrefix(`/`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTLSConfig{},
},
},
@ -4273,17 +4339,20 @@ func TestLoadMixedRoutes(t *testing.T) {
EntryPoints: []string{"tcp"},
Service: "bar-tcp-app-bar-my-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
},
"bar-tcp-app-bar-my-gateway-tls-1-e3b0c44298fc1c149afb": {
EntryPoints: []string{"tls-1"},
Service: "bar-tcp-app-bar-my-gateway-tls-1-e3b0c44298fc1c149afb-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{},
},
"bar-tls-app-bar-my-gateway-tls-2-59130f7db6718b7700c1": {
EntryPoints: []string{"tls-2"},
Service: "bar-tls-app-bar-my-gateway-tls-2-59130f7db6718b7700c1-wrr-0",
Rule: "HostSNI(`pass.tls.foo.example.com`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{
Passthrough: true,
},
@ -4342,11 +4411,13 @@ func TestLoadMixedRoutes(t *testing.T) {
EntryPoints: []string{"web"},
Service: "bar-http-app-bar-my-gateway-web-a431b128267aabc954fd-wrr",
Rule: "PathPrefix(`/`)",
RuleSyntax: "v3",
},
"bar-http-app-bar-my-gateway-websecure-a431b128267aabc954fd": {
EntryPoints: []string{"websecure"},
Service: "bar-http-app-bar-my-gateway-websecure-a431b128267aabc954fd-wrr",
Rule: "PathPrefix(`/`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTLSConfig{},
},
},
@ -4423,11 +4494,13 @@ func TestLoadMixedRoutes(t *testing.T) {
EntryPoints: []string{"tcp"},
Service: "default-tcp-app-default-my-gateway-tcp-e3b0c44298fc1c149afb-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
},
"default-tcp-app-default-my-gateway-tls-e3b0c44298fc1c149afb": {
EntryPoints: []string{"tls"},
Service: "default-tcp-app-default-my-gateway-tls-e3b0c44298fc1c149afb-wrr-0",
Rule: "HostSNI(`*`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTCPTLSConfig{},
},
},
@ -4474,11 +4547,13 @@ func TestLoadMixedRoutes(t *testing.T) {
EntryPoints: []string{"web"},
Service: "default-http-app-default-my-gateway-web-a431b128267aabc954fd-wrr",
Rule: "PathPrefix(`/`)",
RuleSyntax: "v3",
},
"default-http-app-default-my-gateway-websecure-a431b128267aabc954fd": {
EntryPoints: []string{"websecure"},
Service: "default-http-app-default-my-gateway-websecure-a431b128267aabc954fd-wrr",
Rule: "PathPrefix(`/`)",
RuleSyntax: "v3",
TLS: &dynamic.RouterTLSConfig{},
},
},
@ -4807,7 +4882,7 @@ func Test_extractRule(t *testing.T) {
},
},
},
expectedRule: "Path(`/foo/`) || Headers(`my-header`,`foo`)",
expectedRule: "Path(`/foo/`) || Header(`my-header`,`foo`)",
},
{
desc: "Path && Header rules",
@ -4828,7 +4903,7 @@ func Test_extractRule(t *testing.T) {
},
},
},
expectedRule: "Path(`/foo/`) && Headers(`my-header`,`foo`)",
expectedRule: "Path(`/foo/`) && Header(`my-header`,`foo`)",
},
{
desc: "Host && Path && Header rules",
@ -4850,7 +4925,7 @@ func Test_extractRule(t *testing.T) {
},
},
},
expectedRule: "Host(`foo.com`) && Path(`/foo/`) && Headers(`my-header`,`foo`)",
expectedRule: "Host(`foo.com`) && Path(`/foo/`) && Header(`my-header`,`foo`)",
},
{
desc: "Host && (Path || Header) rules",
@ -4874,7 +4949,7 @@ func Test_extractRule(t *testing.T) {
},
},
},
expectedRule: "Host(`foo.com`) && (Path(`/foo/`) || Headers(`my-header`,`foo`))",
expectedRule: "Host(`foo.com`) && (Path(`/foo/`) || Header(`my-header`,`foo`))",
},
}

View file

@ -22,6 +22,11 @@
"priority": 2147483645
}
},
"services": {
"api": {},
"dashboard": {},
"noop": {}
},
"middlewares": {
"dashboard_redirect": {
"redirectRegex": {
@ -38,11 +43,6 @@
]
}
}
},
"services": {
"api": {},
"dashboard": {},
"noop": {}
}
},
"tcp": {},

View file

@ -54,6 +54,14 @@
"priority": 2147483647
}
},
"services": {
"api": {},
"dashboard": {},
"noop": {},
"ping": {},
"prometheus": {},
"rest": {}
},
"middlewares": {
"dashboard_redirect": {
"redirectRegex": {
@ -70,14 +78,6 @@
]
}
}
},
"services": {
"api": {},
"dashboard": {},
"noop": {},
"ping": {},
"prometheus": {},
"rest": {}
}
},
"tcp": {},

View file

@ -12,6 +12,9 @@
"rule": "HostRegexp(`^.+$`)"
}
},
"services": {
"noop": {}
},
"middlewares": {
"redirect-web-to-websecure": {
"redirectScheme": {
@ -20,11 +23,8 @@
"permanent": true
}
}
},
"services": {
"noop": {}
}
},
"tcp": {},
"tls": {}
}
}

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